自定義ViewGroup筆記--ViewDragHelper

在自定義ViewGroup中,使用ViewDragHelper會非常方便.

參考文章:http://wenku.baidu.com/link?url=Z-BbAV7WaxKJ0i14tyQMIwK1DtSNzJ_dW1dKv2HI3xQKNkKYCYz9-xSZVDsYqtZi9uISleDbNpMe_eus4utCDTj_xFE_St7PAlSg372kysO-zwJ9wUzamU83QS-Wwzr7

1.一般在ViewGroup的構造函數中初始化拖拽輔助類

參數
//父類的容器
// sensitivity 敏感度, 越大越敏感.1.0f是默認值 也可以稱之爲敏感閥值
// Callback 事件回調
mhelper = ViewDragHelper.create(forParent, sensitivity, Callback);

2.轉交觸摸事件,由ViewDragHelper來處理我們的touch事件

轉交事件攔截
        @Override
public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
        // 由 ViewDragHelper 判斷觸摸事件是否該攔截
        return mHelper.shouldInterceptTouchEvent(ev);
}

轉交事件處理
        @Override
public boolean onTouchEvent(MotionEvent event) {
        // 由 ViewDragHelper 處理事件
    try {
        mHelper.processTouchEvent(event);
    } catch (Exception e) {
        e.printStackTrace();
    }
        return true;
}

3.接收事件的回調 ViewDragHelper.Callback

1.public boolean tryCaptureView(View child, int pointerId) {} -->確定當前手指觸摸的view是否可以被拖動

參數:
// child 被用戶拖拽的子View
// pointerId 多點觸摸的手指id

返回值:
確定當前點擊的view是否可以拖動,如果返回false,則接下來的方法都不會調用,拿不到增量
如果我們不想某個view被拖動,那麼我們可以在這裏判斷,如果參數裏面的view等於不想拖動的view返回false就ok


2.public int getViewHorizontalDragRange(View child) {} -->確定拖拽的範圍.只需要返回一個>0的值,決定了動畫的執行時長,水平方向是否可以被滑開,用於底層的計算,並不能真正的限定水平拖拽的範圍

參數:
//child 被用戶拖拽的子view

返回值:
返回拖拽的範圍  在這裏,一般設置爲滑動的邊界值,這個值可以隨便設置


3.public int clampViewPositionHorizontal(View child, int left, int dx) {} -->修正子View水平方向的位置. 此時還沒有發生真正的移動.

參數:
//child 被用戶拖拽的子view
//left 建議移動到的位置
//dx 與舊位置的差值

返回值:
返回需要移動的位置值,在這裏我們需要做邊界的處理


4.public void onViewPositionChanged(View changedView, int left, int top,int dx, int dy) {} -->當控件位置變化時 調用, 可以做 : 伴隨動畫, 狀態的更新, 事件的回調.

參數:
//changedView 位置發生變化的子孩子
//left 最新的水平方向的位置
//top 最新的垂直方向的位置
//dx 剛剛發生的水平偏移量
//dy 剛剛發生的垂直偏移量

在這裏,我們一般是根據需求去傳遞位置的變化量,以及伴隨動畫, 狀態的更新, 事件的回調
爲了兼容低版本,在此方法裏需要調用重繪的方法 invalidate();


5.public void onViewReleased(View releasedChild, float xvel, float yvel) {} -->決定了鬆手之後要做的事情, 結束的動畫

參數:
//releasedChild 手指鬆開後,被釋放的子view
//xvel 水平方向的速度 正數代表向右滑動,負值代表向左滑動
//yvel 垂直方向的速度 正數代表向下滑動,負值代表向上滑動

在這裏我們一般是處理手指鬆開後的平滑動畫,底部封裝了Scroller(實現步驟和使用Scroller類似)
    (1).觸發平滑動畫
    mHelper.smoothSlideViewTo(View child, int finalLeft, int finalTop)
    參數:
    //child 哪一個子view觸發
    //水平方向要滑動到的位置
    //垂直方向要滑動到的位置
    返回值:
    boolean類型值,如果當前位置不是指定的最終位置,返回true

    (2).開始重繪界面
    ViewCompat.postInvalidateOnAnimation(View view); 重繪界面,爲了使滑動流暢不掉幀,
    我們利用的v4包裏的ViewCompat來實現
    參數:
    //view 是執行動畫的父佈局

    (3).維持動畫的繼續, 高頻率調用
        public void computeScroll() {
            if(mHelper.continueSettling(true)){
            // 如果當前位置還沒有移動到最終位置. 返回true.需要繼續重繪界面
            ViewCompat.postInvalidateOnAnimation(this);
        }

4: invalidate&postInvalidate&postInvalidateOnAnimation區別

    invalidate:主線程重繪

    postInvalidate:可在子線程重繪

    postInvalidateOnAnimation
    在16之前,跟調用invalidate沒有區別,api16之後,會調用View. postInvalidateOnAnimation新增方法。
    所以新增了postInvalidateOnAnimation方法,這個會對invalidate的頻率做調整,減少阻塞message的機率。

5:在自定義view或者ViewGroup中,以下方法調用順序
onFinishInflate()–>onSizeChanged()–>onLayout()

如何知道view已經繪製完成,並且顯示到了界面
protected void onFinishInflate() {}
當view繪製完成,那麼就可以在這裏拿到子view的引用,可以加以邏輯判斷,使代碼的邏輯性更健壯

當view的尺寸發生變化時調用,一般在這裏我們可以去獲取定義的view的寬高
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    mWidth = w;
    mHeight = h;
} 

6:在做屬性動畫的時候,目的就是找到一個百分比(percent),其他就好辦了,可以利用percent和startValue,endValue做動畫,特別是利用系統提供的估值器

public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }

public Object evaluateColor(float fraction, Object startValue, Object endValue) {
        int startInt = (Integer) startValue;
        int startA = (startInt >> 24) & 0xff;
        int startR = (startInt >> 16) & 0xff;
        int startG = (startInt >> 8) & 0xff;
        int startB = startInt & 0xff;

        int endInt = (Integer) endValue;
        int endA = (endInt >> 24) & 0xff;
        int endR = (endInt >> 16) & 0xff;
        int endG = (endInt >> 8) & 0xff;
        int endB = endInt & 0xff;

        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
                (int)((startR + (int)(fraction * (endR - startR))) << 16) |
                (int)((startG + (int)(fraction * (endG - startG))) << 8) |
                (int)((startB + (int)(fraction * (endB - startB))));
    }

兩個例子,做個筆記.

/**
 * 側滑面板
 * @author poplar
 *
 */
public class DragLayout extends FrameLayout {

    private ViewDragHelper mHelper;

    public static enum Status {
        Close, Open, Draging
    }
    private Status status = Status.Close;

    public interface OnDragChangeListener {
        void onClose();

        void onOpen();

        void onDraging(float percent);
    }

    private OnDragChangeListener onDragChangeListener;

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    public OnDragChangeListener getOnDragChangeListener() {
        return onDragChangeListener;
    }

    public void setOnDragChangeListener(OnDragChangeListener onDragChangeListener) {
        this.onDragChangeListener = onDragChangeListener;
    }

    public DragLayout(Context context) {
        this(context, null);
    }

    public DragLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        // forParent 父類的容器
        // sensitivity 敏感度, 越大越敏感.1.0f是默認值
        // Callback 事件回調
        // 1. 創建 ViewDragHelper 輔助類 
        mHelper = ViewDragHelper.create(this, 1.0f, callback);
    }

    // 3. 接受處理的結果.
    ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {

        // 1. 返回值, 決定了child是否可以被拖拽
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            // child 被用戶拖拽的子View
            // pointerId 多點觸摸的手指id
            System.out.println("tryCaptureView: ");
            return true;
        }

        // 2. 返回拖拽的範圍. 返回一個 >0 的值, 決定了動畫的執行時長, 水平方向是否可以被滑開
        @Override
        public int getViewHorizontalDragRange(View child) {
            return mRange;
        };

        // 3. 修正子View水平方向的位置. 此時還沒有發生真正的移動.
        // 返回值決定了View將會移動到的位置
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            // child 被拖拽的子View
            // left 建議移動到的位置
            // dx 跟舊的位置的差值
//          int oldLeft = mMainContent.getLeft();
//          System.out.println("clamp: " + " left: " + left + " dx: " + dx + " oldLeft: " + oldLeft);

            if(child == mMainContent){
                left = fixLeft(left);
            }
            return left;
        }

        // 4. 當控件位置變化時 調用, 可以做 : 伴隨動畫, 狀態的更新, 事件的回調.
        @Override
        public void onViewPositionChanged(View changedView, int left, int top,
                int dx, int dy) {
            // left最新的水平方向的位置
            // dx 剛剛發生的水平變化量
//          System.out.println("onViewPositionChanged: " + " left:" + left + " dx: " + dx);

            if(changedView == mLeftContent){
                // 如果移動的是左面板
                // 1. 放回原來的位置
                mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight);
                // 2. 把左面板發生的變化量dx轉遞給主面板
                int newLeft = mMainContent.getLeft() + dx;

                // 修正左邊值.
                newLeft = fixLeft(newLeft);
                mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight);
            }

            dispatchDragEvent();

            // 爲了兼容低版本, 手動重繪界面所有內容.
            invalidate();
        }


        //5. 決定了鬆手之後要做的事情, 結束的動畫
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            System.out.println("onViewReleased: xvel: " + xvel);
            // releasedChild 被釋放的孩子
            // xvel 水平方向的速度 向右爲+,  向左爲-
            if(xvel == 0 && mMainContent.getLeft() > mRange * 0.5f){
                open();
            } else if (xvel > 0) {
                open();
            } else {
                close();
            }

        }

        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }

    };

    /**
     * 分發拖拽事件, 伴隨動畫,更新狀態.
     */
    protected void dispatchDragEvent() {
        // 0.0 -> 1.0  
        float percent = mMainContent.getLeft() * 1.0f / mRange;
        System.out.println("percent: " + percent);

        if(onDragChangeListener != null){
            onDragChangeListener.onDraging(percent);
        }

        // 更新狀態
        Status lastStatus = status;
        status = updateStatus(percent);
        if(lastStatus != status && onDragChangeListener != null){
            if(status == Status.Close){
                onDragChangeListener.onClose();
            }else if (status == Status.Open) {
                onDragChangeListener.onOpen();
            }
        }


        // 執行動畫
        animViews(percent);
    }


    /**
     * 更新狀態
     * @param percent 當前動畫執行的百分比
     * @return
     */
    private Status updateStatus(float percent) {
        if(percent == 0){
            return Status.Close;
        }else if (percent == 1) {
            return Status.Open;
        }
        return Status.Draging;
    }

    private void animViews(float percent) {
        //      - 左面板: 縮放動畫, 平移動畫, 透明度動畫
                // 縮放動畫 0.0 -> 1.0  >>> 0.0 -> 0.5 >>>0.5 -> 1.0
                // percent * 0.5 + 0.5
        //      mLeftContent.setScaleX(percent * 0.5f + 0.5f);
        //      mLeftContent.setScaleY(percent * 0.5f + 0.5f);
                ViewHelper.setScaleX(mLeftContent, evaluate(percent, 0.5f, 1.0f));
                ViewHelper.setScaleY(mLeftContent, evaluate(percent, 0.5f, 1.0f));

        //      平移動畫 -mWidth / 2.0f -> 0
                ViewHelper.setTranslationX(mLeftContent, evaluate(percent,  -mWidth / 2.0f, 0));

                // 透明度動畫 0.2f -> 1.0
                ViewHelper.setAlpha(mLeftContent, evaluate(percent,  0.2f, 1.0f));

        //      - 主面板: 縮放動畫 1.0 -> 0.8
                ViewHelper.setScaleX(mMainContent, evaluate(percent, 1.0f, 0.8f));
                ViewHelper.setScaleY(mMainContent, evaluate(percent, 1.0f, 0.8f));

        //      - 背  景: 亮度變化
                getBackground().setColorFilter((Integer)evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), PorterDuff.Mode.SRC_OVER);
    }

    public Object evaluateColor(float fraction, Object startValue, Object endValue) {
        int startInt = (Integer) startValue;
        int startA = (startInt >> 24) & 0xff;
        int startR = (startInt >> 16) & 0xff;
        int startG = (startInt >> 8) & 0xff;
        int startB = startInt & 0xff;

        int endInt = (Integer) endValue;
        int endA = (endInt >> 24) & 0xff;
        int endR = (endInt >> 16) & 0xff;
        int endG = (endInt >> 8) & 0xff;
        int endB = endInt & 0xff;

        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
                (int)((startR + (int)(fraction * (endR - startR))) << 16) |
                (int)((startG + (int)(fraction * (endG - startG))) << 8) |
                (int)((startB + (int)(fraction * (endB - startB))));
    }

    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }

    /**
     * 修正位置
     * @param left
     * @return
     */
    private int fixLeft(int left) {
        if(left < 0){
            return 0;
        }else if (left > mRange) {
            return mRange;
        }
        return left;
    }

    /**
     * 關閉面板
     */
    public void close() {
        close(true);
    }

    public void close(boolean isSmooth){
        int finalLeft = 0;
        if(isSmooth){
            // 走平滑動畫
            // 1. 觸發一個平滑動畫.
            if(mHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)){
                // 如果當前位置不是指定的最終位置. 返回true
                // 需要重繪界面, 一定要傳 子View 所在的容器
                ViewCompat.postInvalidateOnAnimation(this);
            }

        }else {
            System.out.println("open");
            mMainContent.layout(finalLeft, 0, finalLeft + mWidth, 0 + mHeight);
        }
    }


    /**
     * 打開面板
     */
    public void open() {
        open(true);
    }

    public void open(boolean isSmooth){
        int finalLeft = mRange;
        if(isSmooth){
            // 走平滑動畫
            // 1. 觸發一個平滑動畫.
            if(mHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)){
                // 如果當前位置不是指定的最終位置. 返回true
                // 需要重繪界面, 一定要傳 子View 所在的容器
                ViewCompat.postInvalidateOnAnimation(this);
            }

        }else {
            System.out.println("open");
            mMainContent.layout(finalLeft, 0, finalLeft + mWidth, 0 + mHeight);
        }
    }

    //2. 維持動畫的繼續, 高頻率調用.
    @Override
    public void computeScroll() {
        super.computeScroll();

        if(mHelper.continueSettling(true)){
            // 如果當前位置還沒有移動到最終位置. 返回true.需要繼續重繪界面
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    private ViewGroup mLeftContent;
    private ViewGroup mMainContent;
    private int mHeight;
    private int mWidth;
    private int mRange;

    // 2. 轉交觸摸事件
    public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
        // 由 ViewDragHelper 判斷觸摸事件是否該攔截
        return mHelper.shouldInterceptTouchEvent(ev);
    };

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 由 ViewDragHelper 處理事件

        try {
            mHelper.processTouchEvent(event);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        // 當控件尺寸變化的時候調用
        mHeight = getMeasuredHeight();
        mWidth = getMeasuredWidth();

        // 計算拖拽的範圍
        mRange = (int) (mWidth * 0.6f);

        System.out.println("mWidth: " + mWidth + " mHeight: " + mHeight + " mRange: " + mRange);

    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        // 代碼的健壯性.
        // 孩子至少倆
        if(getChildCount() < 2){
            throw new IllegalStateException("Your viewgroup must have 2 children. 子View至少有兩個!");
        } 
        // 孩子必須是ViewGroup的子類
        if(!(getChildAt(0) instanceof ViewGroup) || !(getChildAt(1) instanceof ViewGroup)){
            throw new IllegalArgumentException("Child must be an instance of ViewGroup . 孩子必須是ViewGroup的子類");
        }

        // Github
        mLeftContent = (ViewGroup) getChildAt(0);
        mMainContent = (ViewGroup) getChildAt(1);
    }

}

側拉刪除

/**
 * 側拉刪除
 * @author poplar
 *
 */
public class SwipeLayout extends FrameLayout {

    public static enum Status {
        Close, Open, Swiping
    }

    public interface OnSwipeListener{
        void onClose(SwipeLayout layout);
        void onOpen(SwipeLayout layout);

        void onStartOpen(SwipeLayout layout);
        void onStartClose(SwipeLayout layout);
    }

    private Status status = Status.Close;
    private OnSwipeListener onSwipeListener;

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    public OnSwipeListener getOnSwipeListener() {
        return onSwipeListener;
    }

    public void setOnSwipeListener(OnSwipeListener onSwipeListener) {
        this.onSwipeListener = onSwipeListener;
    }

    private ViewDragHelper mHelper;

    public SwipeLayout(Context context) {
        this(context, null);
    }

    public SwipeLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SwipeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        // 1. 創建ViewDragHelper
        mHelper = ViewDragHelper.create(this, callback);
    }

    // 3. 重寫回調的方法
    ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return true;
        }

        public int getViewHorizontalDragRange(View child) {
            return mRange;
        };

        public int clampViewPositionHorizontal(View child, int left, int dx) {
            // 返回的值決定了將要移動到的位置.
            if(child == mFrontView){
                if(left < - mRange){
                    // 限定左範圍
                    return - mRange;
                }else if (left > 0) {
                    // 限定右範圍
                    return 0;
                }
            }else if (child == mBackView) {
                if(left < mWidth - mRange){
                    // 限定左範圍
                    return mWidth - mRange;
                }else if (left > mWidth) {
                    // 限定右範圍
                    return mWidth;
                }
            }
            return left;
        };

        // 位置發生改變的時候, 把水平方向的偏移量傳遞給另一個佈局
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            if(changedView == mFrontView){
                // 拖拽的是前佈局,  把剛剛發生的 偏移量dx 傳遞給 後佈局
                mBackView.offsetLeftAndRight(dx);
            } else if (changedView == mBackView) {
                // 拖拽的是後佈局,  把剛剛發生的 偏移量dx 傳遞給 前佈局
                mFrontView.offsetLeftAndRight(dx);
            }

            dispatchDragEvent();

            invalidate();
        };

        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            // 鬆手時候會被調用

            // xvel 向右+, 向左-
            if(xvel == 0 && mFrontView.getLeft() < - mRange * 0.5f){
                open();
            }else if(xvel < 0){
                open();
            }else {
                close();
            }
        };
    };
    private View mBackView;
    private View mFrontView;
    private int mRange;
    private int mWidth;
    private int mHeight;

    // 2. 轉交觸摸事件攔截判斷, 處理觸摸事件
    public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
        return mHelper.shouldInterceptTouchEvent(ev);
    };

    /**
     * 更新當前的狀態
     */
    protected void dispatchDragEvent() {


        Status lastStatus = status;
        // 獲取最新的狀態
        status = updateStatus();

        // 狀態改變的時候, 調用監聽裏的方法
        if(lastStatus != status && onSwipeListener != null){
            if(status == Status.Open){
                onSwipeListener.onOpen(this);
            }else if (status == Status.Close) {
                onSwipeListener.onClose(this);
            }else if (status == Status.Swiping) {
                if(lastStatus == Status.Close){
                    onSwipeListener.onStartOpen(this);
                }else if (lastStatus == Status.Open) {
                    onSwipeListener.onStartClose(this);
                }
            }
        }



    }

    private Status updateStatus() {
        int left = mFrontView.getLeft();
        if(left == 0){
            return Status.Close;
        }else if (left == -mRange) {
            return Status.Open;
        }

        return Status.Swiping;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        // 維持平滑動畫繼續
        if(mHelper.continueSettling(true)){
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    /**
     * 關閉
     */
    protected void close() {
        close(true);
    }
    public void close(boolean isSmooth){

        int finalLeft = 0;
        if(isSmooth){
            // 觸發平滑動畫
            if(mHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)){
                ViewCompat.postInvalidateOnAnimation(this);
            }

        }else {
            layoutContent(false);
        }
    }

    /**
     * 打開
     */
    protected void open() {
        open(true);
    }
    public void open(boolean isSmooth){

        int finalLeft = -mRange;
        if(isSmooth){
            // 觸發平滑動畫
            if(mHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)){
                ViewCompat.postInvalidateOnAnimation(this);
            }

        }else {
            layoutContent(false);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        try {
            mHelper.processTouchEvent(event);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return true;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // 默認是關閉狀態
        layoutContent(false);
    }

    /**
     * 根據當前的開啓狀態擺放內容
     * @param isOpen
     */
    private void layoutContent(boolean isOpen) {
        // 設置前佈局位置
        Rect rect = computeFrontRect(isOpen);
        mFrontView.layout(rect.left, rect.top, rect.right, rect.bottom);
        // 根據前佈局位置設置後佈局位置
        Rect backRect = computeBackRectViaFront(rect);
        mBackView.layout(backRect.left, backRect.top, backRect.right, backRect.bottom);

        // 把任意佈局順序調整到最上
        bringChildToFront(mFrontView);
    }

    /**
     * 計算後佈局的矩形區域
     * @param rect
     * @return
     */
    private Rect computeBackRectViaFront(Rect rect) {
        int left = rect.right;
        return new Rect(left, 0, left + mRange , 0 + mHeight);
    }

    /**
     * 計算前佈局的矩形區域
     * @param isOpen
     * @return
     */
    private Rect computeFrontRect(boolean isOpen) {
        int left = 0;
        if(isOpen){
            left = -mRange;
        }
        return new Rect(left, 0, left + mWidth, 0 + mHeight);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mRange = mBackView.getMeasuredWidth();

        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();

    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        mBackView = getChildAt(0);
        mFrontView = getChildAt(1);

    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章