为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

高仿微信对话列表滑动删除效果

2017-12-08 19页 doc 80KB 11阅读

用户头像

is_751406

暂无简介

举报
高仿微信对话列表滑动删除效果高仿微信对话列表滑动删除效果 www.credesign.net 高仿微信对话列表滑动删除效果 前言 用过微信的都知道,微信对话列表滑动删除效果是很不错的,这个效果我们也可以有。思路其实很简单,弄个ListView,然后里面的每个item做成一个可以滑动的自定义控件即可。由于ListView是上下滑动而item是左右滑动,因此会有滑动冲突,也许你需要了解下android中点击事件的派发流程,请参考Android源码分析-点击事件派发机制。我的解决思路是这样的:重写ListView的onInterceptTouchEven...
高仿微信对话列表滑动删除效果
高仿微信对话列表滑动删除效果 www.credesign.net 高仿微信对话列表滑动删除效果 前言 用过微信的都知道,微信对话列表滑动删除效果是很不错的,这个效果我们也可以有。思路其实很简单,弄个ListView,然后里面的每个item做成一个可以滑动的自定义控件即可。由于ListView是上下滑动而item是左右滑动,因此会有滑动冲突,也许你需要了解下android中点击事件的派发,请参考Android源码-点击事件派发机制。我的解决思路是这样的:重写ListView的onInterceptTouchEvent方法,在move的时候做判断,如果是左右滑动就返回false,否则返回true;重写SlideView(即自定义item控件)的onTouchEvent方法来处理滑动。整个思路没有问题,滑动冲突也解决了,可是ListView无法得到焦点了,也就是ListView无法处理点击事件了。让我们回想下问题出在哪里:我的理解是这样的,上述处理滑动本身没有问题,但是有一个副作用,就是会让外层View失去焦点且无法处理点击事件。常见的滑动冲突场景,比如launcher内部嵌入ListView却是没有问题的,因为这个时候launcher不需要获得焦点,需要获得焦点的是内部的ListView。因此,上述处理方式对于外部需要获得焦点的情况(比如外部是ListView)就不太适合了。于是我就和ttdevs探讨,发现他采用了另外一种思路,我从来没有想过还可以这样玩。下面介绍他的思路。 新的思路 不考虑那么复杂,不采用主流玩法,所有的事件均由外层的ListView做拦截,同时把事件传递给SlideView做滑动,这种实现的确可以达到效果,而且代码很简单,根本不需要处理什么复杂的滑动冲突。 效果 下面分别为微信和高仿效果 www.credesign.net www.credesign.net 代码分析 先看SlideView是如何实现的 看layout xml: [html] view plaincopy 1. 2. 5. 6. 11. 12. 13. 19. 20. 29. 30. 31. 上述xml文件中,所有的view都会被放在view_content中,而holder是放置诸如删除按钮之类的东西,我们的 SlideView会加载这个布局。 再看SlideView.java: [java] view plaincopy 1. /** 2. * SlideView 继承自LinearLayout 3. */ 4. public class SlideView extends LinearLayout { 5. 6. private static final String TAG = "SlideView"; 7. 8. private Context mContext; 9. 10. // 用来放置所有view的容器 11. private LinearLayout mViewContent; 12. 13. // 用来放置内置view的容器,比如删除 按钮 14. private RelativeLayout mHolder; 15. 16. // 弹性滑动对象,提供弹性滑动效果 17. private Scroller mScroller; 18. 19. // 滑动回调接口,用来向上层通知滑动事件 20. private OnSlideListener mOnSlideListener; 21. www.credesign.net www.credesign.net 22. // 内置容器的宽度 单位:dp 23. private int mHolderWidth = 120; 24. 25. // 分别记录上次滑动的坐标 26. private int mLastX = 0; 27. private int mLastY = 0; 28. 29. // 用来控制滑动角度,仅当角度a满足如下条件才进行滑动:tan a = deltaX / deltaY > 2 30. private static final int TAN = 2; 31. 32. public interface OnSlideListener { 33. // SlideView的三种状态:开始滑动,打开,关闭 34. public static final int SLIDE_STATUS_OFF = 0; 35. public static final int SLIDE_STATUS_START_SCROLL = 1; 36. public static final int SLIDE_STATUS_ON = 2; 37. 38. /** 39. * @param view 40. * current SlideView 41. * @param status 42. * SLIDE_STATUS_ON, SLIDE_STATUS_OFF or 43. * SLIDE_STATUS_START_SCROLL 44. */ 45. public void onSlide(View view, int status); 46. } 47. 48. public SlideView(Context context) { 49. super(context); 50. initView(); 51. } 52. 53. public SlideView(Context context, AttributeSet attrs) { 54. super(context, attrs); 55. initView(); 56. } 57. 58. private void initView() { 59. mContext = getContext(); 60. // 初始化弹性滑动对象 61. mScroller = new Scroller(mContext); 62. // 设置其方向为横向 63. setOrientation(LinearLayout.HORIZONTAL); 64. // 将slide_view_merge加载进来 65. View.inflate(mContext, R.layout.slide_view_merge, this); www.credesign.net www.credesign.net 66. mViewContent = (LinearLayout) findViewById(R.id.view_content); 67. mHolderWidth = Math.round(TypedValue.applyDimension( 68. TypedValue.COMPLEX_UNIT_DIP, mHolderWidth, getResources() 69. .getDisplayMetrics())); 70. } 71. 72. // 设置按钮的内容,也可以设置图标啥的,我没写 73. public void setButtonText(CharSequence text) { 74. ((TextView) findViewById(R.id.delete)).setText(text); 75. } 76. 77. // 将view加入到ViewContent中 78. public void setContentView(View view) { 79. mViewContent.addView(view); 80. } 81. 82. // 设置滑动回调 83. public void setOnSlideListener(OnSlideListener onSlideListener) { 84. mOnSlideListener = onSlideListener; 85. } 86. 87. // 将当前状态置为关闭 88. public void shrink() { 89. if (getScrollX() != 0) { 90. this.smoothScrollTo(0, 0); 91. } 92. } 93. 94. // 根据MotionEvent来进行滑动,这个方法的作用相当于onTouchEvent 95. // 如果你不需要处理滑动冲突,可以直接重命名,照样能正常工作 96. public void onRequireTouchEvent(MotionEvent event) { 97. int x = (int) event.getX(); 98. int y = (int) event.getY(); 99. int scrollX = getScrollX(); 100. Log.d(TAG, "x=" + x + " y=" + y); 101. 102. switch (event.getAction()) { 103. case MotionEvent.ACTION_DOWN: { 104. if (!mScroller.isFinished()) { 105. mScroller.abortAnimation(); 106. } 107. if (mOnSlideListener != null) { 108. mOnSlideListener.onSlide(this, 109. OnSlideListener.SLIDE_STATUS_START_SCROLL); www.credesign.net www.credesign.net 110. } 111. break; 112. } 113. case MotionEvent.ACTION_MOVE: { 114. int deltaX = x - mLastX; 115. int deltaY = y - mLastY; 116. if (Math.abs(deltaX) < Math.abs(deltaY) * TAN) { 117. // 滑动不满足条件,不做横向滑动 118. break; 119. } 120. 121. // 计算滑动终点是否合法,防止滑动越界 122. int newScrollX = scrollX - deltaX; 123. if (deltaX != 0) { 124. if (newScrollX < 0) { 125. newScrollX = 0; 126. } else if (newScrollX > mHolderWidth) { 127. newScrollX = mHolderWidth; 128. } 129. this.scrollTo(newScrollX, 0); 130. } 131. break; 132. } 133. case MotionEvent.ACTION_UP: { 134. int newScrollX = 0; 135. // 这里做了下判断,当松开手的时候,会自动向两边滑动,具体向哪边滑,要看当前所处的位置 136. if (scrollX - mHolderWidth * 0.75 > 0) { 137. newScrollX = mHolderWidth; 138. } 139. // 慢慢滑向终点 140. this.smoothScrollTo(newScrollX, 0); 141. // 通知上层滑动事件 142. if (mOnSlideListener != null) { 143. mOnSlideListener.onSlide(this, 144. newScrollX == 0 ? OnSlideListener.SLIDE_STATUS_OFF 145. : OnSlideListener.SLIDE_STATUS_ON); 146. } 147. break; 148. } 149. default: 150. break; 151. } 152. 153. mLastX = x; www.credesign.net www.credesign.net 154. mLastY = y; 155. } 156. 157. private void smoothScrollTo(int destX, int destY) { 158. // 缓慢滚动到指定位置 159. int scrollX = getScrollX(); 160. int delta = destX - scrollX; 161. // 以三倍时长滑向destX,效果就是慢慢滑动 162. mScroller.startScroll(scrollX, 0, delta, 0, Math.abs(delta) * 3); 163. invalidate(); 164. } 165. 166. @Override 167. public void computeScroll() { 168. if (mScroller.computeScrollOffset()) { 169. scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 170. postInvalidate(); 171. } 172. } 173. 174. } 上述代码做了很详细的说明,这就是滑动控件的完整代码,大家要明白的是:你所添加的view都是加在SlideView 的子View : view_content中的,而不是直接加在SlideView中,只有这样我们才方便做滑动效果。 接着看ListView的代码:核心就是下面这一个方法,将点击事件发送给SlideView处理。 [java] view plaincopy 1. @Override 2. public boolean onTouchEvent(MotionEvent event) { 3. switch (event.getAction()) { 4. case MotionEvent.ACTION_DOWN: { 5. int x = (int) event.getX(); 6. int y = (int) event.getY(); 7. //我们想知道当前点击了哪一行 8. int position = pointToPosition(x, y); 9. Log.e(TAG, "postion=" + position); 10. if (position != INVALID_POSITION) { 11. //得到当前点击行的数据从而取出当前行的item。 12. //可能有人怀疑,为什么要这么干,为什么不用getChildAt(position), 13. //因为ListView会进行缓存,如果你不这么干,有些行的view你是得不到的。 14. MessageItem data = (MessageItem) getItemAtPosition(position); 15. mFocusedItemView = data.slideView; 16. Log.e(TAG, "FocusedItemView=" + mFocusedItemView); 17. } www.credesign.net www.credesign.net 18. } 19. default: 20. break; 21. } 22. 23. //向当前点击的view发送滑动事件请求,其实就是向SlideView发请求 24. if (mFocusedItemView != null) { 25. mFocusedItemView.onRequireTouchEvent(event); 26. } 27. 28. return super.onTouchEvent(event); 29. } 最后看Activity的代码: [java] view plaincopy 1. public class MainActivity extends Activity implements OnItemClickListener, 2. OnClickListener, OnSlideListener { 3. 4. private static final String TAG = "MainActivity"; 5. 6. private ListViewCompat mListView; 7. 8. private List mMessageItems = new ArrayList(); 9. 10. private SlideAdapter mSlideAdapter; 11. 12. // 上次处于打开状态的SlideView 13. private SlideView mLastSlideViewWithStatusOn; 14. 15. @Override 16. protected void onCreate(Bundle savedInstanceState) { 17. super.onCreate(savedInstanceState); 18. setContentView(R.layout.activity_main); 19. initView(); 20. } 21. 22. private void initView() { 23. mListView = (ListViewCompat) findViewById(R.id.list); 24. 25. for (int i = 0; i < 20; i++) { 26. MessageItem item = new MessageItem(); 27. if (i % 3 == 0) { 28. item.iconRes = R.drawable.default_qq_avatar; www.credesign.net www.credesign.net 29. item.title = "腾讯新闻"; 30. item.msg = "青岛爆炸满月:大量鱼虾死亡"; 31. item.time = "晚上18:18"; 32. } else { 33. item.iconRes = R.drawable.wechat_icon; 34. item.title = "微信团队"; 35. item.msg = "欢迎你使用微信"; 36. item.time = "12月18日"; 37. } 38. mMessageItems.add(item); 39. } 40. mSlideAdapter = new SlideAdapter(); 41. mListView.setAdapter(mSlideAdapter); 42. mListView.setOnItemClickListener(this); 43. } 44. 45. private class SlideAdapter extends BaseAdapter { 46. 47. private LayoutInflater mInflater; 48. 49. SlideAdapter() { 50. super(); 51. mInflater = getLayoutInflater(); 52. } 53. 54. @Override 55. public int getCount() { 56. return mMessageItems.size(); 57. } 58. 59. @Override 60. public Object getItem(int position) { 61. return mMessageItems.get(position); 62. } 63. 64. @Override 65. public long getItemId(int position) { 66. return position; 67. } 68. 69. @Override 70. public View getView(int position, View convertView, ViewGroup parent) { 71. ViewHolder holder; 72. SlideView slideView = (SlideView) convertView; www.credesign.net www.credesign.net 73. if (slideView == null) { 74. // 这里是我们的item 75. View itemView = mInflater.inflate(R.layout.list_item, null); 76. 77. slideView = new SlideView(MainActivity.this); 78. // 这里把item加入到slideView 79. slideView.setContentView(itemView); 80. // 下面是做一些数据缓存 81. holder = new ViewHolder(slideView); 82. slideView.setOnSlideListener(MainActivity.this); 83. slideView.setTag(holder); 84. } else { 85. holder = (ViewHolder) slideView.getTag(); 86. } 87. MessageItem item = mMessageItems.get(position); 88. item.slideView = slideView; 89. item.slideView.shrink(); 90. 91. holder.icon.setImageResource(item.iconRes); 92. holder.title.setText(item.title); 93. holder.msg.setText(item.msg); 94. holder.time.setText(item.time); 95. holder.deleteHolder.setOnClickListener(MainActivity.this); 96. 97. return slideView; 98. } 99. 100. } 101. 102. public class MessageItem { 103. public int iconRes; 104. public String title; 105. public String msg; 106. public String time; 107. public SlideView slideView; 108. } 109. 110. private static class ViewHolder { 111. public ImageView icon; 112. public TextView title; 113. public TextView msg; 114. public TextView time; 115. public ViewGroup deleteHolder; 116. www.credesign.net www.credesign.net 117. ViewHolder(View view) { 118. icon = (ImageView) view.findViewById(R.id.icon); 119. title = (TextView) view.findViewById(R.id.title); 120. msg = (TextView) view.findViewById(R.id.msg); 121. time = (TextView) view.findViewById(R.id.time); 122. deleteHolder = (ViewGroup) view.findViewById(R.id.holder); 123. } 124. } 125. 126. @Override 127. public void onItemClick(AdapterView parent, View view, int position, 128. long id) { 129. // 这里处理ListItem的点击事件 130. Log.e(TAG, "onItemClick position=" + position); 131. } 132. 133. @Override 134. public void onSlide(View view, int status) { 135. // 如果当前存在已经打开的SlideView,那么将其关闭 136. if (mLastSlideViewWithStatusOn != null 137. && mLastSlideViewWithStatusOn != view) { 138. mLastSlideViewWithStatusOn.shrink(); 139. } 140. // 记录本次处于打开状态的view 141. if (status == SLIDE_STATUS_ON) { 142. mLastSlideViewWithStatusOn = (SlideView) view; 143. } 144. } 145. 146. @Override 147. public void onClick(View v) { 148. // 这里处理删除按钮的点击事件,可以删除对话 149. if (v.getId() == R.id.holder) { 150. int position = mListView.getPositionForView(v); 151. if (position != ListView.INVALID_POSITION) { 152. mMessageItems.remove(position); 153. mSlideAdapter.notifyDataSetChanged(); 154. } 155. Log.e(TAG, "onClick v=" + v); 156. } 157. } 158. } 代码我都特意写了注释,就不多说了。 www.credesign.net www.credesign.net 代码下载: www.credesign.net
/
本文档为【高仿微信对话列表滑动删除效果】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索