前面幾節,咱們重點討論了自定義View的三板斧,這節咱們來討論自定義ViewGroup,爲何要自定義ViewGroup,其實就是爲了更好的管理View。ide
自定義ViewGroup無非那麼幾步:佈局
Ⅰ、重寫OnMeasure()方法,測試子控件的大小。測試
Ⅱ、重寫onLayout()方法,計算子控件的佈局。spa
Ⅲ、在onDraw()方法中,繪製子控件,無關緊要。3d
Ⅳ、監聽onTouch事件,響應屏幕觸摸事件。code
相應思惟導圖以下所示:blog
連篇累牘的說了這麼多,咱們經過一個小案例來理解這個自定義ViewGroup把,看看如何實現ViewGroup。事件
簡單的黏性ScrollViewget
簡單概述it
這是一個原生scrollView效果很是相似的效果,他能夠像scrollView同樣上下滑動的效果,不過咱們增長了一個黏性效果。何爲黏性效果了?即當一個子View向上滑動大於必定距離的時候,它將自動向上滑動,顯示下一個子View。同理,若是一個子View滑動距離小於某一個距離,它將滾回到原始的位置。
實現思路
投籃要找角度,控件要找思路。咱們來分析要實現此自定義ViewGroup的基本思路了:
Ⅰ、在OnMeasure()方法中,對每一個子控件的大小進行測量了。
Ⅱ、在OnLayout()方法中,對每一個要顯示控件的位置進行計算。
Ⅲ、緊接着,就是在OnTouchEvent()方法,監聽着手勢觸摸事件,判斷它是上滑仍是下滑,判斷它的滑動距離是否大於咱們設定的值,若是大於這個值,就將它移動到下一個子View,不然,滾回到原來的位置。
有了這樣的思路以後,咱們只須要所作的是循序漸進實現代碼編寫
具體實現
第一步、進行一些變量的初始化,代碼以下:
private void init(Context context) { WindowManager manager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics displayMetrics = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(displayMetrics); mScreenHeight = displayMetrics.heightPixels; mScroller = new Scroller(context); }
獲取屏幕高度做爲每一個控件的高度,將scroller控件進行初始化。
第二步 、實現控件的測量,代碼以下:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec); } }
咱們看到每一個子控件的大小與父控件的大小保持一致,這樣才能造成滾動的效果。
第三步、將子控件從上到下依次排列開來,代碼以下所示:
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams(); layoutParams.height = mScreenHeight * count; for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() == View.VISIBLE) { child.layout(l, i * mScreenHeight, r, (i + 1) * mScreenHeight); } } }
咱們能夠清晰的看到,若是將其子控件進行從上到下依次排列,這個子控件佔一頻,這樣,才能造成能夠上下滾動的必要條件。
第四步、監聽手勢事件,源代碼以下:
@Override public boolean onTouchEvent(MotionEvent event) { int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastY = y; mStart = getScrollY(); break; case MotionEvent.ACTION_MOVE: if (!mScroller.isFinished()) { mScroller.abortAnimation(); } int dy = y - mLastY; if (getScrollY()< 0) { dy = 0; } else if (getScrollY()> getHeight() - mScreenHeight) { dy = 0; } scrollBy(0, dy); mLastY = y; break; case MotionEvent.ACTION_UP: mEnd = getScrollY(); int delta = mEnd - mStart; if (delta > 0) { if (delta < mScreenHeight / 3) { mScroller.startScroll(0, getScrollY(), 0, -delta); } else { mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - delta); } } else { if (Math.abs(delta) < mScreenHeight / 3) { mScroller.startScroll(0, getScrollY(), 0, -delta); } else { mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight - delta); } } break; default: break; } return true; }
過後總結
其實,在這個事件監聽中就作了三件事件
①、根據手勢按下、擡起的距離進行判斷,判斷手勢究竟是上滑仍是下滑。
②、若是手勢滑動的距離,小於小於相應的閾值(這裏爲屏幕高度的三分之一)之後,就滾回到原來的位置,不然自動滑入下一個子View。
③、在手指移動事件,使這個控件可以隨着手勢的滑動而自由的移動。可是,咱們要作好相應臨界值判斷,判斷其是否小於0或者大於屏幕高度,就不進行滑動。
最終效果
這個控件最終運行的效果爲:
這就是,我對自定義viewGroup控件的必定總結。本人才疏學淺,懇請你們指教。