博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android之IphoneTreeView带组指示器的ExpandableListView
阅读量:7049 次
发布时间:2019-06-28

本文共 12693 字,大约阅读时间需要 42 分钟。

  hot3.png

之前实现过一次这种效果的ExpandableListView:,带效果比较挫,最近,在参考联系人源码PinnedHeaderListView,以及网上各位大侠的源码,封装了一个效果最好,而且使用最简单的IphoneTreeView,下面先看看效果图:

  

首先让我们看看封装得比较完善的IphoneTreeView:

public class IphoneTreeView extends ExpandableListView implements		OnScrollListener, OnGroupClickListener {	public IphoneTreeView(Context context, AttributeSet attrs, int defStyle) {		super(context, attrs, defStyle);		registerListener();	}	public IphoneTreeView(Context context, AttributeSet attrs) {		super(context, attrs);		registerListener();	}	public IphoneTreeView(Context context) {		super(context);		registerListener();	}	/**	 * Adapter 接口 . 列表必须实现此接口 .	 */	public interface IphoneTreeHeaderAdapter {		public static final int PINNED_HEADER_GONE = 0;		public static final int PINNED_HEADER_VISIBLE = 1;		public static final int PINNED_HEADER_PUSHED_UP = 2;		/**		 * 获取 Header 的状态		 * 		 * @param groupPosition		 * @param childPosition		 * @return 		 *         PINNED_HEADER_GONE,PINNED_HEADER_VISIBLE,PINNED_HEADER_PUSHED_UP		 *         其中之一		 */		int getTreeHeaderState(int groupPosition, int childPosition);		/**		 * 配置 QQHeader, 让 QQHeader 知道显示的内容		 * 		 * @param header		 * @param groupPosition		 * @param childPosition		 * @param alpha		 */		void configureTreeHeader(View header, int groupPosition,				int childPosition, int alpha);		/**		 * 设置组按下的状态		 * 		 * @param groupPosition		 * @param status		 */		void onHeadViewClick(int groupPosition, int status);		/**		 * 获取组按下的状态		 * 		 * @param groupPosition		 * @return		 */		int getHeadViewClickStatus(int groupPosition);	}	private static final int MAX_ALPHA = 255;	private IphoneTreeHeaderAdapter mAdapter;	/**	 * 用于在列表头显示的 View,mHeaderViewVisible 为 true 才可见	 */	private View mHeaderView;	/**	 * 列表头是否可见	 */	private boolean mHeaderViewVisible;	private int mHeaderViewWidth;	private int mHeaderViewHeight;	public void setHeaderView(View view) {		mHeaderView = view;		AbsListView.LayoutParams lp = new AbsListView.LayoutParams(				ViewGroup.LayoutParams.MATCH_PARENT,				ViewGroup.LayoutParams.WRAP_CONTENT);		view.setLayoutParams(lp);		if (mHeaderView != null) {			setFadingEdgeLength(0);		}		requestLayout();	}	private void registerListener() {		setOnScrollListener(this);		setOnGroupClickListener(this);	}	/**	 * 点击 HeaderView 触发的事件	 */	private void headerViewClick() {		long packedPosition = getExpandableListPosition(this				.getFirstVisiblePosition());		int groupPosition = ExpandableListView				.getPackedPositionGroup(packedPosition);		if (mAdapter.getHeadViewClickStatus(groupPosition) == 1) {			this.collapseGroup(groupPosition);			mAdapter.onHeadViewClick(groupPosition, 0);		} else {			this.expandGroup(groupPosition);			mAdapter.onHeadViewClick(groupPosition, 1);		}		this.setSelectedGroup(groupPosition);	}	private float mDownX;	private float mDownY;	/**	 * 如果 HeaderView 是可见的 , 此函数用于判断是否点击了 HeaderView, 并对做相应的处理 , 因为 HeaderView	 * 是画上去的 , 所以设置事件监听是无效的 , 只有自行控制 .	 */	@Override	public boolean onTouchEvent(MotionEvent ev) {		if (mHeaderViewVisible) {			switch (ev.getAction()) {			case MotionEvent.ACTION_DOWN:				mDownX = ev.getX();				mDownY = ev.getY();				if (mDownX <= mHeaderViewWidth && mDownY <= mHeaderViewHeight) {					return true;				}				break;			case MotionEvent.ACTION_UP:				float x = ev.getX();				float y = ev.getY();				float offsetX = Math.abs(x - mDownX);				float offsetY = Math.abs(y - mDownY);				// 如果 HeaderView 是可见的 , 点击在 HeaderView 内 , 那么触发 headerClick()				if (x <= mHeaderViewWidth && y <= mHeaderViewHeight						&& offsetX <= mHeaderViewWidth						&& offsetY <= mHeaderViewHeight) {					if (mHeaderView != null) {						headerViewClick();					}					return true;				}				break;			default:				break;			}		}		return super.onTouchEvent(ev);	}	@Override	public void setAdapter(ExpandableListAdapter adapter) {		super.setAdapter(adapter);		mAdapter = (IphoneTreeHeaderAdapter) adapter;	}	/**	 * 	 * 点击了 Group 触发的事件 , 要根据根据当前点击 Group 的状态来	 */	@Override	public boolean onGroupClick(ExpandableListView parent, View v,			int groupPosition, long id) {		if (mAdapter.getHeadViewClickStatus(groupPosition) == 0) {			mAdapter.onHeadViewClick(groupPosition, 1);			parent.expandGroup(groupPosition);			parent.setSelectedGroup(groupPosition);		} else if (mAdapter.getHeadViewClickStatus(groupPosition) == 1) {			mAdapter.onHeadViewClick(groupPosition, 0);			parent.collapseGroup(groupPosition);		}		// 返回 true 才可以弹回第一行 , 不知道为什么		return true;	}	@Override	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {		super.onMeasure(widthMeasureSpec, heightMeasureSpec);		if (mHeaderView != null) {			measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);			mHeaderViewWidth = mHeaderView.getMeasuredWidth();			mHeaderViewHeight = mHeaderView.getMeasuredHeight();		}	}	private int mOldState = -1;	@Override	protected void onLayout(boolean changed, int left, int top, int right,			int bottom) {		super.onLayout(changed, left, top, right, bottom);		final long flatPostion = getExpandableListPosition(getFirstVisiblePosition());		final int groupPos = ExpandableListView				.getPackedPositionGroup(flatPostion);		final int childPos = ExpandableListView				.getPackedPositionChild(flatPostion);		int state = mAdapter.getTreeHeaderState(groupPos, childPos);		if (mHeaderView != null && mAdapter != null && state != mOldState) {			mOldState = state;			mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);		}		configureHeaderView(groupPos, childPos);	}	public void configureHeaderView(int groupPosition, int childPosition) {		if (mHeaderView == null || mAdapter == null				|| ((ExpandableListAdapter) mAdapter).getGroupCount() == 0) {			return;		}		int state = mAdapter.getTreeHeaderState(groupPosition, childPosition);		switch (state) {		case IphoneTreeHeaderAdapter.PINNED_HEADER_GONE: {			mHeaderViewVisible = false;			break;		}		case IphoneTreeHeaderAdapter.PINNED_HEADER_VISIBLE: {			mAdapter.configureTreeHeader(mHeaderView, groupPosition,					childPosition, MAX_ALPHA);			if (mHeaderView.getTop() != 0) {				mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);			}			mHeaderViewVisible = true;			break;		}		case IphoneTreeHeaderAdapter.PINNED_HEADER_PUSHED_UP: {			View firstView = getChildAt(0);			int bottom = firstView.getBottom();			// intitemHeight = firstView.getHeight();			int headerHeight = mHeaderView.getHeight();			int y;			int alpha;			if (bottom < headerHeight) {				y = (bottom - headerHeight);				alpha = MAX_ALPHA * (headerHeight + y) / headerHeight;			} else {				y = 0;				alpha = MAX_ALPHA;			}			mAdapter.configureTreeHeader(mHeaderView, groupPosition,					childPosition, alpha);			if (mHeaderView.getTop() != y) {				mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight						+ y);			}			mHeaderViewVisible = true;			break;		}		}	}	@Override	/**	 * 列表界面更新时调用该方法(如滚动时)	 */	protected void dispatchDraw(Canvas canvas) {		super.dispatchDraw(canvas);		if (mHeaderViewVisible) {			// 分组栏是直接绘制到界面中,而不是加入到ViewGroup中			drawChild(canvas, mHeaderView, getDrawingTime());		}	}	@Override	public void onScroll(AbsListView view, int firstVisibleItem,			int visibleItemCount, int totalItemCount) {		final long flatPos = getExpandableListPosition(firstVisibleItem);		int groupPosition = ExpandableListView.getPackedPositionGroup(flatPos);		int childPosition = ExpandableListView.getPackedPositionChild(flatPos);		configureHeaderView(groupPosition, childPosition);	}	@Override	public void onScrollStateChanged(AbsListView view, int scrollState) {	}}

使用起来也是比较简单的,先在布局文件中声明activity_main.xml:

然后在MainActivity中调用,为了缩减代码,我把Adapter作为内部类放在MainActivity中了:

public class MainActivity extends Activity {	private LayoutInflater mInflater;	private IphoneTreeView iphoneTreeView;	@Override	protected void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.activity_main);		initView();	}	private void initView() {		// TODO Auto-generated method stub		mInflater = LayoutInflater.from(this);		iphoneTreeView = (IphoneTreeView) findViewById(R.id.iphone_tree_view);		iphoneTreeView.setHeaderView(getLayoutInflater().inflate(				R.layout.list_head_view, iphoneTreeView, false));		iphoneTreeView.setGroupIndicator(null);		iphoneTreeView.setAdapter(new IphoneTreeViewAdapter());	}	public class IphoneTreeViewAdapter extends BaseExpandableListAdapter			implements IphoneTreeHeaderAdapter {		// Sample data set. children[i] contains the children (String[]) for		// groups[i].		private HashMap
groupStatusMap; private String[] groups = { "第一组", "第二组", "第三组", "第四组" }; private String[][] children = { { "Way", "Arnold", "Barry", "Chuck", "David", "Afghanistan", "Albania", "Belgium", "Lily", "Jim", "LiMing", "Jodan" }, { "Ace", "Bandit", "Cha-Cha", "Deuce", "Bahamas", "China", "Dominica", "Jim", "LiMing", "Jodan" }, { "Fluffy", "Snuggles", "Ecuador", "Ecuador", "Jim", "LiMing", "Jodan" }, { "Goldy", "Bubbles", "Iceland", "Iran", "Italy", "Jim", "LiMing", "Jodan" } }; public IphoneTreeViewAdapter() { // TODO Auto-generated constructor stub groupStatusMap = new HashMap
(); } public Object getChild(int groupPosition, int childPosition) { return children[groupPosition][childPosition]; } public long getChildId(int groupPosition, int childPosition) { return childPosition; } public int getChildrenCount(int groupPosition) { return children[groupPosition].length; } public Object getGroup(int groupPosition) { return groups[groupPosition]; } public int getGroupCount() { return groups.length; } public long getGroupId(int groupPosition) { return groupPosition; } public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } public boolean hasStableIds() { return true; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { // TODO Auto-generated method stub if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item_view, null); } TextView tv = (TextView) convertView .findViewById(R.id.contact_list_item_name); tv.setText(getChild(groupPosition, childPosition).toString()); TextView state = (TextView) convertView .findViewById(R.id.cpntact_list_item_state); state.setText("爱生活...爱Android..."); return convertView; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { // TODO Auto-generated method stub if (convertView == null) { convertView = mInflater.inflate(R.layout.list_group_view, null); } TextView groupName = (TextView) convertView .findViewById(R.id.group_name); groupName.setText(groups[groupPosition]); ImageView indicator = (ImageView) convertView .findViewById(R.id.group_indicator); TextView onlineNum = (TextView) convertView .findViewById(R.id.online_count); onlineNum.setText(getChildrenCount(groupPosition) + "/" + getChildrenCount(groupPosition)); if (isExpanded) { indicator.setImageResource(R.drawable.indicator_expanded); } else { indicator.setImageResource(R.drawable.indicator_unexpanded); } return convertView; } @Override public int getTreeHeaderState(int groupPosition, int childPosition) { final int childCount = getChildrenCount(groupPosition); if (childPosition == childCount - 1) { return PINNED_HEADER_PUSHED_UP; } else if (childPosition == -1 && !iphoneTreeView.isGroupExpanded(groupPosition)) { return PINNED_HEADER_GONE; } else { return PINNED_HEADER_VISIBLE; } } @Override public void configureTreeHeader(View header, int groupPosition, int childPosition, int alpha) { // TODO Auto-generated method stub ((TextView) header.findViewById(R.id.group_name)) .setText(groups[groupPosition]); ((TextView) header.findViewById(R.id.online_count)) .setText(getChildrenCount(groupPosition) + "/" + getChildrenCount(groupPosition)); } @Override public void onHeadViewClick(int groupPosition, int status) { // TODO Auto-generated method stub groupStatusMap.put(groupPosition, status); } @Override public int getHeadViewClickStatus(int groupPosition) { if (groupStatusMap.containsKey(groupPosition)) { return groupStatusMap.get(groupPosition); } else { return 0; } } }}

好了,简单的一个例子就完成了,

总结一下:

1. 原理: 在正在显示的最上面的组的标签位置添加一个和组视图完全一样的视图,作为组标签。这个标签的位置要随着列表的滑动不断变化,以保持总是显示在最上方,并且该消失的时候就消失。给这个标签添加点击事件,实现打开和关闭分组的功能。 

2. 组标签总是显示在上方,这是通过不断的调整其在布局中的位置来实现的。这个调整的过程,在初始化的时候,在 onLayout 方法中实现一次,后面都是在滚动过程中,根据对滚动状态的监听来实现的。

3. 实例化要添加的标签的时候(在外面实现,即使调用 setTreeHeaderView之前),parent 要设为该ExpandableListView. 

4. 要学习以及好好理解这个,最好的方法是将添加进来的组标签设为半透明,便于观察整个过程。

源码奉上:

转载于:https://my.oschina.net/cjkall/blog/195785

你可能感兴趣的文章
我的友情链接
查看>>
一张图看清楚成功人士与失败人士的差别,成功人士的10个标志
查看>>
SimpleGame分析
查看>>
Cocos-2d CCLayer的触摸响应CCTouchDelegate和CCStandardTouchDelegate 和 CCTargetedTouchDelegate
查看>>
C#系列视频教程-字符和字符串操作
查看>>
asp.net中使用窗体身份验证
查看>>
别以为真懂Openstack: 虚拟机创建的50个步骤和100个知识点(5)
查看>>
ORA-01172、ORA-01151错误处理
查看>>
pl/sql 找到数组中公有的字符
查看>>
Spring MVC +MyBatis +MySQL 登录查询Demo 解决了mybatis异常【转】
查看>>
Junk Dimension
查看>>
win7下安装mysql-connector遇到的问题
查看>>
OSPF复习1
查看>>
恢复oracle数据库误更新
查看>>
ucenter整合心的
查看>>
回归测试
查看>>
eclipse+maven远程(自动)部署web项目到tomcat
查看>>
logrotate 介绍及使用
查看>>
chromium buildbot tree
查看>>
js禁止用户打开浏览器控制台或右键菜单查看源码
查看>>