欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android多种滑动冲突的解决方案

程序员文章站 2023-12-19 14:14:28
一、前言 android 中解决滑动的方案有2种:外部拦截法 和内部拦截法。 滑动冲突也存在2种场景: 横竖滑动冲突、同向滑动冲突。 所以我就写了4...

一、前言

android 中解决滑动的方案有2种:外部拦截法 和内部拦截法。

滑动冲突也存在2种场景: 横竖滑动冲突、同向滑动冲突。

所以我就写了4个例子来学习如何解决滑动冲突的,这四个例子分别为: 外部拦截法解决横竖冲突、外部拦截法解决同向冲突、内部拦截法解决横竖冲突、内部拦截法解决同向冲突。

先上效果图:

android多种滑动冲突的解决方案

二、实战

1、外部拦截法,解决横竖冲突

思路是,重写父控件的onintercepttouchevent方法,然后根据具体的需求,来决定父控件是否拦截事件。如果拦截返回返回true,不拦截返回false。如果父控件拦截了事件,则在父控件的ontouchevent进行相应的事件处理。

我的这个例子,是一个横向滑动的viewgroup里面包含了3个竖向滑动的listview。下面我附上代码,horizontalex.java:

/**
 * created by blueberry on 2016/6/20.
 *
 * 解决交错的滑动冲突
 *
 * 外部拦截法
 */
public class horizontalex extends viewgroup {

 private static final string tag = "horizontalex";

 private boolean isfirsttouch = true;
 private int childindex;
 private int childcount;
 private int lastxintercept, lastyintercept, lastx, lasty;

 private scroller mscroller;
 private velocitytracker mvelocitytracker;

 public horizontalex(context context) {
  super(context);
  init();
 }

 public horizontalex(context context, attributeset attrs) {
  super(context, attrs);
  init();
 }

 public horizontalex(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
  init();
 }

 private void init() {
  mscroller = new scroller(getcontext());
  mvelocitytracker = velocitytracker.obtain();
 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  int width = measurespec.getsize(widthmeasurespec);
  int height = measurespec.getsize(heightmeasurespec);
  int widthmode = measurespec.getmode(widthmeasurespec);
  int heightmode = measurespec.getmode(heightmeasurespec);

  childcount = getchildcount();
  measurechildren(widthmeasurespec, heightmeasurespec);

  if (childcount == 0) {
   setmeasureddimension(0, 0);
  } else if (widthmode == measurespec.at_most && heightmode == measurespec.at_most) {
   width = childcount * getchildat(0).getmeasuredwidth();
   height = getchildat(0).getmeasuredheight();
   setmeasureddimension(width, height);
  } else if (widthmode == measurespec.at_most) {
   width = childcount * getchildat(0).getmeasuredwidth();
   setmeasureddimension(width, height);
  } else {
   height = getchildat(0).getmeasuredheight();
   setmeasureddimension(width, height);
  }
 }

 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b) {
  int left = 0;
  for (int i = 0; i < getchildcount(); i++) {
   final view child = getchildat(i);
   child.layout(left + l, t, r + left, b);
   left += child.getmeasuredwidth();
  }
 }

 /**
  * 拦截事件
  * @param ev
  * @return
  */
 @override
 public boolean onintercepttouchevent(motionevent ev) {
  boolean intercepted = false;
  int x = (int) ev.getx();
  int y = (int) ev.gety();

  switch (ev.getaction()) {
   /*如果拦截了down事件,则子类不会拿到这个事件序列*/
   case motionevent.action_down:
    lastxintercept = x;
    lastyintercept = y;
    intercepted = false;
    if (!mscroller.isfinished()) {
     mscroller.abortanimation();
     intercepted = true;
    }
    break;
   case motionevent.action_move:
    final int deltax = x - lastxintercept;
    final int deltay = y - lastyintercept;
    /*根据条件判断是否拦截该事件*/
    if (math.abs(deltax) > math.abs(deltay)) {
     intercepted = true;
    } else {
     intercepted = false;
    }
    break;
   case motionevent.action_up:
    intercepted = false;
    break;

  }
  lastxintercept = x;
  lastyintercept = y;
  return intercepted;
 }


 @override
 public boolean ontouchevent(motionevent event) {
  int x = (int) event.getx();
  int y = (int) event.gety();
  mvelocitytracker.addmovement(event);
  viewconfiguration configuration = viewconfiguration.get(getcontext());
  switch (event.getaction()) {
   case motionevent.action_down:
    if (!mscroller.isfinished()) {
     mscroller.abortanimation();
    }
    break;
   case motionevent.action_move:
    /*因为这里父控件拿不到down事件,所以使用一个布尔值,
     当事件第一次来到父控件时,对lastx,lasty赋值*/
    if (isfirsttouch) {
     lastx = x;
     lasty = y;
     isfirsttouch = false;
    }
    final int deltax = x - lastx;
    scrollby(-deltax, 0);
    break;
   case motionevent.action_up:
    int scrollx = getscrollx();
    final int childwidth = getchildat(0).getwidth();
    mvelocitytracker.computecurrentvelocity(1000, configuration.getscaledmaximumflingvelocity());
    float xvelocity = mvelocitytracker.getxvelocity();
    if (math.abs(xvelocity) > configuration.getscaledminimumflingvelocity()) {
     childindex = xvelocity < 0 ? childindex + 1 : childindex - 1;
    } else {
     childindex = (scrollx + childwidth / 2) / childwidth;
    }
    childindex = math.min(getchildcount() - 1, math.max(childindex, 0));
    smoothscrollby(childindex * childwidth - scrollx, 0);
    mvelocitytracker.clear();
    isfirsttouch = true;
    break;
  }

  lastx = x;
  lasty = y;
  return true;
 }

 void smoothscrollby(int dx, int dy) {
  mscroller.startscroll(getscrollx(), getscrolly(), dx, dy, 500);
  invalidate();
 }

 @override
 public void computescroll() {
  if (mscroller.computescrolloffset()) {
   scrollto(mscroller.getcurrx(), mscroller.getcurry());
   invalidate();
  }
 }

 @override
 protected void ondetachedfromwindow() {
  super.ondetachedfromwindow();
  mvelocitytracker.recycle();
 }
}

