前言
最近在做项目的时候,经常使用到上面图标带下面文字的显示方式,并不是多复杂的业务,但是在很多需求场景都会用到,因此直接做成了自定义View的方式,同时也总结一下对自定义View的个人浅见。
1.自定义View的意义和实现的方式
让很多常用的UI业务需求,封装成一个View来操作,总比不停的用布局来写方便的多。少用几个ctrl+c/ctrl+v,能有效加快编码和开发效率。
实现的方式本人常用的时是两种:
1.通过继承Layout布局(相对来说简单易实现)
2.通过继承View(需要对onMeasure和onDraw有一定的了解)
另外很重要的一点是自定义View和自定义属性的联合使用,这点在文章最后详解。
下面具体介绍两种方式的实现过程:
继承Layout布局
1.首先你要写一个布局文件,里面包括了一个ImageView展示图片,一个TextView展示文字
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/top_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
android:layout_centerInParent="true"
android:layout_alignParentTop="true"/>
<TextView
android:id="@+id/bottom_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_below="@+id/top_image"
android:layout_marginTop="5dp"
android:layout_centerInParent="true" />
</RelativeLayout>
2.写一个继承于对应Layout的自定义类,(注释解释的很清楚,就不过多表述)
public class ImageTextLayoutView extends RelativeLayout {
private ImageView topImage;
private TextView bottomText;
/**
* 这个构造方法是在代码中new的时候调用的
* @param context
*/
public ImageTextLayoutView(Context context) {
super(context);
}
/**
* 这个构造方法是在xml文件中初始化调用的
* @param context
* @param attrs View的xml属性
*/
public ImageTextLayoutView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView(context,attrs);
}
/**
* 这个方法不常用,有前两个足够了
* @param context
* @param attrs
* @param defStyleAttr 应用到View的主题风格(定义在主题中)
*/
public ImageTextLayoutView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void initView(Context context,AttributeSet attrs){
//获取子控件
LayoutInflater.from(context).inflate(R.layout.image_text_view_layout,this);
topImage = findViewById(R.id.top_image);
bottomText = findViewById(R.id.bottom_text);
//获取xml中定义的所有属性
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.ImageTextLayoutView);
//获取ImageView相关自定义属性并设置
int image_width = typedArray.getDimensionPixelSize(R.styleable.ImageTextLayoutView_image_width,30);
int image_height = typedArray.getDimensionPixelSize(R.styleable.ImageTextLayoutView_image_height,30);
Drawable drawable = typedArray.getDrawable(R.styleable.ImageTextLayoutView_image_src);
//topImage.setLayoutParams(new LayoutParams(image_width,image_height));
drawable.setBounds(0,0,image_width,image_height);
topImage.setImageDrawable(drawable);
//获取TextView相关的自定义属性并设置
String content = typedArray.getString(R.styleable.ImageTextLayoutView_text_content);
float text_size = typedArray.getDimension(R.styleable.ImageTextLayoutView_text_size,12);
int text_color = typedArray.getColor(R.styleable.ImageTextLayoutView_text_color, Color.BLACK);
bottomText.setText(content);
bottomText.setTextSize(text_size);
bottomText.setTextColor(text_color);
}
}
3.最后在xml文件引用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<com.ctsaing.flyandroid.ImageTextLayoutView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:image_height="40dp"
app:image_src="@mipmap/ic_launcher"
app:image_width="40dp"
app:text_color="#aaff00"
app:text_size="8sp"
app:text_content="自定义View"
android:layout_marginTop="20dp"
android:layout_marginLeft="20dp"/>
运行效果
2.通过继承View的方式
你可以不懂其他任何关于View的方法,但是一定要懂得两个方法:
onMeasure()和onDraw(),这两个方法是实现的核心。
onMeasure():用来测量宽高,整个View的。
onDraw():在这里画内容
再加上画笔Paint,和画布canvas的一些常用属性,就可以画出各种我们想要的。
话不多说,开始上代码:
1.不需要布局,直接继承View
public class ImageTextCustomView extends View {
private Drawable drawable;//画上方图片
private String content;// 画下方文字
//默认的View宽高
private int viewWidth;
private int viewHeight;
2.重写onMeasure()和onDraw()方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMoede = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//获取到View的整个高度
viewWidth = xmlImageWidth > textWidth ? xmlImageWidth : textWidth;
viewHeight = xmlImageHeight + imageAndTextMargin + textHeight;
viewWidth = Math.max(viewWidth, getSuggestedMinimumWidth());
viewHeight = Math.max(viewHeight, getSuggestedMinimumHeight());
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//xml文件中image设置的宽高时,而实际要显示的图片宽高可能要大于设置值;因此要要对bitmap按比例缩放
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
//获得bitmap的宽高
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
//计算缩放比列
float scaleWidth = ((float)xmlImageWidth)/bitmapWidth;
float scaleHeight = ((float)xmlImageHeight)/bitmapHeight;
//设置缩放的Matrix参数
自此实现了继承View的方式。
(篇幅有限,不全部上代码了,需要的同学,可以上github下载):
3.自定义属性
自定义布局和自定义属性是很难分开的,自定义属性的好处是我们可以再xml文件中直接使用。下面是实现过程:
1.在value文件夹中新建attrs.xml文件
2.在attrs文件中声明我们想要的自定义属性
<declare-styleable name="ImageTextCustomView">
<attr name="imageWidth" format="dimension"/>
<attr name="imageHeight" format="dimension"/>
<attr name="imageSrc" format="reference"/>
<attr name="textSize" format="float|dimension"/>
<attr name="textColor" format="color|reference"/>
<attr name="textWidth" format="dimension"/>
<attr name="textHeight" format="dimension"/>
<attr name="textContent" format="string"/>
<attr name="imageAndTextMargin" format="dimension"/>
3.在我们的自定义文件中,通过typedArray取到我们在xml关于自定义View的属性值。
private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
//先获取xml中的属性值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ImageTextCustomView);
xmlImageWidth = typedArray.getDimensionPixelSize(R.styleable.ImageTextCustomView_imageWidth, defalutImageWidth);
xmlImageHeight = typedArray.getDimensionPixelSize(R.styleable.ImageTextCustomView_imageHeight, defalutImageHeight);
drawable = typedArray.getDrawable(R.styleable.ImageTextCustomView_imageSrc);
xmlTextColor = typedArray.getColor(R.styleable.ImageTextCustomView_textColor, defalutTextColor);
xmlTextSize = typedArray.getDimension(R.styleable.ImageTextCustomView_textSize, defalutTextSize);
content = typedArray.getString(R.styleable.ImageTextCustomView_textContent);
imageAndTextMargin = typedArray.getDimensionPixelSize(R.styleable.ImageTextCustomView_imageAndTextMargin, 0);
textHeight = (int) xmlTextSize;
textPaint.setColor(xmlTextColor);
textPaint.setTextSize(xmlTextSize);
textWidth = (int) Layout.getDesiredWidth(content,textPaint);
typedArray.recycle();
}
4。使用自定义属性的方式:
<com.ctsaing.flyandroid.ImageTextCustomView
android:layout_marginTop="50dp"
android:layout_marginLeft="50dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:imageSrc="@mipmap/ic_launcher"
app:imageWidth="40dp"
app:imageHeight="40dp"
app:imageAndTextMargin="10dp"
app:textSize="10sp"
app:textColor="@android:color/holo_red_dark"
app:textContent="另一个自定义view"/>
上面app: 后面的都是我们在attrs文件中声明的属性值,可以直接这样使用。
4.结语
本文讲述了关于自定义View的简单实现,主要是展示方面的,自定义view的内容还很多,之后再学习再交流。