一步一步走向自定義控件

上個月搭配了自己的開發環境。
有了自己的環境=有了環境想搞事情。
然後這個月就想搞搞事情了。
自定義控件確實很強大,看到靈機上的OppositeLayout不禁深深欽佩。
其實一直想自定義控件,但是到底怎麼自定義的呢,需要怎麼樣去學呢?我也不怎麼曉得。
我比較笨–>我的想法是一步一步慢慢探索,摸着石頭過河。–>會用別人定義的–>看懂別人的代碼–>模仿着來寫–>寫自己的


大神博客



自定義dialog

這個自定義dialog的靈感來自於加載動畫–>
正常來說我們可以先寫一個BaseDialog

BaseDialog

/**
 * <b>Project:</b> ${file_name}<br>
 * <b>Create Date:</b> 2017/3/18<br>
 * <b>Author:</b> Tongson<br>
 * <b>Description:</b> Tongson's Dialog的爸爸 <br>
 */

public abstract class TongsonBaseDialog extends Dialog {

    private Context mContext;

    public TongsonBaseDialog(Context context) {
        super(context, R.style.TongsonBaseDialogStyle);
        mContext = context;
        initEnterExitAnim();
    }

    public TongsonBaseDialog(Context context, int theme) {
        super(context, theme);
        mContext = context;
        initEnterExitAnim();
    }

    /**
     * 進場動畫
     */
    public void initEnterExitAnim() {
        Window dialogWindow = getWindow();
        dialogWindow.setGravity(Gravity.CENTER); // 此處可以設置dialog顯示的位置爲居中
        dialogWindow.setWindowAnimations(R.style.dialogWindowAnim);// 添加動畫效果
        int widthPixels;
        int heightPixels;
        WindowManager.LayoutParams layoutParams = dialogWindow.getAttributes();
        DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
        widthPixels = dm.widthPixels;
        heightPixels = dm.heightPixels;
        layoutParams.height = heightPixels;
        layoutParams.width = widthPixels;
        dialogWindow.setAttributes(layoutParams);
    }
}

BaseDialog:身爲一個dialog的爸爸應該怎麼去寫?我的話開始的時候寫得不多,感覺不需要寫太多–>先寫孩紙,然後把孩紙的共同點交給爸爸。爸爸是有很多共同點的存在,而孩紙是青出於藍而勝於藍的存在。

R.style.dialogWindowAnim:這個就是動畫效果。先講完dialog的,動畫等等再說。

ChildrenDialog

然後我們來看看這個孩紙的dialog怎麼去寫呢?
在項目裏,我們有各種各樣的dialog
而且根據需求會寫各種各樣的dialog,那我們就寫唄

列舉一下項目中我們都有用到的dialog

  • LoadingDialog
  • GetPhotoDialog
  • TipsDialog
  • PayDialog
  • PermissionDialog
  • ExitDialog
  • DownloadDialog

等等

關鍵代碼

        setContentView(R.layout.dialog_layout);

然後自己想幹嘛幹嘛。哈。哈。

Animation

感覺是很重要的,Dialog飛來飛去的炫酷感會使用戶飛。

怎麼加入自己想要的動畫呢?

這個開始我也不大會,然後就找別人的代碼啊。

https://github.com/gepriniusce/NiftyDialogEffects
https://github.com/gepriniusce/NiftyNotification

核心代碼

        this.setOnShowListener(new OnShowListener() {
            @Override
            public void onShow(DialogInterface dialogInterface) {

                mLinearLayoutView.setVisibility(View.VISIBLE);
                if (type == null) {
                    type = Effectstype.Slidetop;
                }
                start(type);


            }
        });
    private void start(Effectstype type) {
        BaseEffects animator = type.getAnimator();
        if (mDuration != -1) {
            animator.setDuration(Math.abs(mDuration));
        }
        animator.start(mRelativeLayoutView);
    }

其實原理很簡單,dialog有一個OnShowListener,然後再start動畫!

Dialog

其實自定義Dialog的話,還是要多看看Dialog這個類的代碼,我們重寫一下方法就行了是不是好簡單!?


自定義TextView

對於自定義的TextView感覺比較有用的是setTypeface(加載自己的字體庫)

直接看代碼吧–>

/**
 * <b>Project:</b> ${file_name}<br>
 * <b>Create Date:</b> 2017/4/4<br>
 * <b>Author:</b> Tongson<br>
 * <b>Description:</b>  自定義字體庫TextView <br>
 */
