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

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

程序员文章站 2024-02-28 15:14:04
效果图: 看网上的都是两个view拼接,默认右侧的不显示,水平移动的时候把右侧的view显示出来。但是看最新版qq上的效果不是这样的,但给人的感觉却很好,所以献丑来一...

效果图:

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

看网上的都是两个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实现侧滑效果,希望对大家有所帮助