当Android学习了一段时间后,可能就不满足于系统所提供的控件了,特别是有个性的开发者有着自己的想法,这时就需要学习Android自定义控件了,这里简单介绍一下自定义控件的几个步骤:
- 编写自定义控件属性;
- 编写自定义控件类,继承View类型或者ViewGroup类型;
- 在构造方法中获取xml属性值;
- 重写onMeasure,测量子View的宽高;
- 重写onLayout,布置子View的位置;
- 重写onDraw,绘制元素;
这6个步骤并不是必须的,按需所取。
入门
既然是个入门,那就从最简单的开始吧。我们的目标是实现一个自定义View,能够显示图片文字水印的控件,能够自定义文字水印的位置、大小、字体颜色等。效果图如下:
额,我承认是有点low,但这个作为自定义控件入门还是非常不错的,然后根据上面的步骤一步步来看,首先我们需要编写自定义控件属性,在values文件夹下面新建attrs.xml文件,这个里面可以声明我们想要自定义控件的属性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyImageView">
<attr name="img_description" format="string" />
<attr name="description_size" format="dimension" />
<attr name="description_color" format="color" />
<attr name="description_position" format="enum">
<enum name="left_top" value="0" />
<enum name="right_top" value="1" />
<enum name="center" value="2" />
<enum name="left_bottom" value="3" />
<enum name="right_bottom" value="4" />
</attr>
<attr name="description_padding" format="dimension" />
</declare-styleable>
</resources>
根据属性命名,结合效果图来看不难理解,这里面定义了图片描述、字体大小、字体颜色、文字位置、文字边距。
有了这些自定义属性,那么我们就要新建我们自定义的控件类,这里就是MyImageView,继承自ImageView,这边代码比较多,主要是为了能够完整地看到一个自定义控件类的初级编写:
package com.cjt.customview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ImageView;
/**
* Created by mvp-cjt on 2016/6/21.
* Email:879309896@qq.com
*/
public class MyImageView extends ImageView{
/**
* 左上
*/
public static final int LEFT_TOP = 0;
/**
* 右上
*/
public static final int RIGHT_TOP = 1;
/**
* 中间
*/
public static final int CENTER = 2;
/**
* 左下
*/
public static final int LEFT_BOTTOM = 3;
/**
* 右下
*/
public static final int RIGHT_BOTTOM = 4;
/**
* 画笔
*/
private Paint mPaint;
/**
* 文字水印
*/
private String imgDescription;
/**
* 字体大小
*/
private int descriptionSize;
/**
* 字体宽高
*/
private Rect mBound;
/**
* 水印位置,默认是CENTER
*/
private int descriptionPosition = CENTER;
/**
* 文字颜色
*/
private int descriptionColor = Color.BLACK;
/**
* 文字水印与图片边距的距离
*/
private int descriptionPadding;
public MyImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
* 获得我们所定义的自定义样式属性
*/
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyImageView, defStyleAttr, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++)
{
int attr = a.getIndex(i);
switch (attr)
{
case R.styleable.MyImageView_img_description:
imgDescription = a.getString(attr);
break;
case R.styleable.MyImageView_description_size:
// 获取文字大小,默认是16sp,这里需要转换为px
descriptionSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 10, getResources().getDisplayMetrics()));
break;
case R.styleable.MyImageView_description_position:
descriptionPosition = a.getInt(attr, CENTER);
break;
case R.styleable.MyImageView_description_color:
descriptionColor = a.getColor(attr, Color.BLACK);
break;
case R.styleable.MyImageView_description_padding:
descriptionPadding = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
mPaint = new Paint();
/**
* 获得绘制文本的宽和高
*/
mPaint.setTextSize(descriptionSize);
mBound = new Rect();
mPaint.getTextBounds(imgDescription, 0, imgDescription.length(), mBound);
mPaint.setTextSize(descriptionSize);
mPaint.setColor(descriptionColor);
// 消除锯齿
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float x = 0;
float y = 0;
switch (descriptionPosition){
case LEFT_TOP:
x = descriptionPadding;
y = mBound.height() + descriptionPadding;
break;
case RIGHT_TOP:
x = getWidth() - mBound.width() - descriptionPadding;
y = mBound.height() + descriptionPadding;
break;
case CENTER:
x = getWidth()/2 - mBound.width()/2;
y = getHeight()/2 + mBound.height()/2;
break;
case LEFT_BOTTOM:
x = descriptionPadding;
y = getHeight() - descriptionPadding;
break;
case RIGHT_BOTTOM:
x = getWidth() - mBound.width() - descriptionPadding;
y = getHeight() - descriptionPadding;
break;
}
canvas.drawText(imgDescription, x, y, mPaint);
}
}
由于这是一个自定义View,所以就没有重写onMeasure和onLayout方法,这两个主要是针对于自定义ViewGroup而言的。这里在构造方法通过固定的方式,获取到xml中编写的属性值。然后在onDraw方法中通过一定的计算,画出我们的文字水印。
注意:重写控件类一定要注意几个构造方法的区别,还有java中的super和this的差异。
大功告成!!撒花撒花- -。那么具体怎么用,这里给个最简单的例子:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:custom="http://schemas.android.com/apk/res-auto"
...>
<com.cjt.customview.MyImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/curry"
custom:description_padding="10dp"
custom:description_position="right_bottom"
custom:description_size="24sp"
custom:description_color="#FFF"
custom:img_description="Stephen Curry" />
</RelativeLayout>
是不是灰常灰常的方便呢?就是这么酷炫,O(∩_∩)O哈哈~。入了门,还是要自己动动手才会印象深刻,写个验证码控件、多点触控图片控件都是可以的。加油,敬请期待下篇Android自定义控件(二):提高篇。