2012年9月29日 星期六

ExpandableListView 裡面的groupIndicator

SDK 4.0裡面 ExpandableListView的 groupIndicator有點醜 (左邊收合與展開的表現圖示),想要把他換掉,有一點教學的需求就順便把流程記下來

預設值是@android:drawable/expander_group_holo_light
換掉groupIndicator的方法很簡單,就在xml檔案裡面修改Group Indicator屬性即可

android:groupIndicator="stateful Drawable "

這裡需要一個drawable的.xml檔案,可以直接拷貝expander_group_holo_light.xml來改,如果不曉得這個檔案在哪裡,用最笨的方法在sdk了目錄裡面用Windows的檔案搜尋就可以找到。

檔案的內容如下
Source file
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 3     <item
 4         android:state_expanded="true"
 5         android:drawable="@drawable/expander_open" />
 6     <item
 7         android:drawable="@drawable/expander_close" />
 8 </selector>

這時候需要準備兩張圖片expander_open.9.png 和 expander_close.9.png。這份文件的重點有一部分在9png檔案,如果檔案不是用9png檔會被系統自動縮放且會失去原本的長寬比例,反而讓畫面變醜。

2012年8月15日 星期三

實現Activity間的轉換動畫

Android 2.0以後提供了一個簡單的方式讓兩個Activity之間的切換能夠生動一點。利用:

public void overridePendingTransition (int enterAnim, int exitAnim)



這個函式只要放在startActivity()或是finish()之後就可以。它主要是利用Tween animation來完成頁面的轉跳。

enterAnim為將要進入的頁面的animation。exitAnim為要轉出的頁面的animation。這兩個animation可以寫在res/anim目錄下,以下是我測試過還不錯的動畫

enterAnim: in_from_right.xml

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_interpolator">
     <translate
        android:duration="300"
        android:fromXDelta="100%p"
        android:toXDelta="0%p" />
</set>


exitAnim: out_to_left.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_interpolator" >

    <translate
        android:duration="300"
        android:fromXDelta="0%p"
        android:toXDelta="-100%p"
        android:startOffset="100" />
</set>

java code:

...
startActivity(intent);
overridePendingTransition(R.anim.in_from_right, R.anim.out_to_left);
...


補充:Fragment間的切換可以利用下面兩種方式來處理
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

或是
ft.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);


2012年6月27日 星期三

這個很好玩 - MIT App Inventor

原本由Google lab所開發的App Inventor已經交給MIT行動學習中心維護,現改名為MIT App Inventor,很好玩的一個程式開發概念。

還有一個中文學習網http://www.appinventor.tw/

2012年5月13日 星期日

了解Android resource檔案 (三)

這份文件說明resource的命名,辨識以及取用所需要注意的一些小細節
  • @+id/text1
<TextView
        android:id="@+id/text1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

1. id並不是一定需要的,除非在java程式當中需要引用此控制項
2. "@+id/" 表示在這個activity當中如果沒有存在此id則新建一個。"@id/"則是表示使用已經定義過的id
3. id是以activity為範圍,在一個activity中id不可以重覆,但在不同activity當中可以容許重覆的id
4. 產生的id會在R.java類別當中產生一個public static final int 的constant來唯一的指名。不管你的應用程是裡面有多少個activity,使用了多少個同名的id,在R.jav當中所有同名的id會共用同一個constant。
    R.java範例
package xxx.xxxxx.xxx;


public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class id {
        public static final int text1=0x7f050000;
    }
    public static final class layout {
        public static final int main=0x7f030000;
    }
    public static final class string {
        public static final int app_name=0x7f040001;
        public static final int hello=0x7f040000;
    }
}


  • 放在res/raw裡面的xml檔案並不會被編輯成為binary file,也不會被放入.apk當中
  • String Arrays
<?xml version="1.0" encoding="utf-8"?>
<!-- This file is /res/values/arrays.xml -->
<resources>
    <string-array name="week">
        <item >星期日</item>
        <item >星期一</item>
        <item >星期二</item>
        <item >星期三</item>
        <item >星期四</item>
        <item >星期五</item>
        <item >星期六</item>
    </string-array>   
</resources>







/** .java file for getting String Array from xml */
Resource res = activity-name.getResources();
String[] st = res.getStringArray(R.array.week);


  • Plurals
<?xml version="1.0" encoding="utf-8"?>
<!-- This file is /res/values/students_in_class.xml -->
<resources>
    <plurals name="students_in_class">
        <item quantity = "one">There is 1 student</item>
        <item quantity = "other">There are %d students</item>     </plurals>   
</resources>


/** .java for getting Plurals from xml */
Resource res = activity-name.getResources();
String st1 = res.getQuantityString(R.plurals.students_in_class, 0, 0);
String st2 = res.getQuantityString(R.plurals.students_in_class, 1, 1);
String st3 = res.getQuantityString(R.plurals.students_in_class, 2, 2);
String st4 = res.getQuantityString(R.plurals.students_in_class, 100, 100);


輸出:
There are 0 students
There is 1 student
There are 2 students
There are 100 students


Plurals是為了語系上面有單複數型差異的語言所設計,在中文的用法上較少被使用,不過用它來產生鑲嵌一個整數的字串還是蠻方便的。



  • String
<?xml version="1.0" encoding="utf-8"?>
<!-- This file is /res/values/strings.xml -->
<resources>   
    <string name="SimpleString">Simple String</string>
    <string name="good_example">"This'll work"</string>
    <string name="good_example_2">This\'ll also work</string>
    <string name="bad_example">This doesn't work</string>
    <string name="bad_example_2">XML encodings don&apos;t work</string>
</resources>

