您的位置:首页 > Web前端 > CSS

expandableListView 自定义样式以及scrollerView嵌套

2016-01-15 17:43 477 查看
最近写项目需要用到ExpandableListView,由于第一次使用,被它各种虐。网上也查看很多资料,但都没有实质的对它做个总结。本人不才,把自己项目中遇到的问题小记一下,分享给初学者。(部分是网上copy的,敬请谅解)

ExpandableListView组件是android中一个比较常用的组件,当点击一个父item的时候可以将他的子item显示出来,像手机QQ中的好友列表就是实现的类型效果。

1、Expandablelistview的基本使用:

第一步:在main.xml中定义ExpandableListView(由于项目的需求要scrollerView嵌套ExpandableListView所以这里引用的事自定义的)

<com.demo.widget.ScrollDisabledExpandableListView

android:id="@+id/yuncomputer_screen_id_expandablelist"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_below="@id/yuncomputer_screen_id_tips"

android:dividerHeight="1dp"

android:childDivider="#e3e3e3"

android:divider="#e3e3e3"

android:listSelector="@color/no_color" />

注:如果要定义分割线的颜色,divider属性要直接写颜色值,要不然会不显示

第二步:定义group.xml

<TextView android:id="@+id/group_id_name"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_vertical"

android:textColor="@color/black"

android:textSize="17sp"

android:paddingLeft="10dp"

/>

注:由于我要自定义箭头而且要更改箭头位置所以这里多了个imageView。

第三步:定义chilid.xml

<TextView android:id="@+id/child_id_pkg_name"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="@color/black"

android:textSize="17sp"

/>

第四步:重头戏,适配BaseExpandableListAdapter

public class MyAdapter extends BaseExpandableListAdapter {

List<String> groups;

Map<String, List<String>> packages;

Context context;

public YunPackageAdapter(Context context,List<String> groups,Map<String, List<String>> packages){

super();

this.context = context;

this.groups = groups;

this.packages = packages;

}

/**

* 获取父item的个数

*/

@Override

public int getGroupCount() {

// TODO Auto-generated method stub

return groups.size();

}

/**

* 获取当前父item下的子item的个数

*/

@Override

public int getChildrenCount(int groupPosition) {

// TODO Auto-generated method stub

String strKey = groups.get(groupPosition);

return packages.get(strKey).size();

}

/**

* 获取当前父item的数据

*/

@Override

public Object getGroup(int groupPosition) {

// TODO Auto-generated method stub

return groups.get(groupPosition);

}

/**

* 得到子item需要关联的数据

*/

@Override

public Object getChild(int groupPosition, int childPosition) {

// TODO Auto-generated method stub

String strKey = groups.get(groupPosition);

return packages.get(strKey).get(childPosition);

}

/**

* 获取父item的ID

*/

@Override

public long getGroupId(int groupPosition) {

// TODO Auto-generated method stub

return groupPosition;

}

/**

* 获取子item的ID

*/

@Override

public long getChildId(int groupPosition, int childPosition) {

// TODO Auto-generated method stub

return childPosition;

}

@Override

public boolean hasStableIds() {

// TODO Auto-generated method stub

return true;

}

/**

* 设置父item组件(这里是自定义的箭头)

*/

@Override

public View getGroupView(int groupPosition, boolean isExpanded,

View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

convertView = LayoutInflater.from(context).inflate(R.layout.group, null);

ImageView imgIndicator = (ImageView) convertView.findViewById(R.id.group_id_indicator);

TextView tvName = (TextView) convertView.findViewById(R.id.group_id_name);

tvName.setText(groups.get(groupPosition));

//key值必须为资源ID

convertView.setTag(R.layout.item_yuncomputer_group, groupPosition);

convertView.setTag(R.layout.item_yuncomputer_child,-1);//设置-1表示长按时点击的是父项。

return convertView;

}

/**

* 设置item的组件

*/

@Override

public View getChildView(int groupPosition, int childPosition,

boolean isLastChild, View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

convertView = LayoutInflater.from(context).inflate(R.layout.child, null);

TextView tvPackageName = (TextView) convertView.findViewById(R.id.child_id_name);

convertView.setTag(R.layout.item_yuncomputer_child,childPosition);

convertView.setTag(R.layout.item_yuncomputer_group, groupPosition);

return convertView;

}

@Override

public boolean isChildSelectable(int groupPosition, int childPosition) {

// TODO Auto-generated method stub

return true;

}

}

