Android自定义控件的重要性就不多说了,总之是技术进阶,面试常见,高薪必备。
本篇博文的目的很简单,就是希望通过自定义控件来解决一个常见需求点,从而感受一下自定义控件的魅力与强大。
案例描述:
有一张正方形图片,希望在屏幕上还以正方形,并且不会出现变形,与此同时,希望图片的宽正好是屏幕的宽。
案例效果:
案例实现:
方式一: 使用ImageView来显示图片
要显示一张图片,首先我们就会想到使用Android自身的控件ImageView。而需求点要求图片显示的宽要和屏幕的宽一样,所以ImageView的宽我们自然使用android:layout_width="match_parent",这样显示的图片宽就会随着设备屏幕的宽自适应。宽确定了之后,我们来确定高,因为图片的高我们布局的时候不一定知道,所以不能定死了(比如100dp,200dp等),那这样我们就只有两个选择,一是填充父布局,让ImageView的高与父布局一样高( android:layout_height="match_parent"),二是包裹内容,让ImageView的高与图片一样高,(android:layout_height="wrap_content"),第二种理论上应该是我们想要的,接下来我们看看分别使用match_parent和wrap_content确定高之后的效果:
显示这两种都有变形的情况,且math_parent肯定不行,而wrap_content时,虽有变形,但是这个时候肯定有人想到ImageView控件有个属性scaleType,它可以对ImageView上要显示的图片进行缩放,裁剪等操作。下面是一个使用此属性的效果:
(1)android:scaleType="matrix"
(2)android:scaleType="fitXY"
(3)android:scaleType="fitStart"
(4)android:scaleType="fitCenter"
(5)android:scaleType="fitEnd"
(6)android:scaleType="Center"
(7)android:scaleType="CenterCrop"
(8)android:scaleType="CenterInside"
上面把ImageView的ScaleType中的所有情况都测试了一遍,都不能满足我们的需求,在这种情况下,我们不得不放弃使用Android原生态控件来满足需求了,这也就正是Android自定控件要解决的问题。
方式二:使用自定义控件来显示图片
自定义控件有完全自定义、组合自定义、基于原生态控件自定义这几种常用的方式,分析我们的需求本质问题还是要显示一张图片,所以我们采用基于原生态显示图片的控件ImageView进行定制,从方式一我们发现,使用ImageView不能实现不变形且图片宽与屏幕宽一致的统一,所以自定义控件的目的就是要解决这个统一问题。明白了自定义控件我解决的问题,接下来就来进行定制。
(1)首先创建一个类MyImageView,继承于ImageView:
package com.kedi.mylayout;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
public class MyImageView extends ImageView{
}
(2)重写父类构造方法:
一般需要实现三个(一参、二参、三参),并且为了方便管理构造方法,我们改一参构造方法调用二参,二参调三参,然后如果有初始化逻辑,我们统一写在三参构造方法中。
package com.kedi.mylayout;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
public class MyImageView extends ImageView {
public MyImageView(Context context) {
this(context,null);
}
public MyImageView(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs,defStyleAttr);
}
}
(3)实现自定义控件要解决的问题:
接下来就是写自定义控件要解决的问题了,根据要解决的问题可以需要重写的方法不一样,比如onMeasure()、onLayout()、onDraw()、onFinishInflate()、onSizeChanged()、onInterceptTouchEvent()、onTouchEvent()等。就当前需求而言,我们要解决的是图片显示的宽高之类问题,所以需要重写onMeasure()方法,这个方法是由布局测量宽高大小并建议子控件使用,用来确定或修改子控件的大小。
package com.kedi.mylayout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.RelativeLayout;
public class MyImageView extends ImageView{
public MyImageView(Context context) {
this(context,null);
}
public MyImageView(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs,defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
package com.kedi.mylayout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.RelativeLayout;
/**
* 自定义控件
* @author 张科勇
*
*/
public class MyImageView extends RelativeLayout {
public MyImageView(Context context) {
this(context,null);
}
public MyImageView(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs,defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
<span style="color:#ff0000;"> //获取父布局推荐的宽高,并把小者作为自定义控件的宽高
int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
//将自定义控件的宽高设置成父布局推荐的宽
setMeasuredDimension(width, width);</span>
}
}
(4)使用自定义控件布局:
使用自定控件布局与Android原生态控件布局没什么区别,唯一需要注意的是自定义控件在使用的时候需要加类全名(包名+类名)。需要注意的是自己在使用自定控件布局的时候记得把这个包名换成自己的包名和类名。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<span style="color:#ff0000;"><com.kedi.mylayout.MyImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="@drawable/pic" >
</com.kedi.mylayout.MyImageView></span>
</RelativeLayout>
运行程序,效果如下图:
我们可以看到使用自定义控件确定解决了使用方式一时出现的变形和宽高问题,这就是自定义控件的强大和灵活。但自定控件容易入门,难于精通,需要我们不断积累才能够运用自如。
读完本篇,可能会有点感觉,但如想进一步学习自定义布局,自定义属性的案例可以阅读Android自定义控件系列案例【二】