2013/03/08

Basic ExpandableListView

最近做了一個 app 而使用到 ExpandableListView, 這邊就紀錄下來如何使用.

下面是我的主 layout, 很簡單就只是一個 ExpandableListView 的 widiget.

activity_main.xml




    


ExpandableListView 有分 Group 和 Child, 因此分別對 Group 和 Child 設定各自的 View.

group_row.xml




    


child_row.xml




    


接下來就是主要程式的部份, 這邊我不使用 ExpandableListActivity, 原因是
在很多情況下直接繼承 (extend) ExpandableListActivity 是沒辦法, 比方說當你要使用 Fragment 的時候.
這邊為了簡化, 所以所有的 import 都不會列出來.

因為會使用到 ExpandableListView 這元件, 所以我們特別使用一個變數 (mExpandableListView) 來指向此元件.


public class MainActivity extends Activity {
 private ExpandableListView mExpandableListView = null;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  mExpandableListView = (ExpandableListView) findViewById(R.id.elistview);

  initData();
 }
}
有了 ExpandableListView 元件, 接著必須把所需的資料放進此元件.
groupItems 使主要的群組, 而 childItems 是每個群組底下的資料
跟 ListView 一樣, 這邊定義一個 MyExpandListAdapter 來給 ExpandableListView 使用.
MyExpandListAdapter 會包含我們如何顯示每個行(row)資料和呈現的畫面.


private void initData() {
 final String[] groupItems = { "Group1", "Group2", "Group3", "Group4", "Group5", };
 final String[] childItems = { "Child1", "Child2", "Child3", "Child4", "Child5", };

 MyExpandListAdapter adapter = new MyExpandListAdapter(this, groupItems);

 int i = 0;
 int count = groupItems.length;
 
 /**
  * add child data to groups
  */
 for (i = 0; i < count; i++) {
  adapter.addChild(i, childItems);
 }
 mExpandableListView.setAdapter(adapter);
}
MyExpandListAdapter 比較複雜, 因為這個主要是用來顯示資料並且在 ExpandableListView 裡的畫面呈現.


class MyExpandListAdapter extends BaseExpandableListAdapter {

 Context ctxt = null;

 /**
  * Use SparseArray to store my group-child mapping datas,
  */
 SparseArray data = new SparseArray();
 
 String[] groups = null;

 public MyExpandListAdapter(Context c, String[] groupitems) {
  super();
  ctxt = c;
  groups = groupitems;
 }

 @Override
 public String getChild(int groupPosition, int childPosition) {
  String[] childs = data.get(groupPosition);
  if (childs == null) {
   return null;
  }
  if (childPosition >= childs.length) {
   return null;
  }
  return childs[childPosition];
 }

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

 @Override
 public View getChildView(int groupPosition, int childPosition,
   boolean isLastChild, View convertView, ViewGroup parent) {
  /**
   * in this function, we can customize our child view and show data.
   */

  View row = convertView;
  if (row == null) {
   LayoutInflater inflater = (LayoutInflater) ctxt
     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   row = inflater.inflate(R.layout.child_row, parent, false);
  }

  TextView tv = (TextView) row.findViewById(R.id.childlabel);

  String child = getChild(groupPosition, childPosition);

  if (child != null) {
   tv.setText(child);
  }

  return row;
 }

 @Override
 public int getChildrenCount(int groupPosition) {
  String[] childs = data.get(groupPosition);
  if (childs == null) {
   return 0;
  }
  return childs.length;
 }

 @Override
 public String getGroup(int groupPosition) {
  if (groupPosition >= groups.length) {
   return null;
  }
  return groups[groupPosition];
 }

 @Override
 public int getGroupCount() {
  return groups.length;
 }

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

 @Override
 public View getGroupView(int groupPosition, boolean isExpanded,
   View convertView, ViewGroup parent) {
  /**
   * in this function, we can customize our group view and show data.
   */

  View row = convertView;
  if (row == null) {
   LayoutInflater inflater = (LayoutInflater) ctxt
     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   row = inflater.inflate(R.layout.group_row, parent, false);
  }

  TextView tv = (TextView) row.findViewById(R.id.grouplabel);

  String group = getGroup(groupPosition);

  if (group != null) {
   tv.setText(group);
  }

  return row;
 }

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

 @Override
 public boolean isChildSelectable(int groupPosition, int childPosition) {
  /**
   * If set to false, we can't click child item.
   */
  return false;
 }
 
 public void addChild(int group, String[] childs) {
  /**
   * Add this function for us to add childs.
   * It will overwrite previsou data
   * But this is just an example.
   */
  data.put(group, childs);
  notifyDataSetChanged();
 }
}
這樣就是基本的 ExpandListView 了. 以下是畫面呈現,
如果注意到的話, 群組的折疊圖示竟然重疊了.
修改一下 group_row 的 layout
Screenshot from 2013-03-08 14:07:22



    

修改後的結果如下
Screenshot from 2013-03-08 14:19:45

不過一般通常折疊的圖示都是在右邊, 所以再次修改一下把折疊圖示移到最右邊.


private void initData() {
 final String[] groupItems = { "Group1", "Group2", "Group3", "Group4", "Group5", };
 final String[] childItems = { "Child1", "Child2", "Child3", "Child4", "Child5", };
 MyExpandListAdapter adapter = new MyExpandListAdapter(this, groupItems);

 int i = 0;
 int count = groupItems.length;
 
 /**
  * add child data to groups
  */
 for (i = 0; i < count; i++) {
  adapter.addChild(i, childItems);
 }
 
 setIndicatorToRight();
 mExpandableListView.setAdapter(adapter);

 
 mExpandableListView.setOnChildClickListener(new OnChildClickListener() {
  @Override
  public boolean onChildClick(ExpandableListView parent, View v,
    int groupPosition, int childPosition, long id) {

   MyExpandListAdapter adapter = (MyExpandListAdapter) parent
     .getAdapter();

   String group = adapter.getGroup(groupPosition);
   String child = adapter.getChild(groupPosition, childPosition);

   Toast.makeText(MainActivity.this, group + " - " + child,
     Toast.LENGTH_SHORT).show();

   return true;
  }
 });
}

private void setIndicatorToRight() {
 final float scale = getResources().getDisplayMetrics().density;
 
 /**
  * Calcaulate device screen width in pixel
  */
 DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    int width = metrics.widthPixels;
    
    mExpandableListView.setIndicatorBounds(width - (int)(50 * scale), 
      width - (int)(10 * scale));
}
下面就是最終畫面的呈現

Screenshot from 2013-03-08 14:33:31

原始碼下載: here