<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                #### **六、自定義ViewGroup** ViewGroup存在的目的就是為了對其子view進行管理,為其子view添加顯示、響應的規則,因此,自定義ViewGroup通常需要重寫onMeasure()方法來對子view進行測量,重寫onLayout()方法來確定子view的位置,重寫onTouchEvent()來增加響應事件。 實現類似于原生控件ScrollView的自定義ViewGroup,這個“ScrollView”除了可以上下滑動,還有一個粘性的效果:即一個子view向上滑動大于一定的距離后,松開手指,它將自動向上滑動,顯示下一個子view,同理,如果滑動距離小于一定的距離,松開手指,它將自動滑動到開始的位置。 如下圖所示 :-: ![](https://box.kancloud.cn/f47a8ca3d3209a1559b7896e5fe65148_363x602.gif) 圖12 自定義ScrollView 示例:[自定義ScrollView](https://github.com/xuyisheng/AndroidHeroes/blob/master/3.Android控件架構/SystemWidget/app/src/main/java/com/imooc/systemwidget/MyScrollView.java) 代碼如下所示 ~~~ /** * 彈性scroll,滑動過程增加一個粘性效果,當一個子view向上滑動大于一定距離后,松開手指,它將自動向上滑動顯示下一個子view * 同理,小于一定距離,松開手指,將滑動到開始的位置 */ public class MyScrollView extends ViewGroup { private int mScreenHeight; private Scroller mScroller; private int mLastY; private int mStart;//觸摸起點 private int mEnd;//觸摸終點 public MyScrollView(Context context) { super (context); initView (context); } public MyScrollView(Context context, AttributeSet attrs) { super (context, attrs); initView (context); } public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); initView (context); } private void initView(Context context) { WindowManager wm = (WindowManager) context.getSystemService ( Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics (); wm.getDefaultDisplay ().getMetrics (dm); mScreenHeight = dm.heightPixels; mScroller = new Scroller (context); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount (); // 設置ViewGroup的高度,每個子view占一屏的高度, MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams (); mlp.height = mScreenHeight * childCount; setLayoutParams (mlp); //調用子view的layout方法,設定每一個子view需要放置的位置 for (int i = 0; i < childCount; i++) { View child = getChildAt (i); if (child.getVisibility () != View.GONE) { child.layout (l, i * mScreenHeight, r, (i + 1) * mScreenHeight); //修改每一個子view的top和bottom屬性 } } } //使用遍歷的方式來通知子view對自身進行測量 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure (widthMeasureSpec, heightMeasureSpec); int count = getChildCount (); for (int i = 0; i < count; ++i) { View childView = getChildAt (i); measureChild (childView, widthMeasureSpec, heightMeasureSpec); } } @Override public boolean onTouchEvent(MotionEvent event) { int y = (int) event.getY ();//獲取點擊事件距離控件左邊的距離 switch (event.getAction ()) { case MotionEvent.ACTION_DOWN: mLastY = y; //記錄觸摸起點 mStart = getScrollY (); break; //ACTION_MOVE事件中,計算dy,手指滑動的時候,調用scrollBy()方法,讓viewGroup的所有子view跟著滾動dy case MotionEvent.ACTION_MOVE: if (!mScroller.isFinished ()) { mScroller.abortAnimation (); } int dy = mLastY - y; if (getScrollY () < 0) { dy = 0; } if (getScrollY () > getHeight () - mScreenHeight) { dy = 0; } scrollBy (0, dy); mLastY = y; break; //ACTION_UP事件中,判斷手指滑動的距離,若超過一定距離,調用scroller類來平滑移動到下一個子view, // 否則滾到原來位置 case MotionEvent.ACTION_UP: int dScrollY = checkAlignment (); if (dScrollY > 0) {//向下滑動 //超過屏幕高度的1/3,則展示上一張圖片;沒有則使上一張圖片返回去,顯示還是當前圖片 if (dScrollY < mScreenHeight / 3) { mScroller.startScroll ( 0, getScrollY (), 0, -dScrollY); } else { mScroller.startScroll ( 0, getScrollY (), 0, mScreenHeight - dScrollY); } } else {//向上滑動 if (-dScrollY < mScreenHeight / 3) { mScroller.startScroll ( 0, getScrollY (), 0, -dScrollY); } else { mScroller.startScroll ( 0, getScrollY (), 0, -mScreenHeight - dScrollY); } } break; } postInvalidate (); return true; } private int checkAlignment() { int mEnd = getScrollY (); boolean isUp = ((mEnd - mStart) > 0) ? true : false; int lastPrev = mEnd % mScreenHeight; int lastNext = mScreenHeight - lastPrev; if (isUp) { //向下的 return lastPrev; } else { return -lastNext; } } //重寫computeScroll()方法,實現模擬滑動,系統在繪制view的時候,會在draw()方法中調用該方法 //通常可以以以下代碼作為模板,詳情見群英傳 P97 @Override public void computeScroll() { super.computeScroll (); //computeScrollOffset ()來判斷是否完成了滑動,false,中斷循環,完成平移移動過程 if (mScroller.computeScrollOffset ()) { scrollTo (0, mScroller.getCurrY ()); //通過不斷重繪來不斷調用computeScroll()方法,因為只能在computeScroll()方法才能獲得模擬滑動中 // 的scroll坐標,但是computeScrollOffset ()不會自動調用 postInvalidate (); } } } ~~~ **分析如下:** 首先,讓ViewGroup能夠實現類似ScrollView的功能,當然在ViewGroup能滾動之前,需要先放置好它的子view,使用遍歷的方式來通知子view對自身進行測量 ~~~ //使用遍歷的方式來通知子view對自身進行測量 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure (widthMeasureSpec, heightMeasureSpec); int count = getChildCount (); for (int i = 0; i < count; ++i) { View childView = getChildAt (i); measureChild (childView, widthMeasureSpec, heightMeasureSpec); } } ~~~ 接著,就要對子view進行位置的設定。讓每個子view都顯示完整的一屏(這樣在滑動的時候,可以比較較好地實現后面的效果),因此viewgroup的高度就是子view的個數乘以屏幕的高度,如下代碼確定整個ViewGroup的高度。 ~~~ // 設置ViewGroup的高度,每個子view占一屏的高度, MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams (); mlp.height = mScreenHeight * childCount; setLayoutParams (mlp); ~~~ 在獲取了整個ViewGroup的高度后,就可以通過遍歷來設定每個子view需要放置的位置了,直接通過調用子view的layout方法,并將具體的位置傳遞進去即可。代碼中主要是修改每個子view的top和bottom這2個屬性,讓他們可以依次排列。 ~~~ //調用子view的layout方法,設定每一個子view需要放置的位置 for (int i = 0; i < childCount; i++) { View child = getChildAt (i); if (child.getVisibility () != View.GONE) { child.layout (l, i * mScreenHeight, r, (i + 1) * mScreenHeight); //修改每一個子view的top和bottom屬性 } } ~~~ 通過上面的操作,我們已經可以將子view放置到ViewGroup中了,但此時的ViewGroup還不能夠響應任何觸控事件,自然也不能滑動,因此我們需要重寫onTouchEvent()方法,為ViewGroup添加響應事件。在ViewGroup中添加滑動事件,通常可以使用scrollBy()方法來輔助滑動。在onTouchEvent()的ACTION_MOVE事件中,只要使用scrollBy(0,dy)方法,讓手指滑動的時候讓ViewGroup中的所有子view也跟著滑動dy即可,計算dy的方法有很多,如下代碼就是一種思路 ~~~ @Override public boolean onTouchEvent(MotionEvent event) { int y = (int) event.getY ();//獲取點擊事件距離控件左邊的距離 switch (event.getAction ()) { case MotionEvent.ACTION_DOWN: mLastY = y; //記錄觸摸起點 mStart = getScrollY (); break; //ACTION_MOVE事件中,計算dy,手指滑動的時候,調用scrollBy()方法,讓viewGroup的所有子view跟著滾動dy case MotionEvent.ACTION_MOVE: if (!mScroller.isFinished ()) { mScroller.abortAnimation (); } int dy = mLastY - y; if (getScrollY () < 0) { dy = 0; } if (getScrollY () > getHeight () - mScreenHeight) { dy = 0; } scrollBy (0, dy); mLastY = y; break; //ACTION_UP事件中,判斷手指滑動的距離,若超過一定距離,調用scroller類來平滑移動到下一個子view, // 否則滾到原來位置 case MotionEvent.ACTION_UP: int dScrollY = checkAlignment (); if (dScrollY > 0) {//向下滑動 //超過屏幕高度的1/3,則展示上一張圖片;沒有則使上一張圖片返回去,顯示還是當前圖片 if (dScrollY < mScreenHeight / 3) { mScroller.startScroll ( 0, getScrollY (), 0, -dScrollY); } else { mScroller.startScroll ( 0, getScrollY (), 0, mScreenHeight - dScrollY); } } else {//向上滑動 if (-dScrollY < mScreenHeight / 3) { mScroller.startScroll ( 0, getScrollY (), 0, -dScrollY); } else { mScroller.startScroll ( 0, getScrollY (), 0, -mScreenHeight - dScrollY); } } break; } postInvalidate (); return true; } ~~~ 最后實現這個粘性效果,要實現該效果,自然想到了onTouchEvent()的ACTION_UP事件和Scroll類,在ACTION_UP事件中判斷手指滑動的距離,如果超過一定的距離,則使用Scroller類來平滑移動到下一個view,如果下雨一定距離,則滾回到原來的位置。 當然最后不要忘記加上computeScroll(), ~~~ //重寫computeScroll()方法,實現模擬滑動,系統在繪制view的時候,會在draw()方法中調用該方法 //通常可以以以下代碼作為模板,詳情見群英傳 P97 @Override public void computeScroll() { super.computeScroll (); //computeScrollOffset ()來判斷是否完成了滑動,false,中斷循環,完成平移移動過程 if (mScroller.computeScrollOffset ()) { scrollTo (0, mScroller.getCurrY ()); //通過不斷重繪來不斷調用computeScroll()方法,因為只能在computeScroll()方法才能獲得模擬滑動中 // 的scroll坐標,但是computeScrollOffset ()不會自動調用 postInvalidate (); } } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看