一、原型:支付宝手机充值中话费充值按钮。
二、实现效果:
三、在res/drawable下面创建两个xml:
custom_border_txt_bg.xml用户可点击时,点击切换背景
custom_border_txt_bg2.xml不可以点击时,自定义的背景
custom_border_txt_bg.xml:
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="/apk/res/android"><item android:drawable="@null" android:state_pressed="true"><shape android:shape="rectangle"><padding android:bottom="7dp" android:left="7dp" android:right="7dp" android:top="7dp" /><corners android:radius="5dp" /><stroke android:width="1dp" android:color="@color/border_txt_color_default" /><solid android:color="@color/border_txt_color_default" /></shape></item><item android:drawable="@null"><shape android:shape="rectangle"><padding android:bottom="7dp" android:left="7dp" android:right="7dp" android:top="7dp" /><corners android:radius="5dp" /><stroke android:width="1dp" android:color="@color/border_txt_color_default" /></shape></item></selector>
custom_border_txt_bg2.xml:
<?xml version="1.0" encoding="utf-8"?><shape android:shape="rectangle"xmlns:android="/apk/res/android"><paddingandroid:bottom="7dp"android:left="7dp"android:right="7dp"android:top="7dp" /><corners android:radius="5dp" /><strokeandroid:color="@color/gray"android:width="1dp" /></shape>
四、在res/values/attrs.xml中自定义属性
<!-- 带边框点击背景变色的textview --><declare-styleable name="borderTxt"><attr name="bt_checkable" format="boolean"/><!-- 能否选择 --><attr name="bt_unCheckedColor" format="reference|color"/><!-- bt_checkable=true时,未选中时颜色 --><attr name="bt_checkedColor" format="reference|color"/><!-- bt_checkable=true时,选中时文本颜色 --><attr name="bt_text1" format="string"/><!-- 第一行文本内容 --><attr name="bt_textSize1" format="dimension"/><!-- 第一行字体大小 --><attr name="bt_text2" format="string"/><!-- 第二行文本内容 --><attr name="bt_textSize2" format="dimension"/><!-- 第二行字体大小 --></declare-styleable>
五、自定义控件代码。
package com.custom.controls.button;import android.content.Context;import android.content.res.TypedArray;import android.graphics.PointF;import android.util.AttributeSet;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.RelativeLayout;import android.widget.TextView;import com.custom.R;import com.custom.utils.StringUtil;/*** 带边框点击背景变色的textview* Created by Kevin on /5/16.*/public class BorderTxt extends RelativeLayout implements View.OnTouchListener {//不可以点击时,初始颜色private int CHECKABLE_FALSE_DEFAULT = getResources().getColor(R.color.gray);//可以点击时,初始颜色private int CHECKABLE_TRUE_DEFAULT = getResources().getColor(R.color.border_txt_color_default);//可以点击时,选中时文本颜色private int CHECKABLE_TRUE_TXT = getResources().getColor(R.color.white);//是否可以点击private boolean checkable;/*** checkable=false时:unCheckedColor代表不可点击时的初始颜色;* checkable=true时:unCheckedColor代表可点击时的初始颜色;checkedColor表示选中时的文字颜色*/private int unCheckedColor,checkedColor;private String text1, text2;//文本内容private float textSize1, textSize2;//文字大小private TextView txt1, txt2;//文本private LayoutParams txt1Params, txt2Params, relParams;//位置属性private RelativeLayout relativeLayout;//中间两行文本防止在此布局中,便于定位private PointF pointF;//存储手指按下的位置private Context mContext;//上下文public BorderTxt(Context context) {super(context);}public BorderTxt(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public BorderTxt(Context context, AttributeSet attrs) {super(context, attrs);this.mContext = context;pointF = new PointF();//初始化手指按下时的坐标对象//获取自定义属性TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.borderTxt);unCheckedColor = array.getColor(R.styleable.borderTxt_bt_unCheckedColor, CHECKABLE_TRUE_DEFAULT);checkedColor = array.getColor(R.styleable.borderTxt_bt_checkedColor, CHECKABLE_TRUE_TXT);checkable = array.getBoolean(R.styleable.borderTxt_bt_checkable, false);text1 = array.getString(R.styleable.borderTxt_bt_text1);text2 = array.getString(R.styleable.borderTxt_bt_text2);textSize1 = array.getDimensionPixelSize(R.styleable.borderTxt_bt_textSize1, 14);textSize2 = array.getDimensionPixelSize(R.styleable.borderTxt_bt_textSize2, 10);//自定义属性获取完之后,及时回收array.recycle();//初始化背景、文字颜色及事件绑定initUi();//控件位置布局relativeLayout = new RelativeLayout(mContext);relParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);relParams.addRule(CENTER_IN_PARENT);relativeLayout.setLayoutParams(relParams);txt1 = new TextView(mContext);txt1.setId(StringUtil.generateViewId());txt1.setText(text1);txt1.setTextColor(unCheckedColor);txt1.setTextSize(textSize1);//当第二行文本内容为空时,第一行居中显示,否则,将整体居中if(StringUtil.isEmpty(text2)){txt1Params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);txt1Params.addRule(RelativeLayout.CENTER_IN_PARENT);}else{txt1Params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);txt1Params.addRule(RelativeLayout.CENTER_HORIZONTAL);txt1.setGravity(Gravity.CENTER);txt2 = new TextView(mContext);txt2.setText(text2);txt2.setTextSize(textSize2);txt2.setTextColor(unCheckedColor);txt2Params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);txt2Params.addRule(RelativeLayout.CENTER_HORIZONTAL);txt2Params.addRule(BELOW, txt1.getId());relativeLayout.addView(txt2, txt2Params);}//初始化文本颜色setTxtColor(unCheckedColor);relativeLayout.addView(txt1, txt1Params);addView(relativeLayout);}@Overridepublic boolean onTouch(View v, MotionEvent event) {/*** 可以点击时,手指第一次按下时存储此时的位置坐标;* 当手指抬起或者移动与按下时的坐标任意方向上的长度大于三分之一控件长度时,* 控件的press效果失效,并改变文本颜色。*/if(checkable){if (event.getAction() == MotionEvent.ACTION_DOWN) {pointF.x = event.getX();pointF.y = event.getY();//按下setTxtColor(checkedColor);} else if (event.getAction() == MotionEvent.ACTION_UP|| getWidth() / 3 - Math.abs(event.getX() - pointF.x) < 0|| getHeight() / 3 - Math.abs(event.getY() - pointF.y) < 0) {//抬起this.setPressed(false);setTxtColor(unCheckedColor);}}return false;}/*** 设置文本颜色* @param color*/public void setTxtColor(int color){txt1.setTextColor(color);if(!StringUtil.isEmpty(text2)){txt2.setTextColor(color);}}/*** 得到是否可以点击* @return*/public boolean isCheckable() {return checkable;}/*** 设置是否可以点击* 并刷新控件状态* @param checkable*/public void setCheckable(boolean checkable) {this.checkable = checkable;refreshDrawableState();//执行改变逻辑setTxtColor(unCheckedColor);//初始化文字颜色}/*** 执行控件改变*/@Overrideprotected void drawableStateChanged() {super.drawableStateChanged();//改变时的切换逻辑initUi();}/*** 初始化背景、文字颜色及事件绑定*/private void initUi() {if(checkable){unCheckedColor = CHECKABLE_TRUE_DEFAULT;checkedColor = CHECKABLE_TRUE_TXT;setBackgroundResource(R.drawable.custom_border_txt_bg);setOnTouchListener(this);/*** 绑定点击事件监听,否则点击时无法切换背景,selector效果无效;* 我的理解就是让空间拥有获得焦点的能力,尽管什么都没做*/this.setOnClickListener(null);}else{unCheckedColor = CHECKABLE_FALSE_DEFAULT;setBackgroundResource(R.drawable.custom_border_txt_bg2);}}}
六、res\layout中定义布局文件:activity_border_txt.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"xmlns:borderTxt="/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.custom.controls.button.BorderTxtandroid:id="@+id/btt"android:layout_width="200dp"android:layout_height="120dp"android:layout_centerHorizontal="true"android:layout_gravity="center"borderTxt:bt_text1="第一行"borderTxt:bt_text2="第二行" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="60dp"android:layout_marginLeft="20dp"android:layout_marginRight="20dp"android:layout_marginTop="20dp"android:orientation="horizontal"><com.custom.controls.button.BorderTxtandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_centerHorizontal="true"android:layout_marginLeft="5dp"android:layout_marginRight="5dp"android:layout_weight="1"borderTxt:bt_text1="10元"borderTxt:bt_text2="售价10.00元" /><com.custom.controls.button.BorderTxtandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_centerHorizontal="true"android:layout_marginLeft="5dp"android:layout_marginRight="5dp"android:layout_weight="1"borderTxt:bt_checkable="true"borderTxt:bt_text1="20元"borderTxt:bt_text2="售价20.00元" /><com.custom.controls.button.BorderTxtandroid:id="@+id/border_txt_flow"android:layout_width="0dp"android:layout_height="match_parent"android:layout_centerHorizontal="true"android:layout_marginLeft="5dp"android:layout_marginRight="5dp"android:layout_weight="1"borderTxt:bt_text1="30M" /></LinearLayout><Buttonandroid:id="@+id/btn_change_border_txt"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="改变按钮可选与否"></Button></LinearLayout>
七、注意
1、颜色是在colors.xml中自定义的,
R.color.gray #C0C0C0
R.color.border_txt_color_default #3399FF
R.color.white #FFFFFF
2、this.setOnClickListener(null);一定要写,绑定点击事件监听,否则点击时无法切换背景,selector效果无效;
我的理解就是让空间拥有获得焦点的能力,尽管什么都没做。
3、注意setCheckable()和drawableStateChanged()中的逻辑位置,此例中
setCheckable()中的setTxtColor(unCheckedColor)如果放到drawableStateChanged()中的话,会因为drawableStateChanged()的重复调用而导致预期结果异常。
4、使用时,一定要加上命名空间
xmlns:borderTxt="/apk/res-auto"
5、StringUtils工具:
package com.custom.utils;import java.util.concurrent.atomic.AtomicInteger;/*** Created by Kevin on /5/13.*/public class StringUtil {//结合下面的方法可以来或者随机并不重复的控件IDprivate static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);public static int generateViewId() {for (; ; ) {final int result = sNextGeneratedId.get();// aapt-generated IDs have the high byte nonzero; clamp to the range under that.int newValue = result + 1;if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.if (pareAndSet(result, newValue)) {return result;}}}public static boolean isEmpty(String str) {if (null == str || "".equals(str.trim())) {return true;}else{return false;}}}
以下问题可以直接点击了解:
declare-styleable:自定义控件的属性自定义控件中setText()设置字体相同大小无法与原生控件一致
项目的github地址如下:/gubaijin/custom-controls-and-result-set