基本实现 步骤
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>
我们定义了字体,字体颜色,字体大小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>
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>
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); } }
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); }
通过上面的实现可以实现文字的wrap_conent