当前位置: 移动技术网 > 移动技术>移动开发>Android > android自定义View滑动删除效果

android自定义View滑动删除效果

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

view滑动删除效果图

实现功能

1、可以向左滑动,右侧出现删除
2、向左滑动如果删除出现一大半,松手打开删除,反之关闭删除
3、应用场景
          微信消息的删除功能

实现原理

1、外面是一个listview
2、条目是一个自定义控件继承viewgroup
    1)、左边一个textview,右侧屏幕外也有一个textview
    2)、所以继承viewgroup

实现步骤

1、创建一个slidedeleteview类

    1).构造方法要关联

public class slidedelete extends viewgroup {

 private view leftview;
 private view rightview;
 private viewdraghelper helper;

 //第一步关联构造方法
 //第二步重写onmeasure和onlviewayout测量子view和布局子view
 public slidedelete(context context) {
  this(context,null);
 }

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

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

  helper = viewdraghelper.create(this, callback);

 }
}

2、在布局文件中设置slidedeleteview里面的子view

    slidedeleteview height=80

        textview
            width:matchparent
            height:matchparent
        textview

<com.example.movedelete.slidedelete
  android:id="@+id/container"
  android:layout_width="match_parent"
  android:layout_height="80dp">

  <textview

   android:id="@+id/content"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center"
   android:background="#33000000"
   android:text="第0个条目"
   android:textcolor="#fff"
   android:textsize="20sp" />
  <textview

   android:id="@+id/delete"
   android:layout_width="80dp"
   android:layout_height="match_parent"
   android:background="#f00"
   android:gravity="center"
   android:text="删除"
   android:textcolor="#fff"
   android:textsize="20sp" />

 </com.example.movedelete.slidedelete>

3、重写onmeasure,给子view进行测量

 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  //对当前组合view的测量,不使用的话,也可以自己设置
  measurechildren(widthmeasurespec,heightmeasurespec);
  super.onmeasure(widthmeasurespec, heightmeasurespec);

 }

4、重写onlayout,给子view进行布局

注意事项:要设置leftview,也要设置rightview,不要都写成leftview了

 protected void onlayout(boolean changed, int l, int t, int r, int b) {
  //第一步获取里面子view
  leftview = getchildat(0);
  rightview = getchildat(1);
  //第二步给子view提供相应的布局
  int leftl = 0;
  int leftt = 0;
  int leftr = leftview.getmeasuredwidth();
  int leftb = leftview.getmeasuredheight();
  leftview.layout(leftl,leftt,leftr,leftb);

  //给rightview提供相应的布局
  int rightl = leftview.getmeasuredwidth();
  int rightt = 0;
  int rightr = leftview.getmeasuredwidth()+ rightview.getmeasuredwidth();
  int rightb = rightview.getmeasuredheight();
  rightview.layout(rightl,rightt,rightr,rightb);

 }

5、设置view的滑动事件ontouchevent,实现滑动
    1).因为要滑动,所以消费该事件返回true
    2).使用viewdraghelper来实现滑动效果

注意事项:

只能实现leftview的滑动,右侧rightview看不到所以滑动不了

只能给滑动的view设置监听,当滑动的时候,重新设置另一个view的布局跟着滑动

 @override
 public boolean ontouchevent(motionevent event) {
  //1,要消费该事件,所以直接返回true
  //2,使用viewdraghelper来实现滑动效果
  helper.processtouchevent(event);
  return true;
 }