public class TongsonTextView extends TextView {

    public static Typeface myTypeface;

    public TongsonTextView(Context context) {
        super(context);
        setTTFstyle();
    }

    public TongsonTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setTTFstyle();
    }

    public TongsonTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setTTFstyle();
    }

    private void setTTFstyle() {
        Typeface typeface= getTtf();
        if (null != typeface) {
            setTypeface(typeface);
        }
    }


    /**
     * 加載字體庫
     * <p>
     * 此處應該在Application中
     *
     * @param context
     */
    public static void loadTtf(Context context) {
        Typeface fontFace = null;
        try {
            fontFace = Typeface.createFromAsset(context.getAssets(), "fonts/msyhl.ttc");
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        if (fontFace != null) {
            myTypeface = fontFace;
        }
    }

    /**
     * 獲取字體
     *
     * @return
     */
    private Typeface getTtf() {
        return myTypeface;
    }
}

核心代碼

setTypeface(typeface);

自定義Button

Button的話個人比較喜歡StateButton
https://github.com/niniloveyou/StateButton

先看懂大神代碼吧–>

values中的attrs –>自定義屬性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    </declare-styleable>
        <declare-styleable name="StateButton">
        <attr name="xxx" format="xxx|reference"/>
    </declare-styleable>
</resources>

獲取attrs屬性
利用這些屬性與GradientDrawable對控件的屬性做設置
真的寫得不錯的代碼,覺得很值得我學習

StateButton

public class StateButton extends AppCompatButton {

    //text color
    private int mNormalTextColor = 0;
    private int mPressedTextColor = 0;
    private int mUnableTextColor = 0;
    ColorStateList mTextColorStateList;

    //animation duration
    private int mDuration = 0;

    //radius
    private float mRadius = 0;
    private boolean mRound;

    //stroke
    private float mStrokeDashWidth = 0;
    private float mStrokeDashGap = 0;
    private int mNormalStrokeWidth = 0;
    private int mPressedStrokeWidth = 0;
    private int mUnableStrokeWidth = 0;
    private int mNormalStrokeColor = 0;
    private int mPressedStrokeColor = 0;
    private int mUnableStrokeColor = 0;

    //background color
    private int mNormalBackgroundColor = 0;
    private int mPressedBackgroundColor = 0;
    private int mUnableBackgroundColor = 0;

    private GradientDrawable mNormalBackground;
    private GradientDrawable mPressedBackground;
    private GradientDrawable mUnableBackground;

    private int[][] states;

    StateListDrawable mStateBackground;

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

    public StateButton(Context context, AttributeSet attrs) {
        this(context, attrs, android.support.v7.appcompat.R.attr.buttonStyle);
    }

