QQ聊天界面侧滑删除
来源:互联网 发布:java多线程调用方法 编辑:程序博客网 时间:2024/06/11 07:35
侧滑删除
效果图:
实现步骤:
1. 创建SwipeLayout
public class SwipeLayout extends FrameLayout { public SwipeLayout(Context context) { super(context); } public SwipeLayout(Context context, AttributeSet attrs) { super(context, attrs); }}
2. 创建SwipeLayout的布局
<com.itcast.jq.swipelayout.view.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="55dp" > <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="right" android:orientation="horizontal" > <TextView android:layout_width="55dp" android:layout_height="match_parent" android:background="#44ff0000" android:gravity="center" android:text="删除" /> <TextView android:layout_width="55dp" android:layout_height="match_parent" android:background="#4400ff00" android:gravity="center" android:text="取消" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#33ff0000" > <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" android:paddingLeft="10dp" android:text="帅的人" /> </LinearLayout></com.itcast.jq.swipelayout.view.SwipeLayout>
3. 重写回调方法获取控件尺寸及初始化位置
@Overrideprotected void onFinishInflate() { super.onFinishInflate(); // System.out.println("---------onFinishInflate"); if (getChildCount() < 2) { throw new IllegalStateException("SwipeLayout must has at least 2 children"); } mBackView = getChildAt(0); mFrontView = getChildAt(1);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // System.out.println("---------onMeasure"); // 注意不能在onFinishInflate方法中获取宽高 mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); mBackWidth = mBackView.getMeasuredWidth();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // System.out.println("---------onMeasure");}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // System.out.println("---------onMeasure"); // 设置两个子控件的初始化位置 mFrontView.layout(0, 0, mWidth, mHeight); mBackView.layout(mWidth, 0, mWidth + mBackWidth, mHeight);}
4. 使用ViewDragHelper实现拖动效果
public SwipeLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { // 1. 创建ViewDragHelper对象 mDragHelper = ViewDragHelper.create(this, mCallback); } // 4. 处理回调方法 Callback mCallback = new Callback() { // 根据返回决定是否可以拖动 public boolean tryCaptureView(View child, int pointerId) { return true; }; // 根据返回值决定被拖动的视图将要显示的位置 public int clampViewPositionHorizontal(View child, int left, int dx) { return left; }; }; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // 2. 让ViewDragHelper决定是否拦截事件 return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { // 3. 传触摸事件传给ViewDragHelper处理 mDragHelper.processTouchEvent(event); // 按下时需要返回true以便能持续地接收到触摸事件 if (event.getAction() == MotionEvent.ACTION_DOWN) { return true; } return super.onTouchEvent(event); }
5. 限制滑动范围
在clampViewPositionHorizontal方法中进行两个View的滑动范围限制
// 根据返回值决定被拖动的视图将要显示的位置public int clampViewPositionHorizontal(View child, int left, int dx) { // 限制frontView拖动的范围, [-mBackWidth, 0] if (child == mFrontView) { if (left < -mBackWidth) { left = -mBackWidth; } else if (left > 0) { left = 0; } } if (child == mBackView) { // 限制backView拖动的范围: [mWidth-mBackWidth, mWidth] if (left < mWidth-mBackWidth) { left = mWidth-mBackWidth; } else if (left > mWidth) { left = mWidth; } } return left;}
6. 关联滑动效果
在onViewPositionChanged方法通过offsetLeftAndRight方法关联滑动
// 当子控件位置发生改变后调用public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { // 关联两个子控件的滑动 if (changedView == mFrontView) { // 滑动mFrontView时,同时滑动mBackView mBackView.offsetLeftAndRight(dx); } else if (changedView == mBackView) {// 滑动mBackView时,同时滑动mFrontView mFrontView.offsetLeftAndRight(dx); } // 解决往左滑时mBackView空白的问题,同时兼容低版本2.3 invalidate();};
7. 打开或关闭SwipeLayout
// 松开手释放子控件时回调// xvel: 往右甩速度大于0public void onViewReleased(View releasedChild, float xvel, float yvel) { int left = mFrontView.getLeft(); if (xvel == 0 && left < -mBackWidth /2) { open(); } else if (xvel < 0) { // 往左甩 open(); } else { close(); }};/** 关闭SwipeLayout*/public void close() { // 平滑滚动到某一位置: 第一步 mDragHelper.smoothSlideViewTo(mFrontView, 0, 0); ViewCompat.postInvalidateOnAnimation(this);}/** 打开SwipeLayout */public void open() { mDragHelper.smoothSlideViewTo(mFrontView, -mBackWidth, 0); ViewCompat.postInvalidateOnAnimation(this);}// 平滑滚动到某一位置: 第二步@Overridepublic void computeScroll() { super.computeScroll(); if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); }}
8. 监听滑动状态(打开、关闭、准备打开、准备关闭)
// 当子控件位置发生改变后调用public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { ... // 监听滑动状态(打开,关闭,准备打开,准备关闭) listenDragStatus(); };// ===========监听拖动状态(begin)============public enum DragStatus { OPEN, CLOSE, DRAGGING}private DragStatus mCurrentStatus = DragStatus.CLOSE;public DragStatus getDragStatus() { return mCurrentStatus;}public interface OnDragListener { public void open(SwipeLayout swipeLayout); public void close(SwipeLayout swipeLayout); /** 准备打开 */ public void onStartOpen(SwipeLayout swipeLayout); /** 准备关闭 */ public void onStartClose(SwipeLayout swipeLayout);}private OnDragListener mOnDragListener;public void setOnDragListener(OnDragListener onDragListener) { this.mOnDragListener = onDragListener;}/** 监听拖动状态 */protected void listenDragStatus() { int left = mFrontView.getLeft(); DragStatus status; if (left == 0) { status = DragStatus.CLOSE; } else if (left == -mBackWidth) { status = DragStatus.OPEN; } else { status = DragStatus.DRAGGING; } if (mOnDragListener != null) { if (status == DragStatus.OPEN) { mOnDragListener.open(this); } else if (status == DragStatus.CLOSE) { mOnDragListener.close(this); } else { // 当前状态为拖动状态 // 上一次状态为关闭,当前状态为拖动,则表示准备打开 if (mCurrentStatus == DragStatus.CLOSE) { mOnDragListener.onStartOpen(this); } else if (mCurrentStatus == DragStatus.OPEN) { // 上一次状态为打开,当前状态为拖动,则表示准备关闭 mOnDragListener.onStartClose(this); } } } mCurrentStatus = status;}// ===========监听拖动状态(end)============
9. 集成到ListView(显示列表)
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView = (ListView) findViewById(R.id.list_view); mAdapter = new ContactAdapter(this, Arrays.asList(Constant.LIST_DATAS2)); mListView.setAdapter(mAdapter);}public class ContactAdapter extends MyBaseAdapter<String> { public ContactAdapter(Context context, List<String> listDatas) { super(context, listDatas); } @Override public BaseHolder<String> createViewHolder(Context context, ViewGroup parent, String bean, int position) { return new ContactHolder(context, parent, this, position, bean); }}public class ContactHolder extends BaseHolder<String> { private TextView tvName; public ContactHolder(Context context, ViewGroup parent, MyBaseAdapter<String> adapter, int position, String bean) { super(context, parent, adapter, position, bean); } @Override public View createView(Context context, ViewGroup parent, int position, String bean) { return super.inflater.inflate(R.layout.item_list, parent, false); } @Override protected void initView() { tvName = (TextView) super.itemView.findViewById(R.id.tv_name); } @Override protected void onRefreshViews(String bean, int position) { tvName.setText(bean); }}
10. 只打开一个SwipeLayout
public class SwipeLayoutManager { // 单例 private static SwipeLayoutManager instance = new SwipeLayoutManager(); private SwipeLayoutManager() { } public static SwipeLayoutManager getInstance() { return instance; } /** 用来记录打开的SwipeLayout */ private SwipeLayout mOpenSwipeLayout; public SwipeLayout getOpenSwipeLayout() { return mOpenSwipeLayout; } /** 记录列表中打开的SwipeLayout */ public void setOpenSwipeLayout(SwipeLayout swipeLayout) { this.mOpenSwipeLayout = swipeLayout; } /** 关闭之前打开的SwipeLayout */ public void closeSwipeLayout() { if (mOpenSwipeLayout != null) { mOpenSwipeLayout.close(); mOpenSwipeLayout = null; // 关闭后需要置为空 } }}// 列表的Holder类的查找item子控件的方法protected void initView() { ... SwipeLayout layout = (SwipeLayout) super.itemView; layout.setOnDragListener(new OnDragListener() { @Override public void onOpen(SwipeLayout swipeLayout) { } // 准备打开某一个列表项的SwipeLayout时,先关闭之前打开的,再保存当前打开的 @Override public void onStartOpen(SwipeLayout swipeLayout) { if (SwipeLayoutManager.getInstance().getOpenSwipeLayout() != swipeLayout) { SwipeLayoutManager.getInstance().closeSwipeLayout(); } SwipeLayoutManager.getInstance().setOpenSwipeLayout(swipeLayout); } @Override public void onStartClose(SwipeLayout swipeLayout) { } @Override public void onClose(SwipeLayout swipeLayout) { } });}
11. 优化体验:如果左右滑动请求父控件不要拦截事件
当左右滑动时,请求列表不要拦截事件// 当子控件位置发生改变后调用public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { ... // 如果是左右滑动SwipeLayout,请求父控件ListView不要拦截事件,则 // ListView不会上下滚动,并且会把触摸事件传递下来,让SwipeLayout处理 if (Math.abs(dx) > Math.abs(dy)) { requestDisallowInterceptTouchEvent(true); } }
12. 滚动列表时关闭打开的SwipeLayout
// 滚动列表时,关闭打开的SwipeLayoutmListView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { SwipeLayoutManager.getInstance().closeSwipeLayout(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { }});
13. 列表项中的操作菜单点击事件
// 在列表的Holder类中添加protected void initView() { tvName = (TextView) super.itemView.findViewById(R.id.tv_name); tv_delete = (TextView) super.itemView.findViewById(R.id.tv_delete); tv_cancel = (TextView) super.itemView.findViewById(R.id.tv_cancel); OnClickListener onClickListener = new OnClickListener() { @Override public void onClick(View v) { SwipeLayoutManager.getInstance().closeSwipeLayout(); switch (v.getId()) { case R.id.tv_delete: showToast("取消:" + bean); break; case R.id.tv_cancel: showToast("删除:" + bean); break; } } }; tv_delete.setOnClickListener(onClickListener); tv_cancel.setOnClickListener(onClickListener); ...}
完整源码:点击下载
0 0
- QQ聊天界面侧滑删除
- 仿QQ聊天界面侧滑
- 【3】QQ 聊天界面
- 仿qq聊天界面
- 山寨QQ聊天界面
- qq 聊天界面
- QQ聊天界面
- QQ聊天界面实现
- 练习:QQ 聊天界面
- 仿微信.QQ聊天界面
- 类似QQ聊天界面
- 高仿qq聊天界面
- Android 仿微信QQ聊天界面
- Android 仿微信QQ聊天界面
- 仿QQ聊天界面<一>
- 第二十九天qq聊天界面
- QQ聊天界面注意点
- 小结:QQ 聊天界面总结
- jvm 指令集代码
- BZOJ 4653: [Noi2016]区间
- BigDecimal使用技巧
- Linux之文件通配符
- 多线程——用创建线程的两种方式分别解决经典窗口卖票问题
- QQ聊天界面侧滑删除
- python linux上交互模式tab自动补全代码
- javaScript笔记(二十五) Cookie与存储
- asm (1) helloworld
- linux 批量创建用户和修改口令
- 【牛客网】合并两个排序的链表
- 超简单的ViewPager导航栏联动实现--TabLayout
- linux之文本处理工具
- hdoj5867Water problem