一、背景:
项目中要用到好多一模一样的控件,且每一个控件有自己的各种状态和相对独立的业务逻辑,为了使代码简洁、可复用可移植,把这个控件独立抽取出来,做成了一个自定义的控件。
二、自定义属性
实现一个自定义的控件,避免不了使用自己自定义的属性,自定义属性一般是在value文件夹中的attr.xml中建立自己自定义的标签。
<!--自定义指纹图标的属性-->
<declare-styleable name="FingerImage">
<attr name="android:text"/> <!--显示的文字,这里没有写format,意思是引用系统的属性,但是又不能不写,因为当前控件可能没有这个属性-->
<attr name="android:textSize"/> <!--文字大小,这里没有写format,意思是引用系统的属性,但是又不能不写,因为当前控件可能没有这个属性-->
<attr name="img_bg" format="reference" /> <!--背景图-->
<attr name="seleted" format="boolean" /> <!--是否被选中-->
<attr name="finger_index"> <!--枚举类型不用指定format,且value必须为int类型-->
<enum name="left_1" value="1" />
<enum name="left_2" value="2" />
<enum name="left_3" value="3" />
<enum name="left_4" value="4" />
<enum name="left_5" value="5" />
<enum name="right_1" value="6" />
<enum name="right_2" value="7" />
<enum name="right_3" value="8" />
<enum name="right_4" value="9" />
<enum name="right_5" value="10" />
</attr>
</declare-styleable>
三、在布局文件中使用自定义属性
首先在根布局声明命名空间 : xmlns:myApp=”http://schemas.android.com/apk/res-auto”
<com.hisign.id_verification.viewsupport.FingerImage
android:id="@+id/img_l1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拇指"
android:textSize="@dimen/img_fp_txt_size"
myApp:img_bg="@drawable/img_finger_selector"
myApp:finger_index="left_1"
myApp:seleted="true"/>
四、自定义控件的布局
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:id="@+id/img_finger"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
*style="@style/img_finger"* />
<TextView
android:id="@+id/tv_finger"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:textColor="@color/white_alpha_percentage50"
android:textSize="18sp"/>
</LinearLayout>
这里面还用到了自定义的样式(其实就是把多个原生的属性放在了一起,减少了代码量,看起来更简洁更好修改)
在value文件夹的style.xml中建立一个新的标签
<style name="img_finger">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginLeft">@dimen/img_fp_margin</item>
<item name="android:layout_marginTop">@dimen/img_fp_margin</item>
<item name="android:layout_marginRight">@dimen/img_fp_margin</item>
<item name="android:layout_marginBottom">@dimen/img_fp_margin_bottom</item>
<item name="android:background">@drawable/img_finger_selector</item>
<item name="android:scaleType">fitXY</item>
</style>
使用时:style=”@style/img_finger”
五、自定义控件类中自定义属性的获得
TypedArray ta = mContext.obtainStyledAttributes(attrs, R.styleable.FingerImage);
String txt = ta.getString(R.styleable.FingerImage_android_text);
int bgResId = ta.getResourceId(R.styleable.FingerImage_img_bg,R.drawable.img_finger_selector);
int mTextSize = ta.getDimensionPixelSize(R.styleable.FingerImage_android_textSize, 18);
boolean isSelected = ta.getBoolean(R.styleable.FingerImage_seleted,false);
hasFocus = isSelected;
String fingerIndex = ta.getString(R.styleable.FingerImage_finger_index);
六、全部代码
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.hisign.id_verification.R;
import com.hisign.id_verification.app.MyApplication;
import com.hisign.id_verification.manager.CapFingerManager;
import com.hisign.id_verification.manager.Const;
import com.hisign.id_verification.manager.EventObject;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Created by lixm on 2017/12/11.
*/
public class FingerImage extends LinearLayout implements View.OnClickListener,View.OnFocusChangeListener{
@BindView(R.id.img_finger)
ImageView imgFinger;
@BindView(R.id.tv_finger)
TextView tvFinger;
private Context mContext;
private OnClickListener listener;
/**
* 当前指位的名称
*/
private String curFingerIndexName;
/**
* 当前指位的第一枚指纹
*/
private Bitmap fpBmp1;
/**
* 当前指位的第二枚指纹
*/
private Bitmap fpBmp2;
/**
* 当前指位的采集状态
*/
private FingerState curState;
private String[] fingerIndexNames = {"左手拇指","左手食指","左手中指","左手无名指","左手小指",
"右手拇指","右手食指","右手中指","右手无名指","右手小指"};
/**
* 采集完当前两枚指纹后的回调
*/
private OnCapComeleptedListener onCapComeleptedListener;
private boolean hasFocus = false;
public FingerImage(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
View view = LayoutInflater.from(context).inflate(R.layout.layout_icon_finger_item, this, true);
ButterKnife.bind(view);
Log.d("lixm","FingerImage(2)... ");
initAttributes(attrs);
}
public FingerImage(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
Log.d("lixm","FingerImage(3)... ");
View view = LayoutInflater.from(context).inflate(R.layout.layout_icon_finger_item, this, true);
ButterKnife.bind(view);
initAttributes(attrs);
}
/**
* 解析属性信息
* @param attrs
*/
private void initAttributes(AttributeSet attrs){
// 设置点击事件
imgFinger.setOnClickListener(this);
// 设置焦点变化事件
imgFinger.setOnFocusChangeListener(this);
TypedArray ta = mContext.obtainStyledAttributes(attrs, R.styleable.FingerImage);
String txt = ta.getString(R.styleable.FingerImage_android_text);
int bgResId = ta.getResourceId(R.styleable.FingerImage_img_bg,R.drawable.img_finger_selector);
int mTextSize = ta.getDimensionPixelSize(R.styleable.FingerImage_android_textSize, 18);
boolean isSelected = ta.getBoolean(R.styleable.FingerImage_seleted,false);
hasFocus = isSelected;
String fingerIndex = ta.getString(R.styleable.FingerImage_finger_index);
curFingerIndexName = fingerIndexNames[Integer.parseInt(fingerIndex) - 1];
Log.d("lixm","txt = " + txt + ", mTextSize = " + mTextSize + ", curFingerIndexName = " + curFingerIndexName + ", isSelected = " + isSelected);
imgFinger.setBackgroundResource(bgResId);
tvFinger.setText(txt);
tvFinger.setTextSize(mTextSize);
if(isSelected){
setState(FingerState.SELECTED);
}else{
setState(FingerState.NORMAL);
}
}
/**
* 设置指纹图片的背景
* @param bgRes
*/
public void setBg(int bgRes){
imgFinger.setBackgroundResource(bgRes);
}
/**
* 设置指纹的文字信息
* @param txt
*/
public void setTxt(String txt){
tvFinger.setText(txt);
}
/**
* 设置文字的大小
* @param txtSize
*/
public void setTxtSize(int txtSize){
tvFinger.setTextSize(txtSize);
}
/**
* 指纹采集当前的状态
* @param state
*/
public void setState(FingerState state){
curState = state;
if(state == FingerState.SELECTED){
imgFinger.setBackgroundResource(R.drawable.img_fp_p);
tvFinger.setTextColor(mContext.getResources().getColor(R.color.white));
}else if(state == FingerState.CAP_ONE){
imgFinger.setBackgroundResource(R.drawable.img_fp_one);
tvFinger.setTextColor(mContext.getResources().getColor(R.color.white));
}else if(state == FingerState.CAP_TWO){
imgFinger.setBackgroundResource(R.drawable.img_fp_two);
tvFinger.setTextColor(mContext.getResources().getColor(R.color.white));
}else if(state == FingerState.NORMAL){
imgFinger.setBackgroundResource(R.drawable.img_fp_n);
tvFinger.setTextColor(mContext.getResources().getColor(R.color.white_alpha_percentage50));
}else{
imgFinger.setBackgroundResource(R.drawable.img_fp_n);
tvFinger.setTextColor(mContext.getResources().getColor(R.color.white_alpha_percentage50));
}
}
@Override
public void onClick(View v) {
Log.d("lixm","onClick...");
CapFingerManager.getInstance().startCapFpbmp();
imgFinger.requestFocus();
this.setState(FingerState.SELECTED);
}
public void setOnClickListener(OnClickListener listener){
this.listener = listener;
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
this.hasFocus = hasFocus;
if(hasFocus){
this.setState(FingerState.SELECTED);
EventBus.getDefault().register(this);
}else{
EventBus.getDefault().unregister(this);
if(curState != FingerState.CAP_TWO){ // 如果已经采集完成,在失去焦点时依然保持采集完成的状态
this.setState(FingerState.NORMAL);
fpBmp1 = null;
fpBmp2 = null;
}
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void showEventBusMsg(EventObject obj) {
String action = obj.eventAction;
if(Const.EVENT_ACTION.CAP_FINGER_INFO.equals(action)) {
int type = obj.eventType;
if (type == Const.EVENT_TYPE.TYPE_SUCCEED) {
Bitmap bmp = (Bitmap) obj.eventData;
if(curState == FingerState.SELECTED){
fpBmp1 = bmp;
setState(FingerState.CAP_ONE);
MyApplication.getMyApplication().mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if(hasFocus){
CapFingerManager.getInstance().startCapFpbmp();
}
}
},2000);
}else if(curState == FingerState.CAP_ONE){
fpBmp2 = bmp;
setState(FingerState.CAP_TWO);
if(onCapComeleptedListener != null){
onCapComeleptedListener.onCapFingerFinish(curFingerIndexName,fpBmp1,fpBmp2);
}
CapFingerManager.getInstance().stopCapFpbmp();
}
}
}else if(Const.EVENT_ACTION.CAP_FINGER_INFO_TIME_OUT.equals(action)){
}
}
/**
* 返回当前指位的名称
* @return
*/
public String getFingerName(){
return curFingerIndexName;
}
public void setOnCapComeleptedListener(OnCapComeleptedListener onCapComeleptedListener) {
this.onCapComeleptedListener = onCapComeleptedListener;
}
public interface OnCapComeleptedListener {
void onCapFingerFinish(String curFingerIndexName,Bitmap bmp1,Bitmap bmp2);
}
/**
* 指纹采集当前的状态
*/
public enum FingerState{
NORMAL,
SELECTED,
CAP_ONE,
CAP_TWO
}
}