    public StateButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setup(attrs);
    }

    private void setup(AttributeSet attrs) {

        states = new int[4][];

        Drawable drawable = getBackground();
        if(drawable != null && drawable instanceof StateListDrawable){
            mStateBackground = (StateListDrawable) drawable;
        }else{
            mStateBackground = new StateListDrawable();
        }

        mNormalBackground = new GradientDrawable();
        mPressedBackground = new GradientDrawable();
        mUnableBackground = new GradientDrawable();

        //pressed, focused, normal, unable
        states[0] = new int[] { android.R.attr.state_enabled, android.R.attr.state_pressed };
        states[1] = new int[] { android.R.attr.state_enabled, android.R.attr.state_focused };
        states[3] = new int[] { -android.R.attr.state_enabled};
        states[2] = new int[] { android.R.attr.state_enabled };

        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.StateButton);

        //get original text color as default
        //set text color
        mTextColorStateList = getTextColors();
        int mDefaultNormalTextColor = mTextColorStateList.getColorForState(states[2], getCurrentTextColor());
        int mDefaultPressedTextColor = mTextColorStateList.getColorForState(states[0], getCurrentTextColor());
        int mDefaultUnableTextColor = mTextColorStateList.getColorForState(states[3], getCurrentTextColor());
        mNormalTextColor = a.getColor(R.styleable.StateButton_normalTextColor, mDefaultNormalTextColor);
        mPressedTextColor = a.getColor(R.styleable.StateButton_pressedTextColor, mDefaultPressedTextColor);
        mUnableTextColor = a.getColor(R.styleable.StateButton_unableTextColor, mDefaultUnableTextColor);
        setTextColor();

        //set animation duration
        mDuration = a.getInteger(R.styleable.StateButton_animationDuration, mDuration);
        mStateBackground.setEnterFadeDuration(mDuration);
        mStateBackground.setExitFadeDuration(mDuration);

        //set background color
        mNormalBackgroundColor = a.getColor(R.styleable.StateButton_normalBackgroundColor, 0);
        mPressedBackgroundColor = a.getColor(R.styleable.StateButton_pressedBackgroundColor, 0);
        mUnableBackgroundColor = a.getColor(R.styleable.StateButton_unableBackgroundColor, 0);
        mNormalBackground.setColor(mNormalBackgroundColor);
        mPressedBackground.setColor(mPressedBackgroundColor);
        mUnableBackground.setColor(mUnableBackgroundColor);

        //set radius
        mRadius = a.getDimensionPixelSize(R.styleable.StateButton_radius, 0);
        mRound = a.getBoolean(R.styleable.StateButton_round, false);
        mNormalBackground.setCornerRadius(mRadius);
        mPressedBackground.setCornerRadius(mRadius);
        mUnableBackground.setCornerRadius(mRadius);

        //set stroke
        mStrokeDashWidth = a.getDimensionPixelSize(R.styleable.StateButton_strokeDashWidth, 0);
        mStrokeDashGap = a.getDimensionPixelSize(R.styleable.StateButton_strokeDashWidth, 0);
        mNormalStrokeWidth = a.getDimensionPixelSize(R.styleable.StateButton_normalStrokeWidth, 0);
        mPressedStrokeWidth = a.getDimensionPixelSize(R.styleable.StateButton_pressedStrokeWidth, 0);
        mUnableStrokeWidth = a.getDimensionPixelSize(R.styleable.StateButton_unableStrokeWidth, 0);
        mNormalStrokeColor = a.getColor(R.styleable.StateButton_normalStrokeColor, 0);
        mPressedStrokeColor = a.getColor(R.styleable.StateButton_pressedStrokeColor, 0);
        mUnableStrokeColor = a.getColor(R.styleable.StateButton_unableStrokeColor, 0);
        setStroke();

        //set background
        mStateBackground.addState(states[0], mPressedBackground);
        mStateBackground.addState(states[1], mPressedBackground);
        mStateBackground.addState(states[3], mUnableBackground);
        mStateBackground.addState(states[2], mNormalBackground);
        setBackgroundDrawable(mStateBackground);
        a.recycle();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setRound(mRound);
    }

    /****************** stroke color *********************/

    public void setNormalStrokeColor(@ColorInt int normalStrokeColor) {
        this.mNormalStrokeColor = normalStrokeColor;
        setStroke(mNormalBackground, mNormalStrokeColor, mNormalStrokeWidth);
    }

    public void setPressedStrokeColor(@ColorInt int pressedStrokeColor) {
        this.mPressedStrokeColor = pressedStrokeColor;
        setStroke(mPressedBackground, mPressedStrokeColor, mPressedStrokeWidth);
    }

    public void setUnableStrokeColor(@ColorInt int unableStrokeColor) {
        this.mUnableStrokeColor = unableStrokeColor;
        setStroke(mUnableBackground, mUnableStrokeColor, mUnableStrokeWidth);
    }

    public void setStateStrokeColor(@ColorInt int normal, @ColorInt int pressed, @ColorInt int unable){
        mNormalStrokeColor = normal;
        mPressedStrokeColor = pressed;
        mUnableStrokeColor = unable;
        setStroke();
    }

    /****************** stroke width *********************/

    public void setNormalStrokeWidth(int normalStrokeWidth) {
        this.mNormalStrokeWidth = normalStrokeWidth;
        setStroke(mNormalBackground, mNormalStrokeColor, mNormalStrokeWidth);
    }

    public void setPressedStrokeWidth(int pressedStrokeWidth) {
        this.mPressedStrokeWidth = pressedStrokeWidth;
        setStroke(mPressedBackground, mPressedStrokeColor, mPressedStrokeWidth);
    }

    public void setUnableStrokeWidth(int unableStrokeWidth) {
        this.mUnableStrokeWidth = unableStrokeWidth;
        setStroke(mUnableBackground, mUnableStrokeColor, mUnableStrokeWidth);
    }

    public void setStateStrokeWidth(int normal, int pressed, int unable){
        mNormalStrokeWidth = normal;
        mPressedStrokeWidth = pressed;
        mUnableStrokeWidth= unable;
        setStroke();
    }

    public void setStrokeDash(float strokeDashWidth, float strokeDashGap) {
        this.mStrokeDashWidth = strokeDashWidth;
        this.mStrokeDashGap = strokeDashWidth;
        setStroke();
    }

    private void setStroke(){
        setStroke(mNormalBackground, mNormalStrokeColor, mNormalStrokeWidth);
        setStroke(mPressedBackground, mPressedStrokeColor, mPressedStrokeWidth);
        setStroke(mUnableBackground, mUnableStrokeColor, mUnableStrokeWidth);
    }

    private void setStroke(GradientDrawable mBackground, int mStrokeColor, int mStrokeWidth) {
        mBackground.setStroke(mStrokeWidth, mStrokeColor, mStrokeDashWidth, mStrokeDashGap);
    }

    /********************   radius  *******************************/

    public void setRadius(@FloatRange(from = 0) float radius) {
        this.mRadius = radius;
        mNormalBackground.setCornerRadius(mRadius);
        mPressedBackground.setCornerRadius(mRadius);
        mUnableBackground.setCornerRadius(mRadius);
    }

    public void setRound(boolean round){
        this.mRound = round;
        int height = getMeasuredHeight();
        if(mRound){
            setRadius(height / 2f);
        }
    }

    public void setRadius(float[] radii){
        mNormalBackground.setCornerRadii(radii);
        mPressedBackground.setCornerRadii(radii);
        mUnableBackground.setCornerRadii(radii);
    }

    /********************  background color  **********************/

    public void setStateBackgroundColor(@ColorInt int normal, @ColorInt int pressed, @ColorInt int unable){
        mPressedBackgroundColor = normal;
        mNormalBackgroundColor = pressed;
        mUnableBackgroundColor = unable;
        mNormalBackground.setColor(mNormalBackgroundColor);
        mPressedBackground.setColor(mPressedBackgroundColor);
        mUnableBackground.setColor(mUnableBackgroundColor);
    }

    public void setNormalBackgroundColor(@ColorInt int normalBackgroundColor) {
        this.mNormalBackgroundColor = normalBackgroundColor;
        mNormalBackground.setColor(mNormalBackgroundColor);
    }

    public void setPressedBackgroundColor(@ColorInt int pressedBackgroundColor) {
        this.mPressedBackgroundColor = pressedBackgroundColor;
        mPressedBackground.setColor(mPressedBackgroundColor);
    }

    public void setUnableBackgroundColor(@ColorInt int unableBackgroundColor) {
        this.mUnableBackgroundColor = unableBackgroundColor;
        mUnableBackground.setColor(mUnableBackgroundColor);
    }

    /*******************alpha animation duration********************/
    public void setAnimationDuration(@IntRange(from = 0)int duration){
        this.mDuration = duration;
        mStateBackground.setEnterFadeDuration(mDuration);
    }

    /***************  text color   ***********************/

    private void setTextColor() {
        int[] colors = new int[] {mPressedTextColor, mPressedTextColor, mNormalTextColor, mUnableTextColor};
        mTextColorStateList = new ColorStateList(states, colors);
        setTextColor(mTextColorStateList);
    }

    public void setStateTextColor(@ColorInt int normal, @ColorInt int pressed, @ColorInt int unable){
        this.mNormalTextColor = normal;
        this.mPressedTextColor = pressed;
        this.mUnableTextColor = unable;
        setTextColor();
    }

    public void setNormalTextColor(@ColorInt int normalTextColor) {
        this.mNormalTextColor = normalTextColor;
        setTextColor();

    }

    public void setPressedTextColor(@ColorInt int pressedTextColor) {
        this.mPressedTextColor = pressedTextColor;
        setTextColor();
    }

    public void setUnableTextColor(@ColorInt int unableTextColor) {
        this.mUnableTextColor = unableTextColor;
        setTextColor();
    }
}

神一樣的控件自定義FrameLayout

這個就是公司的OppositeLayout
好強大,也是看懂代碼先,然後一步一步學習,代碼看多了,敲多了就會自己寫了嘛。

代碼不曉得貼不貼不出來好[捂臉]。


以上的算是自定義控件的一個入門吧。

自定義View

回過頭來,我們可以再看看大神的博客

大神博客

更詳細的入門教程。

總結

如果想真正弄明白怎樣自定義View,繪製各種View,還是要先把基礎搞好,參考優秀代碼,多敲大神demo,學着寫,自己寫。

先睡覺,以後再補充

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