调用代码:

@override
 public void showouthvdata(list<string> data1, list<string> data2, list<string> data3) {
  listview listview1 = new listview(getcontext());
  arrayadapter<string> adapter1 = new arrayadapter<string>(getcontext(), android.r.layout.simple_list_item_1, data1);
  listview1.setadapter(adapter1);

  listview listview2 = new listview(getcontext());
  arrayadapter<string> adapter2 = new arrayadapter<string>(getcontext(), android.r.layout.simple_list_item_1, data2);
  listview2.setadapter(adapter2);

  listview listview3 = new listview(getcontext());
  arrayadapter<string> adapter3 = new arrayadapter<string>(getcontext(), android.r.layout.simple_list_item_1, data3);
  listview3.setadapter(adapter3);

  viewgroup.layoutparams params
    = new viewgroup.layoutparams(viewgroup.layoutparams.match_parent,
    viewgroup.layoutparams.match_parent);

  mhorizontalex.addview(listview1, params);
  mhorizontalex.addview(listview2, params);
  mhorizontalex.addview(listview3, params);
 }

其实外部拦截的主要思想都在于对onintercepttouchevent的重写。

@override
 public boolean onintercepttouchevent(motionevent ev) {
  boolean intercepted = false;
  int x = (int) ev.getx();
  int y = (int) ev.gety();

  switch (ev.getaction()) {
   /*如果拦截了down事件,则子类不会拿到这个事件序列*/
   case motionevent.action_down:
    lastxintercept = x;
    lastyintercept = y;
    intercepted = false;
    if (!mscroller.isfinished()) {
     mscroller.abortanimation();
     intercepted = true;
    }
    break;
   case motionevent.action_move:
    final int deltax = x - lastxintercept;
    final int deltay = y - lastyintercept;
    /*根据条件判断是否拦截该事件*/
    if (math.abs(deltax) > math.abs(deltay)) {
     intercepted = true;
    } else {
     intercepted = false;
    }
    break;
   case motionevent.action_up:
    intercepted = false;
    break;

  }
  lastxintercept = x;
  lastyintercept = y;
  return intercepted;
 }

这几乎是一个实现外部拦截事件的模板,这里一定不要在action_down 中返回 true,否则会让子view没有机会得到事件,因为如果在action_down的时候返回了 true,同一个事件序列viewgroup的dispatchtouchevent就不会在调用onintercepttouchevent方法了。

还有就是 在action_up中返回false,因为如果父控件拦截了action_up,那么子view将得不到up事件,那么将会影响子view的 onclick方法等。但这对父控件是没有影响的,因为如果是父控件子aciton_move中 就拦截了事件,他们up事件必定也会交给它处理,因为有那么一条定律叫做:父控件一但拦截了事件,那么同一个事件序列的所有事件都将交给他处理。这条结论在我的上一篇文章中已经分析过。

最后就是在 action_move中根据需求决定是否拦截。

2、内部拦截法,解决横竖冲突

内部拦截主要依赖于父控件的 requestdisallowintercepttouchevent方法,关于这个方法我的上篇文章其实已经分析过。他设置父控件的一个标志(flag_disallow_intercept)

这个标志可以决定父控件是否拦截事件,如果设置了这个标志则不拦截,如果没设这个标志,它就会调用父控件的onintercepttouchevent()来询问父控件是否拦截。但这个标志对down事件无效。

可以参考一下源码:   

 // handle an initial down.
   if (actionmasked == motionevent.action_down) {
    // throw away all previous state when starting a new touch gesture.
    // the framework may have dropped the up or cancel event for the previous gesture
    // due to an app switch, anr, or some other state change.
    cancelandcleartouchtargets(ev);
    //清楚标志
    resettouchstate();
   }

   // check for interception.
   final boolean intercepted;
   if (actionmasked == motionevent.action_down
     || mfirsttouchtarget != null) {
     //标志
    final boolean disallowintercept = (mgroupflags & flag_disallow_intercept) != 0;
    if (!disallowintercept) {
     intercepted = onintercepttouchevent(ev);
     ev.setaction(action); // restore action in case it was changed
    } else {
     intercepted = false;
    }
   } else {
    // there are no touch targets and this action is not an initial down
    // so this view group continues to intercept touches.
    intercepted = true;
   }

那么我们如果想使用 内部拦截法拦截事件。

第一步:

a、我们要重写父控件的onintercepttouchevent,在action_down的时候返回false,负责的话子view调用requestdisallowintercepttouchevent也将无能为力。

b、还有就是其他事件的话都返回true,这样就把能否拦截事件的权利交给了子view。

第二步:

在子view的dispatchtouchevent中 来决定是否让父控件拦截事件。

a. 先要在motionevent.action_down:的时候使用mhorizontalex2.requestdisallowintercepttouchevent(true);,负责的话,下一个事件到来时,就交给父控件了。

b. 然后在motionevent.action_move: 根据业务逻辑决定是否调用mhorizontalex2.requestdisallowintercepttouchevent(false);来决定父控件是否拦截事件。

上代码horizontalex2.java:

/**
 * created by blueberry on 2016/6/20.
 * 内部拦截
 * 和 listviewex配合使用
 */
public class horizontalex2 extends viewgroup {

 private int lastx, lasty;
 private int childindex;
 private scroller mscroller;
 private velocitytracker mvelocitytracker;

 public horizontalex2(context context) {
  super(context);
  init();
 }

 public horizontalex2(context context, attributeset attrs) {
  super(context, attrs);
  init();
 }

 public horizontalex2(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
  init();
 }

 private void init() {
  mscroller = new scroller(getcontext());
  mvelocitytracker = velocitytracker.obtain();
 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  int width = measurespec.getsize(widthmeasurespec);
  int widthmode = measurespec.getmode(widthmeasurespec);
  int height = measurespec.getsize(heightmeasurespec);
  int heightmode = measurespec.getmode(heightmeasurespec);

  int childcount = getchildcount();
  measurechildren(widthmeasurespec, heightmeasurespec);

  if (childcount == 0) {
   setmeasureddimension(0, 0);
  } else if (widthmode == measurespec.at_most && heightmode == measurespec.at_most) {
   height = getchildat(0).getmeasuredheight();
   width = childcount * getchildat(0).getmeasuredwidth();
   setmeasureddimension(width, height);
  } else if (widthmode == measurespec.at_most) {
   width = childcount * getchildat(0).getmeasuredwidth();
   setmeasureddimension(width, height);
  } else {
   height = getchildat(0).getmeasuredheight();
   setmeasureddimension(width, height);
  }
 }

 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b) {
  int leftoffset = 0;
  for (int i = 0; i < getchildcount(); i++) {
   view child = getchildat(i);
   child.layout(l + leftoffset, t, r + leftoffset, b);
   leftoffset += child.getmeasuredwidth();
  }
 }

 /**
  * 不拦截down事件,其他一律拦截
  * @param ev
  * @return
  */
 @override
 public boolean onintercepttouchevent(motionevent ev) {
  if (ev.getaction() == motionevent.action_down) {
   if (!mscroller.isfinished()) {
    mscroller.abortanimation();
    return true;
   }
   return false;
  } else {
   return true;
  }
 }

 private boolean isfirsttouch = true;

 @override
 public boolean ontouchevent(motionevent event) {
  int x = (int) event.getx();
  int y = (int) event.gety();
  mvelocitytracker.addmovement(event);
  viewconfiguration configuration = viewconfiguration.get(getcontext());
  switch (event.getaction()) {
   case motionevent.action_down:
    if (!mscroller.isfinished()) {
     mscroller.abortanimation();
    }
    break;
   case motionevent.action_move:
    if (isfirsttouch) {
     isfirsttouch = false;
     lasty = y;
     lastx = x;
    }
    final int deltax = x - lastx;
    scrollby(-deltax, 0);
    break;
   case motionevent.action_up:
    isfirsttouch = true;
    int scrollx = getscrollx();
    mvelocitytracker.computecurrentvelocity(1000, configuration.getscaledmaximumflingvelocity());
    float mvelocityx = mvelocitytracker.getxvelocity();
    if (math.abs(mvelocityx) > configuration.getscaledminimumflingvelocity()) {
     childindex = mvelocityx < 0 ? childindex + 1 : childindex - 1;
    } else {
     childindex = (scrollx + getchildat(0).getwidth() / 2) / getchildat(0).getwidth();
    }
    childindex = math.min(getchildcount() - 1, math.max(0, childindex));
    smoothscrollby(childindex*getchildat(0).getwidth()-scrollx,0);
    mvelocitytracker.clear();
    break;
  }

  lastx = x;
  lasty = y;
  return true;
 }

 private void smoothscrollby(int dx, int dy) {
  mscroller.startscroll(getscrollx(), getscrolly(), dx, dy,500);
  invalidate();
 }

 @override
 public void computescroll() {
  if(mscroller.computescrolloffset()){
   scrollto(mscroller.getcurrx(),mscroller.getcurry());
   postinvalidate();
  }
 }

 @override
 protected void ondetachedfromwindow() {
  super.ondetachedfromwindow();
  mvelocitytracker.recycle();
 }
}

listviewex.java

/**
 * 内部拦截事件
 */
public class listviewex extends listview {

 private int lastxintercepted, lastyintercepted;

 private horizontalex2 mhorizontalex2;

 public listviewex(context context) {
  super(context);
 }

 public listviewex(context context, attributeset attrs) {
  super(context, attrs);
 }

 public listviewex(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
 }

 public horizontalex2 getmhorizontalex2() {
  return mhorizontalex2;
 }

 public void setmhorizontalex2(horizontalex2 mhorizontalex2) {
  this.mhorizontalex2 = mhorizontalex2;
 }

 /**
  * 使用 outter.requestdisallowintercepttouchevent();
  * 来决定父控件是否对事件进行拦截
  * @param ev
  * @return
  */
 @override
 public boolean dispatchtouchevent(motionevent ev) {
  int x = (int) ev.getx();
  int y = (int) ev.gety();
  switch (ev.getaction()) {
   case motionevent.action_down:
    mhorizontalex2.requestdisallowintercepttouchevent(true);
    break;
   case motionevent.action_move:
    final int deltax = x-lastyintercepted;
    final int deltay = y-lastyintercepted;
    if(math.abs(deltax)>math.abs(deltay)){
     mhorizontalex2.requestdisallowintercepttouchevent(false);
    }
    break;
   case motionevent.action_up:
    break;
  }
  lastxintercepted = x;
  lastyintercepted = y;
  return super.dispatchtouchevent(ev);
 }
}

调用代码:

 @override
 public void showinnerhvdata(list<string> data1, list<string> data2, list<string> data3) {

  listviewex listview1 = new listviewex(getcontext());
  arrayadapter<string> adapter1 = new arrayadapter<string>(getcontext(), android.r.layout.simple_list_item_1, data1);
  listview1.setadapter(adapter1);
  listview1.setmhorizontalex2(mhorizontalex2);

  listviewex listview2 = new listviewex(getcontext());
  arrayadapter<string> adapter2 = new arrayadapter<string>(getcontext(), android.r.layout.simple_list_item_1, data2);
  listview2.setadapter(adapter2);
  listview2.setmhorizontalex2(mhorizontalex2);

  listviewex listview3 = new listviewex(getcontext());
  arrayadapter<string> adapter3 = new arrayadapter<string>(getcontext(), android.r.layout.simple_list_item_1, data3);
  listview3.setadapter(adapter3);
  listview3.setmhorizontalex2(mhorizontalex2);

  viewgroup.layoutparams params
    = new viewgroup.layoutparams(viewgroup.layoutparams.match_parent,
    viewgroup.layoutparams.match_parent);

  mhorizontalex2.addview(listview1, params);
  mhorizontalex2.addview(listview2, params);
  mhorizontalex2.addview(listview3, params);
 }

至此,2种拦截方法已经学习完毕,下面我们来学习如何解决同向滑动冲突。

其实和上面的2个例子思路是一样的,只是用来判断是否拦截的那块逻辑不同而已。

下面的例子,是一个下拉刷新的一个控件。

3、外部拦截 解决同向滑动冲突

refreshlayoutbase.java

package com.blueberry.sample.widget.refresh;

import android.content.context;
import android.graphics.color;
import android.util.attributeset;
import android.util.displaymetrics;
import android.util.log;
import android.util.typedvalue;
import android.view.motionevent;
import android.view.view;
import android.view.viewconfiguration;
import android.view.viewgroup;
import android.view.windowmanager;
import android.widget.progressbar;
import android.widget.scroller;
import android.widget.textview;

import com.blueberry.sample.r;

/**

 *外部拦截(同向)
 *
 */
public abstract class refreshlayoutbase<t extends view> extends viewgroup {

 private static final string tag = "refreshlayoutbase";

 public static final int status_loading = 1;
 public static final int status_release_to_refresh = 2;
 public static final int status_pull_to_refresh = 3;
 public static final int status_idle = 4;
 public static final int status_load_more =5;
 private static int scroll_duration =500;

 protected viewgroup mheadview;
 protected viewgroup mfootview;
 private t contentview;
 private progressbar headprogressbar;
 private textview headtv;
 private progressbar footprogressbar;
 private textview foottv;

 private boolean isfisttouch = true;

 protected int currentstatus = status_idle;
 private int mscreenwidth;
 private int mscreenheight;
 private int mlastxintercepted;
 private int mlastyintercepted;
 private int mlastx;
 private int mlasty;
 protected int minitscrolly = 0;
 private int mtouchslop;

 protected scroller mscoller;

 private onrefreshlistener monrefreshlistener;

 public refreshlayoutbase(context context) {
  this(context, null);
 }

 public refreshlayoutbase(context context, attributeset attrs) {
  this(context, attrs, 0);
 }

 public refreshlayoutbase(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
  getscreensize();
  initview();
  mscoller = new scroller(context);
  mtouchslop = viewconfiguration.get(context).getscaledtouchslop();
  setpadding(0, 0, 0, 0);
 }

 public void setcontentview(t view) {
  addview(view, 1);
 }

 public onrefreshlistener getonrefreshlistener() {
  return monrefreshlistener;
 }

 public void setonrefreshlistener(onrefreshlistener monrefreshlistener) {
  this.monrefreshlistener = monrefreshlistener;
 }

 private void initview() {
  setupheadview();
  setupfootview();
 }

 private void getscreensize() {
  windowmanager wm = (windowmanager) getcontext().getsystemservice(context.window_service);
  displaymetrics metrics = new displaymetrics();
  wm.getdefaultdisplay().getmetrics(metrics);
  mscreenwidth = metrics.widthpixels;
  mscreenheight = metrics.heightpixels;
 }

 private int dp2px(int dp) {
  windowmanager wm = (windowmanager) getcontext().getsystemservice(context.window_service);
  displaymetrics metrics = new displaymetrics();
  wm.getdefaultdisplay().getmetrics(metrics);
  return (int) typedvalue.applydimension(typedvalue.complex_unit_dip, dp, metrics);
 }

 /**
  * 设置头布局
  */
 private void setupheadview() {
  mheadview = (viewgroup) view.inflate(getcontext(), r.layout.fresh_head_view, null);
  mheadview.setbackgroundcolor(color.red);
  headprogressbar = (progressbar) mheadview.findviewbyid(r.id.head_progressbar);
  headtv = (textview) mheadview.findviewbyid(r.id.head_tv);
  /*设置 实际高度为 1/4 ,但内容区域只有 100dp*/
  viewgroup.layoutparams layoutparams = new viewgroup.layoutparams(layoutparams.match_parent, mscreenheight / 4);
  mheadview.setlayoutparams(layoutparams);
  mheadview.setpadding(0, mscreenheight / 4 - dp2px(100), 0, 0);
  addview(mheadview);
 }

 /**
  * 设置尾布局
  */
 private void setupfootview() {
  mfootview = (viewgroup) view.inflate(getcontext(), r.layout.fresh_foot_view, null);
  mfootview.setbackgroundcolor(color.blue);
  footprogressbar = (progressbar) mfootview.findviewbyid(r.id.fresh_foot_progressbar);
  foottv = (textview) mfootview.findviewbyid(r.id.fresh_foot_tv);
  addview(mfootview);
 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  int widthsize = measurespec.getsize(widthmeasurespec);
  int widthmode = measurespec.getmode(widthmeasurespec);
  int height = measurespec.getsize(heightmeasurespec);
  int heightmode = measurespec.getmode(heightmeasurespec);

  int finalheight = 0;
  for (int i = 0; i < getchildcount(); i++) {
   view child = getchildat(i);
   measurechild(child, widthmeasurespec, heightmeasurespec);
   finalheight += child.getmeasuredheight();
  }

  if (widthmode == measurespec.at_most && heightmode == measurespec.at_most) {
   widthsize = getchildat(0).getmeasuredwidth();
   setmeasureddimension(widthsize, finalheight);
  } else if (widthmode == measurespec.at_most) {
   widthsize = getchildat(0).getmeasuredwidth();
   setmeasureddimension(widthsize, height);
  } else {
   setmeasureddimension(widthsize, finalheight);
  }

 }


 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b) {
  int topoffset = 0;
  for (int i = 0; i < getchildcount(); i++) {
   view child = getchildat(i);
   child.layout(getpaddingleft(), getpaddingtop() + topoffset, r, getpaddingtop() + child.getmeasuredheight() + topoffset);
   topoffset += child.getmeasuredheight();
  }
  minitscrolly = mheadview.getmeasuredheight() + getpaddingtop();
  scrollto(0, minitscrolly);

 }

 @override
 public boolean onintercepttouchevent(motionevent ev) {
  boolean intercepted = false;
  int x = (int) ev.getx();
  int y = (int) ev.gety();
  switch (ev.getaction()) {
   case motionevent.action_down:
    mlastxintercepted = x;
    mlastyintercepted = y;
    break;
   case motionevent.action_move:
    final int deltay = x - mlastyintercepted;
    if (istop() && deltay > 0 && math.abs(deltay) > mtouchslop) {
     /*下拉*/
     intercepted = true;
    }
    break;
   case motionevent.action_up:
    break;
  }
  mlastxintercepted = x;
  mlastyintercepted = y;
  return intercepted;
 }

 private void dorefresh() {
  log.i(tag, "dorefresh: ");
  if (currentstatus == status_release_to_refresh) {
   mscoller.startscroll(0, getscrolly(), 0, minitscrolly - getscrolly(), scroll_duration);
   currentstatus = status_idle;
  } else if (currentstatus == status_pull_to_refresh) {
   mscoller.startscroll(0,getscrolly(),0,0-getscrolly(),scroll_duration);
   if (null != monrefreshlistener) {
    currentstatus = status_loading;
    monrefreshlistener.refresh();
   }
  }
  invalidate();
 }

 @override
 public boolean ontouchevent(motionevent event) {
  int x = (int) event.getx();
  int y = (int) event.gety();
  switch (event.getaction()) {
   case motionevent.action_down:
    if (!mscoller.isfinished()) {
     mscoller.abortanimation();
    }
    mlastx = x;
    mlasty = y;
    break;
   case motionevent.action_move:
    if (isfisttouch) {
     isfisttouch = false;
     mlastx = x;
     mlasty = y;
    }
    final int deltay = y - mlasty;
    if (currentstatus != status_loading) {
     changescrolly(deltay);
    }
    break;
   case motionevent.action_up:
    isfisttouch = true;
    dorefresh();
    break;
  }

  mlastx = x;
  mlasty = y;
  return true;
 }

 private void changescrolly(int deltay) {
  log.i(tag, "changescrolly: ");
  int cury = getscrolly();
  if (deltay > 0) {
   /*下拉*/
   if (cury - deltay > getpaddingtop()) {
    scrollby(0, -deltay);
   }
  } else {
   /*上拉*/
   if (cury - deltay <= minitscrolly) {
    scrollby(0, -deltay);
   }
  }

  cury = getscrolly();
  int slop = minitscrolly / 2;
  if (cury > 0 && cury <=slop) {
   currentstatus = status_pull_to_refresh;
  } else if (cury > 0 && cury >= slop) {
   currentstatus = status_release_to_refresh;
  }
 }

 @override
 public void computescroll() {
  if (mscoller.computescrolloffset()) {
   scrollto(mscoller.getcurrx(), mscoller.getcurry());
   postinvalidate();
  }
 }

 /**
  * 加载完成调用这个方法
  */
 public void refreshcomplete() {
  mscoller.startscroll(0, getscrolly(), 0, minitscrolly - getscrolly(), scroll_duration);
  currentstatus = status_idle;
  invalidate();
 }

 /**
  * 显示 footer
  */
 public void showfooter() {
  if(currentstatus==status_load_more) return ;
  currentstatus = status_load_more ;
  mscoller.startscroll(0, getscrolly(), 0, mfootview.getmeasuredheight()
    , scroll_duration);
  invalidate();

 }


 /**
  * loadmore完成之后调用
  */
 public void footercomplete() {
  mscoller.startscroll(0, getscrolly(), 0, minitscrolly - getscrolly(), scroll_duration);
  invalidate();
  currentstatus = status_idle;
 }

 public interface onrefreshlistener {
  void refresh();
 }

 abstract boolean istop();

 abstract boolean isbottom();

}

它是一个抽象类,需要编写子类继承istop()和 isbottom()方法。下面给出它的一个实现类:

package com.blueberry.sample.widget.refresh;

import android.content.context;
import android.util.attributeset;
import android.widget.abslistview;
import android.widget.listview;

/**
 * created by blueberry on 2016/6/21.
 *
 * refreshlayoutbase 的一个实现类
 */
public class refreshlistview extends refreshlayoutbase<listview> {

 private static final string tag = "refreshlistview";

 private listview listview;
 private onloadlistener loadlistener;

 public refreshlistview(context context) {
  super(context);
 }

 public refreshlistview(context context, attributeset attrs) {
  super(context, attrs);
 }

 public refreshlistview(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
 }

 public listview getlistview() {
  return listview;
 }

 public void setlistview(final listview listview) {
  this.listview = listview;
  setcontentview(listview);

  this.listview.setonscrolllistener(new abslistview.onscrolllistener() {
   @override
   public void onscrollstatechanged(abslistview view, int scrollstate) {
   }

   @override
   public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) {

    /*这里存在一个bug: 当listview滑动到底部的时候,如果下拉也会出现footer
    * 这是因为,暂时还没有想到如何判断是下拉还是上拉。
    * 如果要解决此问题,我觉得应该重写listview 的ontouchevent来判断手势方向
    * 次模块主要解决竖向滑动冲突,故现将此问题放下。
    * */
    if (currentstatus == status_idle
      && getscrolly() <= minitscrolly && isbottom()
      ) {
     showfooter();
     if (null != loadlistener) {
      loadlistener.onloadmore();
     }
    }

   }
  });
 }

 public onloadlistener getloadlistener() {
  return loadlistener;
 }

 public void setloadlistener(onloadlistener loadlistener) {
  this.loadlistener = loadlistener;
 }

 @override
 boolean istop() {
  return listview.getfirstvisibleposition() == 0
    && getscrolly() <= mheadview.getmeasuredheight();
 }

 @override
 boolean isbottom() {
  return listview.getlastvisibleposition() == listview.getadapter().getcount() - 1;
 }

 public interface onloadlistener {
  void onloadmore();
 }
}

4、内部拦截法解决同向滑动

同样是一个下拉刷新组件,因为实现原理都一样,所以这个写的比较随意些。主要还是如果解决滑动冲突。

refreshlayoutbase2.java

package com.blueberry.sample.widget.refresh;

import android.content.context;
import android.graphics.color;
import android.util.attributeset;
import android.util.log;
import android.view.motionevent;
import android.view.view;
import android.view.viewgroup;
import android.widget.arrayadapter;
import android.widget.listview;
import android.widget.scroller;

import com.blueberry.sample.r;

import java.util.arraylist;
import java.util.list;

/**
 * created by blueberry on 2016/6/22.
 * 结合内部类 listvieex
 * 内部拦截法,同向
 */
public class refreshlayoutbase2 extends viewgroup {

 private static final string tag = "refreshlayoutbase2";

 private static list<string> datas;

 static {
  datas = new arraylist<>();
  for (int i = 0; i < 40; i++) {
   datas.add("数据—" + i);
  }
 }

 private viewgroup headview;
 private listviewex lv;

 private int lasty;
 public int minitscrolly;

 private scroller mscroller;

 public refreshlayoutbase2(context context) {
  this(context, null);
 }

 public refreshlayoutbase2(context context, attributeset attrs) {
  this(context, attrs, 0);

 }

 public refreshlayoutbase2(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
  mscroller = new scroller(context);
  setupheadview(context);
  setupcontentview(context);

 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  int widthsize = measurespec.getsize(widthmeasurespec);
  int widthmode = measurespec.getmode(widthmeasurespec);
  int height = measurespec.getsize(heightmeasurespec);
  int heightmode = measurespec.getmode(heightmeasurespec);

  int finalheight = 0;
  for (int i = 0; i < getchildcount(); i++) {
   view child = getchildat(i);
   measurechild(child, widthmeasurespec, heightmeasurespec);
   finalheight += child.getmeasuredheight();
  }

  if (widthmode == measurespec.at_most && heightmode == measurespec.at_most) {
   widthsize = getchildat(0).getmeasuredwidth();
   setmeasureddimension(widthsize, finalheight);
  } else if (widthmode == measurespec.at_most) {
   widthsize = getchildat(0).getmeasuredwidth();
   setmeasureddimension(widthsize, height);
  } else {
   setmeasureddimension(widthsize, finalheight);
  }

 }

 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b) {
  int topoffset = 0;
  for (int i = 0; i < getchildcount(); i++) {
   view child = getchildat(i);
   child.layout(getpaddingleft(), getpaddingtop() + topoffset, r, getpaddingtop() + child.getmeasuredheight() + topoffset);
   topoffset += child.getmeasuredheight();
  }
  minitscrolly = headview.getmeasuredheight() + getpaddingtop();
  scrollto(0, minitscrolly);

 }

 /**
  * 不拦截down 其他一律拦截
  * @param ev
  * @return
  */
 @override
 public boolean onintercepttouchevent(motionevent ev) {
  if (ev.getaction() == motionevent.action_down) return false;
  return true;
 }

 @override
 public boolean ontouchevent(motionevent event) {
  int y = (int) event.gety();
  switch (event.getaction()) {
   case motionevent.action_down:
    break;
   case motionevent.action_move:
    final int deltay = y-lasty;
    log.i(tag, "ontouchevent: deltay: "+deltay);
    if (deltay >= 0 && lv.istop() && getscrolly() - deltay >=getpaddingtop()) {
      scrollby(0, -deltay);
    }
    break;
   case motionevent.action_up:
    this.postdelayed(new runnable() {
     @override
     public void run() {
      mscroller.startscroll(0,getscrolly(),0,minitscrolly-getscrolly());
      invalidate();
     }
    },2000);
    break;
  }

  lasty = y ;
  return true;
 }

 private void setupheadview(context context) {
  headview = (viewgroup) view.inflate(context, r.layout.fresh_head_view, null);
  headview.setbackgroundcolor(color.red);
  viewgroup.layoutparams params = new viewgroup.layoutparams(viewgroup.layoutparams.match_parent, 300);
  addview(headview, params);
 }

 public void setupcontentview(context context) {
  lv = new listviewex(context, this);
  lv.setbackgroundcolor(color.blue);
  arrayadapter<string> adapter = new arrayadapter<string>(getcontext(), android.r.layout.simple_list_item_1, datas);
  lv.setadapter(adapter);
  addview(lv, new viewgroup.layoutparams(viewgroup.layoutparams.match_parent, viewgroup.layoutparams.match_parent));
 }

 @override
 public void computescroll() {
  if(mscroller.computescrolloffset()){
   scrollto(mscroller.getcurrx(),mscroller.getcurry());
   postinvalidate();
  }
 }

 public static class listviewex extends listview {

  private refreshlayoutbase2 outter;

  public listviewex(context context, refreshlayoutbase2 outter) {
   super(context);
   this.outter = outter;
  }

  public listviewex(context context, attributeset attrs) {
   super(context, attrs);
  }

  public listviewex(context context, attributeset attrs, int defstyleattr) {
   super(context, attrs, defstyleattr);
  }

  /**
   * 使用 outter.requestdisallowintercepttouchevent();
   * 来决定父控件是否对事件进行拦截
   * @param ev
   * @return
   */
  @override
  public boolean dispatchtouchevent(motionevent ev) {
   switch (ev.getaction()) {
    case motionevent.action_down:
     outter.requestdisallowintercepttouchevent(true);
     break;
    case motionevent.action_move:

     if ( istop() && outter.getscrolly() <= outter.minitscrolly) {
      outter.requestdisallowintercepttouchevent(false);
     }
     break;

   }
   return super.dispatchtouchevent(ev);
  }

  public boolean istop() {
   return getfirstvisibleposition() ==0;
  }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: