AndroidのカスタムviewGroupシミュレーションscrollViewの詳細

10664 ワード

アンドロイドを学んだ友人は、カスタムviewGroupがonmeasure()とonLayout()の書き換えから離れられないことを知っていると信じています.コードの説明を始める前に、この2つの方法に関する知識を見てみましょう.
一、onMeasure():これは自身の幅の高さとサブviewの幅の高さを測定する方法であり、測定に関する知識点は幅の高さのほかに、3つのモードがある.
(1)三つのパターンは以下の通りである.
               1、MeasureSpec.EXACTLY:正確値モード:コントロールのlayout_widthまたはlayout_heihtは200 dpなどの具体的な値として指定したり、match_として指定したりします.parent(親viewのサイズを占める)は、このモードを返します.
               2、MeasureSpec.AT_MOST:最大値モード、コントロールのlayout_widthまたはlayout_heihtはwrap_として指定contentの場合、コントロールのサイズは通常、コントロールのサブコントロールまたはコンテンツの変化に伴って変化します.この場合、コントロールのサイズは親コントロールを超えてはいけません.
              3、MeasureSpec.UNSPECIFIED:サイズ測定モードを指定しないで、通常は定義viewを描くときに使用されます.つまり、開発者がonDraw()のときにサイズを指定するのは、開発者です.
(2)アスペクト測定に用いる知識点:
私たちはonMeasure(int widthMeasureSpec,int heightMeasureSpec)が一般的に自分の幅の高さとサブviewの幅の高さを測定するために使用されていることを知っていますが、この2つのパラメータはシステムで測定された自分のviewの幅の高さですが、この幅の高さを直接使用することはできません.では、それらはどのように意味しますか?またどのように使いますか?
実は:widthMeasureSpec,heightMeasureSpecの2つのパラメータは32ビットで、2つの意味を含んでいます.
これらの高さ2ビットは、xmlレイアウトで設定したlayout_をシステムで測定する前に示した3つのモードの1つを表します.widthとlayout_Heightの結果,30ビット低いのは,このコントロールの幅と高さを系統的に測定することである.
では、どのようにして私たち自身が先に求めているデータに変換しますか?実はグーグルは短くて精悍なクラスMeasureSpecクラスを提供し、次の2つの方法を提供しています.
             
//自己のアスペクトを取得し、200 pxのようなアスペクトを返します.パラメータwidthMeasureSpecはonmeasure()のパラメータです.
               int measureSelfWidth = MeasureSpec.getSize(widthMeasureSpec);
//viewは、以上の3つのモードのうちの1つint mode=MeasureSpecを用いるモードであることを確認する.getMode(widthMeasureSpec)
二、onLayout():View Groupの抽象的な方法で、一般的にこの方法で計算によってサブviewの座標を設定します(サブviewの位置)
返されるパラメータonLayout(boolean changed,int l,int t,int r,int b)は、requestLayout()が呼び出されると、このviewが変更されたかどうかを表すためにchangedが使用され、他のパラメータはそれぞれ自分の座標(左上右下)を表す
三、ondraw()viewGroupではこの方法を書き換えることは提唱されていません.背景とかでない限り、一般的にはViewGroupは容器としてのみで、その他はそのサブviewで描くので、viewGroupのonDraw()はviewのonDraw()である
四、次はScrollerを使うので、基本的な使い方を話します.Scrollerはとても特別なクラスです.viewやView Groupはよくスライドするときに使いますが、それは何ですか.何に使うの?
(1)何の鬼ですか.
名前からスライドに関連していることを推測するのは難しくありません.確かに、はっきり言って、それは補間器でしょう(物理速度の変化を制御します)、補間計算を提供して、スクロール過程を滑らかにしてアニメーション化して、単純にスライドに計算を提供します.
(2)作用:スライド時に座標始点と始点区間のスムーズな遷移座標点を計算する補助クラスである.
主な方法:
//前scrollerを使用してxを初期化し、yは始点座標、dx、dyはオフセット量、正数は指を上へスライドさせ、負数は指を下へスライドさせる
      mScroller.startScroll(x, y, dx, dy);
//boolean値を返し、計算済みかどうかを判断します.例えば、スクロール時にスクロール完了かどうかを判断します.
       mScroller.computeScrollOffset();
//始点から終点までの区間の遷移座標点mScrollerを取得することができる.getCurrY();
一般的には、Viewの方法の1つとして使用されます.
        computeScroll();//この方法は空の方法ですが、ondraw()のときに呼び出され、主にスライドに合わせて使用されます.
初期化後にinvalidate()またはpostInvalidate()メソッドを呼び出してonDraw()を実行すると、このメソッドでscrollerに必要な論理を処理できます.
注意点:
getScrollY()はviewの始点座標点までスクロールするので計算を間違えるな
         scrollTo(x,y);//パラメータ指定座標(x,y)にスクロール
        scrollBy(dx, (int) dy);//パラメータで指定したオフセット量、すなわち現在の座標(x+dx,y+dy)までスクロール
はい、基本的な知识はすべて理解して、以下は简単なシミュレーションのScrollViewのdemoを见て、简単に理解しやすいため、直接コードに行って、demoは最后に、ダウンロードを歓迎して、间违った地方があったら指摘してください
   
package com.example.administrator.customscrollviewdemo;

import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Scroller;

/**
 * author : zhongwr on 2016/7/20
 */
public class CustomScrollView extends ViewGroup {
    private static final String TAG = "CustomScrollView";
    private Context mContext;
    private int mScreenHeight;
    private int totalHeight;
    private Scroller mScroller;

    public CustomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public CustomScrollView(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context) {
        mContext = context;
        mScreenHeight = getScreenSize(mContext).heightPixels;
        mScroller = new Scroller(mContext);
    }

    /***
     *           200px
     *
     * @param widthMeasureSpec
     * @return
     */
    public int measureRealWidth(int widthMeasureSpec) {
        int result = 200;
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int realWidth = MeasureSpec.getSize(widthMeasureSpec);
        switch (specMode) {
            case MeasureSpec.EXACTLY:
                //MeasureSpec.EXACTLY:     :    layout_width layout_heiht      ,  200dp,     match_parent(   view   ),          
                result = realWidth;
                Log.d(TAG, "EXACTLY result " + result);
                break;
            case MeasureSpec.AT_MOST:
                // MeasureSpec.AT_MOST:      ,   layout_width layout_heiht   wrap_content ,                       ,              
                result = Math.min(result, realWidth);
                Log.d(TAG, "AT_MOST result " + result);
                break;
            case MeasureSpec.UNSPECIFIED:
                    // MeasureSpec.UNSPECIFIED:          ,       view       ,        onDraw()       
                result = realWidth;
                Log.d(TAG, "UNSPECIFIED result " + result);
                break;
        }
        return result;
    }

    /***
     * @param widthMeasureSpec            32    2       30     
     * @param heightMeasureSpec           32    2       30     
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(TAG, "widthMeasureSpec " + widthMeasureSpec);
        Log.d(TAG, "heightMeasureSpec " + heightMeasureSpec);
        /***   */
        int measureSelfWidth = measureRealWidth(widthMeasureSpec);
        int measureSelfHeight = MeasureSpec.getSize(heightMeasureSpec);
        Log.d(TAG, "widthMeasure " + measureSelfWidth);
        Log.d(TAG, "widthMode " + MeasureSpec.getMode(widthMeasureSpec));
        Log.d(TAG, "heightMeasure " + MeasureSpec.getSize(heightMeasureSpec));
        Log.d(TAG, "heightMode " + MeasureSpec.getMode(heightMeasureSpec));

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        }
        //  viewGroup   ,    onlayout   layoutParams  
        totalHeight = getScreenSize(mContext).heightPixels * childCount;
        setMeasuredDimension(measureSelfWidth, totalHeight);
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.d(TAG, "onLayout left " + l);
        Log.d(TAG, "onLayout top " + t);
        Log.d(TAG, "onLayout right " + r);
        Log.d(TAG, "onLayout bottom " + b);
        Log.d(TAG, "onLayout heightPixels " + getScreenSize(mContext).heightPixels);
        int childCount = getChildCount();
//        LayoutParams lp = getLayoutParams();
//        totalHeight = getScreenSize(mContext).heightPixels * childCount;
//        lp.height = totalHeight;//  viewgroup   
//        setLayoutParams(lp);

        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            childView.layout(l, i * mScreenHeight, r, (i + 1) * mScreenHeight);
        }
    }

    private float lastDownY;
    private float mScrollStart;
    private float mScrollEnd;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastDownY = event.getY();
                mScrollStart = getScrollX();
                Log.d(TAG, "totalHeight = " + totalHeight);
                break;
            case MotionEvent.ACTION_MOVE:
                float currentY = event.getY();
                float dy;
                dy = lastDownY - currentY;
                Log.d(TAG, "dy = " + dy);
                Log.d(TAG, "getScrollY() = " + getScrollY());
                Log.d(TAG, "getHeight()  = " + getHeight());
                Log.d(TAG, "getHeight() - mScreenHeight = " + (getHeight() - mScreenHeight));
                if (getScrollY() < 0) {
                    dy = 0;
                    //   ,  0 ,    ,       ,getScrollY     
//                    setScrollY(0);
                } else if (getScrollY() > getHeight() - mScreenHeight) {
                    dy = 0;
                    //      ,    ,       ,getScrollY     getHeight() - mScreenHeight  ,     
//                    setScrollY(getHeight() - mScreenHeight);
                }
                scrollBy(0, (int) dy);
                //     Y,       view      
                lastDownY = event.getY();
                break;
            case MotionEvent.ACTION_UP:
                mScrollEnd = getScrollY();
                int dScrollY = (int) (mScrollEnd - mScrollStart);
                if (mScrollEnd < 0) {//    :      ,      
                    Log.d(TAG, "mScrollEnd < 0" + dScrollY);
                    mScroller.startScroll(0, getScrollY(), 0, -getScrollY());
                } else if (mScrollEnd > getHeight() - mScreenHeight) {//      ,            
                    Log.d(TAG, "getHeight() - mScreenHeight - (int) mScrollEnd " + (getHeight() - mScreenHeight - (int) mScrollEnd));
                    mScroller.startScroll(0, getScrollY(), 0, getHeight() - mScreenHeight - (int) mScrollEnd);
                }
                postInvalidate();//     computeScroll()
                break;
        }
        return true;//    true  down     move up  
    }

    /**
     * Scroller      ,      ,           ,     UI,       UI  ,             
     *   invalidate()      ,     onDraw()   
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        Log.d(TAG, "mScroller.getCurrY() " + mScroller.getCurrY());
        if (mScroller.computeScrollOffset()) {//        
            scrollTo(0, mScroller.getCurrY());//     ,startScroll()    ,         
            postInvalidate();
        }
    }

    /**
     *       ,                
     *
     * @param context
     * @return
     */
    public static DisplayMetrics getScreenSize(Context context) {
        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getMetrics(metrics);
        return metrics;
    }

}

demo: http://download.csdn.net/detail/zhongwn/9582516