您的位置:首页 > 其它

自定义一个圆圈View

2016-04-14 16:37 399 查看
这篇博客的目的是如何开发一个继承自View的自定义控件。最后的效果是能够得到一个任何页面都能使用的圆圈控件。代码很简单,也很粗陋。但是高楼平地起。

先贴代码,可以先不看,看完后面的解释再看代码:

package com.example.administrator.myfirstandroidstudioproject.customView;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
* Created by Administrator on 2016/4/13.
*/
public class CustomView extends View {
//园的默认颜色
private int mColor = Color.RED;
//一个画笔,用于在onDraw函数中画圆
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

private void init() {
mPaint.setColor(mColor);
}

public CustomView(Context context) {
this(context,null);
init();
}

public CustomView(Context context, AttributeSet attrs) {
this(context, attrs,0);
init();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//让warp生效,核心思想就是:
// 当设置成warp时,就用控件自己本身的长度
// (这个本身的长度不是说你在xml中配置的长度,而是比如一个字的长度是10px,那么有两个字的textView的长度就应该是20px)
// 否则就是用控件测量的长度。之前一直以为测量的长度就是控件本身的长度,原来不是。
// 测量的长度是通过父容器的MeasureSpec和自己的LayoutParams和本身的长度共同决定的结果。(这个就得看Android源码了)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpaceMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpaceMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpaceMode == MeasureSpec.AT_MOST && heightSpaceMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(100, 100);
} else if (widthSpaceMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(100, heightSpaceSize);
} else if (heightSpaceSize == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpaceSize, 100);
}
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//让padding生效,核心思想就是自己计算让padding之后的宽度和高度,最后画出来。
final int paddingLeft = getPaddingLeft();
final int paddingRight = getPaddingRight();
final int paddingTop = getPaddingTop();
final int paddingBottom = getPaddingBottom();
int width = getWidth() - paddingLeft - paddingRight;
int height = getHeight() - paddingTop - paddingBottom;
int radius = Math.min(width, height) / 2;
canvas.drawCircle(width / 2, height / 2, radius, mPaint);
}

public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

}


布局

<com.example.administrator.myfirstandroidstudioproject.customView.CustomView
android:id="@+id/circleView1"
android:layout_width="wrap_content"
android:layout_height="100px"
android:background="#000000"
/>


形成一个View有三个过程。

1. onMeasure:测量控件的高和宽,并通过setMeasuredDimension(int measuredWidth, int measuredHeight)方法设置控件的高宽。

2. onLayout:计算控件在视图中的坐标。控件会按照这里设置的坐标决定自己在屏幕上的位置。

3. onDraw:画图,位置确定后,控件通过这个方法画出控件的图形。像上面的例子就是自己在控件的位置上话一个红色的圆。

知道了上面三个方法后就能解决继承View自定义控件所产生的两个问题:

问题1:wrap_content失效。给控件的布局中设置wrap_content将不会再有效果,解决方法参考上面的onMeasure代码。

问题2:padding失效。设置的padding将不会再有效。解决方法参考上面的onDraw方法。

如何给控件创建自定义属性

1. 在values文件夹下创建一个用于创建自定义属性的xml文件,这里就写成attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomView">
<attr name="circle_color" format="color">
</attr>
</declare-styleable>
</resources>


declare-styleable元素指的是一个属性集合,里面可以有多个属性,在本例中只有一个颜色属性。还可以定义其他格式的属性,比如reference是id属性等等。

2. 在构造方法中解析这个xml文件,获取属性值,获得了属性的值,当然可以根据值来设置控件的样子了。

public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/*
获取属性集合
*/
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
/*
从属性集合中获取单个属性
第一个参数里的是获取的属性
第二个参数是如果没有获取到属性的默认属性
*/
mColor=a.getColor(R.styleable.CustomView_circle_color,Color.RED);
/*
释放资源
*/
a.recycle();
init();

}


最后,在布局文件中使用自定义属性。很简单,只有一个地方需要注意,需要指定一个命名空间xmlns,就是下面的 xmlns:app=”http://schemas.android.com/apk/res-auto”,app这个名字可以随便取,报错的原因是,xml必须按照命名空间中的约定编写。这是为了规范xml的格式。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:context="com.example.administrator.myfirstandroidstudioproject.MainActivity">

<com.example.administrator.myfirstandroidstudioproject.customView.CustomView
android:id="@+id/circleView1"
android:layout_width="wrap_content"
android:layout_height="100px"
android:padding="20px"
app:circle_color="@color/colorAccent"
android:background="#000000"
/>
</LinearLayout>


注意的点:自定义view有三个构造函数

在代码中直接new一个Custom View实例的时候,会调用第一个构造函数。这个没有任何争议。

在xml布局文件中调用Custom View的时候,会调用第二个构造函数。这个也没有争议。

在xml布局文件中调用Custom View,并且Custom View标签中还有自定义属性时,这里调用的还是第二个构造函数。

所以,在第三个构造函数如果不在第二个函数里用this()调用的化,自定义的属性将不会有效
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: