原文地址:http://blog.csdn.net/risky78125/article/details/50609538
自定义View入门
long time no see,这次写一个灰常简单的一个自定义的view.当然,虽然说简单,但是麻雀虽小五脏俱全,自定义view的流程基本涵盖了.
从简单入手,然后大致了解一下view的绘制过程.这个view有多简单呢,实际上咱们就画一个圆,然后上个色.
下面开始:
public class CircleView extends View {
private static final String TAG = "CircleView"; /**
* 定义一个默认的圆形颜色
*/
private static final int DEFAULT_COLOR = Color.GRAY; /**
* 圆形的颜色
*/
private int color = DEFAULT_COLOR; /**
* 画笔
*/
private Paint mPaint; public CircleView(Context context) { this(context, null);
} public CircleView(Context context, AttributeSet attrs) { this(context, attrs, 0);
} public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);
initPaint();
} private void initPaint() { // 设置画笔抗锯齿,可以让图形的边缘更平滑.如果需要画的图形四四方方的那就没必要设置了
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 设置画笔的颜色
mPaint.setColor(color);
} @Override
protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 获取控件本身的宽和高,并选择较短的一边的一半作为圆的半径
int width = getWidth(); int height = getHeight(); int radius = Math.min(width, height) / 2; // 画圆,前两个参数确定圆心的位置,之后圆的半径,画笔
canvas.drawCircle(width / 2, height / 2, radius, mPaint);
}
}123456789101112131415161718192021222324252627282930313233343536373839404142434445
很简单吧,那么看一下布局文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical">
<com.lanou3g.drawview.widget.CircleView android:layout_width="wrap_content"
android:layout_height="wrap_content"/></LinearLayout>12345678910111213
布局也非常简单对吧,那好,看一下预览,还算是正常的吧
那好,接下来,咱们自定义一下这个圆圈的颜色,也就是需要在布局中有一条属性,可以控制这个view的颜色.
创建自定义属性文件,在res/values下,创建一个资源文件,命名为attrs,如下图:
文件里面就可以创建自定义的属性,继续贴代码:
<?xml version="1.0" encoding="utf-8"?><resources>
<!--一般来说,为某个控件自定义属性,name就跟控件名字一样-->
<declare-styleable name="CircleView">
<!--定义一条属性叫circleColor,规定参数填颜色值-->
<attr name="circleColor" format="color"/>
</declare-styleable></resources>12345678
属性定义好了,就可以到代码中把颜色值取出,然后设置给画笔就可以了,继续代码
public class CircleView extends View {
/** 这部分代码与上文一样,省略... */
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 获取自定义属性
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView); // 取出对应的值设置给color,并提供一个默认值
color = a.getColor(R.styleable.CircleView_circleColor, DEFAULT_COLOR);
initPaint();
} private void initPaint() { // 设置画笔抗锯齿,可以让图形的边缘更平滑.如果需要画的图形四四方方的那就没必要设置了
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 设置画笔的颜色
mPaint.setColor(color);
} /** 画圆部分与上文一样,省略 */}12345678910111213141516171819
自定义的属性获取就写完了.然后在布局文件中,设置命名空间,目的为可以获取到自定义的属性,然后在CircleView中使用自定义的属性为圆上色.当然,命名空间的名字我写的是view,这个可以根据自己的爱好命名.
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical">
<com.lanou3g.drawview.widget.CircleView android:layout_width="wrap_content"
android:layout_height="wrap_content"
view:circleColor="#0088ff"/></LinearLayout>123456789101112131415
ok,看预览,可以看到颜色咱们可以自定义了
实 际上这个自定义的控件是灰常不专业的,不知道大家注意到没有.咱们在布局文件中给这个控件设置的宽高都为wrap_content,那显示成这么大肯定是 不正常的.实际上虽然给的是wrap_content,但是系统是按照match_parent来计算的.那为什么会出现这种情况呢?给10秒钟大家思考 一下
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}12345
思考完了吧,那记住一句话就得了,对于直接继承自view的自定义组件,如果不在代码中对wrap_content进行处理,那么系统就会把它理解成match_parent,呵呵哒,气人不.
那接下来,咱们就处理一下吧.在自定义组件中,重写onMeasure()这个方法,处理的过程就在此方法中.思路就是,如果布局中的宽或高设置成了wrap_content,那么对应的宽高就给设置成188.
/** 其他部分的代码省略 */@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
setMeasuredDimension(188,188);
}else if (widthMode == MeasureSpec.AT_MOST){
setMeasuredDimension(188,heightSize);
}else if (heightMode == MeasureSpec.AT_MOST){
setMeasuredDimension(widthSize,188);
}
}12345678910111213141516
接下来再看一下预览(PS:Android Studio真是个好东西)
<com.lanou3g.drawview.widget.CircleView
android:layout_width="match_parent"
android:layout_height="wrap_content"
view:circleColor="#0088ff"/>1234
接下来另一个问题,写到现在这个程度的时候,大家可以试一下给这个控件加上margin属性和padding属性,看一下效果,给大家半个小时尝试一下
try {
Thread.sleep(30 * 60 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}12345
擦擦擦
/** Application Not Responding !!!!! */1
实 际结果是,margin是生效的,而padding是不会生效的.因为margin属性是由父容器控制的,所以会生效.而padding是控件本身控制 的,咱们没有进行处理,所有是无效的.那么接下来,继续处理一下padding.处理的过程也非常简单,在绘制的过程中,考虑一下padding即可.那 么修改一下onDraw方法,如下:
@Override
protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 获取padding值
int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); // 获取控件本身的宽和高,并选择较短的一边的一半作为圆的半径.计算时将padding考虑进去
int width = getWidth() - paddingLeft - paddingRight; int height = getHeight() - paddingTop - paddingBottom; int radius = Math.min(width, height) / 2; // 画圆,前两个参数确定圆心的位置,之后圆的半径,画笔
canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2, radius, mPaint);
}123456789101112131415
布局文件
<com.lanou3g.drawview.widget.CircleView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:paddingTop="5dp"
android:paddingBottom="20dp"
view:circleColor="#0088ff"/>1234567