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

android自定义View滑动删除效果

程序员文章站 2023-12-05 20:44:58
view滑动删除效果图 实现功能 1、可以向左滑动,右侧出现删除 2、向左滑动如果删除出现一大半,松手打开删除,反之关闭删除 3、应用场景  &nb...

view滑动删除效果图

android自定义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>

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