当前位置: 移动技术网 > IT编程>移动开发>Android > Android中自定义view实现侧滑效果

Android中自定义view实现侧滑效果

2019年07月24日  | 移动技术网IT编程  | 我要评论

送子丫头,关于竹子的诗句,天泽江鼎

效果图:

看网上的都是两个view拼接,默认右侧的不显示,水平移动的时候把右侧的view显示出来。但是看最新版qq上的效果不是这样的,但给人的感觉却很好,所以献丑来一发比较高仿的。

知识点:

1、viewdraghelper 的用法;
2、滑动冲突的解决;
3、自定义viewgroup。

viewdraghelper 出来已经比较久了 相信大家都比较熟悉,不熟悉的话google一大把这里主要简单用一下它的几个方法

1、trycaptureview(view child, int pointerid) :确定那个子view可以滑动

2、 getviewhorizontaldragrange(view child):用我蹩脚的英语翻译一下是‘返回的是子view在水平方向上可移动的大小,以像素为单位,返回0的时候表示水平方向上不能拖动'

3、clampviewpositionhorizontal(view child, int left, int dx):在这里可以对边界进行检查,left和dx分别代表即将移动到的位置

4、onviewpositionchanged(view changedview, int left, int top,
int dx, int dy):当要捕获view,由于拖曳或者设定而发生位置变更时回调

它的基本用法是:

public swipelayout(context context, attributeset attrs, int defstyle) {
    super(context, attrs, defstyle);
    init();
  }
  private void init() {
    viewdraghelper = viewdraghelper.create(this, callback);
  }
   public boolean onintercepttouchevent(motionevent ev) {
    boolean result = viewdraghelper.shouldintercepttouchevent(ev);
   }
   public boolean ontouchevent(motionevent event) {
    viewdraghelper.processtouchevent(event);
    return true;
  }

1)、在构造方法中创建

2)、在onintercepttouchevent 中判断是否拦截

3 )、 在 ontouchevent出来事件

好了 最不好理解的已经搞定了。接下来看看具体实现:

首先看布局:

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="vertical" >
  <scrollviewgroup.lly.com.swiplayout.swipelayout
    android:id="@+id/swipelayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >
    <!-- delete区域的布局 -->
    <include layout="@layout/layout_delete" />
    <!-- item内容的布局 -->
    <include layout="@layout/layout_content" />
  </scrollviewgroup.lly.com.swiplayout.swipelayout>
</linearlayout>

这个没什么好说的,一个自定义viewgroup包含两个子控件。

接着看看swipelayout是怎么实现的:

@override
  protected void onfinishinflate() {
    super.onfinishinflate();
    deleteview = getchildat(0);
    contentview = getchildat(1);
  }
  @override
  protected void onsizechanged(int w, int h, int oldw, int oldh) {
    super.onsizechanged(w, h, oldw, oldh);
    deleteheight = deleteview.getmeasuredheight();
    deletewidth = deleteview.getmeasuredwidth();
    contentwidth = contentview.getmeasuredwidth();
    screenwidth = getwidth();
  }
  @override
  protected void onlayout(boolean changed, int left, int top, int right,
              int bottom) {
    // super.onlayout(changed, left, top, right, bottom);
    deleteview.layout(screenwidth - deletewidth, 0, (screenwidth - deletewidth)
        + deletewidth, deleteheight);
    contentview.layout(0, 0, contentwidth, deleteheight);
  }

上面代码进行了一些初始化的操作,重点看看onlayout里面的,我们继承的是framelayout 这里先画出来 deleteview并让他在右边,然后在上面改了一层contentview,这样显示的时候只会显示contentview。

接下来看ontouch方法

public boolean ontouchevent(motionevent event) {
    //如果当前有打开的,则下面的逻辑不能执行
    if(!swipelayoutmanager.getinstance().isshouldswipe(this)){
      requestdisallowintercepttouchevent(true);
      return true;
    }
    switch (event.getaction()) {
      case motionevent.action_down:
        downx = event.getx();
        downy = event.gety();
        break;
      case motionevent.action_move:
        //1.获取x和y方向移动的距离
        float movex = event.getx();
        float movey = event.gety();
        float delatx = movex - downx;//x方向移动的距离
        float delaty = movey - downy;//y方向移动的距离
        if(math.abs(delatx)>math.abs(delaty)){
          //表示移动是偏向于水平方向,那么应该swipelayout应该处理,请求父view不要拦截
          requestdisallowintercepttouchevent(true);
        }
        //更新downx,downy
        downx = movex;
        downy = movey;
        break;
      case motionevent.action_up:
        break;
    }
    viewdraghelper.processtouchevent(event);
    return true;
  }

上面主要就是对事件冲突的处理,当是水平移动的时候就请求父视图不要拦截。

接下来来重点就来了

private viewdraghelper.callback callback = new viewdraghelper.callback() {
    @override
    public boolean trycaptureview(view child, int pointerid) {
      return child==contentview;
    }
    @override
    public int getviewhorizontaldragrange(view child) {
      return deletewidth;
    }
    @override
    public int clampviewpositionhorizontal(view child, int left, int dx) {
      if(child==contentview){
        if(left>0)left = 0;
        if(left<-deletewidth)left = -deletewidth;
      }
      return left;
    }
    @override
    public void onviewpositionchanged(view changedview, int left, int top,
                     int dx, int dy) {
      super.onviewpositionchanged(changedview, left, top, dx, dy);
      //判断开和关闭的逻辑
      if(contentview.getleft()==0 && currentstate!=swipestate.close){
        //说明应该将state更改为关闭
        currentstate = swipestate.close;
        //回调接口关闭的方法
        if(listener!=null){
          listener.onclose(gettag());
        }
        //说明当前的swipelayout已经关闭,需要让manager清空一下
        swipelayoutmanager.getinstance().clearcurrentlayout();
      }else if (contentview.getleft()==-deletewidth && currentstate!=swipestate.open) {
        //说明应该将state更改为开
        currentstate = swipestate.open;
        //回调接口打开的方法
        if(listener!=null){
          listener.onopen(gettag());
        }
        //当前的swipelayout已经打开,需要让manager记录一下下
        swipelayoutmanager.getinstance().setswipelayout(swipelayout.this);
      }
    }
    @override
    public void onviewreleased(view releasedchild, float xvel, float yvel) {
      super.onviewreleased(releasedchild, xvel, yvel);
      if(contentview.getleft()<-deletewidth/2){
        //应该打开
        open();
      }else {
        //应该关闭
        close();
      }
    }
  };

上面这段代码里面的方法一开始我们都说过了,在来看下在trycaptureview中我们让 contentview可以滑动,在getviewhorizontaldragrange中却东滑动范围是deletewidth,在clampviewpositionhorizontal中对边界进行了下限制,在onviewpositionchanged中进行状态的更新, 最后在手指抬起的时候让view自动回滚,

 /**
   * 打开的方法
   */
  public void open() {
    viewdraghelper.smoothslideviewto(contentview,-deletewidth,contentview.gettop());
    viewcompat.postinvalidateonanimation(swipelayout.this);
  }
  /**
   * 关闭的方法
   */
  public void close() {
    viewdraghelper.smoothslideviewto(contentview,0,contentview.gettop());
    viewcompat.postinvalidateonanimation(swipelayout.this);
  };
  public void computescroll() {
    if(viewdraghelper.continuesettling(true)){
      viewcompat.postinvalidateonanimation(this);
    }
  }

这里注意一定要重写computescroll方法,不然滑动效果动一下就不动了。

至此这个自定义framelayout就完成了

但是发现一个问题,我们在已经滑动出来的view中上下滑动时,这个view的deleteview还是显示状态,所以还要在activity中处理一下:

 recyview.setonscrolllistener(new recyclerview.onscrolllistener() {
      @override
      public void onscrollstatechanged(recyclerview recyclerview, int newstate) {
        super.onscrollstatechanged(recyclerview, newstate);
      }
      @override
      public void onscrolled(recyclerview recyclerview, int dx, int dy) {
        super.onscrolled(recyclerview, dx, dy);
        if(dy>0 || dy<0){
          swipelayoutmanager.getinstance().closecurrentlayout();
        }
      }
    });

当这个recyclerview是上下滑动时,让子view复位。
收工。

ps:本来是在eclipse中listview中实现的,但是想想google都已经不支持eclipse了,而listview也快被recyclerview代替了,所以最后还是切换到android studio,用recyclerview实现了一套。

源码地址

以上所述是小编给大家介绍的android中自定义view实现侧滑效果,希望对大家有所帮助

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网