最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Android造了個滾輪控件輪子示例

 更新時間:2018年02月23日 09:02:40   作者:奮斗的Leo  
這篇文章主要介紹了使用Android造了個滾輪控件輪子示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

關于 Android 實現(xiàn) iOS 上的滾輪選擇效果的控件,到 github 上一搜一大堆,之所以還要造這個輪子,目的是為了更好的學習自定義控件,這個控件是幾個月前寫的了,經過一段時間的完善,現(xiàn)在開源,順便寫這一篇簡單的介紹文章。

效果如下,錄屏軟件看起來可能有點卡頓,具體可以下載源碼運行:

自定義控件無非是 measure,draw,layout 三個過程,如果要支持手勢動作,那么就再加上 touch 。

measure

測量過程比較簡單,以文本大小所需要的尺寸,再加上 padding。

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
   int wantWith = getPaddingLeft() + getPaddingRight();
   int wantHeight = getPaddingTop() + getPaddingBottom();
   calculateTextSize();
   wantWith += mTextRect.width();
   //可見 item 數量計算文本尺寸
   if (mVisibilityCount > 0) {
     wantHeight += mTextRect.height() * mVisibilityCount;
   } else {
     wantHeight += mTextRect.height() * DEFALUT_VISIBILITY_COUNT;
   }
   setMeasuredDimension(
       resolveSize(wantWith, widthMeasureSpec),
       resolveSize(wantHeight, heightMeasureSpec)
   );
   mNeedCalculate = true;
 }

draw

繪制過程是通過 canvas 的位移去繪制不同位置的部件,包括文本內容和選擇框之類的,這里可能需要注意下的地方是,不要一次性把所有文本繪制出來,只需要繪制可見文本即可。

@Override
 protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);

   if (hasDataSource()) {
     // 省略
     // 這里計算下需要繪制的數量,+2 只是確保不會出現(xiàn)空白
     final int drawCount = mContentRect.height() / mTextRect.height() + 2;
     int invisibleCount = 0;
     int dy = -mDistanceY;
     // 省略
     // 通過 translate 繪制文本
     for (int i = 0; (i < drawCount && mDataSources.size() > (invisibleCount + i));
        i++) {
       final int position = invisibleCount + i;
       String text = mDataSources.get(position);
       if (i > 0) {
         canvas.translate(0, mTextRect.height());
       }

       final PointF pointF = calculateTextGravity(text);
       mTextPaint.setTextSize(mTextSize);
       if (position == selctPosition) {
         mTextPaint.setColor(mSelectedTextColor);
       } else {
         mTextPaint.setColor(mNormalTextColor);
       }
       canvas.drawText(text, pointF.x, pointF.y, mTextPaint);
     }
     canvas.restoreToCount(saveCount);
   }
   
   // 繪制選擇框
   int saveCount = canvas.save();
   mDrawPaint.setColor(mSelectedLineColor);
   canvas.translate(mContentRect.left, mContentRect.top);
   canvas.drawLine(
       mSelctedRect.left,
       mSelctedRect.top,
       mSelctedRect.right,
       mSelctedRect.top,
       mDrawPaint
   );
   canvas.drawLine(
       mSelctedRect.left,
       mSelctedRect.bottom,
       mSelctedRect.right,
       mSelctedRect.bottom,
       mDrawPaint
   );
   canvas.restoreToCount(saveCount);
 }

layout

因為這個控件是繼承于 View,所以不需要處理 onLayout。

touch

如果對 touch event 分發(fā)流程熟悉的話,那么很多處理可以說是模版代碼,可以參考 NestedScrollView、ScrollView。

在 onInterceptTouchEvent 中,判斷是否開始進行拖動手勢,保存到變量(mIsBeingDragged)中:

// 多指處理
final int pointerIndex = ev.findPointerIndex(activePointerId);
       if (pointerIndex == -1) {
         Log.e(TAG, "Invalid pointerId=" + activePointerId
             + " in onInterceptTouchEvent");
         break;
       }

       final int y = (int) ev.getY(pointerIndex);
       final int yDiff = Math.abs(y - mLastMotionY);
       if (yDiff > mTouchSlop && (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
         // 開始拖動
         mIsBeingDragged = true;
         mLastMotionY = y;
         initVelocityTrackerIfNotExists();
         mVelocityTracker.addMovement(ev);
         mNestedYOffset = 0;
         if (mScrollStrictSpan == null) {
           mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
         }
         final ViewParent parent = getParent();
         if (parent != null) {
           // 禁止父控件攔截事件分發(fā)
           parent.requestDisallowInterceptTouchEvent(true);
         }
       }

在 onTouchEvent 中對 ACTION_MOVR 進行拖動的處理,如果支持嵌套滾動,那么會預先進行嵌套滾動的分發(fā)。如果支持陰影效果,那么使用 EdgeEffect。

// 和 onInterceptTouchEvent 一樣進行拖動手勢開始的判斷
if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
         final ViewParent parent = getParent();
         if (parent != null) {
           parent.requestDisallowInterceptTouchEvent(true);
         }
         mIsBeingDragged = true;
         if (deltaY > 0) {
           deltaY -= mTouchSlop;
         } else {
           deltaY += mTouchSlop;
         }
       }
       if (mIsBeingDragged) {
         // 拖動處理
         // Scroll to follow the motion event
         mLastMotionY = y - mScrollOffset[1];

         final int oldY = mScrollY;
         final int range = getScrollRange();
         final int overscrollMode = getOverScrollMode();
         boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
             (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);

         // Calling overScrollBy will call onOverScrolled, which
         // calls onScrollChanged if applicable. 
         // 滾動處理,overScrollBy 中會處理嵌套滾動預先分發(fā)
         if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true)
             && !hasNestedScrollingParent()) {
           // Break our velocity if we hit a scroll barrier.
           mVelocityTracker.clear();
         }

         final int scrolledDeltaY = mScrollY - oldY;
         final int unconsumedY = deltaY - scrolledDeltaY;
         // 嵌套滾動
         if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
           mLastMotionY -= mScrollOffset[1];
           vtev.offsetLocation(0, mScrollOffset[1]);
           mNestedYOffset += mScrollOffset[1];
         } else if (canOverscroll) {
           final int pulledToY = oldY + deltaY;
           // 拖動陰影效果
           if (pulledToY < 0) {
             mEdgeGlowTop.onPull((float) deltaY / getHeight(),
                 ev.getX(activePointerIndex) / getWidth());
             if (!mEdgeGlowBottom.isFinished()) {
               mEdgeGlowBottom.onRelease();
             }
           } else if (pulledToY > range) {
             mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
                 1.f - ev.getX(activePointerIndex) / getWidth());
             if (!mEdgeGlowTop.isFinished()) {
               mEdgeGlowTop.onRelease();
             }
           }
           if (mEdgeGlowTop != null
               && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
             postInvalidateOnAnimation();
           }
         }
       }

支持滾動手勢的控件,一般都會支持 fling 手勢,可以理解為慣性滾動。這也是模版代碼,在 onTouchEvent 中對 ACTION_UP 中對拖動速度進行分析。

case MotionEvent.ACTION_UP:
       if (mIsBeingDragged) {
         final VelocityTracker velocityTracker = mVelocityTracker;
         velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
         // 獲取拖動速度
         int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);

         if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
           // 可以進行 fling 操作
           flingWithNestedDispatch(-initialVelocity);
         } else if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0,
             getScrollRange())) {
           postInvalidateOnAnimation();
         }

         mActivePointerId = INVALID_POINTER;
         endDrag();
       }
       break;

具體的代碼可以在 ScrollView 中閱讀。

回到我實現(xiàn)的自定義控件來,對 touch event 的處理代碼可以說是和系統(tǒng)控件的處理沒有什么兩樣,在獲取到拖動的距離后,根據這個值繪制不同位置的可見區(qū)域。這里多了兩個處理是:

第一拖動結束后,進行復位處理。拖動結束后,選擇框如果停留在兩個 item 之間,那么根據和兩個 item 的距離進行比較,選擇更近的 item。

private void correctionDistanceY() {
   if (mDistanceY % mTextRect.height() != 0) {
     int position = mDistanceY / mTextRect.height();
     int remainder = mDistanceY % mTextRect.height();
     if (remainder >= mTextRect.height() / 2f) {
       position++;
     }
     int newDistanceY = position * mTextRect.height();
     animChangeDistanceY(newDistanceY);
   }
 }

