安卓自定義View——實現相似微信狀態按鈕的文字與邊框漸變

在這裏插入圖片描述

由於項目須要只有邊框和文字漸變效果得按鈕,嘗試過用drawable文件設置,發現只能設置全背景漸變效果,也懶得用其餘方式,就本身動手畫了一個,畫完以後就把全背竟的也給加上了,由於這個項目相似這種的按鈕還很多,避免了挨個去建立drawable文件了。當我寫完以後,微信就更新了8.0發現這個按鈕跟微信8.0的狀態按鈕挺像,就起了這麼一個標題java

自定義View相關的代碼

最開始畫這個控件的時候,繼承的是View,本身用畫筆去繪製文字,發現居中一直有問題,算了一下午的文字居中發現沒有任何屌用,後來小夥伴跟我說,那你就繼承TextView,而後直接設置文字居中不就行了,當時感受本身的智商被人摁在地上摩擦。。。。。android

package com.ltb.myroundtextlibrary.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;

import androidx.annotation.Nullable;

import com.ltb.myroundtextlibrary.R;
import com.ltb.myroundtextlibrary.utils.SizeUtils;

public class MyRoundTextView extends androidx.appcompat.widget.AppCompatTextView { 

    //邊框畫筆
    private Paint mPaint;

    private Context mContext;

    private int mHeight;
    private int mWidth;
    //文字寬度
    private int txtWidth;
    //文字長度
    private int txtLength;

    private int strokeWidth = SizeUtils.dp2px(1);

    //起始顏色
    private int startColor;
    //結束顏色
    private int endColor;
    //文字
    private String txt = "";

    private String content1;
    private String content2;

    //判斷是不是全漸變背景
    private boolean isFull = false;

    public MyRoundTextView(Context context) { 
        super(context);
    }

    public MyRoundTextView(Context context, @Nullable AttributeSet attrs) { 
        super(context, attrs);
        init(attrs);
    }

    public MyRoundTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) { 
        mContext = getContext();

        startColor = mContext.getResources().getColor(R.color.colorForgetPwd);
        endColor = mContext.getResources().getColor(R.color.colorForgetPwd);

        //獲取自定義參數
        TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.MyRoundTextView, 0, 0);
        //獲取起始顏色
        startColor = typedArray.getColor(R.styleable.MyRoundTextView_m_gradient_color_start, startColor);
        //獲取結束顏色
        endColor = typedArray.getColor(R.styleable.MyRoundTextView_m_gradient_color_end, endColor);
        //獲取邊框粗細
        strokeWidth = (int) typedArray.getDimension(R.styleable.MyRoundTextView_m_stroke_width, strokeWidth);
        //是否填滿
        isFull = typedArray.getBoolean(R.styleable.MyRoundTextView_m_is_full, isFull);

        txt = getText().toString();
        typedArray.recycle();
        //配置邊框畫筆
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(strokeWidth);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
        mWidth = measureWidth(widthMeasureSpec);
        mHeight = measureHeight(heightMeasureSpec);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) { 
        //判斷是否漸變鋪滿
        mPaint.setStyle(!isFull ? Paint.Style.STROKE : Paint.Style.FILL);

        //設置漸變參數
        LinearGradient linearGradient = new LinearGradient(
                strokeWidth,
                strokeWidth,
                mWidth,
                mHeight,
                endColor,
                startColor,
                Shader.TileMode.CLAMP);

        mPaint.setShader(linearGradient);

        RectF r2 = new RectF();
        r2.set(strokeWidth * 2,
                strokeWidth * 2,
                mWidth - strokeWidth * 4,
                mHeight - strokeWidth * 4);

        //繪製圓角
        canvas.drawRoundRect(r2, SizeUtils.dp2px(20), SizeUtils.dp2px(20), mPaint);

        super.onDraw(canvas);

        if (txtLength == 0) { 
            if (!txtIsEmpty(txt)) { 
                setTextViewStyles();
            }
        }

    }

    private boolean txtIsEmpty(String txt) { 
        return TextUtils.isEmpty(txt);
    }

    /** * 設置文字漸變 */
    private void setTextViewStyles() { 
        txtWidth = (int) getPaint().measureText(txt);
        txtLength = txt.length();

        if (getGravity() != Gravity.CENTER) { 
            setGravity(Gravity.CENTER);
        }

        //當全漸變背景時候 默認設置文字顏色爲白色 不設置成爲漸變
        if (!isFull) { 

            int x0 = mWidth / 2 - txtWidth / 2;
            int y0 = getBottom();

            int x1 = mWidth / 2 + txtWidth / 2;

            LinearGradient txtLinearGradient = new LinearGradient(
                    x0,
                    y0,
                    x1,
                    y0,
                    endColor,
                    startColor,
                    Shader.TileMode.CLAMP
            );
            getPaint().setShader(txtLinearGradient);
        } else { 
            getPaint().setShader(null);
            setTextColor(Color.WHITE);
            getPaint().setColor(Color.WHITE);
        }
        if (!TextUtils.isEmpty(content1) || !TextUtils.isEmpty(content2)) { 
            txt = isFull ? content1 : content2;
        }
        setText(txt);
        invalidate();
    }

    /** * 設置狀態改變先後的文字信息 * * @param content1 點擊前展現的文字 * @param content2 點擊後展現的文字 */
    public void setContent(String content1, String content2) { 
        this.content1 = content1;
        this.content2 = content2;
    }

    /** * 設置是點擊後的狀態仍是點擊前的狀態 * * @param full 是否充滿背景 */
    public void setFull(boolean full) { 
        if (TextUtils.isEmpty(content1) || TextUtils.isEmpty(content2)) { 
            throw new NullPointerException("content1 and content2 must not be null!!");
        }
        isFull = full;
        txtLength = 0;
        invalidate();
    }

    public boolean isFull() { 
        return isFull;
    }

    /** * 根據模式計算高度 * * @param heightMeasureSpec */
    private int measureHeight(int heightMeasureSpec) { 
        //獲取模式
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //獲取高度
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int height = 0;
        switch (heightMode) { 
            case MeasureSpec.EXACTLY://固定值或者match_content
                height = heightSize + strokeWidth * 2;
                break;
        }
        return height;
    }

    /** * 根據模式計算寬度 * * @param widthMeasureSpec */
    private int measureWidth(int widthMeasureSpec) { 
        //獲取模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        //獲取寬度
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int width = 0;
        switch (widthMode) { 
            case MeasureSpec.EXACTLY://固定值或者match_content
                width = widthSize + strokeWidth * 2;
                break;
        }
        return width;
    }

}

相關輔助類代碼——SizeUtils

package com.ltb.myroundtextlibrary.utils;

import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;

public class SizeUtils { 
    private SizeUtils() { 
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /** * Value of dp to value of px. * * @param dpValue The value of dp. * @return value of px */
    public static int dp2px(final float dpValue) { 
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /** * Value of px to value of dp. * * @param pxValue The value of px. * @return value of dp */
    public static int px2dp(final float pxValue) { 
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    /** * Value of sp to value of px. * * @param spValue The value of sp. * @return value of px */
    public static int sp2px(final float spValue) { 
        final float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    /** * Value of px to value of sp. * * @param pxValue The value of px. * @return value of sp */
    public static int px2sp(final float pxValue) { 
        final float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }

    /** * Converts an unpacked complex data value holding a dimension to its final floating * point value. The two parameters <var>unit</var> and <var>value</var> * are as in {@link TypedValue#TYPE_DIMENSION}. * * @param value The value to apply the unit to. * @param unit The unit to convert from. * @return The complex floating point value multiplied by the appropriate * metrics depending on its unit. */
    public static float applyDimension(final float value, final int unit) { 
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        switch (unit) { 
            case TypedValue.COMPLEX_UNIT_PX:
                return value;
            case TypedValue.COMPLEX_UNIT_DIP:
                return value * metrics.density;
            case TypedValue.COMPLEX_UNIT_SP:
                return value * metrics.scaledDensity;
            case TypedValue.COMPLEX_UNIT_PT:
                return value * metrics.xdpi * (1.0f / 72);
            case TypedValue.COMPLEX_UNIT_IN:
                return value * metrics.xdpi;
            case TypedValue.COMPLEX_UNIT_MM:
                return value * metrics.xdpi * (1.0f / 25.4f);
        }
        return 0;
    }

    /** * Force get the size of view. * <p>e.g.</p> * <pre> * SizeUtils.forceGetViewSize(view, new SizeUtils.OnGetSizeListener() { * Override * public void onGetSize(final View view) { * view.getWidth(); * } * }); * </pre> * * @param view The view. * @param listener The get size listener. */
    public static void forceGetViewSize(final View view, final OnGetSizeListener listener) { 
        view.post(new Runnable() { 
            @Override
            public void run() { 
                if (listener != null) { 
                    listener.onGetSize(view);
                }
            }
        });
    }

    /** * Return the width of view. * * @param view The view. * @return the width of view */
    public static int getMeasuredWidth(final View view) { 
        return measureView(view)[0];
    }

    /** * Return the height of view. * * @param view The view. * @return the height of view */
    public static int getMeasuredHeight(final View view) { 
        return measureView(view)[1];
    }

    /** * Measure the view. * * @param view The view. * @return arr[0]: view's width, arr[1]: view's height */
    public static int[] measureView(final View view) { 
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        if (lp == null) { 
            lp = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT
            );
        }
        int widthSpec = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
        int lpHeight = lp.height;
        int heightSpec;
        if (lpHeight > 0) { 
            heightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY);
        } else { 
            heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        }
        view.measure(widthSpec, heightSpec);
        return new int[]{ view.getMeasuredWidth(), view.getMeasuredHeight()};
    }

    ///
    // interface
    ///

    public interface OnGetSizeListener { 
        void onGetSize(View view);
    }
}

自定義屬性代碼

<declare-styleable name="MyRoundTextView">
        <!--起始顏色-->
        <attr name="m_gradient_color_start" format="color|reference" />
        <!--結束顏色-->
        <attr name="m_gradient_color_end" format="color|reference" />
        <!--線條寬度-->
        <attr name="m_stroke_width" format="integer|dimension" />
        <!--是否漸變背景充滿-->
        <attr name="m_is_full" format="boolean" />
    </declare-styleable>

Demo的佈局代碼

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity">

    <com.ltb.myroundtextlibrary.widget.MyRoundTextView android:id="@+id/btn1" android:layout_width="300dp" android:layout_height="40dp" android:layout_centerInParent="true" android:layout_gravity="center" android:layout_marginLeft="30dp" android:layout_marginTop="30dp" android:layout_marginRight="30dp" android:text="註冊" app:m_gradient_color_end="@color/colorEnd" app:m_gradient_color_start="@color/colorStart" />

    <com.ltb.myroundtextlibrary.widget.MyRoundTextView android:id="@+id/btn2" android:layout_width="300dp" android:layout_height="40dp" android:layout_centerInParent="true" android:layout_gravity="center" android:layout_marginLeft="30dp" android:layout_marginTop="20dp" android:layout_marginRight="30dp" android:gravity="center" android:text="確認" app:m_gradient_color_end="@color/colorEnd" app:m_gradient_color_start="@color/colorStart" app:m_is_full="true" />

</LinearLayout>

源碼地址git

共同開發,共同進步,歡迎你們Start!!!github

相關文章
相關標籤/搜索