顯示具有 View 標籤的文章。 顯示所有文章
顯示具有 View 標籤的文章。 顯示所有文章

2011年12月29日 星期四

Android - 按鈕平均分配寬度 (rid of button padding)

如果想在畫面的「某一列」,放3個按鈕,而這3個按鈕要平均分配寬度,程式如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="
horizontal" android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <Button android:id="@+id/button1" android:layout_height="wrap_content" android:text="11111"
            android:layout_width="fill_parent" android:layout_weight="1"/>
           
    <Button android:id="@+id/button2" android:layout_height="wrap_content" android:text="2222222"
            android:layout_width="fill_parent" android:layout_weight="1"/>
           
    <Button android:id="@+id/button3" android:layout_height="wrap_content" android:text="3333333333"
            android:layout_width="fill_parent" android:layout_weight="1"/>

</LinearLayout>
如果不要按鈕之間有padding,有幾種作法

(1) 把padding設負值

(2) 給Button 設android:background="#000cab" 隨便給一個顏色,但是高度會變小,要自己設一下

(3) 自己做一張.9.png圖,給Button 設android:background="你的圖"

Android - 動畫TranslateAnimation 和 AnimationSet 的使用

下面範例是利用 TranslateAnimationAnimationSet 來製造ImageView上下移動的動畫。主要觀念為
(1)使用TranslateAnimation設定動畫時間、移動位置、重覆…等效果
(2)AnimationSet可以把多個動畫集合後,一次交付給某元件執行。本例就是下、上、下、上等四個動畫

程式如下:
public class Main extends Activity {
    ImageView img;
    AnimationSet animSet;
    TranslateAnimation anim;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        img = (ImageView) findViewById(R.id.img);
        img.setImageResource(R.drawable.icon);
        animSet = new AnimationSet(true);

        int[] step = {100,-100,100,-100 };
        for (int index = 0; index < 4; index++) {
            anim = new TranslateAnimation(0, 0, 0, step[index]);

            //動畫週期為0.5秒
            anim.setDuration(500);

            //避免元件又回到初始位置
            anim.setFillAfter(true);

            //因為這裡用了四個動畫,所以必須為每個動畫設置初始時間
            //0秒、0.5秒、1秒、1.5秒 為四個動畫初始執行的時間
            anim.setStartOffset(500 * index);

            // 把動畫集合起來
            animSet.addAnimation(anim);
        }

        // img元件開始執行動畫
        img.startAnimation(animSet);
    }
}

Android - 產生scroll bar讓使用者往下拉

我們可以把所有的元件放到ScrollView裡面,這樣就可以在畫面產生scroll bar讓使用者往下拉。但是,ScrollView裡面只可以作用於一個元件,最常見的做法就是,在ScrollView裡面放一個LinearLayout,再把所有的元件放在這個LinearLayout裡面就可以了。

xml範例如下:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

   <ScrollView   
             android:layout_width="fill_parent"   
             android:layout_height="wrap_content" > 

   
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="This is a TextView" />

    <CheckBox
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:text="This is a CheckBox" />

    </LinearLayout>

  </ScrollView>

</LinearLayout>

Android - View常見的attribute

layout_marginTop = "10px" 本元件和上元件距離10px
layout_marginBottom = "10px" 本元件和下元件距離10px
layout_marginLeft = "10px" 本元件和左元件距離10px
layout_marginRight = "10px" 本元件和右元件距離10px

singleLine="true"  文字內容最多只有一行,常用於TextView元件

layout_above = "@id/xxxxx" 本元件放在xxxxx元件的上方
layout_below = "@id/xxxxx" 本元件放在xxxxx元件的下方
layout_toLeftOf = "@id/xxxxx" 本元件放在xxxxx元件的左方
layout_toRightOf = "@id/xxxxx" 本元件放在xxxxx元件的右方

layout_alignTop = "@id/xxxxx" 本元件頂部和xxxxx元件頂部切齊
layout_alignBottom = "@id/xxxxx" 本元件底部和xxxxx元件底部切齊
layout_alignLeft = "@id/xxxxx" 本元件左側和xxxxx元件左側切齊
layout_alignRight = "@id/xxxxx" 本元件右側和xxxxx元件右側切齊

layout_alignParentTop = "true" 本元件頂部和外層父元件頂部切齊
layout_alignParentBottom = "true" 本元件底部和外層父元件底部切齊
layout_alignParentLeft = "true" 本元件左側和外層父元件左側切齊
layout_alignParentRight = "true" 本元件右側和外層父元右側切齊

layout_centerHorizontal = "true" 本元件位於水平方向的中央
layout_centerVertical = "true" 本元件位於垂直方向的中央
layout_centerInParent = "true" 本元件位於外層父元件內,水平和垂直方向的中央

background="@drawable/background_pic" 指定background_pic為背景圖片
background="#000000" 指定背景為黑色

visibility="gone" 該元件不可視,而且不佔用面積

Android - Drawable 轉成 Bitmap

在Drawable資料夾中有一檔案為download.png,如果要轉成 Bitmap格式,可用程式:

    Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.download);

2011年12月28日 星期三

Android - 偵測螢幕方向 (ORIENTATION)

如果你想偵測目前手機的方向是直式還是橫式,程式如下:

        if (getResources().getConfiguration().orientation ==
            Configuration.ORIENTATION_PORTRAIT){
            //直式
        }
        if (getResources().getConfiguration().orientation ==
            Configuration.ORIENTATION_LANDSCAPE){
            //橫式
        }

Android - 取得螢幕大小(screen size)

在Activity底下,使用下列程式碼

Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();

Android - 解析度 - resolution dpi分別

在Android1.5(含)之前drawable資料夾只有一個,現在分成3個

(1) drawable-ldpi,裡面建議存放低解析的圖片,如QVGA (240x320)

(2) drawable-mdpi,裡面建議存放中等解析的圖片,如HVGA (320x480)
 
(3) drawable-hdpi,裡面建議存放高等解析的圖片如WVGA (480x800),FWVGA (480x854)

(4) drawable-xdpi,裡面存放更大的尺寸(for tablet)

目前市面上典型的分類是
  • QVGA (240x320, low density, small screen)
  • WQVGA400 (240x400, low density, normal screen)
  • WQVGA432 (240x432, low density, normal screen)
  • HVGA (320x480, medium density, normal screen)
  • WVGA800 (480x800, high density, normal screen)
  • WVGA854 (480x854 high density, normal screen)
  • WXGA720 (1280x720, extra-high density, normal screen)
  • WSVGA (1024x600, medium density, large screen)
  • WXGA (1280x800, medium density, xlarge screen)



系統會根據不同Device的解析度到這幾個資料夾裡面去找合適的圖片。


下圖是官網公佈的資訊:
http://developer.android.com/guide/practices/screens_support.html


Table 3. Various screen configurations available from emulator skins in the Android SDK (indicated in bold) and other representative resolutions.


Low density (120), ldpi Medium density (160), mdpi High density (240), hdpi Extra high density (320), xhdpi
Small screen QVGA (240x320)

480x640

Normal screen WQVGA400 (240x400) WQVGA432 (240x432) HVGA (320x480) WVGA800 (480x800) WVGA854 (480x854)
600x1024
640x960
Large screen WVGA800** (480x800) WVGA854** (480x854) WVGA800* (480x800) WVGA854* (480x854)
600x1024




Extra Large screen 1024x600 WXGA (1280x800)
1024x768
1280x768
1536x1152
1920x1152
1920x1200
2048x1536
2560x1536
2560x1600


Android - Runtime Changes (2) - Handling the Configuration

相較於 onRetainNonConfigurationInstance() 和 getLastNonConfigurationInstance()來控制Activity資料的保存,另一個選擇方式是改寫 AndroidManifest.xml裡面Activity的Configuration屬性。正常情況下,執行Activity然後旋轉方向後的 Life Cycle如下:
onCreate()
onStart()
onResume()

onPause()
onStop()
onDestroy()
onCreate()
onStart()
onResume()

然後我們改變Configuration後如下:
        <activity android:name=".Main"
                  android:configChanges="orientation|keyboardHidden"
                  android:label="@string/app_name">

如此一來,執行程式後,再把畫面旋轉或是開啟硬體鍵盤後的Life Cycle如下:

