Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發之ExpandableListView

Android開發之ExpandableListView

編輯:關於Android編程

/*
 *  Android開發之ExpandableListView
 *  北京Android俱樂部群:167839253
 * Created on: 2012-7-23
 * Author: blueeagle
 *  Email:[email protected]
 */
有時候,使用ListView並不能滿足應用程序所需要的功能。有些應用程序需要多組ListView,這時候我們就要使用一種新的控件ExpandableListView——可以擴展的ListView。它的作用就是將ListView進行分組。就好像我們使用QQ的時候,有“我的好友”,“陌生人”,“黑名單”一樣,點擊一下會擴展開,再點擊一下又會收縮回去。
ExpandableListView是一個垂直滾動顯示兩級列表項的視圖,與ListView不同的是,它可以有兩層:每一層都能夠被獨立的展開並顯示其子項。這些子項來自於與該視圖關聯的ExpandableListAdapter。
每一個可以擴展的列表項的旁邊都有一個指示符(箭頭)用來說明該列表項目前的狀態(這些狀態一般是已經擴展開的列表項,還沒有擴展開的列表項,子列表項和最後一個子列表項)。可以使用方法:setChildIndicator(Drawable),setGroupIndicator(Drawable)(或者相應的XML文件的屬性) 去設置這些指示符的樣式。當然也可以使用默認的指示符。布android.R.layout.simple_expandable_list_item_1,android.R.layout.simple_expandable_list_item_2
和ListView一樣,ExpandableListView也是一個需要Adapter作為橋梁來取得數據的控件。一般適用於ExpandableListView的Adapter都要繼承BaseExpandableListAdapter這個類,並且必須重載getGroupView和getChildView這兩個最為重要的方法。
BaseExpandableListAdapter的主要重載方法如下:
public abstract ObjectgetChild (int groupPosition, int childPosition)
取得與指定分組、指定子項目關聯的數據.
參數
groupPosition 包含子視圖的分組的位置.
childPosition   指定的分組中的子視圖的位置.
返回
與子視圖關聯的數據.
public abstract long getChildId (int groupPosition, intchildPosition)
取得給定分組中給定子視圖的ID. 該組ID必須在組中是唯一的.必須不同於其他所有ID(分組及子項目的ID).
參數
groupPosition 包含子視圖的分組的位置.
childPosition   要取得ID的指定的分組中的子視圖的位置.
返回
與子視圖關聯的ID.
public abstract View getChildView (int groupPosition, intchildPosition, boolean isLastChild, View convertView, ViewGroup parent)
取得顯示給定分組給定子位置的數據用的視圖.
參數
groupPosition 包含要取得子視圖的分組位置.
childPosition   分組中子視圖(要返回的視圖)的位置.
isLastChild     該視圖是否為組中的最後一個視圖.
convertView   如果可能,重用舊的視圖對象.使用前你應該保證視圖對象為非空,並且是否是合適的類型.如果該對象不能轉換為可以正確顯示數據的視圖,該方法就創建新視圖.不保證使用先前由 getChildView(int, int,boolean, View, ViewGroup)創建的視圖.
parent     該視圖最終從屬的父視圖.
返回
指定位置相應的子視圖.
public abstract int getChildrenCount (int groupPosition)
取得指定分組的子元素數.
參數
groupPosition 要取得子元素個數的分組位置.
返回
指定分組的子元素個數.
public abstract long getCombinedChildId (long groupId, long childId)
取得一覽中可以唯一識別子條目的 ID(包括分組ID和子條目ID).可擴展列表要求每個條目 (分組條目和子條目)具有一個可以唯一識別列表中子條目和分組條目的ID. 該方法根據給定子條目ID和分組條目ID返回唯一識別ID.另外,如果 hasStableIds() 為真,該函數返回的ID必須是固定不變的.
參數
groupId   包含子條目ID的分組條目ID.
childId    子條目的ID.
返回
可以在所有分組條目和子條目中唯一識別該子條目的ID(可能是固定不變的).
public abstract long getCombinedGroupId (long groupId)
取得一覽中可以唯一識別子條目的 ID(包括分組ID和子條目ID).可擴展列表要求每個條目 (分組條目和子條目)具有一個可以唯一識別列表中子條目和分組條目的ID. 該方法根據給定子條目ID和分組條目ID返回唯一識別ID.另外,如果 hasStableIds() 為真,該函數返回的ID必須是固定不變的.
參數
groupId   分組條目ID.
返回
可以在所有分組條目和子條目中唯一識別該分組條目的ID(可能是固定不變的).
public abstract Object getGroup (int groupPosition)
取得與給定分組關聯的數據.
參數
groupPosition 分組的位置.
返回
指定分組的數據.
public abstract int getGroupCount ()
取得分組數.
返回
分組數.
public abstract long getGroupId (int groupPosition)
取得指定分組的ID.該組ID必須在組中是唯一的.必須不同於其他所有ID(分組及子項目的ID).
參數
groupPosition 要取得ID的分組位置.
返回
與分組關聯的ID.
public abstract View getGroupView (int groupPosition, booleanisExpanded, View convertView, ViewGroup parent)
取得用於顯示給定分組的視圖. 這個方法僅返回分組的視圖對象, 要想獲取子元素的視圖對象,就需要調用 getChildView(int, int, boolean, View, ViewGroup).
參數
groupPosition 決定返回哪個視圖的組位置 .
isExpanded     該組是展開狀態還是收起狀態 .
convertView   如果可能,重用舊的視圖對象.使用前你應該保證視圖對象為非空,並且是否是合適的類型.如果該對象不能轉換為可以正確顯示數據的視圖,該方法就創建新視圖.不保證使用先前由 getGroupView(int, boolean,View, ViewGroup)創建的視圖.
parent     該視圖最終從屬的父視圖.
返回
指定位置相應的組視圖.
public abstract boolean hasStableIds ()
是否指定分組視圖及其子視圖的ID對應的後台數據改變也會保持該ID.
返回
是否相同的ID總是指向同一個對象.
public abstract boolean isChildSelectable (int groupPosition, intchildPosition)
指定位置的子視圖是否可選擇.
參數
groupPosition 包含要取得子視圖的分組位置.
childPosition   分組中子視圖的位置.
返回
是否子視圖可選擇.
注意:
在XML布局文件中,如果ExpandableListView上一級視圖的大小沒有嚴格定義的話,則不能對ExpandableListView的android:layout_height 屬性使用wrap_content值。 (例如,如果上一級視圖是ScrollView的話,則不應該指定wrap_content的值,因為它可以是任意的長度。不過,如果ExpandableListView的上一級視圖有特定的大小的話,比如100像素,則可以使用wrap_content)
如果由於開發的時候粗心,對ExpandableListView指定wrap_content的值,則會報一個在SetContentView處的空指針錯誤。
根據描述,先看一個簡單的例子:
定義XML的代碼如下:
[html]  
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical" > 
    <ExpandableListView    
        android:id = "@+id/myExpandableListView" 
        android:layout_width="fill_parent"                   
        android:layout_height="fill_parent"                 
        android:layout_weight="1"                  
        />    
</LinearLayout> 
就是在LinearLayout中布置了一個ExpandableListView。
在JAVA中的代碼如下:
[java] 
/*
 *  Android開發之ExpandableListView
 *  北京Android俱樂部群:167839253
*  Created on: 2012-7-23
 *  ExpandableListViewActivity.java
 *  Author: blueeagle
 *  Email: [email protected]
 */ 
 
package com.blueeagle.www; 
 
import java.util.ArrayList; 
import java.util.List; 
import android.app.Activity; 
import android.os.Bundle; 
import android.view.Gravity; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.AbsListView; 
import android.widget.BaseExpandableListAdapter; 
import android.widget.ExpandableListView; 
import android.widget.TextView; 
 
public class ExpandableListViewActivity extends Activity { 
    private List<String> GroupData;//定義組數據   
    private List<List<String>> ChildrenData;//定義組中的子數據 
    private void LoadListDate() {   
        GroupData = new ArrayList<String>();   
        GroupData.add("國家");   
        GroupData.add("人物");   
        GroupData.add("武器");   
   
        ChildrenData = new ArrayList<List<String>>();   
        List<String> Child1 = new ArrayList<String>();   
        Child1.add("蜀國");   
        Child1.add("魏國"); 
        Child1.add("吳國"); 
        ChildrenData.add(Child1);   
        List<String> Child2 = new ArrayList<String>();   
        Child2.add("關羽");   
        Child2.add("張飛");   
        Child2.add("典韋");   
        Child2.add("呂布"); 
        Child2.add("曹操"); 
        Child2.add("甘寧"); 
        Child2.add("郭嘉"); 
        Child2.add("周瑜"); 
        ChildrenData.add(Child2);   
        List<String> Child3 = new ArrayList<String>();   
        Child3.add("青龍偃月刀");   
        Child3.add("丈八蛇矛槍");   
        Child3.add("青鋼劍");   
        Child3.add("麒麟弓");   
        Child3.add("銀月槍");   
        ChildrenData.add(Child3);   
    }   
   
    @Override   
    public void onCreate(Bundle savedInstanceState) {   
        super.onCreate(savedInstanceState);   
        setContentView(R.layout.main);   
           
        LoadListDate();   
           
        ExpandableListView myExpandableListView = (ExpandableListView)findViewById(R.id.myExpandableListView);   
        myExpandableListView.setAdapter(new ExpandableAdapter());   
    }   
       
    private class ExpandableAdapter extends BaseExpandableListAdapter {   
        @Override 
        public Object getChild(int groupPosition, int childPosition) { 
            return ChildrenData.get(groupPosition).get(childPosition); 
        } 
 
        @Override 
        public long getChildId(int groupPosition, int childPosition) { 
            return 0; 
        } 
 
        @Override 
        public View getChildView(int groupPosition, int childPosition, 
                boolean isLastChild, View convertView, ViewGroup parent) { 
            TextView myText = null;   
            if (convertView != null) {   
                myText = (TextView)convertView;   
                myText.setText(ChildrenData.get(groupPosition).get(childPosition));   
            } else {   
                myText = createView(ChildrenData.get(groupPosition).get(childPosition));   
            }   
            return myText;   
        } 
 
        @Override 
        public int getChildrenCount(int groupPosition) { 
            return ChildrenData.get(groupPosition).size(); 
        } 
 
        @Override 
        public Object getGroup(int groupPosition) { 
            return GroupData.get(groupPosition); 
        } 
 
        @Override 
        public int getGroupCount() { 
            return GroupData.size(); 
        } 
 
        @Override 
        public long getGroupId(int groupPosition) { 
            return 0; 
        } 
 
        @Override 
        public View getGroupView(int groupPosition, boolean isExpanded, 
                View convertView, ViewGroup parent) { 
            TextView myText = null;   
            if (convertView != null) {   
                myText = (TextView)convertView;   
                myText.setText(GroupData.get(groupPosition));   
            } else {   
                myText = createView(GroupData.get(groupPosition));   
            }   
            return myText; 
        } 
 
        @Override 
        public boolean hasStableIds() { 
            return false; 
        } 
 
        @Override 
        public boolean isChildSelectable(int groupPosition, int childPosition) { 
            return false; 
        }   
        private TextView createView(String content) {   
            AbsListView.LayoutParams layoutParams = new AbsListView.LayoutParams(     
                    ViewGroup.LayoutParams.FILL_PARENT, 80);     
            TextView myText = new TextView(ExpandableListViewActivity.this);     
            myText.setLayoutParams(layoutParams);     
            myText.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);     
            myText.setPadding(80, 0, 0, 0);     
            myText.setText(content);   
            return myText;   
        } 
    }   

效果如下圖所示:


這樣就完成了一個最簡單的ExpandableListView的控件。
       在實際開發過程中,常常有不同的需求,比如每一個child需要不同的控件,每一個group或者child需要有圖標,圖標顯示需要不一樣,需要設置背景等各種各樣能夠讓我們的程序變得美觀的需求。那麼下面就逐一討論一下ExpandableListView如何實現這些需求。
       比如,為ExpandableListView設置背景,並且默認展開第n組,n從0開始計數,則只需要添加如下代碼:
[java] 
myExpandableListView.setBackgroundResource(R.drawable.background); 
myExpandableListView.expandGroup(0); 
則效果如下:

改變每個組前面的圖標,並且圖標樣式隨著合攏和展開不同,則只需要在res/drawable目錄下定義文件:Indicator.xml
[html] 
<selector xmlns:android="http://schemas.android.com/apk/res/android">   
    <item android:state_expanded="true" android:drawable="@drawable/right" />   
    <item android:drawable="@drawable/down"></item>   
</selector> 
在JAVA文件中添加:
[java] 
myExpandableListView.setGroupIndicator(this.getResources().getDrawable(R.drawable.indicator)); 
效果如下:

對於其他的屬性設置,可以參考以下屬性說明:
android:childDivider
來分離子列表項的圖片或者是顏色。注:圖片不會完全顯示,分離子列表項的是一條直線
android:childIndicator
在子列表項旁邊顯示的指示符。注:可以是一個圖片
android:childIndicatorLeft
子列表項指示符的左邊約束位置。注:即從左端0位置開始計數,比如,假設指示符是一個圖標,給定這個屬性值為3dip,則表示從左端起3dip開始顯示此圖標。
android:childIndicatorRight
子列表項指示符的右邊約束位置。注:表示右端到什麼位置結束
android:groupIndicator
在組列表項旁邊顯示的指示符。注:可以是一個圖片。
android:indicatorLeft
組列表項指示器的左邊約束位置。注:表示左端從什麼位置開始。
android:indicatorRight
組列表項指示器的右邊約束位置。注:表示右端到什麼位置結束。
 
當然,還可以使用自定義的View去描述group和child,自定義的View可以和布局文件一樣,寫在layout文件夾下。例如命名為group.xml或者child.xml。
例如,我們定義一個child項由一個ImageView和一個TextView來組成,則可以定義child.xml為:
[html] 
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:orientation = "horizontal" 
android:layout_width="match_parent" 
android:layout_height="match_parent"> 
<ImageView 
android:layout_gravity = "center_vertical" 
android:id = "@+id/imageView01" 
android:layout_width = "70px" 
android:layout_height = "70px" 
android:paddingLeft = "30px" 
android:paddingTop = "2px" 
android:paddingBottom = "5px" 
android:src = "@drawable/ic_launcher"/> 
<TextView 
android:layout_gravity = "center_vertical" 
android:id = "@+id/childTV" 
android:layout_width = "match_parent" 
android:layout_height = "match_parent" 
android:paddingLeft = "30px" 
android:paddingTop = "10px" 
android:paddingBottom = "5px" 
android:textSize = "30sp"/> 
</LinearLayout> 
對於其中的屬性不再做詳細說明。
對於ExpandableListView中的數據,還可以用以下方式定義:
[java] 
List<Map<String, String>> groups = new ArrayList<Map<String, String>>(); 
        Map<String, String> group1 = new HashMap<String, String>(); 
        group1.put("group", "國家"); 
         … …  
        groups.add(group1); 
… … 
        //准備第一個一級列表中的二級列表數據:三個二級列表,分別顯示"魏國"、"蜀國"和"吳國" 
        List<Map<String, String>> child1 = new ArrayList<Map<String, String>>(); 
        Map<String, String> child1Data1 = new HashMap<String, String>(); 
        child1Data1.put("child", "魏國"); 
… …         
child1.add(child1Data1); 
         … … 
        //准備第二個一級列表中的二級列表數據:八個二級列表,顯示"關羽"、"張飛"、"典韋"、"呂布"、"曹操"、"甘寧"、"郭嘉"、"周瑜" 
        List<Map<String, String>> child2 = new ArrayList<Map<String, String>>(); 
        Map<String, String> child2Data1 = new HashMap<String, String>(); 
        child2Data1.put("child", "關羽"); 
        … … 
        child2.add(child2Data1); 
        … … 
 
        //准備第三個一級列表中的二級列表數據:五個二級列表,顯示 "青龍偃月刀"、"丈八蛇矛槍"、 "青鋼劍"、"麒麟弓"、"銀月槍"  
        List<Map<String, String>> child3 = new ArrayList<Map<String, String>>(); 
        Map<String, String> child3Data1 = new HashMap<String, String>(); 
        child3Data1.put("child", "青龍偃月刀"); 
            … … 
        child3.add(child3Data1); 
        … … 
         
      //用一個list對象保存所有的二級列表數據 
        List<List<Map<String, String>>> childs = new ArrayList<List<Map<String, String>>>(); 
        childs.add(child1); 
        childs.add(child2); 
        childs.add(child3); 
針對上述數據定義方式,修改java文件: 
例如:在getChildView函數中做如下編寫, 
            String text = groups.get(groupPosition).get("group"); 
            LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
            //獲取一級列表布局文件,設置相應元素屬性 
            LinearLayout linearLayout = (LinearLayout) layoutInflater.inflate(R.layout.group, null); 
            TextView textView = (TextView)linearLayout.findViewById(R.id.textView01); 
            textView.setText(text); 
            return linearLayout; 
這樣就可以將自定義的View寫入到child中,當然,這裡也可以不用布局文件來定義View,也可以自己用代碼實現View。
補充知識:
對於ExpandableListView相應的,也有一個ExpandableListActivity與之對應,對於只需要一個ExpandableListView的Activity,則只需要使用ExpandableListActivity來完成相應的功能就可以了。但是需要注意一點的是:在main.xml頁面中添加如下代碼: www.2cto.com
[html] 
<ListView android:id="@android:id/list" 或android:id="@id/android:list" 
android:layout_width="fill_parent" 
android:layout_height="wrap_content"> 
</ListView> 


作者:redoffice

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved