Android自己定義組件系列【5】——進階實踐(2)
上一篇《Android自己定義組件系列【5】——進階實踐(1)》中對任老師的《可下拉的PinnedHeaderExpandableListView的實現》前一部分進行了實現,這一篇我們來看看ExpandableListView的使用並實現剩下的部分。
原文出處:http://blog.csdn.net/singwhatiwanna/article/details/25546871
一、ExpandableListView的使用方法
ExpandableListView是ListView的子類,它在普通ListView的基礎上進行了擴展,適配器為ExpandableListAdapter。
與Adapter相似的是。實現ExpandableListAdapter也有例如以下方式:
1、擴展BaseExpandableListAdapter實現ExpandableListAdapter
2、使用SimpleExpandableListAdapter將兩個List集合包裝成ExpandableListAdapter
3、使用SimpleCursorTreeAdapter將Cursor中的數據包裝成SimpleCursorTreeAdapter
接下來用第一種方式來做個小樣例。來看看ExpandableListView的使用
ExpandableListAdapter adapter = new BaseExpandableListAdapter() { @Override public boolean isChildSelectable(int arg0, int arg1) { // TODO Auto-generated method stub return false; } @Override public boolean hasStableIds() { // TODO Auto-generated method stub return false; } @Override public View getGroupView(int arg0, boolean arg1, View arg2, ViewGroup arg3) { // TODO Auto-generated method stub return null; } @Override public long getGroupId(int arg0) { // TODO Auto-generated method stub return 0; } @Override public int getGroupCount() { // TODO Auto-generated method stub return 0; } @Override public Object getGroup(int arg0) { // TODO Auto-generated method stub return null; } @Override public int getChildrenCount(int arg0) { // TODO Auto-generated method stub return 0; } @Override public View getChildView(int arg0, int arg1, boolean arg2, View arg3, ViewGroup arg4) { // TODO Auto-generated method stub return null; } @Override public long getChildId(int arg0, int arg1) { // TODO Auto-generated method stub return 0; } @Override public Object getChild(int arg0, int arg1) { // TODO Auto-generated method stub return null; } };
getGroupCount():返回組列表數量
getGroupView():返回的View作為組列表項
getChildrenCount():返回子列表項的數量
getChildView():返回的View作為特定組、特定位置的子列表項
package com.example.expandablelistviewtest; 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.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ExpandableListAdapter adapter = new BaseExpandableListAdapter() { int[] logos = new int[] { R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher }; private String[] groupTypes = new String[]{ "計算機語言", "人類語言", "動物語言" }; private String[][] childTypes = new String[][] { {"Java", "C++", "C", "PHP"}, {"漢語", "英語", "日語", "法語"}, {"咕咕", "汪汪", "喵喵"} }; // 獲取指定組位置、指定子列表項處的子列表項數據 @Override public Object getChild(int groupPosition, int childPosition) { return childTypes[groupPosition][childPosition]; } @Override public long getChildId(int groupPosition, int childPosition) { return childPosition; } @Override public int getChildrenCount(int groupPosition) { return childTypes[groupPosition].length; } private TextView getTextView() { AbsListView.LayoutParams lp = new AbsListView.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, 64); TextView textView = new TextView(MainActivity.this); textView.setLayoutParams(lp); textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); textView.setPadding(36, 0, 0, 0); textView.setTextSize(20); return textView; } // 該方法決定每一個子選項的外觀 @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { TextView textView = getTextView(); textView.setText(getChild(groupPosition, childPosition) .toString()); return textView; } // 獲取指定組位置處的組數據 @Override public Object getGroup(int groupPosition) { return groupTypes[groupPosition]; } @Override public int getGroupCount() { return groupTypes.length; } @Override public long getGroupId(int groupPosition) { return groupPosition; } // 該方法決定每一個組選項的外觀 @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { LinearLayout ll = new LinearLayout(MainActivity.this); ll.setOrientation(0); ImageView logo = new ImageView(MainActivity.this); logo.setImageResource(logos[groupPosition]); ll.addView(logo); TextView textView = getTextView(); textView.setText(getGroup(groupPosition).toString()); ll.addView(textView); return ll; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } @Override public boolean hasStableIds() { return true; } }; ExpandableListView expandListView = (ExpandableListView) findViewById(R.id.list); expandListView.setAdapter(adapter); } }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <ExpandableListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="wrap_content"/> </RelativeLayout>
二、代碼分析
首先看onCreate方法:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); expandableListView = (PinnedHeaderExpandableListView) findViewById(R.id.expandablelist); stickyLayout = (StickyLayout)findViewById(R.id.sticky_layout); initData(); adapter = new MyexpandableListAdapter(this); expandableListView.setAdapter(adapter); // 展開全部group for (int i = 0, count = expandableListView.getCount(); i < count; i++) { expandableListView.expandGroup(i); } expandableListView.setOnHeaderUpdateListener(this); expandableListView.setOnChildClickListener(this); expandableListView.setOnGroupClickListener(this); stickyLayout.setOnGiveUpTouchEventListener(this); }前面幾行非常easy。和上面的樣例差點兒一樣。我們僅僅須要再關註一下initData()方法和以下的幾行監聽函數。
initData()中是模擬的數據。例如以下:
void initData() { groupList = new ArrayList<Group>(); Group group = null; for (int i = 0; i < 3; i++) { group = new Group(); group.setTitle("group-" + i); groupList.add(group); } childList = new ArrayList<List<People>>(); for (int i = 0; i < groupList.size(); i++) { ArrayList<People> childTemp; if (i == 0) { childTemp = new ArrayList<People>(); for (int j = 0; j < 13; j++) { People people = new People(); people.setName("yy-" + j); people.setAge(30); people.setAddress("sh-" + j); childTemp.add(people); } } else if (i == 1) { childTemp = new ArrayList<People>(); for (int j = 0; j < 8; j++) { People people = new People(); people.setName("ff-" + j); people.setAge(40); people.setAddress("sh-" + j); childTemp.add(people); } } else { childTemp = new ArrayList<People>(); for (int j = 0; j < 23; j++) { People people = new People(); people.setName("hh-" + j); people.setAge(20); people.setAddress("sh-" + j); childTemp.add(people); } } childList.add(childTemp); } }接下來我們看看監聽這幾個監聽函數
public class MainActivity extends Activity implements ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupClickListener, OnHeaderUpdateListener, OnGiveUpTouchEventListener {從Activity的繼承關系上看。OnChildClickListener和OnGroupClickListener是ExpandableListView類的監聽函數。
@Override public boolean onGroupClick(final ExpandableListView parent, final View v, int groupPosition, final long id) { return false; } @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { Toast.makeText(MainActivity.this, childList.get(groupPosition).get(childPosition).getName(), 1) .show(); return false; }再來看看OnHeaderUpdateListener,這個監聽函數實際上是在重寫(自己定義)的ExpandableListView中自己定義的監聽。
public void setOnHeaderUpdateListener(OnHeaderUpdateListener listener) { mHeaderUpdateListener = listener; if (listener == null) { return; } mHeaderView = listener.getPinnedHeader(); int firstVisiblePos = getFirstVisiblePosition(); int firstVisibleGroupPos = getPackedPositionGroup(getExpandableListPosition(firstVisiblePos)); listener.updatePinnedHeader(firstVisibleGroupPos); requestLayout(); postInvalidate(); }
getPinnedHeader()方法創建(得到)一個列表頭。然後通過updatePinnedHeader方法設置當前可見的子列表元素的組名稱。
requestLayout()方法的作用是當view確定自身已經不再適合現有的區域時,該view本身調用這種方法要求parent view又一次調用他的onMeasure onLayout來對又一次設置自己位置。特別的當view的layoutparameter發生改變,而且它的值還沒能應用到view上,這時候適合調用這種方法。
postInvalidate()方法的作用是實現界面刷新。
public interface OnHeaderUpdateListener { /** * 採用單例模式返回同一個view對象就可以 * 註意:view必須要有LayoutParams */ public View getPinnedHeader(); public void updatePinnedHeader(int firstVisibleGroupPos); }從OnHeaderUpdateListener監聽函數的定義上看。當觸發監聽後會調用兩個方法的實現例如以下:
@Override public View getPinnedHeader() { if (mHeaderView == null) { mHeaderView = (ViewGroup) getLayoutInflater().inflate(R.layout.group, null); mHeaderView.setLayoutParams(new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); } return mHeaderView; }
@Override public void updatePinnedHeader(int firstVisibleGroupPos) { Group firstVisibleGroup = (Group) adapter.getGroup(firstVisibleGroupPos); TextView textView = (TextView) getPinnedHeader().findViewById(R.id.group); textView.setText(firstVisibleGroup.getTitle()); }接下來我們須要知道什麽情況下回觸發這個監聽函數。
protected void refreshHeader() { if (mHeaderView == null) { return; } int firstVisiblePos = getFirstVisiblePosition(); int pos = firstVisiblePos + 1; int firstVisibleGroupPos = getPackedPositionGroup(getExpandableListPosition(firstVisiblePos)); int group = getPackedPositionGroup(getExpandableListPosition(pos)); if (group == firstVisibleGroupPos + 1) { View view = getChildAt(1); if (view.getTop() <= mHeaderHeight) { int delta = mHeaderHeight - view.getTop(); mHeaderView.layout(0, -delta, mHeaderWidth, mHeaderHeight - delta); } } else { mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight); } if (mHeaderUpdateListener != null) { mHeaderUpdateListener.updatePinnedHeader(firstVisibleGroupPos); } }能夠看到再調用refreshHeader()方法的時候會觸發updatePinnerHeader方法。
@Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (totalItemCount > 0) { refreshHeader(); } if (mScrollListener != null) { mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } }呵呵,這下最終恍然大悟了。在onScroll方法中調用了refreshHeader,這就是說在滑動屏幕的時候OnHeaderUpdateListener監聽會觸發。會不斷的推斷是否應該改變列名稱。
快淩晨1點鐘了,今天就分析到這吧。明天繼續。
再次聲明一下,本文是為了學習Android自己定義組件,對任老師博客《可下拉的PinnedHeaderExpandableListView的實現》進行具體解讀。假設有問題希望指出。
原文出處:http://blog.csdn.net/singwhatiwanna/article/details/25546871
Android自己定義組件系列【5】——進階實踐(2)