Android自带的许多控件已经十分强大,甚至很多功能都已经有现成的控件去使用了,不过界面效果是肯定会打折扣的,幸好android控件自身的扩展性十分优秀,很多时候我们只需要简单继承下现有控件扩展些许功能就能得到一个全新的控件,比如说前面Android进阶——自定义View之继承系统控件实现自带删除按钮动画效果和软键盘自动悬浮于文本框下方 的核心思想就是如此。
之前的项目中曾经使用过单选列表Android进阶——RecycleView的使用之自定义单选列表(二) ,当时由于使用单选列表的业务不多,没有使用自定义的View去实现,最近新项目中又使用了单选和多选列表,而且还比较频繁,有必要封装下,于是就自定义了一个选项Item,支持单选和多选。效果如图:
一般实现这三种即可
public CheckedableItemView(Context context) {
super(context);
this.context=context;
}
public CheckedableItemView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context=context;
init(attrs);
}
public CheckedableItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context=context;
init(attrs);
}
一句话概括其根本思想就是继承系统控件的通用功能,通过去重写构造方法或者其他方法去改变其逻辑,从而实现扩展功能
TextView本身就自带drawableLeft、drawableRight、drawableTop、drawableRight属性可以在TextView的左右上下绘制对应的drawable,再设置下padding值即可,设置drawble之前一定要记得setBounds否则无法显示。
private void setRightIcon(){
if(isChecked) {
rightCheckedIcon.setBounds(0, 0, 68, 68);//一定要记得先设置setBounds,这里我设置为68*68
setCompoundDrawables(getCompoundDrawables()[0],
getCompoundDrawables()[1], rightCheckedIcon, getCompoundDrawables()[3]);
}else {
rightIcon.setBounds(0, 0, 68, 68);
setCompoundDrawables(getCompoundDrawables()[0],
getCompoundDrawables()[1], rightIcon, getCompoundDrawables()[3]);
}
}
这里我用的是两套UI来简单实现UI效果,但是我们TextView 本身并没有提供类似Checked方法,这里我是使用重写onTouchEvent方法来实现的,但是重写了onTouchEvent这个方法是针对整个TextView的,所以我这里自己处理了下仅仅在Touch在勾选框区域才会去处理,此处需要注意OnTouch事件的传递机制,只有在 MotionEvent.ACTION_DOWN 返回为true时,才会继续产生MotionEvent.ACTION_MOVE、UP事件
/**
* 模拟点击事件当我们按下的位置 在 TextView的宽度 - 图标到控件右边的间距 - 图标的宽度 和
* TextView的宽度 - 图标到控件右边的间距之间就相当于点击了右边的Icon
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
return true;//只有在 MotionEvent.ACTION_DOWN 返回为true时,才会继续产生MotionEvent.ACTION_MOVE/UP事件,
case MotionEvent.ACTION_UP:
if (getCompoundDrawables()[2] != null) {
boolean isRightClick = event.getX() > (getWidth() - getTotalPaddingRight())
&& (event.getX() < ((getWidth() - getPaddingRight())));
if (isRightClick) {
if(isCheckedable) {
doChecked();
}
}
}
default:
break;
}
return super.onTouchEvent(event);
}
<declare-styleable name="checkedable_view">
<attr name="rightIcon" format="reference">attr>
<attr name="rightCheckedIcon" format="reference">attr>
<attr name="isChecked" format="boolean">attr>
<attr name="modeType" format="integer">attr>
declare-styleable>
package crazy.com.customview.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;
import crazy.com.customview.R;
/**
* auther: Crazy.Mo
* Date: 2017/3/30
* Time:10:09
* Des:
*/
public class CheckedableItemView extends TextView {
private Context context;
private int modeType;//默认单选0,多选 1
private boolean isCheckedable=true;
private boolean isChecked=false;
private int rightIconId;
private Drawable rightIcon;//未选中
private Drawable rightCheckedIcon;//选中
public CheckedableItemView(Context context) {
super(context);
this.context=context;
}
public CheckedableItemView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context=context;
init(attrs);
}
public CheckedableItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context=context;
init(attrs);
}
private void init(AttributeSet attrs){
initAttrs(attrs);
setRightIcon();
}
private void initAttrs(AttributeSet attrs){
TypedArray types = context.obtainStyledAttributes(attrs,
R.styleable.checkedable_view);
try {
modeType = types.getInt( R.styleable.checkedable_view_modeType,0 );
isCheckedable = types.getBoolean( R.styleable.checkedable_view_isChecked,true );
rightIcon=types.getDrawable(R.styleable.checkedable_view_rightIcon);
rightCheckedIcon=types.getDrawable(R.styleable.checkedable_view_rightCheckedIcon);
} finally {
types.recycle(); // TypeArray用完需要recycle
}
}
public boolean isCheckedable() {
return isCheckedable;
}
public void setCheckedable(boolean checkedable) {
isCheckedable = checkedable;
}
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
private void setRightIcon(){
if(isChecked) {
rightCheckedIcon.setBounds(0, 0, 68, 68);
setCompoundDrawables(getCompoundDrawables()[0],
getCompoundDrawables()[1], rightCheckedIcon, getCompoundDrawables()[3]);
}else {
rightIcon.setBounds(0, 0, 68, 68);
setCompoundDrawables(getCompoundDrawables()[0],
getCompoundDrawables()[1], rightIcon, getCompoundDrawables()[3]);
}
}
/**
* 模拟点击事件当我们按下的位置 在 TextView的宽度 - 图标到控件右边的间距 - 图标的宽度 和
* TextView的宽度 - 图标到控件右边的间距之间就相当于点击了右边的Icon
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
return true;//只有在 MotionEvent.ACTION_DOWN 返回为true时,才会继续产生MotionEvent.ACTION_MOVE/UP事件,
case MotionEvent.ACTION_UP:
if (getCompoundDrawables()[2] != null) {
boolean isRightClick = event.getX() > (getWidth() - getTotalPaddingRight())
&& (event.getX() < ((getWidth() - getPaddingRight())));
if (isRightClick) {
if(isCheckedable) {
doChecked();
}
}
}
default:
break;
}
return super.onTouchEvent(event);
}
private void doChecked(){
if(isChecked){
rightIcon.setBounds(0, 0, 68, 68);
setCompoundDrawables(getCompoundDrawables()[0],getCompoundDrawables()[1], rightIcon, getCompoundDrawables()[3]);
isChecked=false;
}else {
rightCheckedIcon.setBounds(0, 0, 68, 68);
setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], rightCheckedIcon, getCompoundDrawables()[3]);
isChecked=true;
}
}
}
shape_edit_found.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="0.3dp" android:color="#6bb9a9" />
<corners android:radius="4.5dp" />
<solid android:color="#ffffff" />
shape>
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:crazymo="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="crazy.com.customview.MainActivity">
<crazy.com.customview.widget.CheckedableItemView
android:id="@+id/single_checked"
android:gravity="center|left"
android:layout_marginTop="6dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="@drawable/shape_edit_found"
android:text="你的性别是?"
android:textSize="26sp"
crazymo:modeType="0"
crazymo:rightIcon="@mipmap/single"
crazymo:rightCheckedIcon="@mipmap/single_selected"/>
<crazy.com.customview.widget.CheckedableItemView
android:id="@+id/multi_checked"
android:gravity="center|left"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="@drawable/shape_edit_found"
android:layout_below="@id/single_checked"
android:text="你喜欢的运动?"
android:textSize="26sp"
crazymo:modeType="1"
crazymo:rightIcon="@mipmap/multi"
crazymo:rightCheckedIcon="@mipmap/multi_selected"/>
RelativeLayout>
这个控件主要就是分享一种思想,灵活的借用系统自带的功能去扩展自己的控件,灵活地封装会减少很多不必要工作。源码直通车