一:引言
Android的开发中,在移动设备上展示的所有内容,都是靠一个一个具体的视图控件,按照一定的排列规则展示出来的,这些一个个控件,都是系统提供给我们的。但是我们看到,app商店上有些比较炫酷的页面展示,我们会发现,系统根本没有提供那些控件,那么这是怎么实现的呢?对就是通过我们的自定义控件去完成。那么什么是自定义控件呢,这里我个人理解可以分为三类:
- 自定义试图,——继承 View,然后自绘试图内容
- 自定义组合试图,——继承ViewGroup,然后对子类试图进行重新布局。
自定义已有试图,——继承已有的View,比如继承ImageView
自定义控件是android修行道路上的必经之路,也是得道升仙的必备能力。所以我们的跨过去,每天练习一点点,就一点点。
二:自定义View步骤
这里介绍下自定义试图的主要步骤
- 自定义属相
- 继承View重写构造方法
- 获取自定义属性
- 重写测量控件的宽高
- 绘制控件显示
- 提供自定义事件
三:自定义View
- 自定义属性
自定义属性一共有10中定义类型,String,boolean等,具体的类型
和使用对应如下代码
- 编写自定义控件,使用自定义属性
>
- 创建自定义View继承View(重写构造方法)
在创建View的时候,需要重写构造方法,一般重写前三个构造方法就可以了,但是如果我们的自定控件是通过布局文件的形式加载,则第二个构造必须重写,不然会报错。
public MyCuntomView(Context context) {
this(context, null);
}
public MyCuntomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyCuntomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义属性
initViewAtrr(context, attrs, defStyleAttr);
}
- 获取自定属性的值
在获取自定义属性值的时候,我们通过循环的方式来获取值,这样获取到属性值,就是我们xml文件中使用到的,没有使用到的就获取不到。而并获取我们所有自定义的属性。
private void initViewAtrr(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.customView, defStyleAttr, 0);
//获取有几个自定义属相
final int count = a.getIndexCount();
Log.e("TAG", "=====" + count);
for (int i = 0; i < count; i++) {
int type = a.getIndex(i);
switch (type) {
case R.styleable.customView_text:
text = a.getString(type);
if (TextUtils.isEmpty(text)) {
text = "我是文本";
}
break;
case R.styleable.customView_mcolor:
corlor = a.getColor(type, Color.RED);
break;
case R.styleable.customView_msize:
msize = a.getDimensionPixelSize(type, 15);
break;
}
}
a.recycle();
paint = new Paint();
//抗锯齿
paint.setAntiAlias(true);
}
- 测量控件的大小(重写onMeasure方法)
测量之前先了解MeasureSpec的specMode,mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。
MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width=”50dip”,或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
知道以上概念之后我们重写测量就容易的多了
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
int size = MeasureSpec.getSize(widthMeasureSpec);
bounds = new Rect();
if (mode == MeasureSpec.EXACTLY) {
mwidth = size;
} else {
paint.setTextSize(msize);
paint.getTextBounds(text, 0, text.length(), bounds);
mwidth = getPaddingLeft() + getPaddingRight() + bounds.width();
}
mode = MeasureSpec.getMode(heightMeasureSpec);
size = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
mheight = size;
} else {
paint.getTextBounds(text, 0, text.length(), bounds);
mheight = getPaddingBottom() + getPaddingTop() + bounds.height();
}
r=Math.max(mwidth,mheight);
setMeasuredDimension(r, r);
}
- 绘制控件显示(重写onDraw方法)
@Override
protected void onDraw(Canvas canvas) {
paint.setColor(corlor);
canvas.drawCircle(r/2,r/2,r/2,paint);
paint.setColor(Color.BLACK);
canvas.drawText(text,r/2-bounds.width()/2,r/2+bounds.height()/2,paint);
}

- 定义事件
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN){
if(changeColor!=null){
changeColor.change(this,text);
}
}
return super.onTouchEvent(event);
}
public interface ChangeColor{
public void change(MyCuntomView view,String name);
}
public ChangeColor changeColor;
public void setChangeColor(ChangeColor changeColor) {
this.changeColor = changeColor;
}
MainActivity代码
public class MainActivity extends AppCompatActivity {
private MyCuntomView name;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
name = (MyCuntomView) findViewById(R.id.name);
name.setChangeColor(new MyCuntomView.ChangeColor() {
@Override
public void change(MyCuntomView view,String text) {
Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show();
}
});
}
以上就是自定义View的整个流程。