上一篇《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

Android自定义组件系列【5】——进阶实践(2)的更多相关文章

  1. Android自定义组件系列【7】——进阶实践(4)

    上一篇<Android自定义组件系列[6]--进阶实践(3)>中补充了关于Android中事件分发的过程知识,这一篇我们接着来分析任老师的<可下拉的PinnedHeaderExpan ...

  2. Android自定义组件系列【6】——进阶实践(3)

    上一篇<Android自定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计划 ...

  3. Android自定义组件系列【4】——自定义ViewGroup实现双侧滑动

    在上一篇文章<Android自定义组件系列[3]--自定义ViewGroup实现侧滑>中实现了仿Facebook和人人网的侧滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布局示 ...

  4. Android自定义组件系列【3】——自定义ViewGroup实现侧滑

    有关自定义ViewGroup的文章已经很多了,我为什么写这篇文章,对于初学者或者对自定义组件比较生疏的朋友虽然可以拿来主义的用了,但是要一步一步的实现和了解其中的过程和原理才能真真脱离别人的代码,举一 ...

  5. Android自定义组件系列【5】——进阶实践(1)

    接下来几篇文章将对任老师的博文<可下拉的PinnedHeaderExpandableListView的实现>分步骤来详细实现,来学习一下大神的代码并记录一下. 原文出处:http://bl ...

  6. Android自定义组件系列【12】——非UI线程绘图SurfaceView

    一.SurfaceView的介绍 在前面我们已经会自定义View,使用canvas绘图,但是View的绘图机制存在一些缺陷. 1.View缺乏双缓冲机制. 2.程序必须重绘整个View上显示的图片,比 ...

  7. Android自定义组件系列【1】——自定义View及ViewGroup

    View类是ViewGroup的父类,ViewGroup具有View的所有特性,ViewGroup主要用来充当View的容器,将其中的View作为自己孩子,并对其进行管理,当然孩子也可以是ViewGr ...

  8. Android自定义组件系列【17】——教你如何高仿微信录音Toast

    一.Toast介绍 平时我们在Android开发中会经常用到一个叫Toast的东西,官方解释如下 A toast is a view containing a quick little message ...

  9. Android自定义组件系列【15】——四个方向滑动的菜单实现

    今天无意中实现了一个四个方向滑动的菜单,感觉挺好玩,滑动起来很顺手,既然已经做出来了就贴出来让大家也玩弄一下. 一.效果演示 (说明:目前没有安装Android模拟器,制作的动态图片太卡了,就贴一下静 ...

随机推荐

  1. 正确理解Widget::Widget(QWidget *parent) :QWidget(parent)这句话(初始化列表中无法直接初始化基类的数据成员,所以你需要在列表中指定基类的构造函数)

    最近有点忙,先发一篇我公众号的文章,以下是原文. /********原文********/ 最近很多学习Qt的小伙伴在我的微信公众号私信我,该如何理解下面段代码的第二行QWidget(parent) ...

  2. Impala架构

    Impala是Cloudera在受到Google的Dremel启发下开发的实时交互SQL大数据查询工具,Impala没有再使用缓慢的 Hive+MapReduce批处理,而是通过使用与商用并行关系数据 ...

  3. 【MongoDB】The connection between two tables

    In mongoDB, there are two general way to connect with two tables. Manual Connection and use DBRef 1. ...

  4. Hibernate之API初识及增删改查实现

    声明:关于hibernate的学习.非常大一部分东西都是概念性的. 大家最好手里都有一份学习资料,在我的博文中.我不会把书本上的概念一类的东西搬过来.那没有不论什么意义.关于hibernate的学习, ...

  5. HDU 1506 Largest Rectangle in a Histogram(DP)

    Largest Rectangle in a Histogram Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 ...

  6. IIS7性能优化

    http://www.03389.com/BLOG/user1/qq66565841/archives/2014/2014112143553.html IIS7性能优化 IIS7 优化-网站请求并发数 ...

  7. HTML基础第六讲---表格

    转自:https://i.cnblogs.com/posts?categoryid=1121494 上一讲,讲了关于<控制表格及其表项的对齐方式>,在这里我要讲讲表格及其属性 ,然后大家在 ...

  8. js无缝滚动原理及详解[转自刹那芳华]

    刚刚接触JS,网上找了一些关于无缝滚动的教程,但都大同小异,对我这种新手来说也只是会用,不知道什么意思,想要自己写个更是一头雾水.于是找了一些资料,详细说明一下JS无缝滚动的原理,相信看过这篇文章之后 ...

  9. Mongodb总结1-启动和Shell脚本

    2013年,还在秒针,当时听说了Mongodb,就学习了下,搞了下HelloWorld.主要是熟悉Mongodb的启动.命令行的Shell脚本.Java访问的CRUD. 今天,由于需要,再次回顾和进一 ...

  10. VC error link

    错误1:LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main在project-setting-link里找到pro ...