由于项目须要只有边框和文字渐变效果得按钮,尝试过用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