彷QQ滑动删除效果—侧滑删除(置顶、标记、未读标记功能)
2017-01-12 14:29
573 查看
1:功能介绍
QQ条目删除,主要实现的主条目和删除条目的 摆放,使用的类是ViewDragHelper2、布局页面
1.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" tools:context="itheima.com.qqitemdelete.MainActivity"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="#ccc" android:dividerHeight="2dp" /> </RelativeLayout>
2.delete的删除条目——layout_delete <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="60dp" android:orientation="horizontal" > <TextView android:layout_width="70dp" android:layout_height="match_parent" android:textSize="18sp" android:textColor="#ffffff" android:gravity="center" android:background="#D4D4D7" android:text="置顶"/> <TextView android:layout_width="120dp" android:layout_height="match_parent" android:textSize="18sp" android:textColor="#ffffff" android:gravity="center" android:background="#FF9901" android:text="标记未读"/> <TextView android:layout_width="70dp" android:layout_height="match_parent" android:textSize="18sp" android:textColor="#ffffff" android:id="@+id/tv_delete" android:gravity="center" android:background="#FF3A30" android:text="删除"/> </LinearLayout>
3.主条目的布局文件——layout_content <?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:paddingLeft="15dp" android:background="#fff" android:gravity="center_vertical" android:orientation="horizontal" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@mipmap/head_1"/> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#99000000" android:id="@+id/tv_name" android:layout_marginLeft="10dp" android:textSize="20sp" android:text="名称"/> </LinearLayout> </LinearLayout>
4.adapter的item的布局文件 <?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" > <itheima.com.qqitemdelete.SwipeLayout android:id="@+id/swipeLayout" android:layout_width="match_parent" android:layout_height="wrap_content"> <!--content的布局--> <include layout="@layout/layout_content"/> <!--delete的布局--> <include layout="@layout/layout_delete"/> </itheima.com.qqitemdelete.SwipeLayout>
3、先写一个类SwipeLayout,继承自FrameLayout
public class SwipeLayout extends FrameLayout{ private ViewDragHelper viewDragHelper; private View content; private View delete; private long mDownTime; private boolean isOpen = true; //实现三个构造函数 public SwipeLayout(Context context) { this(context, null); } public SwipeLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SwipeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); viewDragHelper = ViewDragHelper.create(this, callback); } //在主页xml页面读取界面时候获取子View @Override protected void onFinishInflate() { super.onFinishInflate(); content = getChildAt(0); delete = getChildAt(1); }
4、在onLayout方法中对contentView和deleteView进行摆放
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { //摆放内容控件 content.layout(0,0,content.getMeasuredWidth(),content.getMeasuredHeight()); //获取内容控件的宽 int L= content.getRight(); delete.layout(L,0,L+delete.getMeasuredWidth(),delete.getMeasuredHeight()); } //把触摸事件传给ViewDragHelper处理,经常两种方法 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean b = viewDragHelper.shouldInterceptTouchEvent(ev); return b; } 6.1、解决第一个bug的思路是:在onTouchEvent方法判断当前手指移动的方向到底是偏向于水平还是偏向于垂直,如果是偏向于水平那么就认为用户是希望滑动item,那么则请求父View不要去拦截事件 float downX,downY; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //获取按下的坐标 downX = event.getX(); downY = event.getY(); //获取按下的时间 mDownTime = System.currentTimeMillis(); break; case MotionEvent.ACTION_MOVE: float moveX = event.getX(); float moveY = event.getY(); //计算移动距离 float dx = moveX-downX; float dy = moveY-downY; //判断到底偏向哪个方向 if(Math.abs(dx)>Math.abs(dy)){ //说明是偏向水平方向,那么就认为用户想滑动条目,此时应该让listview不要拦截 requestDisallowInterceptTouchEvent(true); } break; 6.2、由于我们重写onTouchEvent处理了事件,导致ListView的条目点击无效了,此时最有效最简单的做法是自己去判断触摸事件实现点击行为,思路是:记录按下的坐标和时间,在抬起的时候计算整个按下抬起的时间和距离,如果时间小于400毫秒,并且距离小于touchSlop,则认为是点击事件,事实上系统也是这样实现点击事件的: case MotionEvent.ACTION_UP: //1.计算按下抬起的时间 long duration = System.currentTimeMillis()-mDownTime; //2.计算按下抬起的距离 float deltaX = event.getX() - downX; float deltaY = event.getY() - downY; //deltaX的平方+deltaY的平方和进行开放,获得点与点的直线距离 float distance = (float) Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); //3. 配置,如果duration小于500,并且distance小于8px ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext()); if (duration < getLongPressTimeout() && distance < viewConfiguration.getScaledTouchSlop()) {//8dp //作用就是执行OnClickListener的onClick方法 if(isOpen){ openDeleteMenu(); isOpen=false; }else{ closeDeleteMenu(); isOpen=true; } } break; } viewDragHelper.processTouchEvent(event); return true; }
5、利用ViewDragHelper实现让SwipeLayout的2个子View进行拖拽移动,主要是Callback的实现
ViewDragHelper.Callback callback = new ViewDragHelper.Callback() { //1.捕获子View,谁为true,下面的方法就执行谁 @Override public boolean tryCaptureView(View child, int pointerId) { return true; } //2.鸡肋的方法,大于1就行 @Override public int getViewHorizontalDragRange(View child) { return 1; } //修改水平方向滑动的距离 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { //限制content if(child==content){ if(left>0){ left=0; }else if(left<-delete.getMeasuredWidth()){ left=-delete.getMeasuredWidth(); } }else if(child==delete){ //限制delete if(left>content.getMeasuredWidth()){ left = content.getMeasuredWidth(); }else if(left<(content.getMeasuredWidth()-delete.getMeasuredWidth())){ left = (content.getMeasuredWidth()-delete.getMeasuredWidth()); } } return left; } //经常执行一些伴随动画 @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); //如果移动的是content,那么让delete伴随移动 if(changedView==content){ ViewCompat.offsetLeftAndRight(delete,dx); }else if(changedView==delete){ //让content进行伴随移动 ViewCompat.offsetLeftAndRight(content,dx); } //回调接口的方法 if(listener!=null){ if(content.getLeft()==0){ listener.onClose(SwipeLayout.this); }else if(content.getLeft()==-delete.getMeasuredWidth()){ listener.onOpen(SwipeLayout.this); } } } //手指离开执行的方法 @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); if(content.getLeft()>-delete.getMeasuredWidth()/2){ //关闭 closeDeleteMenu(); isOpen=true; }else{ //打开 openDeleteMenu(); isOpen=false; } } }; /** * 打开页面 */ public void openDeleteMenu() { viewDragHelper.smoothSlideViewTo(content,-delete.getMeasuredWidth(),0); ViewCompat.postInvalidateOnAnimation(this); } /** * 关闭页面 */ public void closeDeleteMenu() { //平滑至 viewDragHelper.smoothSlideViewTo(content,0,0); //动画更新 ViewCompat.postInvalidateOnAnimation(this); } //如果动画还没有结束,继续刷新 compute计算 @Override public void computeScroll() { super.computeScroll(); if(viewDragHelper.continueSettling(true)){ ViewCompat.postInvalidateOnAnimation(this); } } //创建回调,把SwipeLayout返回Activity中 private OnSwipeListener listener; public void setOnSwipeListener(OnSwipeListener listener){ this.listener=listener; } public interface OnSwipeListener{ void onOpen(SwipeLayout currentLayout); void onClose(SwipeLayout currentLayout); } }
6、然后将实现好的可滑动的SwipeLayout放入ListView的adapter的布局中,此时我们遇到2个bug:
1、当我们左右拖动item滑动时,再上下滑动会遇到事件被ListView捕获并处理,导致我们无法继续控制item的滑动; 2、我们可以同时滑动出多个item,而需求是只能允许一个item是打开的;
public class MainActivity extends AppCompatActivity { @BindView(R.id.list_view) ListView listView; boolean isOpen=true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); //设置适配器 listView.setAdapter(new MyAdapter()); //监听listView的滚动,实现功能:当条目滑动的时候,菜单关闭 listView.setOnScrollListener(new AbsListView.OnScrollListener(){ @Override public void onScrollStateChanged(AbsListView absListView, int i) { if(openedLayout!=null){ openedLayout.closeDeleteMenu(); } } @Override public void onScroll(AbsListView absListView, int i, int i1, int i2) { } }); } //用来记录打开的SwipeLayout SwipeLayout openedLayout; class MyAdapter extends BaseAdapter implements SwipeLayout.OnSwipeListener { @Override public int getCount() { return Constant.NAMES.length; } @Override public Object getItem(int i) { return null; } @Override public long getItemId(int i) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { MyHolder myHolder = null; //简单的复用缓存 if (convertView == null) { convertView = View.inflate(parent.getContext(), R.layout.adapter_list_item, null); myHolder = new MyHolder(convertView); convertView.setTag(myHolder); } else { myHolder = (MyHolder) convertView.getTag(); } //绑定数据 myHolder.tvName.setText(Constant.NAMES[position]); //设置监听器 myHolder.swipeLayout.setOnSwipeListener(this); return convertView; } @Override public void onOpen(SwipeLayout currentLayout) { //判断SwipeLayout不为空并且不等于当前条目,就关闭其他条目 if (openedLayout!= null && openedLayout!=currentLayout) { openedLayout.closeDeleteMenu(); } openedLayout=currentLayout; } @Override public void onClose(SwipeLayout currentLayout) { //如果传来的条目是自己,就把SwipeLayout置为空 if (openedLayout == currentLayout) { openedLayout = null; } } } static class MyHolder { @BindView(R.id.tv_name) TextView tvName; @BindView(R.id.tv_delete) TextView tvDelete; @BindView(R.id.swipeLayout) SwipeLayout swipeLayout; MyHolder(View view) { ButterKnife.bind(this, view); } } }
相关文章推荐
- Android仿QQ侧滑(删除、置顶等)功能
- [置顶] Android LRecyclerView实现Item侧滑菜单、长按拖拽Item、滑动删除Item等功能
- 仿照 QQ 的 cell 的左滑删除、置顶、标记未读效果
- Android 高仿微信(QQ)滑动弹出编辑、删除菜单效果,增加下拉刷新功能
- Android实现QQ侧滑(删除、置顶等)功能
- Android开发实现仿QQ消息SwipeMenuListView滑动删除置顶功能【附源码下载】
- Android listview 侧滑 SwipeListView 详解 实现微信,QQ等滑动删除效果
- Android 高仿微信(QQ)滑动弹出编辑、删除菜单效果,增加下拉刷新功能
- (4.2.5) 【android开源组件】SwipeListView 详解 实现微信,QQ等滑动删除效果
- Android ListView 侧滑效果实现(滑动展开、滑动删除)
- ExpandableListView实例(三)_实现QQ中"未分组"效果和"未分组"不可编辑删除功能
- SwipeListView 详解 实现微信,QQ等滑动删除效果
- 高仿 QQ 侧滑删除 Item 的效果
- swift tableview中添加侧滑删除功能 类似qq删除
- Android实现类似QQ的滑动删除效果
- SwipeListView 详解 实现微信,QQ等滑动删除效果
- Android仿qq聊天记录长按删除功能效果
- 安卓listView实现下拉刷新上拉加载滑动仿QQ的删除功能
- Android ListView侧滑item,仿QQ删除效果
- 安卓listView实现下拉刷新上拉加载滑动仿QQ的删除功能