ScrollView嵌套SwipeMenuListView的一些改进

作者 信马归风
2017.06.06 02:06 字数 4397 阅读 308评论 1

概述

dome

这种侧滑菜单大家都不会陌生,自从qq使用了了这种方式之后,网上有很多实现方法。公司有一个业务需要这种模式,本想在github上找个现成的直接使用。于是找到了这个

https://github.com/baoyongzhang/SwipeMenuListView

问题

尽管这个项目实现了大多数功能,但是这个项目存在几个问题

  1. 当其放置于scrollView中,上下滑动和左右滑动有时候会出现冲突,导致菜单收不回来,并且时而滑动艰难

  2. 与部分下拉刷新组件会产生冲突

  3. 每次都只能拉开一个抽屉(业务需要同时打开多个)

解决问题

解决问题1、2

既然是滑动冲突,就按照一般的滑动冲突的解决方法来解决。

为SwipeMenuListView 添加onTouch监听器。根据滑动水平方向与竖直方向的距离来处理。核心是下面的方法

requestDisallowInterceptTouchEvent

这个方法可以让父控件不去拦截这次滑动事件。我们只需设置计算出手指水平方向的偏移量。设置一个阈值即可。


if (Math.abs(localWigth - sx) > 30) {

scrollView.requestDisallowInterceptTouchEvent(true);

}

else{

scrollView.requestDisallowInterceptTouchEvent(false);

}

对于第二个问题,一般出现于那种原理是嵌套于ScrollView或者ListView 并在dispatchTouchEvent进行了流程控制的一些下拉刷新组件。如果你遇到到这种问题用上面的方式是无效的。当然以仿照这种方式在下拉刷新组件的dispatchTouchEvent函数中添加标志位,与scrollView 进行同样的控制

refreshScrollParentViewBase 是下拉刷新组件


@Override

public boolean onTouch(View v, MotionEvent motionEvent) {

if (scrollView == null || refreshScrollParentViewBase == null) {

GetView();

}

switch (motionEvent.getAction()) {

case MotionEvent.ACTION_DOWN:

localWigth = (int) motionEvent.getX();

localHeigth = (int) motionEvent.getY();

break;

case MotionEvent.ACTION_MOVE:

int sx = (int) motionEvent.getX();

if (Math.abs(localWigth - sx) > 30) {

scrollView.requestDisallowInterceptTouchEvent(true);

refreshScrollParentViewBase.setDispath(false);

} else {

scrollView.requestDisallowInterceptTouchEvent(false);

refreshScrollParentViewBase.setDispath(true);

}

break;

case MotionEvent.ACTION_UP:

scrollView.requestDisallowInterceptTouchEvent(false);

refreshScrollParentViewBase.setDispath(true);

localWigth = 0;

localHeigth = 0;

break;

}

return false;

}

别忘了scrollView 与listview 嵌套需要


@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

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

super.onMeasure(widthMeasureSpec, expandSpec);

}

解决问题3

其实解决前两个问题并不需要,对SwipeMenuListView的实现方式进行很深的了解。然而如果想要SwipeMenuListView同时展开多个,便需要大致了解一下SwipeMenuListView的实现原理。

SwipeMenuListView 侧滑本身是由 SwipeMenuLayout 类中 onSwipe()的方式来进行侧滑,onSwipe()里面的实现方式是用onlayout方法进行偏移。但是我在再使用SwipeMenuListView的时候传入的是我们自己的View,它是什么时候讲我们的普通View转变成可以支持侧滑的SwipeMenuLayout的呢?

他提供了SwipeMenuAdapter ,但是我们并不直接使用,那么他的功能是什么呢。让我们看看在适配器中做了什么。


@Override

public View getView(int position, View convertView, ViewGroup parent) {

SwipeMenuLayout layout = null;

if (convertView == null) {

View contentView = mAdapter.getView(position, convertView, parent);

SwipeMenu menu = new SwipeMenu(mContext);

menu.setViewType(mAdapter.getItemViewType(position));

createMenu(menu);

SwipeMenuView menuView = new SwipeMenuView(menu,

(SwipeMenuListView) parent);

menuView.setOnSwipeItemClickListener(this);

SwipeMenuListView listView = (SwipeMenuListView) parent;

layout = new SwipeMenuLayout(contentView, menuView,

listView.getCloseInterpolator(),

listView.getOpenInterpolator());

layout.setPosition(position);

} else {

layout = (SwipeMenuLayout) convertView;

layout.closeMenu();

layout.setPosition(position);

View view = mAdapter.getView(position, layout.getContentView(),

parent);

}

if(SwipeMenuListView.is_edit)

{

layout.setSwipeDirection(1);

layout.smoothOpenMenu();

}

return layout;

}

public void createMenu(SwipeMenu menu) {

// Test Code

SwipeMenuItem item = new SwipeMenuItem(mContext);

item.setTitle("Item 1");

item.setBackground(new ColorDrawable(Color.GRAY));

item.setWidth(300);

menu.addMenuItem(item);

item = new SwipeMenuItem(mContext);

item.setTitle("Item 2");

item.setBackground(new ColorDrawable(Color.RED));

item.setWidth(300);

menu.addMenuItem(item);

}

这是一个很巧妙方式,对我们传入的adapter进行了装饰。在getview 中将我们传入的adapter.getView()得到的View 拼装出SwipeMenuLayout。并将其返回给listview.

粗略的了解了侧滑菜单的实现方式,那我们来解决我们的问题吧,到底是哪里限制了SwipeMenuListView每次只能弹出一个菜单?

在SwipeMenuListView 中声明了如下变量

private SwipeMenuLayout mTouchView;

观测其中的onTouchEvent方法,可知,这个变量便存储当前正在操作的滑动视图

打开某一个菜单有这样一种方法


public void smoothOpenMenu(int position) {

if (position >= getFirstVisiblePosition()

&& position <= getLastVisiblePosition()) {

View view = getChildAt(position - getFirstVisiblePosition());

if (view instanceof SwipeMenuLayout) {

mTouchPosition = position;

if (mTouchView != null && mTouchView.isOpen()) {

mTouchView.smoothCloseMenu();

}

mTouchView = (SwipeMenuLayout) view;

mTouchView.setSwipeDirection(mDirection);

mTouchView.smoothOpenMenu();

}

}

}

由代码可知,每次打开一个菜单,会先调用mTouchView.smoothCloseMenu这样就导致每次只能打开一个菜单。

如果我们希望能够同时打开所有菜单,可以当前listview当中所有的SwipeMenuLayout的smoothOpenMenu即可。

添加打开和关闭所有菜单的方法


public void ShowAllMenu() {

is_edit = true;

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

View view=getChildAt(i);

if (view instanceof SwipeMenuLayout) {

SwipeMenuLayout menuView = (SwipeMenuLayout) view;

if(!menuView.isOpen())

{

menuView.setSwipeDirection(mDirection);

menuView.smoothOpenMenu();

}

}

}

}

public void HideAllMenu() {

is_edit = false;

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

View view=getChildAt(i);

if (view instanceof SwipeMenuLayout) {

SwipeMenuLayout menuView = (SwipeMenuLayout) view;

if(menuView.isOpen())

{

menuView.setSwipeDirection(mDirection);

menuView.smoothCloseMenu();

}

}

}

}

is_edit变量,我说一下,由于listview中视图的复用机制。当前视图不可见时,会被回收,之后通过adapter.getView方法重新创建。因此我们要根据position去保存每个item的状态。然后在getview之后根据状态进行恢复。


if(SwipeMenuListView.is_edit)

{

layout.setSwipeDirection(1);

layout.smoothOpenMenu();

}

我的业务是要求一起开,一起关,所以一个变量即可记录,想要支持记录每个状态,可以对应存储。

最后来看一眼效果图

效果图

发表评论

说点什么吧!留下邮箱让我好回复你。 必填项已用*标注