第二個是在使用上發(fā)現(xiàn)的問題,如果剩余可滾動的距離過短,拖動的手勢速度又很快,就會導致 fling 處理沒結束,視覺上又沒有改變,同時是在滾動結束后才進行選擇的回調,所以體檢上不好,但是 Scroller 并沒有提供 setDuration,所以拷貝 Scroller 中計算 duration 的方法,根據剩余的滾動計算合適的 duration,手動中斷 Scroller 的 fling 處理。

if ((SystemClock.elapsedRealtime() - mStartFlingTime) >= mFlingDuration || currY == mScroller.getFinalY()) {
       //duration or current == final
       if (DEBUG) {
         Logger.d("abortAnimation");
       }
       mScroller.abortAnimation();
     }

具體的代碼可以閱讀源碼。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • Android利用Theme自定義Activity間的切換動畫

    Android利用Theme自定義Activity間的切換動畫

    這篇文章主要為大家詳細介紹了Android利用Theme自定義Activity間的切換動畫,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Android觸摸事件如何實現(xiàn)筆觸畫布詳解

    Android觸摸事件如何實現(xiàn)筆觸畫布詳解

    這篇文章主要給大家介紹了關于Android觸摸事件如何實現(xiàn)筆觸畫布的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-10-10
  • android中選中菜單的顯示跳轉和隱式跳轉的實例介紹

    android中選中菜單的顯示跳轉和隱式跳轉的實例介紹

    android中選中菜單的顯示跳轉和隱式跳轉的實例介紹,需要的朋友可以參考一下
    2013-05-05
  • Android中使用Toast.cancel()方法優(yōu)化toast內容顯示的解決方法

    Android中使用Toast.cancel()方法優(yōu)化toast內容顯示的解決方法

    做程序員的,基本一看api就知道,用這個可以取消上一個toast的顯示,然后顯示下一個,這樣就能解決出現(xiàn)的問題??墒窃跍y試的過程中,發(fā)現(xiàn)卻沒有想象中的那么簡單,不信可以百度一下,很多很多人發(fā)現(xiàn)toast的cancel()方法不起作用
    2013-05-05
  • Android DrawLayout結合ListView用法實例

    Android DrawLayout結合ListView用法實例

    這篇文章主要介紹了Android DrawLayout結合ListView用法實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • Android listview定位到上次顯示的位置的實現(xiàn)方法

    Android listview定位到上次顯示的位置的實現(xiàn)方法

    這篇文章主要介紹了Android listview定位到上次顯示的位置的實現(xiàn)方法的相關資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-08-08
  • Android?文件存儲系統(tǒng)原理

    Android?文件存儲系統(tǒng)原理

    這篇文章主要介紹了Android?文件存儲系統(tǒng)原理,Android?的文件系統(tǒng)類似于其他平臺的基于磁盤的文件系統(tǒng),包括好幾種類別具體詳情感興趣得朋友可以參考一下文章內容
    2022-06-06
  • Android實現(xiàn)鬧鐘功能小Dome

    Android實現(xiàn)鬧鐘功能小Dome

    本篇文章主要介紹了Android實現(xiàn)鬧鐘功能小Dome,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • Android使用Canvas對象實現(xiàn)刮刮樂效果

    Android使用Canvas對象實現(xiàn)刮刮樂效果

    這篇文章主要介紹了Android使用Canvas對象實現(xiàn)刮刮樂效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • Android中音視頻合成的幾種方案詳析

    Android中音視頻合成的幾種方案詳析

    隨著音視頻領域的火熱,在很多領域(教育,游戲,娛樂,體育,跑步,餐飲,音樂等)嘗試做音視頻功能,下面這篇文章主要給大家介紹了關于Android中音視頻合成的幾種方案的相關資料,需要的朋友可以參考借鑒,下面隨著小編來一起看看吧。
    2017-12-12

最新評論

青河县| 沁源县| 新河县| 凤山市| 湘潭县| 芦山县| 泾阳县| 岳阳市| 绩溪县| 乌拉特中旗| 司法| 新民市| 麦盖提县| 凤冈县| 汝南县| 梨树县| 屏东市| 修武县| 娄烦县| 云安县| 登封市| 神农架林区| 三江| 阿坝| 广河县| 名山县| 土默特左旗| 正安县| 宁晋县| 宜城市| 托克逊县| 方城县| 东山县| 夏河县| 多伦县| 保康县| 两当县| 凌海市| 古田县| 洪湖市| 名山县|