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; } };可以看到BaseExpandableListApdater中的方法很多,主要方法介绍如下:
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); }前面几行很容易,和上面的例子几乎一样,我们只需要再关注一下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
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。