6、重写滑动事件的监听onviewpositionchanged解决只有lefview滑动的问题

    1).重写的方法是在viewdraghelper.callback的子实现类中

    2).要实现滑动事件,必须在trycaptureview方法中返回true

 private viewdraghelper.callback callback = new viewdraghelper.callback() {
  //手势滑动时
  @override
  public boolean trycaptureview(view child, int pointerid) {
   return true;
  }
  //监听控件移动状态
  @override
  public void onviewpositionchanged(view changedview, int left, int top, int dx, int dy) {

   //如果左边控件拖动,我们要让右边控件也重新布局
   if(changedview == leftview){
    rightview.layout(rightview.getleft()+dx,0,rightview.getright()+dx,rightview.getbottom()+dy);
   }else if(changedview == rightview){
    leftview.layout(leftview.getleft()+dx,0,leftview.getright()+dx,leftview.getbottom()+dy);
   }


  }

7、重写clampviewpositionhorizontal水平位置移动,解决左右越界问题

    1.返回值为移动时左侧滑动的距离
    2.如果滑动的控件是leftview时,解决越界
    3.如果滑动的控件是rightview时,解决越界

 public int clampviewpositionhorizontal(view child, int left, int dx) {
   //对左右越界问题的处理
   if(child == leftview){
    //处理两边的越界问题
    if(left >= 0){
     left = 0;
    }else if(left <= -rightview.getmeasuredwidth()){
     left = -rightview.getmeasuredwidth();
    }
   }else if(child == rightview){
    //只处理右边的越界问题,因为左侧越界的时看不到该view
    if(left <= leftview.getmeasuredwidth()- rightview.getmeasuredwidth()){
     left = leftview.getmeasuredwidth()- rightview.getmeasuredwidth();
    }else if(left >= leftview.getmeasuredwidth()){
     left = leftview.getmeasuredwidth();
    }


   }

   return left;
  }

8、手松开时重写onviewreleased方法,实现滑动手松开时,rightview是打开还是关闭

    1.使用viewdraghelper滑动时,要调用invalidate方法,回调computescroll方法
    2.重写computescroll方法

        1).先判断是否要继承滑动

        2).使用兼容的invalidate方法来实现匀速滑动

  @override
  public void onviewreleased(view releasedchild, float xvel, float yvel) {

   //松开后,什么时候打开rightview,什么时候关闭leftview
   //临界值,rightview.getleft() 和 屏幕的宽度-rightview.getwidth()/2
   if(releasedchild == leftview){
    if(rightview.getleft() < getmeasuredwidth() - rightview.getmeasuredwidth()/2){
     //使用viewdraghelper来滑动
     helper.smoothslideviewto(rightview,getmeasuredwidth()-rightview.getmeasuredwidth(),0);

     invalidate();
    }else{
     helper.smoothslideviewto(rightview,getmeasuredwidth(),0);
     invalidate();
    }
   }

  }



 //需要重写computescroll

 @override
 public void computescroll() {
  //判断是否要继承滑动
  if(helper.continuesettling(true)){
   //invalidate();
   //兼容使用
   viewcompat.postinvalidateonanimation(this);
  }
 }

9、实现删除rightview的点击删除事件

    1.在listview的adapter中找到右侧的rightview
    2.调用rightview的点击事件
    3.删除该条目

        1)删除集合中的数据
            list.remove(position);
        2)更新adapter
            notifydatasetchanged();
        3)重新绘制整个条目
            requestlayout();

  //设置删除的点击事件
  vh.delete.setonclicklistener(new view.onclicklistener() {
   @override
   public void onclick(view v) {
    //删除当前的数据
    list.remove(position);
    notifydatasetchanged();

    //让父容器更新下

   }
  });

  vh.container.requestlayout();


注意事项

1、在重写onlayout方法的时候,给rightview设置布局的时候,写成leftview一直出错
2、在ontouchevent中要返回true,因为要消费该事件
3、在使用viewdraghelper.callback时,重写trycaptureview时要返回true
4、在ontouchevent中,不使用scrollby或者scrollto,而是使用viewdraghelper工具类,不需要判断滑动的距离

总结

1、首先该控件是自定义view,不是组合控件,因为组合的话rightview在屏幕右侧不能实现
2、是自定义view中的继承viewgroup,因为左侧leftview和右侧rightview都是textview不需要自己画

源码

slidedelete的源码

package com.example.movedelete;

import android.content.context;
import android.support.v4.view.viewcompat;
import android.support.v4.widget.viewdraghelper;
import android.util.attributeset;
import android.view.motionevent;
import android.view.view;
import android.view.viewgroup;

/**
 * created by guixin on 2017/1/5.
 */

public class slidedelete extends viewgroup {

 private view leftview;
 private view rightview;
 private viewdraghelper helper;



 //第一步关联构造方法
 //第二步重写onmeasure和onlviewayout测量子view和布局子view


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

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

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

  helper = viewdraghelper.create(this, callback);

 }

 private viewdraghelper.callback callback = new viewdraghelper.callback() {
  //手势滑动时
  @override
  public boolean trycaptureview(view child, int pointerid) {
   return true;
  }


  //拖动控件水平移动
  @override
  public int clampviewpositionhorizontal(view child, int left, int dx) {
   //对左右越界问题的处理
   if(child == leftview){
    //处理两边的越界问题
    if(left >= 0){
     left = 0;
    }else if(left <= -rightview.getmeasuredwidth()){
     left = -rightview.getmeasuredwidth();
    }
   }else if(child == rightview){
    //只处理右边的越界问题,因为左侧越界的时看不到该view
    if(left <= leftview.getmeasuredwidth()- rightview.getmeasuredwidth()){
     left = leftview.getmeasuredwidth()- rightview.getmeasuredwidth();
    }else if(left >= leftview.getmeasuredwidth()){
     left = leftview.getmeasuredwidth();
    }


   }

   return left;
  }


  //监听控件移动状态
  @override
  public void onviewpositionchanged(view changedview, int left, int top, int dx, int dy) {

   //如果左边控件拖动,我们要让右边控件也重新布局
   if(changedview == leftview){
    rightview.layout(rightview.getleft()+dx,0,rightview.getright()+dx,rightview.getbottom()+dy);
   }else if(changedview == rightview){
    leftview.layout(leftview.getleft()+dx,0,leftview.getright()+dx,leftview.getbottom()+dy);
   }


  }

  //解决滑动一半松手时,view的复位

  /**
   *
   * @param releasedchild 松开的view
   * @param xvel
   * @param yvel
   */
  @override
  public void onviewreleased(view releasedchild, float xvel, float yvel) {

   //松开后,什么时候打开rightview,什么时候关闭leftview
   //临界值,rightview.getleft() 和 屏幕的宽度-rightview.getwidth()/2
   if(releasedchild == leftview){
    if(rightview.getleft() < getmeasuredwidth() - rightview.getmeasuredwidth()/2){
     //使用viewdraghelper来滑动
     helper.smoothslideviewto(rightview,getmeasuredwidth()-rightview.getmeasuredwidth(),0);

     invalidate();
    }else{
     helper.smoothslideviewto(rightview,getmeasuredwidth(),0);
     invalidate();
    }
   }

  }
 };

 //需要重写computescroll

 @override
 public void computescroll() {
  //判断是否要继承滑动
  if(helper.continuesettling(true)){
   //invalidate();
   //兼容使用
   viewcompat.postinvalidateonanimation(this);
  }
 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  //对当前组合view的测量,不使用的话,也可以自己设置
  measurechildren(widthmeasurespec,heightmeasurespec);
  super.onmeasure(widthmeasurespec, heightmeasurespec);



 }

 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b) {
  //第一步获取里面子view
  leftview = getchildat(0);
  rightview = getchildat(1);
  //第二步给子view提供相应的布局
  int leftl = 0;
  int leftt = 0;
  int leftr = leftview.getmeasuredwidth();
  int leftb = leftview.getmeasuredheight();
  leftview.layout(leftl,leftt,leftr,leftb);

  //给rightview提供相应的布局
  int rightl = leftview.getmeasuredwidth();
  int rightt = 0;
  int rightr = leftview.getmeasuredwidth()+ rightview.getmeasuredwidth();
  int rightb = rightview.getmeasuredheight();
  rightview.layout(rightl,rightt,rightr,rightb);

 }

 //view的事件传递

 @override
 public boolean ontouchevent(motionevent event) {
  //1,要消费该事件,所以直接返回true
  //2,使用viewdraghelper来实现滑动效果
  helper.processtouchevent(event);
  return true;
 }
}

mainactivity.java源码

package com.example.movedelete;

import android.os.bundle;
import android.support.v7.app.appcompatactivity;
import android.widget.listview;

import com.example.movedelete.adapter.slidedeleteadapter;

import java.util.arraylist;

public class mainactivity extends appcompatactivity {

 private listview lv;
 private arraylist<string> list;

 @override
 protected void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.activity_main);

  //初始化视图
  initview();
  //初始化数据
  initdata();
  //初始化事件
  initevent();


 }

 //初始化视图
 private void initview() {
  lv = (listview) findviewbyid(r.id.lv);
 }

 //初始化数据
 private void initdata() {
  list = new arraylist<>();
  for (int i = 0; i < 20; i++) {
   list.add("第"+i+"项条目");
  }
 }

 //初始化事件
 private void initevent() {
  slidedeleteadapter adapter = new slidedeleteadapter(list);
  lv.setadapter(adapter);

 }
}

slidedeleteadapter.java源码

package com.example.movedelete.adapter;

import android.view.view;
import android.view.viewgroup;
import android.widget.baseadapter;
import android.widget.textview;

import com.example.movedelete.r;
import com.example.movedelete.slidedelete;

import java.util.arraylist;

/**
 * created by guixin on 2017/1/5.
 */
public class slidedeleteadapter extends baseadapter{
 private arraylist<string> list;

 public slidedeleteadapter(arraylist<string> list) {
  this.list = list;
 }

 @override
 public int getcount() {
  return list == null ? 0 : list.size();
 }

 @override
 public string getitem(int position) {
  return list == null ? null : list.get(position);
 }

 @override
 public long getitemid(int position) {
  return position;
 }

 @override
 public view getview(final int position, view convertview, viewgroup parent) {
  viewholder vh;
  if(convertview == null){
   convertview = view.inflate(parent.getcontext(), r.layout.item_slide,null);
   vh = new viewholder(convertview);

   convertview.settag(vh);
  }else{
   vh = (viewholder) convertview.gettag();
  }

  vh.content.settext(list.get(position));

  //设置删除的点击事件
  vh.delete.setonclicklistener(new view.onclicklistener() {
   @override
   public void onclick(view v) {
    //删除当前的数据
    list.remove(position);
    notifydatasetchanged();

    //让父容器更新下

   }
  });

  vh.container.requestlayout();

  return convertview;
 }

 class viewholder{
  private textview content;
  private textview delete;
  private slidedelete container;

  public viewholder(view v){
    container = (slidedelete) v.findviewbyid(r.id.container);
   content = (textview) v.findviewbyid(r.id.content);
    delete = (textview) v.findviewbyid(r.id.delete);
  }
 }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/activity_main"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <listview
  android:id="@+id/lv"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

 </listview>



</relativelayout>

item_slide.xml

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical" android:layout_width="match_parent"
 android:layout_height="match_parent">
 <com.example.movedelete.slidedelete
  android:id="@+id/container"
  android:layout_width="match_parent"
  android:layout_height="80dp">

  <textview

   android:id="@+id/content"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center"
   android:background="#33000000"
   android:text="第0个条目"
   android:textcolor="#fff"
   android:textsize="20sp" />
  <textview

   android:id="@+id/delete"
   android:layout_width="80dp"
   android:layout_height="match_parent"
   android:background="#f00"
   android:gravity="center"
   android:text="删除"
   android:textcolor="#fff"
   android:textsize="20sp" />

 </com.example.movedelete.slidedelete>

</linearlayout>

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网