上一篇中,了解了基本概念和初次体验了自定义view,接下来以一个实例来梳理一下知识点。
需求:自定义一个View,在设置一个数字之后,有一个自增的效果。
效果如图:
如你所见,这是上一篇自定义view实例的改进,不过没读上一篇也不影响这次阅读。
一,声明新的属性:
<attr name="myTitle" format="string"/>
声明的属性,即 format有一下类型:
- string
字符串 - color
颜色 - dimension
尺寸值 - integer
整型 - enum
枚举 - reference
资源ID - float
浮点型 - fraction
百分数 - flag
位或运算
二,添加相关的逻辑:
初始化参数,
public AddTextView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(getResources().getColor(R.color.colorAccent));
mPaint.setAlpha(4);
num = 0;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyString);
mTitle = ta.getString(R.styleable.MyString_myTitle);
setText(mTitle);
}
初始化画笔Paint,Paint是作用在Canvas上的,就像我们显示生活中画画一样,需要纸和笔。在Android中,Canvas相当于纸,而Paint相当于笔。在这里,初始化了Paint(笔)的颜色,透明度,填充模式,和抗锯齿的属性。
然后,通过Typedarray 获取到xml中的属性值。并通过setText(),来将文本显示出来。
测量view:
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int widthResult = 0;
if (widthSpecMode == MeasureSpec.EXACTLY) {
widthResult = widthSpecSize;
} else {
widthResult = 200;
if (widthSpecMode == MeasureSpec.AT_MOST) {
widthResult = Math.min(widthResult, widthSpecSize);
}
}
一般情况下,如果没有用到wrap_content,是不用重写onMeasure()方法的。因为wrap_content,匹配的是AT_MOST模式(尽可能多),系统是不知道要分配多少的,所以得重写onMeasure()给出一个默认的指,即,使用wrap_content时,就会使用默认的指。上面的这段代码就是进行响应的判断,并给出默认的指。
三,绘制背景
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0,getRight(),getBottom(),mPaint);
invalidate();
}
在上一篇的例子中,我使用了xml直接设置背景,在这里,我用Canvas和Paint来绘制背景。如上,在绘制矩形之后,调用了invalidate()进行刷新UI。
四,响应触控操作
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
translateNum = 0;
mOnMyClickListener.click();
break;
}
postInvalidate();
return true;
}
这里通过接口回调暴露了接口给调用者,如果对接口回调不太熟悉,可以尽情google一把。
public void setNum(final int num) {
this.num = num;
if (num > 1000) {
addNum = num / 35;
} else {
addNum = num / 15;
}
add();
}
public void add() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
translateNum += addNum;
setText(String.valueOf(translateNum));
postInvalidate();
invalidate();
if (translateNum > num) {
setText(String.valueOf(num));
postInvalidate();
return;
}
add();
}
}, 50);
}
通过handler实现延迟和循环调用,当给出的数字大于1000时,将数字分35次进行相加,否则分15次进行相加。最后因为在new Runnable中,所以使用postInvalidate();刷新UI。
最后各部分代码是这样的:
声明属性部分:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyString">
<attr name="myTitle" format="string"/>
</declare-styleable>
</resources>
自定义AddTextView部分:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
/**
* Created by zone on 2017/1/22.
*/
public class AddTextView extends TextView {
private String mTitle;
private int num;
private int translateNum;
private int addNum;
private Handler mHandler = new Handler();
private OnMyClickListener mOnMyClickListener;
private Paint mPaint;
public void setOnMyClickListener(OnMyClickListener onMyClickListener) {
mOnMyClickListener = onMyClickListener;
}
public interface OnMyClickListener {
void click();
}
public AddTextView(Context context) {
super(context);
}
public AddTextView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(getResources().getColor(R.color.colorAccent));
mPaint.setAlpha(4);
num = 0;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyString);
mTitle = ta.getString(R.styleable.MyString_myTitle);
setText(mTitle);
}
public AddTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int widthResult = 0;
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
int heightResult = 0;
if (widthSpecMode == MeasureSpec.EXACTLY) {
widthResult = widthSpecSize;
} else {
widthResult = 200;
if (widthSpecMode == MeasureSpec.AT_MOST) {
widthResult = Math.min(widthResult, widthSpecSize);
}
}
if (heightSpecMode == MeasureSpec.EXACTLY) {
heightResult = heightSpecSize;
} else {
heightResult = 200;
if (widthSpecMode == MeasureSpec.AT_MOST) {
heightResult = Math.min(heightResult, heightSpecSize);
}
}
setMeasuredDimension(widthResult, heightResult);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,getBottom()/10,getRight(),getBottom(),mPaint);
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
translateNum = 0;
mOnMyClickListener.click();
break;
}
postInvalidate();
return true;
}
public void setNum(final int num) {
this.num = num;
if (num > 1000) {
addNum = num / 35;
} else {
addNum = num / 15;
}
add();
}
public void add() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
translateNum += addNum;
setText(String.valueOf(translateNum));
postInvalidate();
invalidate();
if (translateNum > num) {
setText(String.valueOf(num));
postInvalidate();
return;
}
add();
}
}, 50);
}
}
UI引用部分:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.zone.custonview.MainActivity">
<com.zone.custonview.AddTextView
android:id="@+id/mytextview"
android:layout_width="180px"
android:layout_height="180px"
android:gravity="center"
android:textSize="20dp"
custom:myTitle="111111111111111"/>
</LinearLayout>
Activity部分:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private AddTextView mAddTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAddTextView = (AddTextView) findViewById(R.id.mytextview);
mAddTextView.setOnMyClickListener(new AddTextView.OnMyClickListener() {
@Override
public void click() {
mAddTextView.setNum(4000);
}
});
}
}