基本实现 步骤
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
[ 3、重写onMesure ]
4、重写onDraw
在最新的andriod studio 中,选择自定义空间,它会生成相应的attr 文件,布局文件,属性实现文件,通过这三个文件,我们就可以设计自己的控件。
- <resources>
- <declare-styleable name="MyTextView">
- <attr name="codeString" format="string" />
- <attr name="codeDimension" format="dimension" />
- <attr name="codeColor" format="color" />
- <attr name="codeDrawable" format="color|reference" />
- </declare-styleable>
- </resources>
<resources>
<declare-styleable name="MyTextView">
<attr name="codeString" format="string" />
<attr name="codeDimension" format="dimension" />
<attr name="codeColor" format="color" />
<attr name="codeDrawable" format="color|reference" />
</declare-styleable>
</resources>
我们定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:
一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;不清楚的可以google一把。
然后在布局中声明我们的自定义View
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.selftextview.MainActivity">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingTop="@dimen/activity_vertical_margin"
- android:id="@+id/codes_linear">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1">
- <EditText
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/codes"
- android:hint="@string/codes"
- android:textSize="21sp"/>
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:weightSum="1">
- <com.selftextview.MyTextView
- android:layout_width="match_parent"
- android:layout_height="58dp"
- android:background="#c1c7c3"
- app:codeColor="#33b5e5"
- app:codeDimension="24sp"
- app:codeString="@string/codes"
- />
- </LinearLayout>
- </LinearLayout>
- <Button
- android:layout_below="@id/codes_linear"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/sureBtn"
- android:text="@string/sure"
- />
- </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.selftextview.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="@dimen/activity_vertical_margin"
android:id="@+id/codes_linear">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/codes"
android:hint="@string/codes"
android:textSize="21sp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:weightSum="1">
<com.selftextview.MyTextView
android:layout_width="match_parent"
android:layout_height="58dp"
android:background="#c1c7c3"
app:codeColor="#33b5e5"
app:codeDimension="24sp"
app:codeString="@string/codes"
/>
</LinearLayout>
</LinearLayout>
<Button
android:layout_below="@id/codes_linear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sureBtn"
android:text="@string/sure"
/>
</RelativeLayout>
2、在View的构造方法中,获得我们的自定义的样式
- <span style="color:#555555;"> private Paint paint = new Paint();
- /**
- * 验证码内容
- */
- private String[] content = null;
- /**
- * 验证码图片
- */
- private Bitmap bitmap = null;
- private String mCodeString;
- private int mCodeColor = Color.RED;
- private float mCodeDimension = 0;
- private Drawable mCodeDrawable;
- private TextPaint mTextPaint;
- private float mTextWidth;
- private float mTextHeight;
- public MyTextView(Context context) {
- super(context);
- init(null, 0);
- }
- public MyTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(attrs, 0);
- }
- public MyTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(attrs, defStyle);
- }
- private void init(AttributeSet attrs, int defStyle) {
- // Load attributes
- final TypedArray a = getContext().obtainStyledAttributes(
- attrs, R.styleable.MyTextView, defStyle, 0);
- //mCodeString = a.getString(
- // R.styleable.MyTextView_codeString);
- mCodeString = randomText();
- mCodeColor = a.getColor(
- R.styleable.MyTextView_codeColor,
- mCodeColor);
- // Use getDimensionPixelSize or getDimensionPixelOffset when dealing with
- // values that should fall on pixel boundaries.
- mCodeDimension = a.getDimension(
- R.styleable.MyTextView_codeDimension,
- mCodeDimension);
- if (a.hasValue(R.styleable.MyTextView_codeDrawable)) {
- mCodeDrawable = a.getDrawable(
- R.styleable.MyTextView_codeDrawable);
- mCodeDrawable.setCallback(this);
- }
- a.recycle();
- // Set up a default TextPaint object
- mTextPaint = new TextPaint();
- mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
- mTextPaint.setTextAlign(Paint.Align.LEFT);
- invalidateTextPaintAndMeasurements();
- this.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCodeString = randomText();
- postInvalidate(); /</span><span style="color:#ff6666;">/通过这个方法重新绘制</span><span style="color:#555555;">
- }
- });
- }</span>
<span style="color:#555555;"> private Paint paint = new Paint(); /** * 验证码内容 */ private String[] content = null; /** * 验证码图片 */ private Bitmap bitmap = null; private String mCodeString; private int mCodeColor = Color.RED; private float mCodeDimension = 0; private Drawable mCodeDrawable; private TextPaint mTextPaint; private float mTextWidth; private float mTextHeight; public MyTextView(Context context) { super(context); init(null, 0); } public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0); } public MyTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs, defStyle); } private void init(AttributeSet attrs, int defStyle) { // Load attributes final TypedArray a = getContext().obtainStyledAttributes( attrs, R.styleable.MyTextView, defStyle, 0); //mCodeString = a.getString( // R.styleable.MyTextView_codeString); mCodeString = randomText(); mCodeColor = a.getColor( R.styleable.MyTextView_codeColor, mCodeColor); // Use getDimensionPixelSize or getDimensionPixelOffset when dealing with // values that should fall on pixel boundaries. mCodeDimension = a.getDimension( R.styleable.MyTextView_codeDimension, mCodeDimension); if (a.hasValue(R.styleable.MyTextView_codeDrawable)) { mCodeDrawable = a.getDrawable( R.styleable.MyTextView_codeDrawable); mCodeDrawable.setCallback(this); } a.recycle(); // Set up a default TextPaint object mTextPaint = new TextPaint(); mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); mTextPaint.setTextAlign(Paint.Align.LEFT); invalidateTextPaintAndMeasurements(); this.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mCodeString = randomText(); postInvalidate(); /</span><span style="color:#ff6666;">/通过这个方法重新绘制</span><span style="color:#555555;"> } }); }</span>
3.绘制该View
- private void invalidateTextPaintAndMeasurements() {
- mTextPaint.setTextSize(mCodeDimension);
- mTextPaint.setColor(mCodeColor);
- mTextWidth = mTextPaint.measureText(mCodeString);
- Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
- mTextHeight = fontMetrics.bottom;
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // allocations per draw cycle.
- int paddingLeft = getPaddingLeft();
- int paddingTop = getPaddingTop();
- int paddingRight = getPaddingRight();
- int paddingBottom = getPaddingBottom();
- int contentWidth = getWidth() - paddingLeft - paddingRight;
- int contentHeight = getHeight() - paddingTop - paddingBottom;
- // Draw the text.
- canvas.drawText(mCodeString,
- paddingLeft + (contentWidth - mTextWidth) / 2,
- paddingTop + (contentHeight + mTextHeight) / 2,
- mTextPaint);
- // Draw the example drawable on top of the text.
- if (mCodeDrawable != null) {
- mCodeDrawable.setBounds(paddingLeft, paddingTop,
- paddingLeft + contentWidth, paddingTop + contentHeight);
- mCodeDrawable.draw(canvas);
- }
- }
private void invalidateTextPaintAndMeasurements() { mTextPaint.setTextSize(mCodeDimension); mTextPaint.setColor(mCodeColor); mTextWidth = mTextPaint.measureText(mCodeString); Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); mTextHeight = fontMetrics.bottom; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // allocations per draw cycle. int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int paddingRight = getPaddingRight(); int paddingBottom = getPaddingBottom(); int contentWidth = getWidth() - paddingLeft - paddingRight; int contentHeight = getHeight() - paddingTop - paddingBottom; // Draw the text. canvas.drawText(mCodeString, paddingLeft + (contentWidth - mTextWidth) / 2, paddingTop + (contentHeight + mTextHeight) / 2, mTextPaint); // Draw the example drawable on top of the text. if (mCodeDrawable != null) { mCodeDrawable.setBounds(paddingLeft, paddingTop, paddingLeft + contentWidth, paddingTop + contentHeight); mCodeDrawable.draw(canvas); } }
4.关于onmeasure 的相关介绍
把布局文件的宽和高写成wrap_content ,会发生铺满全屏的现象。
这里是鸿洋大神写的解释
系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。
所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:
重写之前先了解MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
下面是我们重写onMeasure代码:
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
- {
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- int width;
- int height ;
- if (widthMode == MeasureSpec.EXACTLY)
- {
- width = widthSize;
- } else
- {
- mPaint.setTextSize(mTitleTextSize);
- mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
- float textWidth = mBounds.width();
- int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
- width = desired;
- }
- if (heightMode == MeasureSpec.EXACTLY)
- {
- height = heightSize;
- } else
- {
- mPaint.setTextSize(mTitleTextSize);
- mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
- float textHeight = mBounds.height();
- int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
- height = desired;
- }
- setMeasuredDimension(width, height);
- }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height ; if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { mPaint.setTextSize(mTitleTextSize); mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds); float textWidth = mBounds.width(); int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight()); width = desired; } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { mPaint.setTextSize(mTitleTextSize); mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds); float textHeight = mBounds.height(); int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom()); height = desired; } setMeasuredDimension(width, height); }
通过上面的实现可以实现文字的wrap_conent