Android“上拉刷新/下拉加载”与“侧滑菜单”的兼容

在Android系统中,“上拉刷新/下拉加载更多”和“侧滑菜单”都是非常常用的操作界面,二者都比较容易,网上也有许多牛人做好的库可以直接使用。可是很少有讲解如何让两者并存的方法,前不久在一个项目中需要在已有侧滑菜单的应用中,对其中一个菜单项加入上拉/下拉菜单。由于都要捕捉触摸事件,这两者之间可能会产生一些冲突。这里记录一下我的解决方案和步骤,也希望能够为遇到同样问题的朋友们提供一些思路。

1、侧滑菜单

单独加入侧滑菜单还是比较容易的,这里我是参照了网上一个牛人写的一个Demo,源代码可以点击下载。 其中SlidingLayout.java这个是侧滑的布局文件,注释很详细,大部分的代码都不需要修改。需要注意的是在xml文件中,SlidingLayout里只能有两个子元素,左侧为菜单(如ListView),右侧为界面。然后将需要监听侧滑事件的控件通过slidingLayout.setScrollEvent(View view)函数设置好就OK。 注:这里实际上是通过view的触摸监听器onTouchListener()实现的

2、上拉刷新/下拉加载更多

对于上拉/下拉界面,网上比较流传的版本是pull_to_refresh这个库,源代码点击下载。同样,注释非常详细,用法也很简单。需要注意在xml中,RefreshableView标签只能有一个ListView。也就是用于下拉刷新的listview,然后在通过RefreshableView.setOnRefreshListener(PullToRefresh Listener listener, int id)方法设置需要下拉刷新的布局即可完成。 **注:同样,在内部这里也是通过控件的触摸监听器完成的。

3、二者的冲突原因及解决方案

以上两种界面分开做都有很多简单易用的库,但是当合在一起的时候会发现容易有冲突。主要原因是两者实现原理都是通过监听控件的触摸事件完成,而大多数时候我们需要下拉的和侧滑的都是同样一个控件,这样就会导致同一个控件被设置了两次setOnTouchListener(),结果后一次的会覆盖掉前一次的,这也就是为什么我们会发现二者无法兼容。我所想到的解决方案有以下几种:

方法一:分开监听布局

既然同一个布局只能设置一次触摸监听器,那么只有让下拉刷新和侧滑分别对不同的控件进行监听。这里很明显下拉刷新肯定是要对listview进行操作的,那么我们需要修改的就是侧滑的监听事件。可以将侧滑的setScrollEvent参数设置为listview的父布局,然后在父布局中判断用户的触摸行为。如果判定用户动作为上下滑动,则将触摸事件传递给子布局处理,即下拉刷新。反之如果判定为左右滑动,则在父布局中直接拦截事件,并在父布局中处理事件,即侧滑菜单。具体操作如下: 1. 新建一个自定义布局,作为下拉的listview父布局。并通过setScrollEvent对父控件加入侧滑监听。 2. 在父布局中覆盖onInterceptTouchEvent方法。用于拦截触摸事件。 3. 接着覆盖onTouch方法,当事件被拦截时,调用本类中的onTouch处理触摸事件。 4. 通过setOnRefreshListener对listview加入下拉刷新功能。

其中需要了解onInterceptTouchEvent的功能。主要用于拦截事件,当控件被触摸的时此方法第一个被调用,返回true则父布局拦截,事件不会传入子布局(即listview)。而在本布局的onTouch方法中处理。若返回false,则事件被传入子布局处理。 这样在父布局中判断用户行为,即可将侧滑和下拉分开处理。 这个方法虽然可以实现功能,但感觉不太灵活,而且本人真机测试后会有明显卡顿现象,最后没有使用方法一,而是使用下面的方法。

方法二:加入官方支持包