Andoird支援部分的java String Format以及部分的HTML格式(ref.http://developer.android.com/guide/topics/resources/string-resource.html)


Formatting strings



<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>


Resources res = getResources();
String text = String.format(res.getString(R.string.welcome_messages), username, mailCount);


Styling with HTML markup



<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="welcome">Welcome to <b>Android</b>!</string>
</resources>

目前支援的tag只有

  • <b> for bold text.
  • <i> for italic text.
  • <u> for underline text.

Note: 使用Eclipse編輯時,需要利用資源編輯視窗來編輯HTML字串,不要直接編寫.xml檔案。編輯器會將上面的標籤再做一次轉換變成

              <string name="welcome">Welcome to &lt;b&gt;Android&lt;/b&gt;!</string>

這樣才能正確顯示。

String htmlString = getApplicationContext().getString(R.string.welcome);
// convert it to a text span so that i can be set in a text view
Spanned textSpan = android.text.Html.fromHtml( htmlString );

其他相關Html格式的運用在http://developer.android.com/reference/android/text/Html.html





2012年4月12日 星期四

了解Android resource檔案 (二)

接續上一個單元了解Android resource檔案 (一)

如果去追一下textAppearanceSmall可以找到

    <style name="TextAppearance.Small">
        <item name="android:textSize">14sp</item>
        <item name="android:textColor">?textColorSecondary</item>
    </style>

這說明textAppearanceSmall這個設定同時設定了字體大小以及顏色。我們有興趣的地方在顏色的設定,所以追蹤一下顏色可以發現

<item name="textColorSecondary">@android:color/secondary_text_dark</item>




以及在....res\color\目錄下的secondary_text_dark.xml檔案





<?xml version="1.0" encoding="utf-8"?>


            .......


<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_disabled"/>
    <item android:state_window_focused="false" android:color="@android:color/dim_foreground_dark"/>
    <item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
    <item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
    <item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
    <item android:state_pressed="true" android:color="@android:color/dim_foreground_dark_inverse"/>
    <item android:state_enabled="false" android:color="@android:color/dim_foreground_dark_disabled"/>
    <item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
</selector>




這份文檔在Android裡面稱做State List,一般用來變換一個繪圖元件在不同狀態下的顯示圖片。


狀態的定義有 (原文照拷比較原汁原味)


android:state_pressed
Boolean. "true" if this item should be used when the object is pressed (such as when a button is touched/clicked); "false" if this item should be used in the default, non-pressed state.
android:state_focused
Boolean. "true" if this item should be used when the object has input focus (such as when the user selects a text input); "false" if this item should be used in the default, non-focused state.
android:state_hovered
Boolean. "true" if this item should be used when the object is being hovered by a cursor; "false" if this item should be used in the default, non-hovered state. Often, this drawable may be the same drawable used for the "focused" state.
Introduced in API level 14.
android:state_selected
Boolean. "true" if this item should be used when the object is the current user selection when navigating with a directional control (such as when navigating through a list with a d-pad); "false" if this item should be used when the object is not selected.
The selected state is used when focus (android:state_focused) is not sufficient (such as when list view has focus and an item within it is selected with a d-pad).
android:state_checkable
Boolean. "true" if this item should be used when the object is checkable; "false" if this item should be used when the object is not checkable. (Only useful if the object can transition between a checkable and non-checkable widget.)
android:state_checked
Boolean. "true" if this item should be used when the object is checked; "false" if it should be used when the object is un-checked.
android:state_enabled
Boolean. "true" if this item should be used when the object is enabled (capable of receiving touch/click events); "false" if it should be used when the object is disabled.
android:state_activated
Boolean. "true" if this item should be used when the object is activated as the persistent selection (such as to "highlight" the previously selected list item in a persistent navigation view); "false" if it should be used when the object is not activated.
Introduced in API level 11.
android:state_window_focused
Boolean. "true" if this item should be used when the application window has focus (the application is in the foreground), "false" if this item should be used when the application window does not have focus (for example, if the notification shade is pulled down or a dialog appears).







回到secondary_text_dark.xml這份文件,每一個<item ... />表示一個狀態,所以在寫的時候需要確認到每一個<item ... />之間是互斥的關係。所以
<item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>


表示當此TextView被按住(state_pressed="true")且此TextView是被除能的(disabled, 看上面的英文會比較理解這在說什麼)則此TextView的顏色變換成dim_foreground_dark_inverse_disabled所定義 (查一下它的顏色代碼是 #80323232)






我們常用State List來變換按鈕的狀態,譬如壓下按鈕然後放開會有一個顏色的變換讓使用者感覺到按鈕確實有被觸碰到。xml範例如下



<!-- filename: buttonbackground.xml  -->


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
   
    <item 
        android:drawable="@drawable/未按下時的圖片.png" 
        android:state_pressed="false" 
        android:state_selected="false"/>
    
    
    <item 
        android:drawable="@drawable/ 按下時的圖片.png 
        android:state_pressed="true"/>
</selector>


然後將Button的視圖的background屬性指向的這xml檔案即可。








2012年4月10日 星期二

了解Android resource檔案 (一)

資源 (Resource) 在Android裡面凡指一切除了程式碼以外的資料。它可以是一個檔案,像是一個mp3音樂檔或是一個xml的版面layout檔;也可以是一個系統參數值像是一個預設的字串,一個定義button按鈕的高度的整數或是一個背景的顏色定義。撰寫程式的時候應該學習將程式碼與資源當作是獨立的文件來處理,這對日後多語系以及多螢幕的支援都有很大的幫助。

這份文件談資源由兩個方向分別討論。第一個以資源的類型來討論,分別討論資源的取用方式。第二個方向則討論系統預設資源的取用以及使用者自定資源的方式。一般來說我們建議盡量使用系統預設的資源,系統沒有的才使用自定資源來輔助,這樣可以加快程式的撰寫以及各平台間的相容性。

 Android常用資源類型:
  • String (字串)
  • Layout (面板配置)
  • String Array (字串陣列)
  • Plurals (一個字串的集合,用來表示數量通常是複數形式)
  • Color (顏色)
  • Dimension (尺寸)
  • Image (圖片)
  • Color-Drawable (一個被著色的方型,由xml寫成)
 Android系統資源存放於<android-sdk-windows>\platforms\<android version>\data\res 目錄下。初學者最先碰到會使用的應該是...\res\values\attrs.xml 檔案裡面的字型設定還有顏色設定。如果利用Eclipse IDE隨便拉一個TextView控制項,它的屬性如下:
    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge" />

這裡表示這個TextView的文字Style以系統內定的?android:attr/textAppearanceLarge來顯示。定義的資料可以在R.attr裡面找到。定義如下:          

public static final int textAppearanceLarge

Since: API Level 1
Text color, typeface, size, and style for "large" text. Defaults to primary text color.
Must be a reference to another resource, in the form "@[+][package:]type:name" or to a theme attribute in the form "?[package:][type:]name".
Constant Value: 16842816 (0x01010040)

如果仔細去追可以在...\res\values\attrs.xml裡面找到
        <!-- Text color, typeface, size, and style for "large" text. Defaults to primary text color. -->
        <attr name="textAppearanceLarge" format="reference" />

一個 <attr>單元有兩屬性:name以及formatname就是這個<attr>的名字,可以讓他在別的地方被參考到。 format表示這個<attr>是符合哪一種格式。formate可用格式有:
    • reference - if it references another resource id (e.g, "@color/my_color", "@layout/my_layout")
    • color
    • boolean
    • dimension
    • float
    • integer
    • string
    • fraction
    • enum - normally implicitly defined
    • flag - normally implicitly defined
舉上面TextView的例子來說,我們可以在...\res\values\attrs.xml裡面找到

<declare-styleable name="ViewGroup_Layout">
                           .....
        <attr name="layout_width" format="dimension">
           
            <enum name="fill_parent" value="-1" />
            
            <enum name="match_parent" value="-1" />
            
            <enum name="wrap_content" value="-2" />
        </attr>
                                 .....
         

這定義"layout_width"是一種"dimension"的屬性,所以在TextView裡面  android:layout_width="wrap_content"  或是 android:layout_width="60dp" 這樣的敘述句就變成有定義。

回到<attr name="textAppearanceLarge" format="reference" />, reference 表示textAppearanceLarge 可以在...\res\values\themes.xml 裡面找到它的定義。
       <style name="Theme">
                    ....
            <item name="textAppearanceLarge">@android:style/TextAppearance.Large</item>
                    ....
        
繼續往下追可以在...\res\values\style.xml裡面找到
      <style name="TextAppearance.Large">
        <item name="android:textSize">22sp</item>
    </style>

在Android程式設計個過程,我們提供程式碼追蹤的過程但不建議更改這些設定值。這裡我們也了解到textAppearanceLarge其實是一個Style設定,所以程式碼寫成
      
android:textAppearance="?android:attr/textAppearanceLarge"  或是

      sytle="?android:attr/textAppearanceLarge" 實際上是一樣的。

 Android 常用系統預設文字大小以及顏色
  • 文字大小  android:textAppearance="?android:attr/textAppearanceLarge"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textAppearance="?android:attr/textAppearanceSmall"
  • 字體顏色 android:textColor="?android:attr/textColorPrimary"
    android:textColor="?android:attr/textColorSecondary"
    android:textColor="?android:attr/textColorTertiary"
    android:textColor="?android:attr/textColorPrimaryInverse"
    android:textColor="?android:attr/textColorSecondaryInverse"

2012年4月6日 星期五

Android上日期與時間的處理 - Calendar

在Android系統裡面可以使用到關於時間的類別有三種


主要使用Calendar類別,Date類別在Android中不建議使用然而我們會用到java.text.SimpleDateFormat這個類別來處理DateString間的轉換所以Data類別常用來當作Calendar類別和String類別轉換的中介類別。

Timestamp類別在Android當中有兩個同名類別,分別是java.sql.Timestamp以及java.security.Timestamp。使用的時候要注意不要import錯誤的package。Timestamp類別使用在SQLite資料庫中。SQLite對於記錄日期時間的方式有兩種欄位宣告的程式撰寫方式,一種是將欄位宣告成為long,直接記錄Calendar.getTimeInMillis()後的數值;另一種則是宣告成為Timestamp,預設值是使用yyyy-MM-dd hh:mm:ss.SSS的字串型式儲存。

整理一些常用的寫法如下:

  • 建立一個新的Calendar物件,預設初值為系統現在時間
    Calendar c = Calendar.getInstance();

    這是一個隱式的敘述,如果追蹤程式碼可以發現它創立了一個新的物件而不是和別人共用物件。Calendar所建立的時間是以197011日開始計算到目前為止的格林威治標準時間的milliseconds,所以不管在哪一個時區所儲存的時間都是一樣的。如果要顯示出不同時區和不同地區的時間則需要在輸出的時候由SimpleDateFormat擔任轉換的工作。
  • 設定Calendar物件到特定的時間
    Calendar c = Calendar.getInstance();
    c.set(2012, 11, 12);        
    //設定日期為20121212日,
                                 //Calendar
    類別中月份的編號是由0~11

    c.set(calendar.YEAR, 2013);                        //將年改成2013 
    c.set(Calendar.MONTH, Calendar.JANUARY);   //
    將月份改成1
    c.set(Calendar.DAY_OF_MONTH, 31);            //
    將日改成31

    c.set(Calendar.HOUR_OF_DAY, 18);               //hour改成下午六點

    c.set(Calendar.AM_PM, Calendar.PM);             //hour改成下午六點 
    c.set(Calendar.HOUR, 6);
    *** 有上面這些範例,其他時間的設定應該可以找出規律來了。
  • 取得Calendar物件資訊
    Calendar c = Calendar.getInstance();
    int year = c.get(Calendar.YEAR);                      //取出年
    int month = c.get(Calendar.MONTH) + 1;           //取出月,月份的編號是由0~11 故+1
    int day = c.get(Calendar.DAY_OF_MONTH);        //取出日

    int weekday = c.get(Calendar.DAY_OF_WEEK);  
    //取出星期幾,編號由Sunday(1)~Saturday(7)


  • CalendarDate間的互轉
    Calendar類別提供getTime()setTime()兩個方法和Date類別互轉,由函式原型來看

    public final Date getTime ()

    public final void setTime (Date date)

    要記得c.getTime()所傳回的型別就是Date類別了,在使用上要稍微留意一下。
    • Calendar to Date
      Date date = c.getTime();
    • Date to Calendar
      c.setTime(date);
  • CalendarTimestamp間的互轉
    CalendarTimestamp之間是利用milliseconds來轉換,利用getTimeInMillis()

    public long getTimeInMillis ()

    • Calendar to Timestamp
      Timestamp time = new Timestamp(c.getTimeInMillis());
    • Timestamp to Calendar
      c.setTime(time)

  • CalendarString間的互轉
    SimpleDateFormat可以用來在StringDate間互轉,透過Date做中介使得Calendar和String間可以互相轉換。
    SimpleDateFormat的樣式表可以由這裡查到,基本用法如下:
    • Calendar to String:
      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 
      Calendar c = Calendar.getInstance();
      String str = df.format(c.getTime());
    • String to Calendar:
      SimpleDateFormat df2 = new SimpleDateFormat("yyyy/MM/dd");
      Calendar cc = Calendar.getInstance();
      try {
          cc.setTime(df2.parse("2012/12/12"));
      } catch (ParseException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      }

      simpleDateFormat
      的模板裡面用大寫的"MM"表示月份,小寫的"mm"表示分鐘;大寫的"HH"表示24小時制的hour (0~23),小寫的"hh"表示12小時制的hour (1~12),大寫的"KK"表示
      12小時制的hour (0~11),小寫的"kk"表示24小時制的hour (1~24)。
  • 常用的SimpleDateFormat格式
    • 取出日期
      SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
      String str = df.format(c.getTime());
    • 取出時間(24 hr)
      SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
      String str = df.format(c.getTime());
    • 取出時間(12 hr am/pm)
      SimpleDateFormat df = new SimpleDateFormat("hh:mm:ss a");
      String str = df.format(c.getTime());

      這個型式會輸出類似:10:25:30 上午 的格式,如果想要輸出成為 10:25:30 AM則需要使用地區設定來幫忙
      SimpleDateFormat df = new SimpleDateFormat("hh:mm:ss a", Locale.US);
      String str = df.format(c.getTime());
    • 模板中如果使用超過3個字符表示需要用全名來印出
      取出月份,以文字模式
      SimpleDateFormat df = new SimpleDateFormat("MMMM dd yy, EEEE");
      會印出 "4月 06 12, 星期五"


  • Calendar常用計算
    • 加減日期 (使用add())
      Calendar c = Calendar.getInstance();
      c.add(Calendar.DAY_OF_MONTH, 2);          //加上兩天
      c.add(Calendar.MONTH, -1);
                               // 減一個月
      在加減日期運算時不需要考慮到是否有跨月份或是跨年度的問題,Calendar會幫我們處理好。
    • 只針對特別欄位作加減運算(使用roll())
      Calendar c = Calendar.getInstance();
      c.roll(Calendar.MONTH, 10); 
         // 若目前日期是2012/6/12
                                                        // 運算過後會變成 2012/4/12
                                                        // 表示只會更改月份,不會影響其他欄位 ( 6 + 10 - 12 = 4)
    • 計算兩個日期間的間隔 ( I )
      Calendar c1 = Calendar.getInstance(); 
      Calendar c2 = Calendar.getInstance(); 
      c1.set(2012, Calendar.JANUARY, 31);
      c2.set(2012, Calendar.MAY, 5);  

      int day1 = c1.get(Calendar.DAY_OF_YEAR);
      int day2 = c2.get(Calendar.DAY_OF_YEAR); 
      int dayDiff = day2 - day1;

      上面的運算中,如果兩個日期不在同一年度則需要先判斷年度再進行日期計算
    • 計算兩個日期間的間隔 ( II )利用millisecond來計算。
      Calendar c1 = Calendar.getInstance(); 
      Calendar c2 = Calendar.getInstance(); 
      c1.set(2012, Calendar.JANUARY, 31);
      c2.set(2012, Calendar.MAY, 5);
       

      long aDayInMilliSecond = 60 * 60 * 24 * 1000;     //一天的毫秒數
      long dayDiff = (c2.getTimeInMillis() - c1.getTimeInMillis()) / aDayInMilliSecond;
    • 取得這個月有幾天
      c.set(Calendar.MONTH, Calendar.FEBRUARY);
      int daysInMonth = c.getActualMaximum(Calendar.DAY_OF_MONTH);

      這個函式會傳回特定欄位的最大值,可以用來傳回一個月有幾天。包含閏年的2月有29天都可以正確的回傳。
  • 利用cursor取得SQLite內的Timestamp內容

    Timestamp c = Timestamp.valueOf(cursor.getString(ColumnIndex);

2012年4月3日 星期二

學習Androidt程式撰寫的第一堂課:程式碼追蹤

首先建立一個簡單的HelloWorld Project。
我們使用debug perspective來追蹤程式碼,在onCreate()裡面的
super.onCreate(savedInstancesState);前面放上一個中斷點。
關於Android Activity的生命週期參考http://developer.android.com/images/activity_lifecycle.png

執行debuge,當程式碼停在中斷點的時候在Outline視窗用滑鼠右鍵點選onCreate(Bundle):void
選擇Open Type Hierarchy會出現如下畫面

按F5 (Step Into)進入super.onCreate()的細節,如果出現Source not found的訊息,則依照使用Android Source Code的內容增加source code。


如果又出現Source not found的訊息而按鈕內容變成 Edit Source Lookup Path... 則參考

Edit Source Lookup的內容,選擇 Add... -->File System Directory 將source code的路徑再指定一次即可



2012年4月2日 星期一

ArrayAdapter的使用

ArrayAdapter是Android當中最簡單的adapter,它可以連結一個字串陣列到一個只有一個TextView元件的List物件當中。語法如下:

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                 android.R.layout.simple_list_item_1, new String[]{"今天", "天氣", "很好"});


android.R.layout.simple_list_item_1.xml是Android SDK所提供的資源檔,放在...\platforms\<android version>\data\res\layout子目錄中。它是一個很好的參考,如果只是需要改動該視圖的長寬、顏色等可以更動這個檔案的內容並以另外的名稱儲存備用。

ArrayAdapter提供其它的建構函式,如果child layout裡面不是只有一個簡單的TextView,我們可以自定layout的型式以及需要輸入的字串陣列。

另外也可以在xml裡面定義字串陣列然後再利用createFromResource()將字串與layout連結起來,範例如下:

ListView listview = (ListView) findViewById(<resource Id>);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
                    R.array.week, android.R.layout.simple_list_item_1);

listview.setAdapter(adapter);


需要在\res\values\子目錄下建立一個week.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- This file is /res/values/week.xml -->
<resources>
    <string-array name="week">
        <item >星期日</item>
        <item >星期一</item>
        <item >星期二</item>
        <item >星期三</item>
        <item >星期四</item>
        <item >星期五</item>
        <item >星期六</item>
    </string-array>   
</resources>




若是資料的內容有變更,要記住ListView類別是負責顯示在螢幕上,adapter類別才是負責處理資料的繫結。可以利用ArrayAdapter的add()方法新增資料到adapter尾端或是用insert()新增資料到特定的位置;刪除資料則使用remove()方法。


一旦adapter內的資料有了變更,則adapter和ListView的內容就失去了同步,這是侯需要由adapter發送一個notifyDataSetChanged()方法通知ListView重新同步顯示的內容。

當需要自定Layout,且layout當中不只有一個view時,除了系統預設的建構函式以外,比較多的用法是覆寫getView()函式。

ArrayAdapter是繼承自BaseAdapter,當使用BaseAdapter來建構使用者自定的Layout時需要覆寫getCount(), getItem(Int position), getItemId(int position), 還有 getView(int position, View convertView, ViewGroup parent)那麼多函式。雖然大部分有書或是範例可以照抄但是誰曉得一不小心寫錯些什麼會不會搞死自己,這時候可以利用ArrayAdapter化簡一下工作。
我利用一個小技巧,先依照所需要的child view的數量產生一個物件陣列,通常就是String[],或是Bitmap[]或是將會用到的任何物件陣列。將這個物件陣列送給ArrayAdapter當作建構子參數,則ArrayAdapter會幫我們處理掉getCount(), getItem(Int position), getItemId(int position)這幾個函式,剩下的只需要覆寫getView(int position, View convertView, ViewGroup parent)即可,這也是主要的工作。

範例程式碼:

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


    ListView listview = (ListView) findViewById(R.id.listView1);
    testAdapter adapter = new testAdapter(this,
                   new String[]{"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"});
    listview.setAdapter(adapter);
  }



private static class testAdapter extends ArrayAdapter<Object> {
    // XML Layout, 通常自定Layou就會寫死在類別裡,不再透過引數傳入以免產生誤用
    private static final int mResourceId = R.layout.statisticcell;  //自定的layout
    private LayoutInflater mInflater;


    // 預設圖片id
    private static final int[] imgId = { R.drawable.ic_01,
        R.drawable.ic_02, R.drawable.ic_03 };


    public testAdapter(Context context, Object[] objects) {
      super(context, mResourceId, objects);
      mInflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);  
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      View view;
      if (convertView == null) {
        view = mInflater.inflate(mResourceId, parent, false);
      } else {
        view = convertView;
      }
      
      /**Layout中的UI在這裡綁定,這裡只有一個TextView以及一個ImageView作為範例 */
      TextView tx;
      ImageView iv;     
      tx = (TextView)view.findViewById(R.id.textHour);
      tx.setText((CharSequence) getItem(position));   //抓取由主程式傳進來的字串陣列內容
      iv = (ImageView)view.findViewById(R.id.imageDay1);
      iv.setImageResource(imgId[0]);
      
      return view;
    }
  }
}