2、自定义groupIndicator

a、直接给groupIndicator自定义图片,为了图片不变形,要.9图片。缺点:只能通过.9图片大致调整箭头的位置,indicatorLeft属性对于某些终端不起作用,具体为啥我也不清楚。

<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:drawable="@drawable/list_item_expand" android:state_expanded="true"/>

<item android:drawable="@drawable/list_item_collapse"></item>

</selector>

===================================================

<ExpandableListView



android:groupIndicator="@drawable/list_expand"



/>

b、网上零零碎碎很多方法,我认为的最简单有效的是:

在group中自定义箭头,这样样式、位置就能精准配置了:首先

在group的xml中加入:

<ImageView android:id="@+id/group_id_indicator"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_vertical"

android:scaleType="fitCenter"

android:src="@drawable/yuncomputer_close"

/>

再者:android:groupIndicator="@null"必须设置为null要不然会出现重叠的箭头而且自定义的无效;

然后在adapter中的getGroupView方法中加入:

if(isExpanded){

imgIndicator.setImageResource(R.drawable.open);

}else{

imgIndicator.setImageResource(R.drawable.close);

}

大功告成,啦啦啦啦......

3、对ExpandableListView的item点击事件是OnChildClickListener: onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) 同时控制groupPosition和childPosition才能精确控制点击的是哪一个item。

4、对ExpandableListView的item的长按事件:(此处为转载:转载地址:http://blog.csdn.net/t5721654/article/details/6857357)

关于ExpandableListView长按事件处理,网上很多都是使用将上下文菜单注册到ExpandableListView上实现长按事件。

/**
* 长按邮箱快捷选项
* @author King
*/
private class QuickWayListener implements OnItemLongClickListener{
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View view,
int pos, long id) {
int groupPos = (Integer)view.getTag(R.id.xxx01); //参数值是在setTag时使用的对应资源id号
int childPos = (Integer)view.getTag(R.id.xxx02);
if(childPos == -1){//长按的是父项
//根据groupPos判断你长按的是哪个父项,做相应处理(弹框等)
} else {
//根据groupPos及childPos判断你长按的是哪个父项下的哪个子项,然后做相应处理。
}
return false;
}
}

这样做弊端显而易见,不够灵活,不能分别对父项、子项、父项之间、子项之间弹出内容做区分。
下面来说我的解决方法,方法有点投机取巧。首先说明一点,使用我这种方法必须使用自定义的BaseExpandableListAdapter,至于为什么,具体后面讲到。

ExpandableListView本身有继承自AdapterView的setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener)方法。
实现监听器:

/**
* 长按邮箱快捷选项
* @author King
*/
private class QuickWayListener implements OnItemLongClickListener{
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View view,
int pos, long id) {
//pos不可用说明见下文
return false;
}
}

如果这个方法是用在ListView长按事件中刚刚好,但在ExpandableListView中,第三个参数pos不能区分开点击的是父项还是子项,以及哪个父项或子项。

在ExpandableListView响应的onItemLongCkick方法中,pos参数值为:从上到下,父项+展现的子项到点击位置的数目(注意:是展现的,隐藏的子项不包括,从0开始)。

例如:

父项1(隐藏3个子项)

父项2

|—子项2-0

|—子项2-1

|—子项2-2

长按子项2-1时,pos值为3。显然根据pos值是无法确定点击的是哪个子项或父项的。

因此依赖pos是很难处理点击位置的。

如果可以直接在onItemLongClick方法中获取groupPos,及childPos该多好呢?

于是看到了onItemLongClick方法第二个参数:view。这里的view是你按中的位置对应的view。view有个方法getTag(int key)。如果在创建此view的时候就把groupPos,childPos通过setTag(int key, Object value)设置进去,在响应onItemLongClick不就可以直接拿出来用了么。

现在就要讲到必须使用自定义的BaseExpandableListAdapter的理由了。

要把groupPos,childPos通过setTag的方式绑定到view中,就必须操作该view的创建过程。要控制这个过程就必须要在自定义BaseExpandableListAdapter中重写getGroupView及getChildView方法进行操作。如下:

public class AccountListAdapter extends BaseExpandableListAdapter {

...省略其他方法

@Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
//我这里仅通过自己写的mkChildView()方法创建TextView来显示文字,更复杂的可以通过LayoutInflater来填充一个view
TextView childTv = mkChildView();
// 标记位置
// 必须使用资源Id当key(不是资源id会出现运行时异常),android本意应该是想用tag来保存资源id对应组件。
// 将groupPosition,childPosition通过setTag保存,在onItemLongClick方法中就可以通过view参数直接拿到了!
childTv.setTag(R.id.xxx01, groupPosition);
childTv.setTag(R.id.xxx02, childPosition);
return childTv;
}

@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
TextView groupTv = mkGroupView();
// 设置同getChildView一样
groupTv.setTag(R.id.xxx01, groupPosition);
groupTv.setTag(R.id.xxx02, -1); //设置-1表示长按时点击的是父项,到时好判断。
groupTv.setText(groups[groupPosition]);
return groupTv;
}
}

完成了这一步,我们只需要在ExpandableListView响应的onItemLongClick方法时通过view.getTag(R.id.xxx01),view.getTag(R.id.xxx02)即可拿到groupPos,childPos.

/**
* 长按邮箱快捷选项
* @author King
*/
private class QuickWayListener implements OnItemLongClickListener{
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View view,
int pos, long id) {
int groupPos = (Integer)view.getTag(R.id.xxx01); //参数值是在setTag时使用的对应资源id号
int childPos = (Integer)view.getTag(R.id.xxx02);
if(childPos == -1){//长按的是父项
//根据groupPos判断你长按的是哪个父项,做相应处理(弹框等)
} else {
//根据groupPos及childPos判断你长按的是哪个父项下的哪个子项,然后做相应处理。
}
return false;
}
}

到这就写完了,貌似比较啰嗦。重写BaseExpandableListAdapter写的比较简洁,没看明白的朋友可以先到网上查下怎么自定义BaseExpandableListAdapter,和自定义BaseAdapter其实是一样的。
这个部分为转载,亲测实在是好用得不得了(偷笑)。

5、scrollerView嵌套ExpandableListView:

a、重写ExpandableListView

public class CustomExpandableListView extends ExpandableListView {

public CustomExpandableListView(Context context, AttributeSet attrs) {

super(context, attrs);

// TODO Auto-generated constructor stub

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

// TODO Auto-generated method stub

int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,

MeasureSpec.AT_MOST);

super.onMeasure(widthMeasureSpec, expandSpec);

}

}

b、动态计算expandablelistview的高度,xxx_group.xml和xxx_child.xml的最外层要用linearlayout,反正relativelayout不行,不知道为什么

private void setListViewHeight(ExpandableListView listView) {

ListAdapter listAdapter = listView.getAdapter();

int totalHeight = 0;

int count = listAdapter.getCount();

for (int i = 0; i < listAdapter.getCount(); i++) {

View listItem = listAdapter.getView(i, null, listView);

listItem.measure(0, 0);

totalHeight += listItem.getMeasuredHeight();

}

ViewGroup.LayoutParams params = listView.getLayoutParams();

params.height = totalHeight

+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));

listView.setLayoutParams(params);

listView.requestLayout();

}

c、较为复杂

public class ViewGroupForListView extends LinearLayout implements View.OnClickListener {

private ListAdapter mAdapter = null;

private OnItemClickListener mListener = null;

public ViewGroupForListView(Context context) {

super(context);

}

public ViewGroupForListView(Context context, AttributeSet attrs) {

super(context, attrs);

// TODO Auto-generated constructor stub

this.setOrientation(VERTICAL);

}

/**

* 绑定数据

*/

protected void bindData() {

int count = mAdapter.getCount();

for(int i = 0; i < count; i++) {

View v = mAdapter.getView(i, null, null);

v.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

v.setOnClickListener(this);

v.setId(i);

addView(v, i);

}

}

/**

* 设置adapter

* @param adapter

*/

public void setAdapter(ListAdapter adapter) {

mAdapter = adapter;

if(this.getChildCount() != 0) {

removeAllViews();

}

bindData();

}

/**

* 获取adapter

* @return

*/

public ListAdapter getAdapter() {

return mAdapter;

}

/**

* 绑定监听

* @param listener

*/

public void setOnItemClickListener(OnItemClickListener listener) {

this.mListener = listener;

}

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

if(mListener != null) {

mListener.onItemClick(v.getId(), mAdapter);

}

}

/**

* 监听接口

* @author Visual

*

*/

public interface OnItemClickListener {

public void onItemClick(int position, ListAdapter adapter);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: