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


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

     

     

    1. /*
    2. * Copyright 2018 The Android Open Source Project
    3. *
    4. * Licensed under the Apache License, Version 2.0 (the "License");
    5. * you may not use this file except in compliance with the License.
    6. * You may obtain a copy of the License at
    7. *
    8. * http://www.apache.org/licenses/LICENSE-2.0
    9. *
    10. * Unless required by applicable law or agreed to in writing, software
    11. * distributed under the License is distributed on an "AS IS" BASIS,
    12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13. * See the License for the specific language governing permissions and
    14. * limitations under the License.
    15. */
    16. package androidx.recyclerview.widget;
    17. import android.animation.Animator;
    18. import android.animation.AnimatorListenerAdapter;
    19. import android.animation.ValueAnimator;
    20. import android.animation.ValueAnimator.AnimatorUpdateListener;
    21. import android.graphics.Canvas;
    22. import android.graphics.drawable.Drawable;
    23. import android.graphics.drawable.StateListDrawable;
    24. import android.view.MotionEvent;
    25. import androidx.annotation.IntDef;
    26. import androidx.annotation.NonNull;
    27. import androidx.annotation.Nullable;
    28. import androidx.annotation.VisibleForTesting;
    29. import androidx.core.view.ViewCompat;
    30. import java.lang.annotation.Retention;
    31. import java.lang.annotation.RetentionPolicy;
    32. /**
    33. * Class responsible to animate and provide a fast scroller.
    34. */
    35. @VisibleForTesting
    36. class FastScroller extends RecyclerView.ItemDecoration implements RecyclerView.OnItemTouchListener {
    37. @IntDef({STATE_HIDDEN, STATE_VISIBLE, STATE_DRAGGING})
    38. @Retention(RetentionPolicy.SOURCE)
    39. private @interface State { }
    40. // Scroll thumb not showing
    41. private static final int STATE_HIDDEN = 0;
    42. // Scroll thumb visible and moving along with the scrollbar
    43. private static final int STATE_VISIBLE = 1;
    44. // Scroll thumb being dragged by user
    45. private static final int STATE_DRAGGING = 2;
    46. @IntDef({DRAG_X, DRAG_Y, DRAG_NONE})
    47. @Retention(RetentionPolicy.SOURCE)
    48. private @interface DragState{ }
    49. private static final int DRAG_NONE = 0;
    50. private static final int DRAG_X = 1;
    51. private static final int DRAG_Y = 2;
    52. @IntDef({ANIMATION_STATE_OUT, ANIMATION_STATE_FADING_IN, ANIMATION_STATE_IN,
    53. ANIMATION_STATE_FADING_OUT})
    54. @Retention(RetentionPolicy.SOURCE)
    55. private @interface AnimationState { }
    56. private static final int ANIMATION_STATE_OUT = 0;
    57. private static final int ANIMATION_STATE_FADING_IN = 1;
    58. private static final int ANIMATION_STATE_IN = 2;
    59. private static final int ANIMATION_STATE_FADING_OUT = 3;
    60. private static final int SHOW_DURATION_MS = 500;
    61. private static final int HIDE_DELAY_AFTER_VISIBLE_MS = 1500;
    62. private static final int HIDE_DELAY_AFTER_DRAGGING_MS = 1200;
    63. private static final int HIDE_DURATION_MS = 500;
    64. private static final int SCROLLBAR_FULL_OPAQUE = 255;
    65. private static final int[] PRESSED_STATE_SET = new int[]{android.R.attr.state_pressed};
    66. private static final int[] EMPTY_STATE_SET = new int[]{};
    67. private final int mScrollbarMinimumRange;
    68. private final int mMargin;
    69. // Final values for the vertical scroll bar
    70. @SuppressWarnings("WeakerAccess") /* synthetic access */
    71. final StateListDrawable mVerticalThumbDrawable;
    72. @SuppressWarnings("WeakerAccess") /* synthetic access */
    73. final Drawable mVerticalTrackDrawable;
    74. private final int mVerticalThumbWidth;
    75. private final int mVerticalTrackWidth;
    76. // Final values for the horizontal scroll bar
    77. private final StateListDrawable mHorizontalThumbDrawable;
    78. private final Drawable mHorizontalTrackDrawable;
    79. private final int mHorizontalThumbHeight;
    80. private final int mHorizontalTrackHeight;
    81. // Dynamic values for the vertical scroll bar
    82. @VisibleForTesting int mVerticalThumbHeight;
    83. @VisibleForTesting int mVerticalThumbCenterY;
    84. @VisibleForTesting float mVerticalDragY;
    85. // Dynamic values for the horizontal scroll bar
    86. @VisibleForTesting int mHorizontalThumbWidth;
    87. @VisibleForTesting int mHorizontalThumbCenterX;
    88. @VisibleForTesting float mHorizontalDragX;
    89. private int mRecyclerViewWidth = 0;
    90. private int mRecyclerViewHeight = 0;
    91. private RecyclerView mRecyclerView;
    92. /**
    93. * Whether the document is long/wide enough to require scrolling. If not, we don't show the
    94. * relevant scroller.
    95. */
    96. private boolean mNeedVerticalScrollbar = false;
    97. private boolean mNeedHorizontalScrollbar = false;
    98. @State private int mState = STATE_HIDDEN;
    99. @DragState private int mDragState = DRAG_NONE;
    100. private final int[] mVerticalRange = new int[2];
    101. private final int[] mHorizontalRange = new int[2];
    102. @SuppressWarnings("WeakerAccess") /* synthetic access */
    103. final ValueAnimator mShowHideAnimator = ValueAnimator.ofFloat(0, 1);
    104. @SuppressWarnings("WeakerAccess") /* synthetic access */
    105. @AnimationState int mAnimationState = ANIMATION_STATE_OUT;
    106. private final Runnable mHideRunnable = new Runnable() {
    107. @Override
    108. public void run() {
    109. hide(HIDE_DURATION_MS);
    110. }
    111. };
    112. private final RecyclerView.OnScrollListener
    113. mOnScrollListener = new RecyclerView.OnScrollListener() {
    114. @Override
    115. public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    116. updateScrollPosition(recyclerView.computeHorizontalScrollOffset(),
    117. recyclerView.computeVerticalScrollOffset());
    118. }
    119. };
    120. FastScroller(RecyclerView recyclerView, StateListDrawable verticalThumbDrawable,
    121. Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
    122. Drawable horizontalTrackDrawable, int defaultWidth, int scrollbarMinimumRange,
    123. int margin) {
    124. mVerticalThumbDrawable = verticalThumbDrawable;
    125. mVerticalTrackDrawable = verticalTrackDrawable;
    126. mHorizontalThumbDrawable = horizontalThumbDrawable;
    127. mHorizontalTrackDrawable = horizontalTrackDrawable;
    128. mVerticalThumbWidth = Math.max(defaultWidth, verticalThumbDrawable.getIntrinsicWidth());
    129. mVerticalTrackWidth = Math.max(defaultWidth, verticalTrackDrawable.getIntrinsicWidth());
    130. mHorizontalThumbHeight = Math
    131. .max(defaultWidth, horizontalThumbDrawable.getIntrinsicWidth());
    132. mHorizontalTrackHeight = Math
    133. .max(defaultWidth, horizontalTrackDrawable.getIntrinsicWidth());
    134. mScrollbarMinimumRange = scrollbarMinimumRange;
    135. mMargin = margin;
    136. mVerticalThumbDrawable.setAlpha(SCROLLBAR_FULL_OPAQUE);
    137. mVerticalTrackDrawable.setAlpha(SCROLLBAR_FULL_OPAQUE);
    138. mShowHideAnimator.addListener(new AnimatorListener());
    139. mShowHideAnimator.addUpdateListener(new AnimatorUpdater());
    140. attachToRecyclerView(recyclerView);
    141. }
    142. public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
    143. if (mRecyclerView == recyclerView) {
    144. return; // nothing to do
    145. }
    146. if (mRecyclerView != null) {
    147. destroyCallbacks();
    148. }
    149. mRecyclerView = recyclerView;
    150. if (mRecyclerView != null) {
    151. setupCallbacks();
    152. }
    153. }
    154. private void setupCallbacks() {
    155. mRecyclerView.addItemDecoration(this);
    156. mRecyclerView.addOnItemTouchListener(this);
    157. mRecyclerView.addOnScrollListener(mOnScrollListener);
    158. }
    159. private void destroyCallbacks() {
    160. mRecyclerView.removeItemDecoration(this);
    161. mRecyclerView.removeOnItemTouchListener(this);
    162. mRecyclerView.removeOnScrollListener(mOnScrollListener);
    163. cancelHide();
    164. }
    165. @SuppressWarnings("WeakerAccess") /* synthetic access */
    166. void requestRedraw() {
    167. mRecyclerView.invalidate();
    168. }
    169. void setState(@State int state) {
    170. if (state == STATE_DRAGGING && mState != STATE_DRAGGING) {
    171. mVerticalThumbDrawable.setState(PRESSED_STATE_SET);
    172. cancelHide();
    173. }
    174. if (state == STATE_HIDDEN) {
    175. requestRedraw();
    176. } else {
    177. show();
    178. }
    179. if (mState == STATE_DRAGGING && state != STATE_DRAGGING) {
    180. mVerticalThumbDrawable.setState(EMPTY_STATE_SET);
    181. resetHideDelay(HIDE_DELAY_AFTER_DRAGGING_MS);
    182. } else if (state == STATE_VISIBLE) {
    183. resetHideDelay(HIDE_DELAY_AFTER_VISIBLE_MS);
    184. }
    185. mState = state;
    186. }
    187. private boolean isLayoutRTL() {
    188. return ViewCompat.getLayoutDirection(mRecyclerView) == ViewCompat.LAYOUT_DIRECTION_RTL;
    189. }
    190. public boolean isDragging() {
    191. return mState == STATE_DRAGGING;
    192. }
    193. @VisibleForTesting boolean isVisible() {
    194. return mState == STATE_VISIBLE;
    195. }
    196. public void show() {
    197. switch (mAnimationState) {
    198. case ANIMATION_STATE_FADING_OUT:
    199. mShowHideAnimator.cancel();
    200. // fall through
    201. case ANIMATION_STATE_OUT:
    202. mAnimationState = ANIMATION_STATE_FADING_IN;
    203. mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 1);
    204. mShowHideAnimator.setDuration(SHOW_DURATION_MS);
    205. mShowHideAnimator.setStartDelay(0);
    206. mShowHideAnimator.start();
    207. break;
    208. }
    209. }
    210. @VisibleForTesting
    211. void hide(int duration) {
    212. switch (mAnimationState) {
    213. case ANIMATION_STATE_FADING_IN:
    214. mShowHideAnimator.cancel();
    215. // fall through
    216. case ANIMATION_STATE_IN:
    217. mAnimationState = ANIMATION_STATE_FADING_OUT;
    218. mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 0);
    219. mShowHideAnimator.setDuration(duration);
    220. mShowHideAnimator.start();
    221. break;
    222. }
    223. }
    224. private void cancelHide() {
    225. mRecyclerView.removeCallbacks(mHideRunnable);
    226. }
    227. private void resetHideDelay(int delay) {
    228. cancelHide();
    229. mRecyclerView.postDelayed(mHideRunnable, delay);
    230. }
    231. @Override
    232. public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
    233. if (mRecyclerViewWidth != mRecyclerView.getWidth()
    234. || mRecyclerViewHeight != mRecyclerView.getHeight()) {
    235. mRecyclerViewWidth = mRecyclerView.getWidth();
    236. mRecyclerViewHeight = mRecyclerView.getHeight();
    237. // This is due to the different events ordering when keyboard is opened or
    238. // retracted vs rotate. Hence to avoid corner cases we just disable the
    239. // scroller when size changed, and wait until the scroll position is recomputed
    240. // before showing it back.
    241. setState(STATE_HIDDEN);
    242. return;
    243. }
    244. if (mAnimationState != ANIMATION_STATE_OUT) {
    245. if (mNeedVerticalScrollbar) {
    246. drawVerticalScrollbar(canvas);
    247. }
    248. if (mNeedHorizontalScrollbar) {
    249. drawHorizontalScrollbar(canvas);
    250. }
    251. }
    252. }
    253. private void drawVerticalScrollbar(Canvas canvas) {
    254. int viewWidth = mRecyclerViewWidth;
    255. int left = viewWidth - mVerticalThumbWidth;
    256. int top = mVerticalThumbCenterY - mVerticalThumbHeight / 2;
    257. mVerticalThumbDrawable.setBounds(0, 0, mVerticalThumbWidth, mVerticalThumbHeight);
    258. mVerticalTrackDrawable
    259. .setBounds(0, 0, mVerticalTrackWidth, mRecyclerViewHeight);
    260. if (isLayoutRTL()) {
    261. mVerticalTrackDrawable.draw(canvas);
    262. canvas.translate(mVerticalThumbWidth, top);
    263. canvas.scale(-1, 1);
    264. mVerticalThumbDrawable.draw(canvas);
    265. canvas.scale(-1, 1);
    266. canvas.translate(-mVerticalThumbWidth, -top);
    267. } else {
    268. canvas.translate(left, 0);
    269. mVerticalTrackDrawable.draw(canvas);
    270. canvas.translate(0, top);
    271. mVerticalThumbDrawable.draw(canvas);
    272. canvas.translate(-left, -top);
    273. }
    274. }
    275. private void drawHorizontalScrollbar(Canvas canvas) {
    276. int viewHeight = mRecyclerViewHeight;
    277. int top = viewHeight - mHorizontalThumbHeight;
    278. int left = mHorizontalThumbCenterX - mHorizontalThumbWidth / 2;
    279. mHorizontalThumbDrawable.setBounds(0, 0, mHorizontalThumbWidth, mHorizontalThumbHeight);
    280. mHorizontalTrackDrawable
    281. .setBounds(0, 0, mRecyclerViewWidth, mHorizontalTrackHeight);
    282. canvas.translate(0, top);
    283. mHorizontalTrackDrawable.draw(canvas);
    284. canvas.translate(left, 0);
    285. mHorizontalThumbDrawable.draw(canvas);
    286. canvas.translate(-left, -top);
    287. }
    288. /**
    289. * Notify the scroller of external change of the scroll, e.g. through dragging or flinging on
    290. * the view itself.
    291. *
    292. * @param offsetX The new scroll X offset.
    293. * @param offsetY The new scroll Y offset.
    294. */
    295. void updateScrollPosition(int offsetX, int offsetY) {
    296. int verticalContentLength = mRecyclerView.computeVerticalScrollRange();
    297. int verticalVisibleLength = mRecyclerViewHeight;
    298. mNeedVerticalScrollbar = verticalContentLength - verticalVisibleLength > 0
    299. && mRecyclerViewHeight >= mScrollbarMinimumRange;
    300. int horizontalContentLength = mRecyclerView.computeHorizontalScrollRange();
    301. int horizontalVisibleLength = mRecyclerViewWidth;
    302. mNeedHorizontalScrollbar = horizontalContentLength - horizontalVisibleLength > 0
    303. && mRecyclerViewWidth >= mScrollbarMinimumRange;
    304. if (!mNeedVerticalScrollbar && !mNeedHorizontalScrollbar) {
    305. if (mState != STATE_HIDDEN) {
    306. setState(STATE_HIDDEN);
    307. }
    308. return;
    309. }
    310. if (mNeedVerticalScrollbar) {
    311. float middleScreenPos = offsetY + verticalVisibleLength / 2.0f;
    312. mVerticalThumbCenterY =
    313. (int) ((verticalVisibleLength * middleScreenPos) / verticalContentLength);
    314. mVerticalThumbHeight = Math.min(verticalVisibleLength,
    315. (verticalVisibleLength * verticalVisibleLength) / verticalContentLength);
    316. }
    317. if (mNeedHorizontalScrollbar) {
    318. float middleScreenPos = offsetX + horizontalVisibleLength / 2.0f;
    319. mHorizontalThumbCenterX =
    320. (int) ((horizontalVisibleLength * middleScreenPos) / horizontalContentLength);
    321. mHorizontalThumbWidth = Math.min(horizontalVisibleLength,
    322. (horizontalVisibleLength * horizontalVisibleLength) / horizontalContentLength);
    323. }
    324. if (mState == STATE_HIDDEN || mState == STATE_VISIBLE) {
    325. setState(STATE_VISIBLE);
    326. }
    327. }
    328. @Override
    329. public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView,
    330. @NonNull MotionEvent ev) {
    331. final boolean handled;
    332. if (mState == STATE_VISIBLE) {
    333. boolean insideVerticalThumb = isPointInsideVerticalThumb(ev.getX(), ev.getY());
    334. boolean insideHorizontalThumb = isPointInsideHorizontalThumb(ev.getX(), ev.getY());
    335. if (ev.getAction() == MotionEvent.ACTION_DOWN
    336. && (insideVerticalThumb || insideHorizontalThumb)) {
    337. if (insideHorizontalThumb) {
    338. mDragState = DRAG_X;
    339. mHorizontalDragX = (int) ev.getX();
    340. } else if (insideVerticalThumb) {
    341. mDragState = DRAG_Y;
    342. mVerticalDragY = (int) ev.getY();
    343. }
    344. setState(STATE_DRAGGING);
    345. handled = true;
    346. } else {
    347. handled = false;
    348. }
    349. } else if (mState == STATE_DRAGGING) {
    350. handled = true;
    351. } else {
    352. handled = false;
    353. }
    354. return handled;
    355. }
    356. @Override
    357. public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent me) {
    358. if (mState == STATE_HIDDEN) {
    359. return;
    360. }
    361. if (me.getAction() == MotionEvent.ACTION_DOWN) {
    362. boolean insideVerticalThumb = isPointInsideVerticalThumb(me.getX(), me.getY());
    363. boolean insideHorizontalThumb = isPointInsideHorizontalThumb(me.getX(), me.getY());
    364. if (insideVerticalThumb || insideHorizontalThumb) {
    365. if (insideHorizontalThumb) {
    366. mDragState = DRAG_X;
    367. mHorizontalDragX = (int) me.getX();
    368. } else if (insideVerticalThumb) {
    369. mDragState = DRAG_Y;
    370. mVerticalDragY = (int) me.getY();
    371. }
    372. setState(STATE_DRAGGING);
    373. }
    374. } else if (me.getAction() == MotionEvent.ACTION_UP && mState == STATE_DRAGGING) {
    375. mVerticalDragY = 0;
    376. mHorizontalDragX = 0;
    377. setState(STATE_VISIBLE);
    378. mDragState = DRAG_NONE;
    379. } else if (me.getAction() == MotionEvent.ACTION_MOVE && mState == STATE_DRAGGING) {
    380. show();
    381. if (mDragState == DRAG_X) {
    382. horizontalScrollTo(me.getX());
    383. }
    384. if (mDragState == DRAG_Y) {
    385. verticalScrollTo(me.getY());
    386. }
    387. }
    388. }
    389. @Override
    390. public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
    391. private void verticalScrollTo(float y) {
    392. final int[] scrollbarRange = getVerticalRange();
    393. y = Math.max(scrollbarRange[0], Math.min(scrollbarRange[1], y));
    394. if (Math.abs(mVerticalThumbCenterY - y) < 2) {
    395. return;
    396. }
    397. int scrollingBy = scrollTo(mVerticalDragY, y, scrollbarRange,
    398. mRecyclerView.computeVerticalScrollRange(),
    399. mRecyclerView.computeVerticalScrollOffset(), mRecyclerViewHeight);
    400. if (scrollingBy != 0) {
    401. mRecyclerView.scrollBy(0, scrollingBy);
    402. }
    403. mVerticalDragY = y;
    404. }
    405. private void horizontalScrollTo(float x) {
    406. final int[] scrollbarRange = getHorizontalRange();
    407. x = Math.max(scrollbarRange[0], Math.min(scrollbarRange[1], x));
    408. if (Math.abs(mHorizontalThumbCenterX - x) < 2) {
    409. return;
    410. }
    411. int scrollingBy = scrollTo(mHorizontalDragX, x, scrollbarRange,
    412. mRecyclerView.computeHorizontalScrollRange(),
    413. mRecyclerView.computeHorizontalScrollOffset(), mRecyclerViewWidth);
    414. if (scrollingBy != 0) {
    415. mRecyclerView.scrollBy(scrollingBy, 0);
    416. }
    417. mHorizontalDragX = x;
    418. }
    419. private int scrollTo(float oldDragPos, float newDragPos, int[] scrollbarRange, int scrollRange,
    420. int scrollOffset, int viewLength) {
    421. int scrollbarLength = scrollbarRange[1] - scrollbarRange[0];
    422. if (scrollbarLength == 0) {
    423. return 0;
    424. }
    425. float percentage = ((newDragPos - oldDragPos) / (float) scrollbarLength);
    426. int totalPossibleOffset = scrollRange - viewLength;
    427. int scrollingBy = (int) (percentage * totalPossibleOffset);
    428. int absoluteOffset = scrollOffset + scrollingBy;
    429. if (absoluteOffset < totalPossibleOffset && absoluteOffset >= 0) {
    430. return scrollingBy;
    431. } else {
    432. return 0;
    433. }
    434. }
    435. @VisibleForTesting
    436. boolean isPointInsideVerticalThumb(float x, float y) {
    437. return (isLayoutRTL() ? x <= mVerticalThumbWidth
    438. : x >= mRecyclerViewWidth - mVerticalThumbWidth)
    439. && y >= mVerticalThumbCenterY - mVerticalThumbHeight / 2
    440. && y <= mVerticalThumbCenterY + mVerticalThumbHeight / 2;
    441. }
    442. @VisibleForTesting
    443. boolean isPointInsideHorizontalThumb(float x, float y) {
    444. return (y >= mRecyclerViewHeight - mHorizontalThumbHeight)
    445. && x >= mHorizontalThumbCenterX - mHorizontalThumbWidth / 2
    446. && x <= mHorizontalThumbCenterX + mHorizontalThumbWidth / 2;
    447. }
    448. @VisibleForTesting
    449. Drawable getHorizontalTrackDrawable() {
    450. return mHorizontalTrackDrawable;
    451. }
    452. @VisibleForTesting
    453. Drawable getHorizontalThumbDrawable() {
    454. return mHorizontalThumbDrawable;
    455. }
    456. @VisibleForTesting
    457. Drawable getVerticalTrackDrawable() {
    458. return mVerticalTrackDrawable;
    459. }
    460. @VisibleForTesting
    461. Drawable getVerticalThumbDrawable() {
    462. return mVerticalThumbDrawable;
    463. }
    464. /**
    465. * Gets the (min, max) vertical positions of the vertical scroll bar.
    466. */
    467. private int[] getVerticalRange() {
    468. mVerticalRange[0] = mMargin;
    469. mVerticalRange[1] = mRecyclerViewHeight - mMargin;
    470. return mVerticalRange;
    471. }
    472. /**
    473. * Gets the (min, max) horizontal positions of the horizontal scroll bar.
    474. */
    475. private int[] getHorizontalRange() {
    476. mHorizontalRange[0] = mMargin;
    477. mHorizontalRange[1] = mRecyclerViewWidth - mMargin;
    478. return mHorizontalRange;
    479. }
    480. private class AnimatorListener extends AnimatorListenerAdapter {
    481. private boolean mCanceled = false;
    482. AnimatorListener() {
    483. }
    484. @Override
    485. public void onAnimationEnd(Animator animation) {
    486. // Cancel is always followed by a new directive, so don't update state.
    487. if (mCanceled) {
    488. mCanceled = false;
    489. return;
    490. }
    491. if ((float) mShowHideAnimator.getAnimatedValue() == 0) {
    492. mAnimationState = ANIMATION_STATE_OUT;
    493. setState(STATE_HIDDEN);
    494. } else {
    495. mAnimationState = ANIMATION_STATE_IN;
    496. requestRedraw();
    497. }
    498. }
    499. @Override
    500. public void onAnimationCancel(Animator animation) {
    501. mCanceled = true;
    502. }
    503. }
    504. private class AnimatorUpdater implements AnimatorUpdateListener {
    505. AnimatorUpdater() {
    506. }
    507. @Override
    508. public void onAnimationUpdate(ValueAnimator valueAnimator) {
    509. int alpha = (int) (SCROLLBAR_FULL_OPAQUE * ((float) valueAnimator.getAnimatedValue()));
    510. mVerticalThumbDrawable.setAlpha(alpha);
    511. mVerticalTrackDrawable.setAlpha(alpha);
    512. requestRedraw();
    513. }
    514. }
    515. }

     

    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

     

  • 相关阅读:
    基于WT588F02A-16S语音芯片在启蒙玩具用品的方案设计应用解析
    【吃瓜之旅】第五章吃瓜学习
    堆外内存和堆内内存及虚引用的应用
    Spring、MyBatis框架和Redis数据库介绍 第1关:Spring 框架简介
    开源短信项目 platform-sms 发布了新版本 0.5.0
    网络流相关(拓扑)CodeForces 269C:Flawed Flow
    web前端期末大作业 基于HTML+CSS+JavaScript绿色的在线教育平台网站响应式企业网站模板
    STM32读保护的解除和出现的原因,使用串口和ST-LINK Utility解除读保护
    <C++> 模拟实现string
    计算机毕业设计之java+springboot基于vue的网上图书商城系统
  • 原文地址:https://blog.csdn.net/zhangphil/article/details/131999933