android自定义View滑动删除效果
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>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
android自定义View滑动删除效果
-
Android自定义View仿华为圆形加载进度条
-
Android开发使用自定义view实现ListView下拉的视差特效功能
-
Android自定义实现顶部粘性下拉刷新效果
-
Android 自定义 HorizontalScrollView 打造多图片OOM 的横向滑动效果(实例代码)
-
Android中的Button自定义点击效果实例代码
-
Android自定义控件之三点循环缩放效果
-
Android Studio使用ViewPager+Fragment实现滑动菜单Tab效果
-
Android自定义标尺滑动选择值效果
-
使用Android自定义控件实现滑动解锁九宫格