1.自定义属性
新建attrs.xml文件(res->values->attrs.xml),定义要自定义的TextView属性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyTextView"> <!--name 属性名称 format 格式--> <attr name="myText" format="string" /> <attr name="myTextColor" format="color" /> <attr name="myTextSize" format="dimension" /> </declare-styleable> </resources>
2.在布局中使用,对比系统TextView
<?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.future.coding.custom_view.MyTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" app:myText="American Presidential Election" app:myTextColor="#D81B60" app:myTextSize="16sp" /> <!--与上面自定义TextView的对比--> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:text="American Presidential Election" android:textColor="#D81B60" android:textSize="16sp" /> </LinearLayout>
3.通过继承View,实现自定义TextView
public class MyTextView extends View { private String mText; private int mTextSize = 16; private int mTextColor = Color.BLACK; private Paint mPaint; private static final String TAG = "MyTextView"; //在代码中使用 public MyTextView(Context context) { this(context, null); } //在布局layout中使用 public MyTextView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } //在布局layout中使用,但会有style public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取自定义属性 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTextView); mText = typedArray.getString(R.styleable.MyTextView_myText); mTextColor = typedArray.getColor(R.styleable.MyTextView_myTextColor, mTextColor); Log.d(TAG, "MyTextView: " + sp2px(mTextSize)); mTextSize = typedArray.getDimensionPixelSize(R.styleable.MyTextView_myTextSize, sp2px(mTextSize)); /** * 解析:TypedArray 为什么需要调用recycle() * https://blog.csdn.net/Monicabg/article/details/45014327 */ typedArray.recycle(); mPaint = new Paint(); mPaint.setAntiAlias(true);//抗锯齿 mPaint.setTextSize(mTextSize); mPaint.setColor(mTextColor); } private int sp2px(int sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } /** * 自定义View的测量方法 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //指定控件的宽高,需要测量 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (widthMode == MeasureSpec.AT_MOST) {//在布局中指定了wrap_content Rect bounds = new Rect(); mPaint.getTextBounds(mText, 0, mText.length(), bounds); widthSize = bounds.width() + getPaddingLeft() + getPaddingRight(); } else if (widthMode == MeasureSpec.EXACTLY) {//在布局中指定了确定的值 比如:100dp / match_parent } else if (widthMode == MeasureSpec.UNSPECIFIED) {//尽可能的大,很少能用到 //ListView、ScrollView在测量子布局的时候会用 } if (heightMode == MeasureSpec.AT_MOST) {//在布局中指定了wrap_content Rect bounds = new Rect(); mPaint.getTextBounds(mText, 0, mText.length(), bounds); heightSize = bounds.height() + getPaddingTop() + getPaddingBottom(); } else if (heightMode == MeasureSpec.EXACTLY) {//在布局中指定了确定的值 比如:100dp / match_parent } else if (heightMode == MeasureSpec.UNSPECIFIED) {//尽可能的大,很少能用到 //ListView、ScrollView在测量子布局的时候会用 } setMeasuredDimension(widthSize, heightSize); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /** * 自定义View之绘图篇(四):baseLine和FontMetrics * https://blog.csdn.net/u012551350/article/details/51361778 */ Paint.FontMetrics fontMetrics = mPaint.getFontMetrics(); //中心线到基线的距离 int dy = (int) ((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom); //得到基线(BaseLine) int baseLine = getHeight() / 2 + dy; int x = getPaddingLeft(); canvas.drawText(mText, x, baseLine, mPaint); } }
--END