谷歌官方在android-support-v4支持包中加入了下拉刷新类库SwipeRefreshLayout。查看官方源码后发现底层并非简单的监听onTouch事件完成,可以完美的解决冲突问题。用法也很简单。 导入v4支持包之后,将要下拉的控件(如listview)布局外再套一个SwipeRefreshLayout布局即可,,然后通过refreshableView.setOnRefreshListener()方法设置一个监听内部类即可:

refreshableView.setOnRefreshListener(new OnRefreshListener(){(){//tbd}});4、加入上拉加载更多

官方的支持包中只有下拉刷新功能,如果需要上拉加载更多,需要对官方包进行扩展。方法如下: 写一个自定义布局继承自SwipeRefreshLayout(直接使用官方的下拉)。然后再里面加入上拉加载的代码,如下:

/** * 继承自SwipeRefreshLayout,从而实现滑动到底部时上拉加载更多的功能. */{/*** 滑动到最下面时的上拉操作*/private int mTouchSlop;/*** listview实例*/private ListView mListView;/*** 上拉监听器, 到了最底部的上拉加载操作*/private OnLoadListener mOnLoadListener;/*** ListView的加载中footer*/private View mListViewFooter;/*** 按下时的y坐标*/private int mYDown;/*** 抬起时的y坐标, 与mYDown一起用于滑动到底部时判断是上拉还是下拉*/private int mLastY;/*** 是否在加载中 ( 上拉加载更多 )*/private boolean isLoading = false;/*** @param context*/public RefreshLayout(Context context){this(context, null);}public RefreshLayout(Context context, AttributeSet attrs){super(context, attrs);mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();mListViewFooter = LayoutInflater.from(context).inflate(R.layout.pull_up_refresh, null, false);}(boolean changed, int left, int top, int right,int bottom){super.onLayout(changed, left, top, right, bottom);// 初始化ListView对象if (mListView == null){getListView();}}/*** 获取ListView对象*/(){int childs = getChildCount();if (childs > 0){View childView = getChildAt(0);if (childView instanceof ListView){mListView = (ListView) childView;// 设置滚动监听器给ListView, 使得滚动的情况下也可以自动加载mListView.setOnScrollListener(this);Log.d(VIEW_LOG_TAG, “### 找到listview”);}}}/** (non-Javadoc)** @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)*/(MotionEvent event){final int action = event.getAction();switch (action){case MotionEvent.ACTION_DOWN:// 按下mYDown = (int) event.getRawY();break;case MotionEvent.ACTION_MOVE:// 移动mLastY = (int) event.getRawY();break;case MotionEvent.ACTION_UP:// 抬起if (canLoad()){loadData();}break;default:break;}return super.dispatchTouchEvent(event);}/*** 是否可以加载更多, 条件是到了最底部, listview不在加载中, 且为上拉操作.** @return*/(){return isBottom() && !isLoading && isPullUp();}/*** 判断是否到了最底部*/(){if (mListView != null && mListView.getAdapter() != null){return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() – 1);}return false;}/*** 是否是上拉操作** @return*/(){return (mYDown – mLastY) >= mTouchSlop;}/*** 如果到了最底部,而且是上拉操作.那么执行onLoad方法*/(){if (mOnLoadListener != null){// 设置状态setLoading(true);//mOnLoadListener.onLoad();}}/*** @param loading*/(boolean loading){isLoading = loading;if (isLoading){mListView.addFooterView(mListViewFooter);}else{mListView.removeFooterView(mListViewFooter);mYDown = 0;mLastY = 0;}}/*** @param loadListener*/(OnLoadListener loadListener){mOnLoadListener = loadListener;}(AbsListView view, int scrollState){}(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount){// 滚动时到了最底部也可以加载更多if (canLoad()){loadData();}}/*** 加载更多的监听器*/{();}}贪婪是最真实的贫穷,满足是最真实的财富

Android“上拉刷新/下拉加载”与“侧滑菜单”的兼容

相关文章:

你感兴趣的文章:

标签云: