《Android开发艺术探索》自定义View中关于“HorizontalScrollViewEx”的改进
生活随笔
收集整理的這篇文章主要介紹了
《Android开发艺术探索》自定义View中关于“HorizontalScrollViewEx”的改进
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在《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”的改进的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网页中获取微信用户是否关注订阅号的思路
- 下一篇: CoordinatorLayout中Ap