onCreate()
onStart()
onResume()

也就是不會消滅後再重新啟動。如果想要在旋轉畫面後做一些特別的事情,可以@Override
onConfigurationChanged() 如下:

public class Main extends Activity {
    /** Called when the activity is first created. */
    private final static String TAG = "TestLife";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate()");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
   
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        // Checks the orientation of the screen
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            Log.d(TAG, "landscape");         
        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
            Log.d(TAG, "portrait");  
            Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
        }
        // Checks whether a hardware keyboard is available
        if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
            Log.d(TAG, "keyboard visible"); 
        } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
            Log.d(TAG, "keyboard hidden");    
        }
    }  
    
    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG, "onStart()");
    }
   
    @Override
    public void onRestart() {
        super.onRestart();
        Log.d(TAG, "onRestart()");
    }
   
    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "onResume()");
    }
   
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause()");
    }
   
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop()");
    }
   
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy()");
    }
}

其它需要注意的事項請參考官網說明,另外Configuration的其他選項還有這些可以使用

Android - Runtime Changes (1) - Retaining an Object (getLastNonConfigurationInstance, onRetainNonConfigurationInstance)

當你旋轉手機由直立改為橫立時,畫面上的Activity的life cycle會進行下列動作
onPause
onStop
onDestroy
onCreate
onStart
onResume

換 言之Activity會被消滅然後再重新產生一次,這時候有一個問題就是原本畫面上資料會消失,或者是在onCreate的動作會重新再做一次。這種問題 應該被避免。假設onCreate會sendBroadcast去啟動某件事情,則你不停的旋轉手機就會不停的發送sendBroadcast。或者你原 本讓使用者在畫面上輸入一些資料,然後進行一些運算,如果使用者不小心旋轉畫面後,運算結果都消失了。這些情況應該都要被避免,官方說法很清楚,這邊簡單示範程式:

(1) 利用一個Class來記錄資料
(2) @Override
      public Object onRetainNonConfigurationInstance()
(3) 在onCreate()裡面利用getLastNonConfigurationInstance()把舊資料取出來

public class Main extends Activity {
   
    private class MyDataClass{
        int mIntValue;
        boolean mBoolValue;
        String mStringValue;
    }
   
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d("Test","onCreate()");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        MyDataClass data = (MyDataClass) getLastNonConfigurationInstance();
        if (data == null) {
            data = loadMyData();
        }  
       
        Log.d("Test","The mIntValue = " + data.mIntValue);
        Log.d("Test","The mBoolValue = " + data.mBoolValue);
        Log.d("Test","The mStringValue = " + data.mStringValue);
       
    }
   
    @Override
    public Object onRetainNonConfigurationInstance() {
        Log.d("Test","onRetainNonConfigurationInstance()");
        final MyDataClass data = collectMyLoadedData();
        return data;
    }
   
    private MyDataClass collectMyLoadedData(){
        Log.d("Test","collectMyLoadedData()");
        MyDataClass returnData = new MyDataClass();
        returnData.mIntValue = 10;
        returnData.mBoolValue = true;
        returnData.mStringValue = "loaded data";
       
        return returnData;
    }
   
    private MyDataClass loadMyData(){
        Log.d("Test","loadMyData()");
        MyDataClass returnData = new MyDataClass();
        returnData.mIntValue = 0;
        returnData.mBoolValue = false;
        returnData.mStringValue = "default data";
       
        return returnData;      
    } 
}

先執行程式,Log如下
DEBUG/Test(11361): onCreate()
DEBUG/Test(11361): loadMyData()
DEBUG/Test(11361): The mIntValue = 0
DEBUG/Test(11361): The mBoolValue = false
DEBUG/Test(11361): The mStringValue = default data

然後再旋轉手機後,Log如下
DEBUG/Test(11361): onRetainNonConfigurationInstance()
DEBUG/Test(11361): collectMyLoadedData()
DEBUG/Test(11361): onCreate()
DEBUG/Test(11361): The mIntValue = 10
DEBUG/Test(11361): The mBoolValue = true
DEBUG/Test(11361): The mStringValue = loaded data

可以看到旋轉完手機後,資料和第一次執行的值不同。