您的位置:首页 > 移动开发 > Android开发

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

2014-05-15 17:15 363 查看
上一篇《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的实现》进行详细解读,如果有问题希望指出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: