SwipeMenuListView框架完全解析,swipemenulistview
SwipeMenuListView(滑动菜单)
A swipe menu for ListView.--一个非常好的滑动菜单开源项目。
Demo
一、简介
看了挺长时间的自定义View和事件分发,想找一个项目练习下。。正好印证自己所学。
在github上找到了这个项目:SwipeMenuListView这的真不错,对事件分发和自定义View都很有启发性,虽然还有点小瑕疵,后面说明。想了解滑动菜单怎么实现的同学,这篇文章绝对对你有帮助,从宏观微观角度详细分析了每个文件。
项目地址:https://github.com/baoyongzhang/SwipeMenuListView/tree/b00e0fe8c2b8d6f08607bfe2ab390c7cee8df274 版本:b00e0fe 它的使用很简单只需要三步,在github上就可以看懂就不占用篇幅啦,本文只分析原理。另外如果你看代码感觉和我不一样,看着困难的话,可以看我加了注释的:http://download.csdn.net/detail/jycboy/9667699
先看两个图:有一个大体的了解
这是框架中所有的类。
1.下面的图是视图层次:
上面的图中:SwipeMenuLayout是ListView中item的布局,分左右两部分,一部分是正常显示的contentView,一部分是滑出来的menuView;滑出来的SwipeMenuView继承自LinearLayout,添加view时,就是横向添加,可以横向添加多个。
2.下面的图是类图结构:
上面是类之间的调用关系,类旁边注明了类的主要作用。
二、源码分析
SwipeMenu?、SwipeMenuItem是实体类,定义了属性和setter、getter方法,看下就行。基本上源码的注释很清楚。
2.1 SwipeMenuView?: 代码中注释的很清楚
/** * 横向的LinearLayout,就是整个swipemenu的父布局 * 主要定义了添加Item的方法及Item的属性设置 * @author baoyz * @date 2014-8-23 * */ public class SwipeMenuView extends LinearLayout implements OnClickListener { private SwipeMenuListView mListView; private SwipeMenuLayout mLayout; private SwipeMenu mMenu; private OnSwipeItemClickListener onItemClickListener; private int position; public int getPosition() { return position; } public void setPosition(int position) { this.position = position; } public SwipeMenuView(SwipeMenu menu, SwipeMenuListView listView) { super(menu.getContext()); mListView = listView; mMenu = menu; // // MenuItem的list集合 List<SwipeMenuItem> items = menu.getMenuItems(); int id = 0; //通过item构造出View添加到SwipeMenuView中 for (SwipeMenuItem item : items) { addItem(item, id++); } } /** * 将 MenuItem 转换成 UI控件,一个item就相当于一个垂直的LinearLayout, * SwipeMenuView就是横向的LinearLayout, */ private void addItem(SwipeMenuItem item, int id) { //布局参数 LayoutParams params = new LayoutParams(item.getWidth(), LayoutParams.MATCH_PARENT); LinearLayout parent = new LinearLayout(getContext()); //设置menuitem的id,用于后边的点击事件区分item用的 parent.setId(id); parent.setGravity(Gravity.CENTER); parent.setOrientation(LinearLayout.VERTICAL); parent.setLayoutParams(params); parent.setBackgroundDrawable(item.getBackground()); //设置监听器 parent.setOnClickListener(this); addView(parent); //加入到SwipeMenuView中,横向的 if (item.getIcon() != null) { parent.addView(createIcon(item)); } if (!TextUtils.isEmpty(item.getTitle())) { parent.addView(createTitle(item)); } } //创建img private ImageView createIcon(SwipeMenuItem item) { ImageView iv = new ImageView(getContext()); iv.setImageDrawable(item.getIcon()); return iv; } /*根据参数创建title */ private TextView createTitle(SwipeMenuItem item) { TextView tv = new TextView(getContext()); tv.setText(item.getTitle()); tv.setGravity(Gravity.CENTER); tv.setTextSize(item.getTitleSize()); tv.setTextColor(item.getTitleColor()); return tv; } @Override /** * 用传来的mLayout判断是否打开 * 调用onItemClick点击事件 */ public void onClick(View v) { if (onItemClickListener != null && mLayout.isOpen()) { onItemClickListener.onItemClick(this, mMenu, v.getId()); } } public OnSwipeItemClickListener getOnSwipeItemClickListener() { return onItemClickListener; } /** * 设置item的点击事件 * @param onItemClickListener */ public void setOnSwipeItemClickListener(OnSwipeItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } public void setLayout(SwipeMenuLayout mLayout) { this.mLayout = mLayout; } /** * 点击事件的回调接口 */ public static interface OnSwipeItemClickListener { /** * onClick点击事件中调用onItemClick * @param view 父布局 * @param menu menu实体类 * @param index menuItem的id */ void onItemClick(SwipeMenuView view, SwipeMenu menu, int index); } }
**SwipeMenuView?就是滑动时显示的View,看他的构造函数SwipeMenuView(SwipeMenu menu, SwipeMenuListView listView)?;遍历Items:menu.getMenuItems();调用addItem方法向?SwipeMenuView中添加item。
在addItem方法中:每一个item都是一个LinearLayout?。
2.2 SwipeMenuLayout?:
这个类代码有点长,我们分成三部分看,只粘贴核心代码,剩下的看一下应该就懂啦。
public class SwipeMenuLayout extends FrameLayout { private static final int CONTENT_VIEW_ID = 1; private static final int MENU_VIEW_ID = 2; private static final int STATE_CLOSE = 0; private static final int STATE_OPEN = 1; //方向 private int mSwipeDirection; private View mContentView; private SwipeMenuView mMenuView; 。。。。。 public SwipeMenuLayout(View contentView, SwipeMenuView menuView) { this(contentView, menuView, null, null); } public SwipeMenuLayout(View contentView, SwipeMenuView menuView, Interpolator closeInterpolator, Interpolator openInterpolator) { super(contentView.getContext()); mCloseInterpolator = closeInterpolator; mOpenInterpolator = openInterpolator; mContentView = contentView; mMenuView = menuView; //将SwipeMenuLayout设置给SwipeMenuView,用于判断是否打开 mMenuView.setLayout(this); init(); } private void init() { setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); mGestureListener = new SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { isFling = false; return true; } @Override //velocityX这个参数是x轴方向的速率,向左是负的,向右是正的 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // TODO if (Math.abs(e1.getX() - e2.getX()) > MIN_FLING && velocityX < MAX_VELOCITYX) { isFling = true; } Log.i("tag","isFling="+isFling+" e1.getX()="+e1.getX()+" e2.getX()="+e2.getX()+ " velocityX="+velocityX+" MAX_VELOCITYX="+MAX_VELOCITYX); // Log.i("byz", MAX_VELOCITYX + ", velocityX = " + velocityX); return super.onFling(e1, e2, velocityX, velocityY); } }; mGestureDetector = new GestureDetectorCompat(getContext(), mGestureListener); 。。。。 LayoutParams contentParams = new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); mContentView.setLayoutParams(contentParams); if (mContentView.getId() < 1) { //noinspection ResourceType mContentView.setId(CONTENT_VIEW_ID); } //noinspection ResourceType mMenuView.setId(MENU_VIEW_ID); mMenuView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); addView(mContentView); addView(mMenuView); }
从上边的init方法中可以看出SwipeMenuLayout由两部分组成,分别是用户的 item View 和 menu View 。手