【Android】ExpandableListView 擴展的ListView


應該很多開發者在開發過程中會需要用到這樣子的功能
可擴展收納式的ListView,好處當然是可以讓APP的UI看起來更簡潔不占空間~
在官方的API文件中其實就可以看到有提供這種功能的API文件
ExpandableListView 官方 API 文件
"ExpandableListView"
網路稍微爬一下就可以看到很多範例,在這我們就做點小修正

成品圖大概長這樣~(展開前後圖)
主要分成幾個部分
1 . xml
2 . fragment
3 . adapter

====================== xml ======================
fragment中的layout中加入
<ExpandableListView
  android:id="@+id/lvExp"
  android:layout_height="match_parent"
  android:layout_width="match_parent"/>
建立一個新的expandable_head.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#FFFFFF"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/lblListHeader_head"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:text="@string/version"
            android:textColor="#000000"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/lblListHeader_memo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/setting_system_version"
            android:text="0.0.0"
            android:textColor="#767676"
            android:textSize="14sp" />

    </LinearLayout>

    <ImageView
        android:id="@+id/img_list_close"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:layout_alignParentRight="true"
        android:src="@mipmap/ic_putup"/>
</RelativeLayout>
建立一個新的expandable_item.xml
<?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="40dp"
    android:orientation="vertical"
    android:padding="8dp"
    android:background="#FFFFFF">
    
    <TextView
        android:id="@+id/lblListItem_head"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
        android:textSize="16sp"
        android:textColor="#767676" />

    <TextView
        android:id="@+id/lblListItem_memo"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
        android:textSize="12sp"
        android:textColor="#FF5722" />
    <View
        android:layout_width="match_parent"
        android:layout_height="1dip"
        android:background="#F2F2F2" />

</LinearLayout>


====================== adapter ======================
public class ExpandableListAdapter extends BaseExpandableListAdapter {
    private Context context;
    private List listDataHeader; // header titles
    // child data in format of header title, child title
    private HashMap> listDataChild;

    public ExpandableListAdapter(Context context, List listDataHeader,
                                 HashMap> listChildData) {
        this.context = context;
        this.listDataHeader = listDataHeader;
        this.listDataChild = listChildData;
    }

    @Override
    public Object getChild(int groupPosition, int childPosititon) {
        return this.listDataChild.get(this.listDataHeader.get(groupPosition))
                .get(childPosititon);
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public View getChildView(int groupPosition, final int childPosition,
                             boolean isLastChild, View convertView, ViewGroup parent) {

        final String childText = (String) getChild(groupPosition, childPosition);

        if (convertView == null) {
            LayoutInflater infalInflater = (LayoutInflater) this.context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = infalInflater.inflate(R.layout.f4_expandable_items, null);
        }

        String[] Child_array = childText.split("(,,,\\+)");

        TextView txtListChild_head = (TextView) convertView.findViewById(R.id.lblListItem_head);
        TextView txtListChild_memo = (TextView) convertView.findViewById(R.id.lblListItem_memo);
        txtListChild_head.setTypeface(null, Typeface.BOLD);
        txtListChild_memo.setTypeface(null, Typeface.BOLD);
        txtListChild_head.setText(Child_array[0]);
        txtListChild_memo.setText(Child_array[1]);
        return convertView;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return this.listDataChild.get(this.listDataHeader.get(groupPosition)).size();
    }

    @Override
    public Object getGroup(int groupPosition) {
        return this.listDataHeader.get(groupPosition);
    }

    @Override
    public int getGroupCount() {
        return this.listDataHeader.size();
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded,
                             View convertView, ViewGroup parent) {
        String headerText = (String) getGroup(groupPosition);
        if (convertView == null) {
            LayoutInflater infalInflater = (LayoutInflater) this.context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = infalInflater.inflate(R.layout.f4_expandable_head, null);
        }
        //在這邊加入Image 在Head點擊擴展時 右邊的圖片會做替換
        ImageView HeaderRightIcon = (ImageView) convertView.findViewById(R.id.img_list_close);
        //isExpanded 判斷擴展的狀態
        if(isExpanded){
            HeaderRightIcon.setImageResource(R.mipmap.ic_putup);
        }else{
            HeaderRightIcon.setImageResource(R.mipmap.ic_putaway);
        }

        String[] Header_array = headerText.split("(,,,\\+)");

        TextView lblListHeader_head = (TextView) convertView.findViewById(R.id.lblListHeader_head);
        TextView lblListHeader_memo = (TextView) convertView.findViewById(R.id.lblListHeader_memo);
        lblListHeader_head.setText(Header_array[0]);
        lblListHeader_memo.setText("1.1.0"+ "." + Header_array[1]);
      
        return convertView;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }
}


====================== fragment ======================
先宣告
ExpandableListAdapter listAdapter;
ExpandableListView expListView;
List<String> listDataHeader;
HashMap<String, List<String>> listDataChild;


在這邊我們需要在adapter中加入每個版本及每個版本更新的內容
所以要在listDataHeader 與 listDataChild 中先塞入資料

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    viewRoot = inflater.inflate(R.layout.fragment_04, container, false);
    expListView = (ExpandableListView) viewRoot.findViewById(R.id.lvExp);
    expListView.setGroupIndicator(null);//預設的箭頭設為null(不使用),沒有設定null的話預設在左邊的箭頭會出現
    prepareListData(); //這邊先做準備資料的動作
    listAdapter = new ExpandableListAdapter(context, listDataHeader, listDataChild);
    expListView.setAdapter(listAdapter);
    return viewRoot;
}


private void prepareListData() {
        listDataHeader = new ArrayList();
        listDataChild = new HashMap>();
        JSONObject jsonObject;
        JSONArray array = null;

        try {
            jsonObject = new JSONObject(getVersionJson(context));
            array = jsonObject.getJSONArray("versionList");
        } catch (JSONException e) {
            e.printStackTrace();
        }

        // 加入Head資料
        listDataHeader.add("1.1.0"+",,,+"+"0003");    //",,,+"  沒有特別的意思,方便做split的動作(投機取巧的方式,建議少這樣用)

        List ItemsList = new ArrayList();
        StringBuilder sb = new StringBuilder();

        // 加入每個item的資料
        for (int i = 0; i <array.length(); i++) {
            sb = new StringBuilder();
            try {
                JSONObject jsonObjectVersionList = array.getJSONObject(i);
                String version = jsonObjectVersionList.getString("Version");
                JSONArray array2 = jsonObjectVersionList.getJSONArray("memoList");
                for (int j = 0; j < array2.length(); j++) {
                    JSONObject jsonObjectMemoList = array2.getJSONObject(j);
                    String memo = jsonObjectMemoList.getString("memo");
                    sb.append(memo);
                }
                ItemsList.add("1.1.0."+version+",,,+"+sb);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
     listDataChild.put(listDataHeader.get(0), ItemsList);
 }

注意:
1 . 在這邊加入的版本資料是從一個json檔內的資料取出(改天寫一篇有關json的)
配合json做成範例的原因,因為在開發的過程中很常見到需要透過API拿回來Response的資料
蠻多都是用json的格式,我們需要解析後再取出需要的資訊來應用,所以這範例就用這種方式呈現。
2 . 在開發的過程中盡量別用"1.1.0"+",,,+"+"0003"類似的方式來加資料
在這是因為寫範例練習文章,先用簡單的方式呈現,但相信新手起步時很多人都會這樣做
因為很方便@@a.....當然方便的過程中後來我也是吃了閉門羹.....
有機會在寫一篇在開發用這種寫法的缺點在哪~



沒有留言:

張貼留言