Android快速滑动条/快速滑块/快速滚动条标准实现,Java

news/2024/12/21 21:52:54/

Android水平和垂直方向的快速滑动条/快速滑块/快滑滚动条/快滑滚动块标准实现,Java

 

 

/** Copyright 2018 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package androidx.recyclerview.widget;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.view.MotionEvent;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.view.ViewCompat;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/*** Class responsible to animate and provide a fast scroller.*/
@VisibleForTesting
class FastScroller extends RecyclerView.ItemDecoration implements RecyclerView.OnItemTouchListener {@IntDef({STATE_HIDDEN, STATE_VISIBLE, STATE_DRAGGING})@Retention(RetentionPolicy.SOURCE)private @interface State { }// Scroll thumb not showingprivate static final int STATE_HIDDEN = 0;// Scroll thumb visible and moving along with the scrollbarprivate static final int STATE_VISIBLE = 1;// Scroll thumb being dragged by userprivate static final int STATE_DRAGGING = 2;@IntDef({DRAG_X, DRAG_Y, DRAG_NONE})@Retention(RetentionPolicy.SOURCE)private @interface DragState{ }private static final int DRAG_NONE = 0;private static final int DRAG_X = 1;private static final int DRAG_Y = 2;@IntDef({ANIMATION_STATE_OUT, ANIMATION_STATE_FADING_IN, ANIMATION_STATE_IN,ANIMATION_STATE_FADING_OUT})@Retention(RetentionPolicy.SOURCE)private @interface AnimationState { }private static final int ANIMATION_STATE_OUT = 0;private static final int ANIMATION_STATE_FADING_IN = 1;private static final int ANIMATION_STATE_IN = 2;private static final int ANIMATION_STATE_FADING_OUT = 3;private static final int SHOW_DURATION_MS = 500;private static final int HIDE_DELAY_AFTER_VISIBLE_MS = 1500;private static final int HIDE_DELAY_AFTER_DRAGGING_MS = 1200;private static final int HIDE_DURATION_MS = 500;private static final int SCROLLBAR_FULL_OPAQUE = 255;private static final int[] PRESSED_STATE_SET = new int[]{android.R.attr.state_pressed};private static final int[] EMPTY_STATE_SET = new int[]{};private final int mScrollbarMinimumRange;private final int mMargin;// Final values for the vertical scroll bar@SuppressWarnings("WeakerAccess") /* synthetic access */final StateListDrawable mVerticalThumbDrawable;@SuppressWarnings("WeakerAccess") /* synthetic access */final Drawable mVerticalTrackDrawable;private final int mVerticalThumbWidth;private final int mVerticalTrackWidth;// Final values for the horizontal scroll barprivate final StateListDrawable mHorizontalThumbDrawable;private final Drawable mHorizontalTrackDrawable;private final int mHorizontalThumbHeight;private final int mHorizontalTrackHeight;// Dynamic values for the vertical scroll bar@VisibleForTesting int mVerticalThumbHeight;@VisibleForTesting int mVerticalThumbCenterY;@VisibleForTesting float mVerticalDragY;// Dynamic values for the horizontal scroll bar@VisibleForTesting int mHorizontalThumbWidth;@VisibleForTesting int mHorizontalThumbCenterX;@VisibleForTesting float mHorizontalDragX;private int mRecyclerViewWidth = 0;private int mRecyclerViewHeight = 0;private RecyclerView mRecyclerView;/*** Whether the document is long/wide enough to require scrolling. If not, we don't show the* relevant scroller.*/private boolean mNeedVerticalScrollbar = false;private boolean mNeedHorizontalScrollbar = false;@State private int mState = STATE_HIDDEN;@DragState private int mDragState = DRAG_NONE;private final int[] mVerticalRange = new int[2];private final int[] mHorizontalRange = new int[2];@SuppressWarnings("WeakerAccess") /* synthetic access */final ValueAnimator mShowHideAnimator = ValueAnimator.ofFloat(0, 1);@SuppressWarnings("WeakerAccess") /* synthetic access */@AnimationState int mAnimationState = ANIMATION_STATE_OUT;private final Runnable mHideRunnable = new Runnable() {@Overridepublic void run() {hide(HIDE_DURATION_MS);}};private final RecyclerView.OnScrollListenermOnScrollListener = new RecyclerView.OnScrollListener() {@Overridepublic void onScrolled(RecyclerView recyclerView, int dx, int dy) {updateScrollPosition(recyclerView.computeHorizontalScrollOffset(),recyclerView.computeVerticalScrollOffset());}};FastScroller(RecyclerView recyclerView, StateListDrawable verticalThumbDrawable,Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,Drawable horizontalTrackDrawable, int defaultWidth, int scrollbarMinimumRange,int margin) {mVerticalThumbDrawable = verticalThumbDrawable;mVerticalTrackDrawable = verticalTrackDrawable;mHorizontalThumbDrawable = horizontalThumbDrawable;mHorizontalTrackDrawable = horizontalTrackDrawable;mVerticalThumbWidth = Math.max(defaultWidth, verticalThumbDrawable.getIntrinsicWidth());mVerticalTrackWidth = Math.max(defaultWidth, verticalTrackDrawable.getIntrinsicWidth());mHorizontalThumbHeight = Math.max(defaultWidth, horizontalThumbDrawable.getIntrinsicWidth());mHorizontalTrackHeight = Math.max(defaultWidth, horizontalTrackDrawable.getIntrinsicWidth());mScrollbarMinimumRange = scrollbarMinimumRange;mMargin = margin;mVerticalThumbDrawable.setAlpha(SCROLLBAR_FULL_OPAQUE);mVerticalTrackDrawable.setAlpha(SCROLLBAR_FULL_OPAQUE);mShowHideAnimator.addListener(new AnimatorListener());mShowHideAnimator.addUpdateListener(new AnimatorUpdater());attachToRecyclerView(recyclerView);}public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {if (mRecyclerView == recyclerView) {return; // nothing to do}if (mRecyclerView != null) {destroyCallbacks();}mRecyclerView = recyclerView;if (mRecyclerView != null) {setupCallbacks();}}private void setupCallbacks() {mRecyclerView.addItemDecoration(this);mRecyclerView.addOnItemTouchListener(this);mRecyclerView.addOnScrollListener(mOnScrollListener);}private void destroyCallbacks() {mRecyclerView.removeItemDecoration(this);mRecyclerView.removeOnItemTouchListener(this);mRecyclerView.removeOnScrollListener(mOnScrollListener);cancelHide();}@SuppressWarnings("WeakerAccess") /* synthetic access */void requestRedraw() {mRecyclerView.invalidate();}void setState(@State int state) {if (state == STATE_DRAGGING && mState != STATE_DRAGGING) {mVerticalThumbDrawable.setState(PRESSED_STATE_SET);cancelHide();}if (state == STATE_HIDDEN) {requestRedraw();} else {show();}if (mState == STATE_DRAGGING && state != STATE_DRAGGING) {mVerticalThumbDrawable.setState(EMPTY_STATE_SET);resetHideDelay(HIDE_DELAY_AFTER_DRAGGING_MS);} else if (state == STATE_VISIBLE) {resetHideDelay(HIDE_DELAY_AFTER_VISIBLE_MS);}mState = state;}private boolean isLayoutRTL() {return ViewCompat.getLayoutDirection(mRecyclerView) == ViewCompat.LAYOUT_DIRECTION_RTL;}public boolean isDragging() {return mState == STATE_DRAGGING;}@VisibleForTesting boolean isVisible() {return mState == STATE_VISIBLE;}public void show() {switch (mAnimationState) {case ANIMATION_STATE_FADING_OUT:mShowHideAnimator.cancel();// fall throughcase ANIMATION_STATE_OUT:mAnimationState = ANIMATION_STATE_FADING_IN;mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 1);mShowHideAnimator.setDuration(SHOW_DURATION_MS);mShowHideAnimator.setStartDelay(0);mShowHideAnimator.start();break;}}@VisibleForTestingvoid hide(int duration) {switch (mAnimationState) {case ANIMATION_STATE_FADING_IN:mShowHideAnimator.cancel();// fall throughcase ANIMATION_STATE_IN:mAnimationState = ANIMATION_STATE_FADING_OUT;mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 0);mShowHideAnimator.setDuration(duration);mShowHideAnimator.start();break;}}private void cancelHide() {mRecyclerView.removeCallbacks(mHideRunnable);}private void resetHideDelay(int delay) {cancelHide();mRecyclerView.postDelayed(mHideRunnable, delay);}@Overridepublic void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {if (mRecyclerViewWidth != mRecyclerView.getWidth()|| mRecyclerViewHeight != mRecyclerView.getHeight()) {mRecyclerViewWidth = mRecyclerView.getWidth();mRecyclerViewHeight = mRecyclerView.getHeight();// This is due to the different events ordering when keyboard is opened or// retracted vs rotate. Hence to avoid corner cases we just disable the// scroller when size changed, and wait until the scroll position is recomputed// before showing it back.setState(STATE_HIDDEN);return;}if (mAnimationState != ANIMATION_STATE_OUT) {if (mNeedVerticalScrollbar) {drawVerticalScrollbar(canvas);}if (mNeedHorizontalScrollbar) {drawHorizontalScrollbar(canvas);}}}private void drawVerticalScrollbar(Canvas canvas) {int viewWidth = mRecyclerViewWidth;int left = viewWidth - mVerticalThumbWidth;int top = mVerticalThumbCenterY - mVerticalThumbHeight / 2;mVerticalThumbDrawable.setBounds(0, 0, mVerticalThumbWidth, mVerticalThumbHeight);mVerticalTrackDrawable.setBounds(0, 0, mVerticalTrackWidth, mRecyclerViewHeight);if (isLayoutRTL()) {mVerticalTrackDrawable.draw(canvas);canvas.translate(mVerticalThumbWidth, top);canvas.scale(-1, 1);mVerticalThumbDrawable.draw(canvas);canvas.scale(-1, 1);canvas.translate(-mVerticalThumbWidth, -top);} else {canvas.translate(left, 0);mVerticalTrackDrawable.draw(canvas);canvas.translate(0, top);mVerticalThumbDrawable.draw(canvas);canvas.translate(-left, -top);}}private void drawHorizontalScrollbar(Canvas canvas) {int viewHeight = mRecyclerViewHeight;int top = viewHeight - mHorizontalThumbHeight;int left = mHorizontalThumbCenterX - mHorizontalThumbWidth / 2;mHorizontalThumbDrawable.setBounds(0, 0, mHorizontalThumbWidth, mHorizontalThumbHeight);mHorizontalTrackDrawable.setBounds(0, 0, mRecyclerViewWidth, mHorizontalTrackHeight);canvas.translate(0, top);mHorizontalTrackDrawable.draw(canvas);canvas.translate(left, 0);mHorizontalThumbDrawable.draw(canvas);canvas.translate(-left, -top);}/*** Notify the scroller of external change of the scroll, e.g. through dragging or flinging on* the view itself.** @param offsetX The new scroll X offset.* @param offsetY The new scroll Y offset.*/void updateScrollPosition(int offsetX, int offsetY) {int verticalContentLength = mRecyclerView.computeVerticalScrollRange();int verticalVisibleLength = mRecyclerViewHeight;mNeedVerticalScrollbar = verticalContentLength - verticalVisibleLength > 0&& mRecyclerViewHeight >= mScrollbarMinimumRange;int horizontalContentLength = mRecyclerView.computeHorizontalScrollRange();int horizontalVisibleLength = mRecyclerViewWidth;mNeedHorizontalScrollbar = horizontalContentLength - horizontalVisibleLength > 0&& mRecyclerViewWidth >= mScrollbarMinimumRange;if (!mNeedVerticalScrollbar && !mNeedHorizontalScrollbar) {if (mState != STATE_HIDDEN) {setState(STATE_HIDDEN);}return;}if (mNeedVerticalScrollbar) {float middleScreenPos = offsetY + verticalVisibleLength / 2.0f;mVerticalThumbCenterY =(int) ((verticalVisibleLength * middleScreenPos) / verticalContentLength);mVerticalThumbHeight = Math.min(verticalVisibleLength,(verticalVisibleLength * verticalVisibleLength) / verticalContentLength);}if (mNeedHorizontalScrollbar) {float middleScreenPos = offsetX + horizontalVisibleLength / 2.0f;mHorizontalThumbCenterX =(int) ((horizontalVisibleLength * middleScreenPos) / horizontalContentLength);mHorizontalThumbWidth = Math.min(horizontalVisibleLength,(horizontalVisibleLength * horizontalVisibleLength) / horizontalContentLength);}if (mState == STATE_HIDDEN || mState == STATE_VISIBLE) {setState(STATE_VISIBLE);}}@Overridepublic boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView,@NonNull MotionEvent ev) {final boolean handled;if (mState == STATE_VISIBLE) {boolean insideVerticalThumb = isPointInsideVerticalThumb(ev.getX(), ev.getY());boolean insideHorizontalThumb = isPointInsideHorizontalThumb(ev.getX(), ev.getY());if (ev.getAction() == MotionEvent.ACTION_DOWN&& (insideVerticalThumb || insideHorizontalThumb)) {if (insideHorizontalThumb) {mDragState = DRAG_X;mHorizontalDragX = (int) ev.getX();} else if (insideVerticalThumb) {mDragState = DRAG_Y;mVerticalDragY = (int) ev.getY();}setState(STATE_DRAGGING);handled = true;} else {handled = false;}} else if (mState == STATE_DRAGGING) {handled = true;} else {handled = false;}return handled;}@Overridepublic void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent me) {if (mState == STATE_HIDDEN) {return;}if (me.getAction() == MotionEvent.ACTION_DOWN) {boolean insideVerticalThumb = isPointInsideVerticalThumb(me.getX(), me.getY());boolean insideHorizontalThumb = isPointInsideHorizontalThumb(me.getX(), me.getY());if (insideVerticalThumb || insideHorizontalThumb) {if (insideHorizontalThumb) {mDragState = DRAG_X;mHorizontalDragX = (int) me.getX();} else if (insideVerticalThumb) {mDragState = DRAG_Y;mVerticalDragY = (int) me.getY();}setState(STATE_DRAGGING);}} else if (me.getAction() == MotionEvent.ACTION_UP && mState == STATE_DRAGGING) {mVerticalDragY = 0;mHorizontalDragX = 0;setState(STATE_VISIBLE);mDragState = DRAG_NONE;} else if (me.getAction() == MotionEvent.ACTION_MOVE && mState == STATE_DRAGGING) {show();if (mDragState == DRAG_X) {horizontalScrollTo(me.getX());}if (mDragState == DRAG_Y) {verticalScrollTo(me.getY());}}}@Overridepublic void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }private void verticalScrollTo(float y) {final int[] scrollbarRange = getVerticalRange();y = Math.max(scrollbarRange[0], Math.min(scrollbarRange[1], y));if (Math.abs(mVerticalThumbCenterY - y) < 2) {return;}int scrollingBy = scrollTo(mVerticalDragY, y, scrollbarRange,mRecyclerView.computeVerticalScrollRange(),mRecyclerView.computeVerticalScrollOffset(), mRecyclerViewHeight);if (scrollingBy != 0) {mRecyclerView.scrollBy(0, scrollingBy);}mVerticalDragY = y;}private void horizontalScrollTo(float x) {final int[] scrollbarRange = getHorizontalRange();x = Math.max(scrollbarRange[0], Math.min(scrollbarRange[1], x));if (Math.abs(mHorizontalThumbCenterX - x) < 2) {return;}int scrollingBy = scrollTo(mHorizontalDragX, x, scrollbarRange,mRecyclerView.computeHorizontalScrollRange(),mRecyclerView.computeHorizontalScrollOffset(), mRecyclerViewWidth);if (scrollingBy != 0) {mRecyclerView.scrollBy(scrollingBy, 0);}mHorizontalDragX = x;}private int scrollTo(float oldDragPos, float newDragPos, int[] scrollbarRange, int scrollRange,int scrollOffset, int viewLength) {int scrollbarLength = scrollbarRange[1] - scrollbarRange[0];if (scrollbarLength == 0) {return 0;}float percentage = ((newDragPos - oldDragPos) / (float) scrollbarLength);int totalPossibleOffset = scrollRange - viewLength;int scrollingBy = (int) (percentage * totalPossibleOffset);int absoluteOffset = scrollOffset + scrollingBy;if (absoluteOffset < totalPossibleOffset && absoluteOffset >= 0) {return scrollingBy;} else {return 0;}}@VisibleForTestingboolean isPointInsideVerticalThumb(float x, float y) {return (isLayoutRTL() ? x <= mVerticalThumbWidth: x >= mRecyclerViewWidth - mVerticalThumbWidth)&& y >= mVerticalThumbCenterY - mVerticalThumbHeight / 2&& y <= mVerticalThumbCenterY + mVerticalThumbHeight / 2;}@VisibleForTestingboolean isPointInsideHorizontalThumb(float x, float y) {return (y >= mRecyclerViewHeight - mHorizontalThumbHeight)&& x >= mHorizontalThumbCenterX - mHorizontalThumbWidth / 2&& x <= mHorizontalThumbCenterX + mHorizontalThumbWidth / 2;}@VisibleForTestingDrawable getHorizontalTrackDrawable() {return mHorizontalTrackDrawable;}@VisibleForTestingDrawable getHorizontalThumbDrawable() {return mHorizontalThumbDrawable;}@VisibleForTestingDrawable getVerticalTrackDrawable() {return mVerticalTrackDrawable;}@VisibleForTestingDrawable getVerticalThumbDrawable() {return mVerticalThumbDrawable;}/*** Gets the (min, max) vertical positions of the vertical scroll bar.*/private int[] getVerticalRange() {mVerticalRange[0] = mMargin;mVerticalRange[1] = mRecyclerViewHeight - mMargin;return mVerticalRange;}/*** Gets the (min, max) horizontal positions of the horizontal scroll bar.*/private int[] getHorizontalRange() {mHorizontalRange[0] = mMargin;mHorizontalRange[1] = mRecyclerViewWidth - mMargin;return mHorizontalRange;}private class AnimatorListener extends AnimatorListenerAdapter {private boolean mCanceled = false;AnimatorListener() {}@Overridepublic void onAnimationEnd(Animator animation) {// Cancel is always followed by a new directive, so don't update state.if (mCanceled) {mCanceled = false;return;}if ((float) mShowHideAnimator.getAnimatedValue() == 0) {mAnimationState = ANIMATION_STATE_OUT;setState(STATE_HIDDEN);} else {mAnimationState = ANIMATION_STATE_IN;requestRedraw();}}@Overridepublic void onAnimationCancel(Animator animation) {mCanceled = true;}}private class AnimatorUpdater implements AnimatorUpdateListener {AnimatorUpdater() {}@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator) {int alpha = (int) (SCROLLBAR_FULL_OPAQUE * ((float) valueAnimator.getAnimatedValue()));mVerticalThumbDrawable.setAlpha(alpha);mVerticalTrackDrawable.setAlpha(alpha);requestRedraw();}}
}

 

Android官方快滑/快速滚动条源代码链接地址:

https://android.googlesource.com/platform/frameworks/support/+/androidx-main/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/FastScroller.javahttps://android.googlesource.com/platform/frameworks/support/+/androidx-main/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/FastScroller.java

 


http://www.ppmy.cn/news/1170205.html

相关文章

Go学习第四章——程序流程控制

Go学习第四章——程序流程控制 1 分支结构1.1 单分支1.2 双分支1.3 多分支1.4 switch分支 2 循环结构2.1 for循环2.2 while和dowhile的实现2.3 经典案例——打印金字塔2.4 经典案例——打印九九乘法表 3 跳转控制语句3.1 break3.2 continue3.3 goto3.4 return 流程控制的作用&a…

《数据结构与算法之美》读书笔记1

Java的学习 方法参数多态&#xff08;向上和向下转型&#xff09; 向上转型&#xff1a; class Text{public static void main(String[] args) {Animals people1 new NiuMa();people1.eat1();//调用继承后公共部分的方法&#xff0c;没重写调用没重写的&#xff0c;重写了调…

Go学习第三章——运算符与进制

Go学习第三章——运算符与进制 1 算术运算符2 关系运算符3 逻辑运算符4 赋值运算符5 其他运算符5.1 位运算符5.2 跟指针有关的运算符 6 运算符的优先级7 获取用户终端输入8 进制转换8.1 进制基本使用8.2 进制之间的转换8.3 原码 反码 补码8.4 位运算符详解 运算符是—种特殊的符…

LabVIEW中PID控制的的高级功能

LabVIEW中PID控制的的高级功能 比例-积分-微分&#xff08;PID&#xff09;控制占当今控制和自动化应用的90%以上&#xff0c;主要是因为它是一种有效且简单的解决方案。虽然PID算法最初用于线性、时不变系统&#xff0c;但现在已经发展到控制具有复杂动力学的系统。在现实世界…

springBoot--web--路径匹配

路径匹配 前言在配置文件中配置路径匹配结果 前言 spring5.3之后加入了更多的请求路径匹配的实现策略 以前只支持antPathMatcher策略&#xff0c;现在提供了PathPatternParse策略&#xff0c;并且可以让我们指定到底使用哪种策略 PathPatternParser: 在jmh基准测试下&#xff…

人工智能、机器学习、深度学习的区别

人工智能涵盖范围最广&#xff0c;它包含了机器学习&#xff1b;而机器学习是人工智能的重要研究内容&#xff0c;它又包含了深度学习。 人工智能&#xff08;AI&#xff09; 人工智能是一门以计算机科学为基础&#xff0c;融合了数学、神经学、心理学、控制学等多个科目的交…

【计算机毕设选题推荐】口腔助手小程序SpringBoot+Vue+小程序

前言&#xff1a;我是IT源码社&#xff0c;从事计算机开发行业数年&#xff0c;专注Java领域&#xff0c;专业提供程序设计开发、源码分享、技术指导讲解、定制和毕业设计服务 项目名 基于SpringBoot的口腔助手小程序 技术栈 SpringBootVue小程序MySQLMaven 文章目录 一、口腔…

vant组件是使用?

首先 在vue项目中使用的时候 要先下载组件 使用npm安装 # Vue 3 项目&#xff0c;安装最新版 Vant npm i vant# Vue 2 项目&#xff0c;安装 Vant 2 npm i vantlatest-v2 使用yarn安装或pnpm # 通过 yarn 安装 yarn add vant# 通过 pnpm 安装 pnpm add vant 在框架中引入即…