文本主要说明两点:
1,Android自定义View(继承View)的基本流程。
2,图标&文本 标题信息小控件的制作。
前言:
1,Android自定义View可以分别继承View,ViewGroup以及View控件(例如ImageView)来实现。本文中只说明继承View的方式来自定义View。
2,项目中经常使用到图标来描述或表示一种操作,而且经常会在图标的底部配上简单的文字说明,以更加清楚的表达图标所代表的意思。如微信主界面底部的4个小图标。
他们的格式都是图标+文本,像笔者这种新手可能就会在XML中使用ImageView+TextView(咳咳,以前真是这样做的,很明显坚持两个月这样做,XML布局就会很牛逼)。言归正传,现在知道使用一个自定义View就能比较简单的完成这个事情。
正文:
先说明继承View自定义控件的流程,一切都在图中。有三大部分,定义属性,继承View重写一些重要的方法,项目中引用。
其中第二部分又分为4个小部分,抽象的来说就是,获取值,根据值测量视图大小,更具视图以及资源完成绘制的过程。待会详讲。
那么接下来就按照这个大体的流程制作上面描述的这个小控件。
一,定义属性
先上代码
<!-- Defines the custom XML attributes supported for a ImageTextView-->
<declare-styleable name="ImageTextView">
<attr name="text_info" format="string"/><!--文本类容-->
<attr name="text_info_size" format="dimension"/><!--文本字体大小-->
<attr name="text_info_color" format="color"/><!--文本颜色-->
<attr name="image_content" format="reference"/><!--图标资源值-->
<attr name="image_content_height" format="dimension"/><!--图标高度-->
</declare-styleable>
定义属性首先要在valuse文件夹下建立attrs.xml文件,用来存放属性表述项。然后再定义declare-styleable以及attr项。
属性定义就是说明自定义控件需要那些新的属性,比如说上面定义了text_info属性就是表示ImageTextView图标下方文本类容。
其中attr name项为属性名,可随意定义。format是说明了该属性值的类型。详情对应方式参照http://blog.youkuaiyun.com/fanxl10/article/details/41316013。其中特别要说的是dp,sp等长度计量单位都是dimension表示。
二,继承View
a,定义类继承View
定义自己的类继承View,并重写3个构造方法,以及在代码中定义相关成员变量(该类名要与attrs文件中declare-styleable name属性名一致)
public class ImageTextView extends View {
private int mImage_src;//图标资源
private int mImage_size = 0;//图标大小
private String mText_info = "";//文本类容
private int mText_info_color = Color.BLACK;//文本颜色
private int mText_info_size = 0;//文本大小
private Rect mRect;//绘制文本矩形区,用于测绘文本高度
private Paint mPaint;//画笔对象
public ImageTextView(Context context) {
this(context, null);
}
public ImageTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ImageTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
这里需要主要的是重写4个方法时如果使用IDE直接生产的其构造方法中是super(context),这里需要改为 this(context, null)。由于第三个构造方式是用于XML构造时使用的,xml中有部分属性是要交给View父类处理(整体View的高度),故要调用super方法。这里我们只处理我们自定义的属性。也就是接下来要将的获取属性值。
开始我们在attrs中定义了5个属性值,如果我们将该控件在XML布局文件中使用后,在构造该控件时,我们能在ImageTextView(Context context, AttributeSet attrs, int defStyleAttr)这个构造方法中通过attrs 参数获取到我们定义的值。
public ImageTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取XML 定义的样式
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ImageTextView, defStyleAttr, 0);
int n = typedArray.getIndexCount();
//对其进行遍历 从第0项节点开始寻找
for(int i = 0;i<n;i++)
{
int attr = typedArray.getIndex(i);
switch (attr)
{
case R.styleable.ImageTextView_text_info://如果找到了定义本文类容的属性
mText_info = typedArray.getString(attr);//就将属性值获取出来,赋值给本类中的成员变量,待会作显示使用
break;
case R.styleable.ImageTextView_text_info_color:
mText_info_color = typedArray.getColor(attr, Color.BLACK);//获取颜色值
break;
case R.styleable.ImageTextView_image_content:
mImage_src = typedArray.getResourceId(attr,0);//获取资源值
break;
case R.styleable.ImageTextView_image_content_height:
mImage_size = (int)typedArray.getDimension(attr,20);//获取图标高度 这里默认单位是dp
break;
case R.styleable.ImageTextView_text_info_size:
mText_info_size = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));//获取文本大小 这里要将sp单位转为px
break;
}
}
typedArray.recycle();//回收资源
mPaint = new Paint();//初始化画笔
mRect = new Rect();//初始化矩形区
}
b,重写onMeasure 定义控件大小
Android View 机制中onMeasure就是用来测绘控件大小的,这里我们要算出各个控件需要的大小以及整体控件的大小,计算完成后将整体控件的大小通过setMeasuredDimension函数通知给系统,让系统知道该控件占用了整个布局中多大的位置。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int width;
int height ;
if (widthMode == MeasureSpec.EXACTLY)//xml中给定了控件的具体宽度的情况下
{
width = widthSize;//这里我们不用计算,直接使用给定的值
} else//xml中没给定具体宽度值,即使用wrap_content的情况下
{
//这里没有指定宽度,故我们需要更具子控件的大小计算出整个控件需要的大小(宽度)
int desired = getPaddingLeft() + mImage_size + getPaddingRight();//这里本人定义控件宽度就是图标的宽度,并且规定图标为正方形。大家完全可更具自己的需求定义。
width = desired;
}
//下面是高度的计算 和上面同理
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY)
{
height = heightSize;
} else
{
mPaint.setTextSize(mText_info_size);
mPaint.getTextBounds(mText_info, 0, mText_info.length(), mRect);
float textHeight = mRect.height();
int desired = (int) (getPaddingTop() + textHeight + mImage_size + getPaddingBottom());
height = desired;
}
//完成测绘告知系统
setMeasuredDimension(width, height);
}
c,重写onDraw完成绘制
在本项目中onDraw方法很简单,只需要绘制图片和文本即可,而且没有动画什么的。如果遇到复杂的控件canvas绘制方面就要多了解些。
@Override
protected void onDraw(Canvas canvas) {
Bitmap bitmap = ((BitmapDrawable)(ContextCompat.getDrawable(getContext(),mImage_src))).getBitmap();//获取图片资源
drawImage(canvas,bitmap,0,0,mImage_size,mImage_size,0,0);//绘制图片
mPaint.setColor(mText_info_color);//设置文本颜色
mPaint.setTextSize(mText_info_size);//设置文本大小
if(mText_info.length()>=5)//设置不能超过5个字
{
mText_info = mText_info.substring(0,5);
}
canvas.drawText(mText_info,5,mImage_size+mText_info_size,mPaint);//绘制文本
}
/*---------------------------------
* 绘制图片
* @param x屏幕上的x坐标
* @param y屏幕上的y坐标
* @param w要绘制的图片的宽度
* @param h要绘制的图片的高度
* @param bx图片上的x坐标
* @param by图片上的y坐标
*
* @return null
------------------------------------*/
public static void drawImage(Canvas canvas, Bitmap blt, int x, int y,
int w, int h, int bx, int by) {
Rect src = new Rect();// 图片 >>原矩形
Rect dst = new Rect();// 屏幕 >>目标矩形
src.left = bx;
src.top = by;
src.right = bx + w;
src.bottom = by + h;
dst.left = x;
dst.top = y;
dst.right = x + w;
dst.bottom = y + h;
// 画出指定的位图,位图将自动--》缩放/自动转换,以填补目标矩形
// 这个方法的意思就像 将一个位图按照需求重画一遍,画后的位图就是我们需要的了
canvas.drawBitmap(blt, null, dst, null);
src = null;
dst = null;
}
drawImage方法是从网上一小哥博客中直接拿过来的,抱歉当时没有记录下链接地址。但是亲测可用,谢谢小哥^.^
d,提供接口
这里当然就是大家公布想要对外提供的接口,我这里就提供个更换图片,更换文本的接口。这就属于很简单的内容了。
public void setInfo(String text)
{
this.mText_info = text;
invalidate();//刷新界面 会重新调用onDraw方法
}
public void setImageSrc(int src)
{
this.mImage_src = src;
invalidate();
}
然后在项目代码中就可以动态的改变这些属性了。
三,xml中使用
<com.ws.coyc.wsnote.UI.Layout.ImageTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:image_content="@mipmap/map"
app:image_content_height="50dp"
app:text_info=" 地图"
app:text_info_size="18sp"
app:text_info_color="@android:color/holo_blue_dark"
/>
<com.ws.coyc.wsnote.UI.Layout.ImageTextView
android:layout_width="50dp"
android:layout_height="wrap_content"
app:image_content="@mipmap/search"
app:image_content_height="50dp"
app:text_info=" 搜索"
app:text_info_size="18sp"
android:layout_marginLeft="70dp"
app:text_info_color="@android:color/holo_blue_dark"
/>
<com.ws.coyc.wsnote.UI.Layout.ImageTextView
android:layout_width="50dp"
android:layout_height="wrap_content"
app:image_content="@mipmap/c"
app:image_content_height="50dp"
app:text_info="风景名胜"
app:text_info_size="12sp"
android:layout_marginLeft="140dp"
app:text_info_color="@android:color/holo_blue_dark"
/>
在代码中就和常规的View一样 findViewbyId即可初始化。
效果展示:
可以看到第三张图的文本还可以向下来一些的,其实实在绘制文本的时候居中绘制就行了。这里我就偷偷懒不重新截图,给大家推荐些博客关于自定义view的。
http://blog.youkuaiyun.com/lmj623565791/article/details/24252901/
http://blog.youkuaiyun.com/huachao1001/article/details/51577291
后语:本来是打算前几天晚上写的,只怪电视太好看,连着看了两部谍战片。哈哈,中秋节后还是逼着自己先写完,不能拖了。个人写博客时间不长,文中定有很多不足之处甚至错误,大家如有发现问题,还请大家多多包含并及时指出,希望能和网友们一并进步,谢谢大家。