Android自定義View--使用ViewAnimator實現一個提交按鈕

效果圖

這裏寫圖片描述

準備知識

SendCommentButton

新建SendCommentButton繼承自ViewAnimator,而ViewAnimator是繼承自FrameLayout的。

public class SendCommentButton extends ViewAnimator{

        //通過代碼new執行該方法
    public SendCommentButton(Context context) {
        super(context);
        init();
    }


    //通過xml引入會執行該方法
    public SendCommentButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
}

init()方法

 private void init() {
        LayoutInflater.from(getContext()).inflate(R.layout.view_send_comment_button, this, true);
    }

把佈局依附到該控件上
R.layout.view_send_comment_button佈局

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
        android:id="@+id/tvSend"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="SEND"
        android:textColor="#ffffff"
        android:textSize="12sp" />

    <TextView
        android:id="@+id/tvDone"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="✓"
        android:textColor="#ffffff"
        android:textSize="12sp" />
</merge>

執行init()方法之後會回調ViewAnimatoraddView方法,我們來看看ViewAnimator是怎麼處理的

    ......

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        if (getChildCount() == 1) {
            child.setVisibility(View.VISIBLE);
        } else {
            child.setVisibility(View.GONE);
        }
        if (index >= 0 && mWhichChild >= index) {
            // Added item above current one, increment the index of the displayed child
            setDisplayedChild(mWhichChild + 1);
        }
    }
    ......

從上面可以看到把第一個子View設置爲可見,其他設置GONE,也就是說merge設置爲GONE了。
接下來我們補充SendCommentButton其他邏輯代碼

public class SendCommentButton extends ViewAnimator implements View.OnClickListener {
    public static final int STATE_SEND = 0;
    public static final int STATE_DONE = 1;
    private static final long RESET_STATE_DELAY_MILLIS = 2000;

    private int currentState;
    private OnSendClickListener onSendClickListener;


    //通過代碼new執行該方法
    public SendCommentButton(Context context) {
        super(context);
        init();
    }


    //通過xml引入會執行該方法
    public SendCommentButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        LayoutInflater.from(getContext()).inflate(R.layout.view_send_comment_button, this, true);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        currentState = STATE_SEND;
        setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        if (onSendClickListener != null) {
            onSendClickListener.onSendClickListener(this);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        removeCallbacks(revertStateRunnable);
        super.onDetachedFromWindow();
    }

    public void setCurrentState(int state) {
        if (state == currentState) {
            return;
        }
        currentState = state;
        if (state == STATE_DONE) {
            setEnabled(false);
            postDelayed(revertStateRunnable, RESET_STATE_DELAY_MILLIS);
            setInAnimation(getContext(), R.anim.slide_in_done);
            setOutAnimation(getContext(), R.anim.slide_out_send);
        } else if (state == STATE_SEND) {
            setEnabled(true);
            setInAnimation(getContext(), R.anim.slide_in_send);
            setOutAnimation(getContext(), R.anim.slide_out_done);
        }
        showNext();
    }

    private Runnable revertStateRunnable = new Runnable() {
        @Override
        public void run() {
            setCurrentState(STATE_SEND);
        }
    };

    public interface OnSendClickListener {
        void onSendClickListener(View v);
    }

    public void setOnSendClickListener(OnSendClickListener onSendClickListener) {
        this.onSendClickListener = onSendClickListener;
    }



}

上面定義了兩種狀態,暴露了一個接口和設置了進入和進出動畫。這裏解析一下ViewAnimatorshowNext()方法,在ViewAnimator的源碼中

    ......
 public void showNext() {
        setDisplayedChild(mWhichChild + 1);
    }
    ......

showNext會調用setDisplayedChild方法

 /**
     * Sets which child view will be displayed.
     *設置哪個子View將被顯示
     * @param whichChild the index of the child view to display 要顯示子view的下標
     */
 public void setDisplayedChild(int whichChild) {
        mWhichChild = whichChild;
        if (whichChild >= getChildCount()) {
            mWhichChild = 0;
        } else if (whichChild < 0) {
            mWhichChild = getChildCount() - 1;
        }
        boolean hasFocus = getFocusedChild() != null;
        // This will clear old focus if we had it
        showOnly(mWhichChild);
        if (hasFocus) {
            // Try to retake focus if we had it
            requestFocus(FOCUS_FORWARD);
        }
    }

setDisplayedChild中的主要展示邏輯交給了showOnly方法

 /**
     * Shows only the specified child. The other displays Views exit the screen,
     * optionally with the with the {@link #getOutAnimation() out animation} and
     * the specified child enters the screen, optionally with the
     * {@link #getInAnimation() in animation}.
     *
     * @param childIndex The index of the child to be shown.
     * @param animate Whether or not to use the in and out animations, defaults
     *            to true.
     */
    void showOnly(int childIndex, boolean animate) {
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (i == childIndex) {
                if (animate && mInAnimation != null) {
                    child.startAnimation(mInAnimation);
                }
                child.setVisibility(View.VISIBLE);
                mFirstTime = false;
            } else {
                if (animate && mOutAnimation != null && child.getVisibility() == View.VISIBLE) {
                    child.startAnimation(mOutAnimation);
                } else if (child.getAnimation() == mInAnimation)
                    child.clearAnimation();
                child.setVisibility(View.GONE);
            }
        }
    }

    /**
     * Shows only the specified child. The other displays Views exit the screen
     * with the {@link #getOutAnimation() out animation} and the specified child
     * enters the screen with the {@link #getInAnimation() in animation}.
         *正在展示的view退出屏幕,展示指定的view,指定的view以getInAnimation的動畫進入,退出的view以getOutAnimation的動畫退出
     * @param childIndex The index of the child to be shown.
     */
    void showOnly(int childIndex) {
        final boolean animate = (!mFirstTime || mAnimateFirstTime);
        showOnly(childIndex, animate);
    }

重點看下這段代碼

  for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (i == childIndex) {
                if (animate && mInAnimation != null) {
                    child.startAnimation(mInAnimation);
                }
                child.setVisibility(View.VISIBLE);
                mFirstTime = false;
            } else {
                if (animate && mOutAnimation != null && child.getVisibility() == View.VISIBLE) {
                    child.startAnimation(mOutAnimation);
                } else if (child.getAnimation() == mInAnimation)
                    child.clearAnimation();
                child.setVisibility(View.GONE);
            }
        }

從上面可以看到ViewAnimator把我們之前設置的setInAnimation(getContext(), R.anim.slide_in_done);
setOutAnimation(getContext(), R.anim.slide_out_send);
應用到進入和退出View上。

下載地址

參考

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