模仿QQ左滑删除当前会话列表
来源:互联网 发布:顶点网络代理 编辑:程序博客网 时间:2024/05/16 02:41
最近无聊,看了很多关于QQ列表侧滑删除的文章和技术文档,结合许多整理一下内容。
侧滑结构如图:
所需填充的数据由“文本”和“删除”2部分组成。代码如下
content.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="60dp" android:background="#97d8da" android:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/Content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Text" android:gravity="center" android:textSize="24dp" /></LinearLayout>
delete.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/Delete" android:layout_width="130dp" android:layout_height="60dp" android:background="#ff0000" android:gravity="center" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="删除" android:textSize="24dp" android:textColor="#ffffff" /></LinearLayout>
ok,既然这2个做好了,那么就到最关键的一步了。就是当我们滑动“文本”和“删除”这一个整体的时候,我们所需要注意的需要删除控件的长度,直接贴代码:
DeleteListView:
import android.content.Context;import android.support.v4.widget.ViewDragHelper;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;public class DeleteListView extends ViewGroup{ // 内容部分 private View mContent; // 删除部分 private View mDelete; private ViewDragHelper viewDragHelper; private int mContentWidth; private int mContentHeight; private int mDeleteWidth; private int mDeleteHeight; public SlideDelete(Context context) { super(context); } public SlideDelete(Context context, AttributeSet attrs) { super(context, attrs); } public SlideDelete(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private OnSlideDeleteListener onSlideDeleteListener; public void setOnSlideDeleteListener(OnSlideDeleteListener onSlideDeleteListener){ this.onSlideDeleteListener = onSlideDeleteListener; } @Override protected void onFinishInflate() { super.onFinishInflate(); mContent = getChildAt(0); mDelete = getChildAt(1); viewDragHelper = ViewDragHelper.create(this,new MyDrawHelper()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 这跟mContent的父亲的大小有关,父亲是宽填充父窗体,高度是和孩子一样是60dp // 测量内容部分的大小 mContent.measure(widthMeasureSpec,heightMeasureSpec); LayoutParams layoutParams = mDelete.getLayoutParams(); int deleteWidth = MeasureSpec.makeMeasureSpec( layoutParams.width, MeasureSpec.EXACTLY); int deleteHeight = MeasureSpec.makeMeasureSpec( layoutParams.height, MeasureSpec.EXACTLY); // 这个参数就需要指定为精确大小 // 测量删除部分的大小 mDelete.measure(deleteWidth, deleteHeight); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mContentWidth = mContent.getMeasuredWidth(); mContentHeight = mContent.getMeasuredHeight(); // 摆放内容部分的位置 mContent.layout(0, 0, mContentWidth, mContentHeight); mDeleteWidth = mDelete.getMeasuredWidth(); mDeleteHeight = mDelete.getMeasuredHeight(); // 摆放删除部分的位置 mDelete.layout(mContentWidth, 0,mContentWidth + mDeleteWidth, mContentHeight); } class MyDrawHelper extends ViewDragHelper.Callback { /** * Touch的down事件会回调这个方法 tryCaptureView * @Child:指定要动的孩子 (哪个孩子需要动起来) * @pointerId: 点的标记 * @return : ViewDragHelper是否继续分析处理 child的相关touch事件 */ @Override public boolean tryCaptureView(View child, int pointerId) { System.out.println("调用tryCaptureView"); System.out.println("contentView : " + (mContent == child)); return mContent == child || mDelete == child; } /** * 捕获了水平方向移动的位移数据 * @param child 移动的孩子View * @param left 父容器的左上角到孩子View的距离 * @param dx 增量值,其实就是移动的孩子View的左上角距离控件(父亲)的距离,包含正负 */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if(child == mContent){ // 解决内容部分左右拖动的越界问题 if(left>0){ return 0; }else if(-left>mDeleteWidth){ return -mDeleteWidth; } } if(child == mDelete){ // 解决删除部分左右拖动的越界问题 if(left<mContentWidth - mDeleteWidth){ return mContentWidth - mDeleteWidth; }else if(left > mContentWidth){ return mContentWidth; } } return left; } @Override public int clampViewPositionVertical(View child, int top, int dy) { return super.clampViewPositionVertical(child, top, dy); } /** * 当View的位置改变时的回调 这个方法的价值是结合clampViewPositionHorizontal * 或者clampViewPositionVertical * @param changedView 哪个View的位置改变了 * @param left changedView的left * @param top changedView的top * @param dx x方向的上的增量值 * @param dy y方向上的增量值 */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { invalidate(); if(changedView == mContent){ // 如果移动的是mContent //我们移动mContent的实惠要相应的联动改变mDelete的位置 // 怎么改变mDelete的位置,当然是mDelete的layput方法啦 int tempDeleteLeft = mContentWidth+left; int tempDeleteRight = mContentWidth+left + mDeleteWidth; mDelete.layout(tempDeleteLeft,0,tempDeleteRight,mDeleteHeight); }else{ // touch的是mDelete int tempContentLeft = left - mContentWidth; int tempContentRight = left; mContent.layout(tempContentLeft,0,tempContentRight,mContentHeight); } } /** * 相当于Touch的up的事件会回调onViewReleased这个方法 * @param releasedChild * @param xvel x方向的速率 * @param yvel y方向的速率 */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { // 方法的参数里面没有left,那么我们就采用 getLeft()这个方法 int mConLeft = mContent.getLeft(); // 这里没必要分来两个孩子判断 if(-mConLeft>mDeleteWidth/2){ // mDelete展示起来 isShowDelete(true); if(onSlideDeleteListener != null){ // 调用接口打开的方法 onSlideDeleteListener.onOpen(SlideDelete.this); } }else{ // mDetele隐藏起来 isShowDelete(false); if(onSlideDeleteListener != null){ // 调用接口的关闭的方法 onSlideDeleteListener.onClose(SlideDelete.this); } } super.onViewReleased(releasedChild, xvel, yvel); } } /** * 是否展示delete部分 * @param isShowDelete */ public void isShowDelete(boolean isShowDelete){ if(isShowDelete){ //采用ViewDragHelper的 smoothSlideViewTo 方法让移动变得顺滑自然,不会太生硬 //smoothSlideViewTo只是模拟了数据,但是不会真正的动起来,动起来需要调用 invalidate // 而 invalidate 通过调用draw()等方法之后最后还是还是会调用 computeScroll 这个方法 // 所以,使用 smoothSlideViewTo 做过渡动画需要结合 invalidate方法 和 computeScroll方法 // smoothSlideViewTo的动画执行时间没有暴露的参数可以设置,但是这个时间是google给我们经过大量计算给出合理时间 viewDragHelper.smoothSlideViewTo(mContent,-mDeleteWidth,0); viewDragHelper.smoothSlideViewTo(mDelete,mContentWidth-mDeleteWidth,0); }else{ //mContent.layout(0,0,mContentWidth,mContentHeight); //mDelete.layout(mContentWidth, 0, mContentWidth + mDeleteWidth, mDeleteHeight); viewDragHelper.smoothSlideViewTo(mContent, 0, 0); viewDragHelper.smoothSlideViewTo(mDelete, mContentWidth, 0); } invalidate(); } @Override public void computeScroll() { //super.computeScroll(); // 把捕获的View适当的时间移动,其实也可以理解为 smoothSlideViewTo 的模拟过程还没完成 if(viewDragHelper.continueSettling(true)){ invalidate(); } // 其实这个动画过渡的过程大概在怎么走呢? // 1、smoothSlideViewTo方法进行模拟数据,模拟后就就调用invalidate(); // 2、invalidate()最终调用computeScroll,computeScroll做一次细微动画, // computeScroll判断模拟数据是否彻底完成,还没完成会再次调用invalidate // 3、递归调用,知道数据noni完成。 } @Override public boolean onTouchEvent(MotionEvent event) { // 使用ViewDragHelper必须复写onTouchEvent并调用这个方法 viewDragHelper.processTouchEvent(event); //消费这个touch return true; } // SlideDlete的接口 public interface OnSlideDeleteListener { void onOpen(SlideDelete slideDelete); void onClose(SlideDelete slideDelete); }}
最主要的我们既然做完了,那么就可以开始剩下的工作的:实现我们需要的功能!
将之前我们写的2个layou文件include到一个item.xml文件中:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.example.listview_delete.DeleteListView android:id="@+id/mSlideDelete" android:layout_width="match_parent" android:layout_height="60dp"> <!--文本部分--> <include layout="@layout/slide_content"/> <!--删除部分--> <include layout="@layout/slide_delete"/> </com.example.listview_delete.DeleteListView></LinearLayout>
然后我们在activity_main.xml中添加代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.listview_delete.MainActivity" > <ListView android:id="@+id/mLv" android:layout_width="match_parent" android:layout_height="match_parent"></ListView></RelativeLayout>
MainActivity.java:
import java.util.ArrayList;import java.util.List;import java.util.ListIterator;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.BaseAdapter;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.TextView;public class MainActivity extends Activity { private ListView mLv; private ArrayList<String> mData; // 继续有多少个条目的delete被展示出来的集合 private List<DeleteListView> slideDeleteArrayList = new ArrayList<DeleteListView>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLv = (ListView) findViewById(R.id.mLv); mData=new ArrayList<String>(); for(int i=0;i<20;i++){ mData.add("Text"+i); } mLv.setAdapter(new MyAdapter()); mLv.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if(scrollState == SCROLL_STATE_FLING || scrollState == SCROLL_STATE_TOUCH_SCROLL){ closeOtherItem(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); } class MyAdapter extends BaseAdapter{ @Override public int getCount() { if(mData!=null){ return mData.size(); } return 0; } @Override public Object getItem(int position) { if(mData!=null){ return mData.get(position); } return null; } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if(convertView == null){ viewHolder = new ViewHolder(); convertView = View.inflate(MainActivity.this,R.layout.item,null); viewHolder.mSlideDelete = (DeleteListView) convertView.findViewById(R.id.mSlideDelete); viewHolder.mTvContent = (TextView) convertView.findViewById(R.id.Content); viewHolder.mLlDelete = (LinearLayout) convertView.findViewById(R.id.Delete); convertView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.mTvContent.setText(mData.get(position)); viewHolder.mSlideDelete.setOnSlideDeleteListener(new DeleteListView.OnSlideDeleteListener() { @Override public void onOpen(DeleteListView slideDelete) { closeOtherItem(); slideDeleteArrayList.add(slideDelete); Log.d("Slide", "slideDeleteArrayList当前数量:" + slideDeleteArrayList.size()); } @Override public void onClose(DeleteListView slideDelete) { slideDeleteArrayList.remove(slideDelete); Log.d("Slide", "slideDeleteArrayList当前数量:" + slideDeleteArrayList.size()); } }); viewHolder.mLlDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mData.remove(position); notifyDataSetChanged(); } }); return convertView; } } class ViewHolder{ DeleteListView mSlideDelete; TextView mTvContent; LinearLayout mLlDelete; } private void closeOtherItem(){ // 采用Iterator的原因是for是线程不安全的,迭代器是线程安全的 ListIterator<DeleteListView> slideDeleteListIterator = slideDeleteArrayList.listIterator(); while(slideDeleteListIterator.hasNext()){ DeleteListView slideDelete = slideDeleteListIterator.next(); slideDelete.isShowDelete(false); } slideDeleteArrayList.clear(); }}
大致代码就像上面的一样了,此外发现一些bug,不能快速左划删除,另外,当我们左划到一定程度,可以看见删除的时候,这时你点击删除,会发现一个问题:就是你选中的被删除了,但是其下面一条则是被选择像又滑动的状态。
能力有限,只能到这里,希望大家多多指教。
由于刚刚上传源码,所以链接的话还要等,需要的可以私聊我(一般都有空。。。)
0 0
- 模仿QQ左滑删除当前会话列表
- 仿QQ列表左滑删除效果
- 仿QQ消息列表的左滑置顶与删除
- 模仿QQ左划显示置顶, 标记, 删除
- 仿QQ左滑删除
- 仿QQ左滑删除
- 仿QQ左滑删除
- H5页面仿原生列表会话左滑显示删除键--整体滑动
- H5页面仿原生列表会话左滑显示删除键/编辑键--局部滑动
- 4.模仿QQ侧滑删除
- Android 模仿QQ侧滑删除ListView
- 模仿qq滑动删除
- 自定义listview 实现仿qq聊天列表左滑删除效果
- RecyclerView仿QQ消息列表左滑弹出菜单,标记已读和删除
- 简单ListView仿QQ左滑删除
- 仿QQ左滑打开或删除
- RecyclerView 左滑(仿QQ左滑删除)
- RecyclerView 左滑(仿QQ左滑删除)
- HEX文件说明
- 插入排序
- Eclipse新建Web项目中J2EE Specification Level选项作用
- 数百个 HTML5 例子学习 HT 图形组件 – WebGL 3D 篇
- Dynamic web version与tomcat支持
- 模仿QQ左滑删除当前会话列表
- heapsort
- ARM中断系统详述4
- Digits of PI (Part 2)
- ubuntu 16.04 apt-get update无法更新
- uva 11374 Dijkstra
- PL/SQL Developer导入导出数据库的方法以及说明
- ubuntu16.04无法安装第三方软件解决方法 、
- geoip PHP版本