在《Android开发艺术探索》一书中自定义View一节中提到了关于一个类似横向滑动List的自定义ViewGroup:HorizontalScrollViewEx。如果你使用过的话就会发现,使用起来十分别扭。下面就是其原代码:

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;public class HorizontalScrollViewEx extends ViewGroup {private static final String TAG = "HorizontalScrollViewEx";private int mChildrenSize;private int mChildWidth;private int mChildIndex;// 分别记录上次滑动的坐标private int mLastX = 0;private int mLastY = 0;// 分别记录上次滑动的坐标(onInterceptTouchEvent)private int mLastXIntercept = 0;private int mLastYIntercept = 0;private Scroller mScroller;private VelocityTracker mVelocityTracker;public HorizontalScrollViewEx(Context context) {super(context);init();}public HorizontalScrollViewEx(Context context, AttributeSet attrs) {super(context, attrs);init();}public HorizontalScrollViewEx(Context context, AttributeSet attrs,int defStyle) {super(context, attrs, defStyle);init();}private void init() {mScroller = new Scroller(getContext());mVelocityTracker = VelocityTracker.obtain();}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {boolean intercepted = false;int x = (int) event.getX();int y = (int) event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN: {intercepted = false;if (!mScroller.isFinished()) {mScroller.abortAnimation();intercepted = true;}break;}case MotionEvent.ACTION_MOVE: {int deltaX = x - mLastXIntercept;int deltaY = y - mLastYIntercept;if (Math.abs(deltaX) > Math.abs(deltaY)) {intercepted = true;} else {intercepted = false;}break;}case MotionEvent.ACTION_UP: {intercepted = false;break;}default:break;}Log.d(TAG, "intercepted=" + intercepted);mLastX = x;mLastY = y;mLastXIntercept = x;mLastYIntercept = y;return intercepted;}@Overridepublic boolean onTouchEvent(MotionEvent event) {mVelocityTracker.addMovement(event);int x = (int) event.getX();int y = (int) event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN: {if (!mScroller.isFinished()) {mScroller.abortAnimation();}break;}case MotionEvent.ACTION_MOVE: {int deltaX = x - mLastX;int deltaY = y - mLastY;scrollBy(-deltaX, 0);break;}case MotionEvent.ACTION_UP: {int scrollX = getScrollX();int scrollToChildIndex = scrollX / mChildWidth;mVelocityTracker.computeCurrentVelocity(1000);float xVelocity = mVelocityTracker.getXVelocity();if (Math.abs(xVelocity) >= 50) {mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;} else {mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;}mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));int dx = mChildIndex * mChildWidth - scrollX;smoothScrollBy(dx, 0);mVelocityTracker.clear();break;}default:break;}mLastX = x;mLastY = y;return true;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int measuredWidth = 0;int measuredHeight = 0;final int childCount = getChildCount();measureChildren(widthMeasureSpec, heightMeasureSpec);int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);if (childCount == 0) {setMeasuredDimension(0, 0);} else if (heightSpecMode == MeasureSpec.AT_MOST) {final View childView = getChildAt(0);measuredHeight = childView.getMeasuredHeight();setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());} else if (widthSpecMode == MeasureSpec.AT_MOST) {final View childView = getChildAt(0);measuredWidth = childView.getMeasuredWidth() * childCount;setMeasuredDimension(measuredWidth, heightSpaceSize);} else {final View childView = getChildAt(0);measuredWidth = childView.getMeasuredWidth() * childCount;measuredHeight = childView.getMeasuredHeight();setMeasuredDimension(measuredWidth, measuredHeight);}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int childLeft = 0;final int childCount = getChildCount();mChildrenSize = childCount;for (int i = 0; i < childCount; i++) {final View childView = getChildAt(i);if (childView.getVisibility() != View.GONE) {final int childWidth = childView.getMeasuredWidth();mChildWidth = childWidth;childView.layout(childLeft, 0, childLeft + childWidth,childView.getMeasuredHeight());childLeft += childWidth;}}}private void smoothScrollBy(int dx, int dy) {mScroller.startScroll(getScrollX(), 0, dx, 0, 500);invalidate();}@Overridepublic void computeScroll() {if (mScroller.computeScrollOffset()) {scrollTo(mScroller.getCurrX(), mScroller.getCurrY());postInvalidate();}}@Overrideprotected void onDetachedFromWindow() {mVelocityTracker.recycle();super.onDetachedFromWindow();}
}

而利用书中提到的使用动画来实现平滑滑动的话,体验会好很多,下面是我改进的代码,主要修改了:smoothScrollBy(int dx, int dy)这个函数,读者可自行比较,以做学习参考:

import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;public class HorizontalScrollViewEx extends ViewGroup {private static final String TAG = "HorizontalScrollViewEx";private int mChildrenSize;private int mChildWidth;private int mChildIndex;// 分别记录上次滑动的坐标private int mLastX = 0;private int mLastY = 0;// 分别记录上次滑动的坐标(onInterceptTouchEvent)private int mLastXIntercept = 0;private int mLastYIntercept = 0;private Scroller mScroller;private VelocityTracker mVelocityTracker;ValueAnimator animator;public HorizontalScrollViewEx(Context context) {super(context);init();}public HorizontalScrollViewEx(Context context, AttributeSet attrs) {super(context, attrs);init();}public HorizontalScrollViewEx(Context context, AttributeSet attrs,int defStyle) {super(context, attrs, defStyle);init();}private void init() {mScroller = new Scroller(getContext());mVelocityTracker = VelocityTracker.obtain();}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {boolean intercepted = false;int x = (int) event.getX();int y = (int) event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN: {intercepted = false;if (!mScroller.isFinished()) {mScroller.abortAnimation();intercepted = true;}break;}case MotionEvent.ACTION_MOVE: {int deltaX = x - mLastXIntercept;int deltaY = y - mLastYIntercept;if (Math.abs(deltaX) > Math.abs(deltaY)) {intercepted = true;} else {intercepted = false;}break;}case MotionEvent.ACTION_UP: {intercepted = false;break;}default:break;}Log.d(TAG, "intercepted=" + intercepted);mLastX = x;mLastY = y;mLastXIntercept = x;mLastYIntercept = y;return intercepted;}@Overridepublic boolean onTouchEvent(MotionEvent event) {mVelocityTracker.addMovement(event);int x = (int) event.getX();int y = (int) event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN: {if(animator != null){if(animator.isRunning()){animator.cancel();}}break;}case MotionEvent.ACTION_MOVE: {int deltaX = x - mLastX;int deltaY = y - mLastY;scrollBy(-deltaX, 0);break;}case MotionEvent.ACTION_UP: {int scrollX = getScrollX();int scrollToChildIndex = scrollX / mChildWidth;mVelocityTracker.computeCurrentVelocity(1000);float xVelocity = mVelocityTracker.getXVelocity();if (Math.abs(xVelocity) >= 50) {//根据速度来决定要移动多少个Child,这里是500,可以调整mChildIndex = mChildIndex - (int)xVelocity/500;} else {mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;}mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));int dx = mChildIndex * mChildWidth - scrollX;smoothScrollBy(dx, 0);mVelocityTracker.clear();break;}default:break;}mLastX = x;mLastY = y;return true;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int measuredWidth = 0;int measuredHeight = 0;final int childCount = getChildCount();measureChildren(widthMeasureSpec, heightMeasureSpec);int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);if (childCount == 0) {setMeasuredDimension(0, 0);} else if (heightSpecMode == MeasureSpec.AT_MOST) {final View childView = getChildAt(0);measuredHeight = childView.getMeasuredHeight();setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());} else if (widthSpecMode == MeasureSpec.AT_MOST) {final View childView = getChildAt(0);measuredWidth = childView.getMeasuredWidth() * childCount;setMeasuredDimension(measuredWidth, heightSpaceSize);} else {final View childView = getChildAt(0);measuredWidth = childView.getMeasuredWidth() * childCount;measuredHeight = childView.getMeasuredHeight();setMeasuredDimension(measuredWidth, measuredHeight);}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int childLeft = 0;final int childCount = getChildCount();mChildrenSize = childCount;for (int i = 0; i < childCount; i++) {final View childView = getChildAt(i);if (childView.getVisibility() != View.GONE) {final int childWidth = childView.getMeasuredWidth();mChildWidth = childWidth;childView.layout(childLeft, 0, childLeft + childWidth,childView.getMeasuredHeight());childLeft += childWidth;}}}private void smoothScrollBy(int dx, int dy) {int startX = getScrollX();animator = ValueAnimator.ofInt(0, 1).setDuration(Math.abs(dx));animator.setInterpolator(new DecelerateInterpolator());animator.addUpdateListener(animation -> {float fraction = animation.getAnimatedFraction();scrollTo(startX + (int)(dx*fraction), 0);});animator.start();invalidate();}@Overrideprotected void onDetachedFromWindow() {mVelocityTracker.recycle();super.onDetachedFromWindow();}
}

《Android开发艺术探索》自定义View中关于“HorizontalScrollViewEx”的改进相关推荐

  1. Android开发艺术探索笔记

    <Android开发艺术探索>这本书在几年前就已经买了,陆陆续续看过几次,都没有看完,没有理解透. 最近重读<Android开发艺术探索>,读了两次,第一次读完感觉还有大量的知 ...

  2. 《Android开发艺术探索》读书笔记——Cha3.2.2使用动画实现View的滑动

    章节来自<Android开发艺术探索> 第一种方式 3.2.2 使用动画 上一节介绍了采用scrollTo/scrollBy来实现View的滑动,本节介绍另外一种滑动方式,即使用动画,通过 ...

  3. 《android开发艺术探索》读书笔记(五)--RemoteViews

    接上篇<android开发艺术探索>读书笔记(四)--View工作原理 No1: RemoteViews使用场景:通知栏和桌面小部件 No2: 通知栏主要通过NotificationMan ...

  4. Android开发艺术探索——第七章:Android动画深入分析

    Android开发艺术探索--第七章:Android动画深入分析 Android的动画可以分成三种,view动画,帧动画,还有属性动画,其实帧动画也是属于view动画的一种,,只不过他和传统的平移之类 ...

  5. Android开发艺术探索读书笔记(一)

    首先向各位严重推荐主席这本书<Android开发艺术探索>. 再感谢主席邀请写这篇读书笔记 + 书评.书已经完整的翻完一遍了,但是还没有细致的品读并run代码,最近有时间正好系统的把整本书 ...

  6. Android开发艺术探索——新的征程,程序人生路漫漫!

    Android开发艺术探索--新的征程,程序人生路漫漫! 偶尔写点东西分享,但是我还是比较喜欢写笔记,看书,群英传看完了,是学到了点东西,开始看这本更加深入Android的书籍了,不知道适不适合自己, ...

  7. Android 开发艺术探索 看不懂对着书敲慢慢理解,设计模式之禅总结,平时记录的笔记,3w多次字防止丢失,留存。

    知识点1: 1.子线程为什么不允许访问ui因为android中的ui控件不是线程安全的. 2.为什么不给Ui加上锁的机制,第一点 会让ui访问的逻辑变得复杂,其次降低ui访问的效率. 3.List转化 ...

  8. 《Android开发艺术探索》图书勘误

    第一章 在13页提到"系统只在Activity异常终止的时候才会调用onSaveInstanceState与onRestoreInstanceState来储存和恢复数据,其他情况不会触发这个 ...

  9. Android开发艺术探索完结篇——天道酬勤

    这片文章发布,代表着我已经把本书和看完并且笔记也发布完成了,回忆了一下我看Android群英传,只用了两个月,但是看本书却花了2016年05月04日 - 2018年07月16日,整整两年多,真是惭愧 ...

最新文章

  1. How to create a site with AJAX enabled in MVC framework.
  2. Python中lambda使用简易教程
  3. .net 内嵌 GeckoWebBrowser (firefox) 核心浏览器
  4. 真正聪明的人,为什么从不去社交?
  5. iOS c语言 基本运算符
  6. guava 之 ImmutableMap 使用实例及好处
  7. Windows Forms、MFC、WTL、WxWidgets、Qt、GTK综合比较
  8. python链表结构_CodeSalt | Python数据结构的实现 — 链表
  9. webstore 与 热编译的配置冲突
  10. Fedora 安装 WPS
  11. chrome插件Adblock Plus拓展程序
  12. 世界流调——Gary
  13. 一级造价工程师(安装)- 计量笔记 - 第五章第二节通风空调工程
  14. nodejs调用java的jar包进行PPT转pdf
  15. 在职研究生计算机专业院校,计算机专业在职研究生报名有哪些院校可以选择?...
  16. 字节跳动面试题汇总 -- C++后端(含答案)
  17. python爬取疫情数据并存入excel中(包括国内各省份,全球,国内外历史疫情数据)代码可以直接运行
  18. scp或者ssh报错“no matching host key type found. Their offer: ssh-rsa,ssh-dss“
  19. 记忆法——《认知天性》
  20. 【数据结构】手撕单链表

热门文章

  1. Spring-profile设置
  2. 利用关系数据库开展智能化营销新思路详解
  3. 搜索引擎爬虫蜘蛛的UserAgent收集
  4. [置顶] Spring中DI设置器注入
  5. 高通平台耳机插拔检测
  6. 在DigitalOcean玩Kubernetes(K8S)
  7. win7下搭建opengles2.0编程环境
  8. Logstash 参考指南(使用Filebeat Modules配置示例)
  9. 【Python基础】字符编码ASCII-GBK-Unicode-UTF-8之间的关系
  10. 第二个例子:单链表实现基排序(桶排序)