使用效果如下:
QuantityView.gif在商城类软件中,�数量控件可以说是必备控件之一,网上开源的数量控件也有很多,但是还是想通过自己实现一把,理解其中的奥秘之处。
1、需求分析(减号按钮和加号按钮 分割线 输入框)
1.1、最左边的控件和最右边的控件中的文字字体大小(op_text_size)、颜色(op_text_color)、宽度(op_width)都是一致的,不同之处在于:文字(sub_text和add_text)和圆角位置不同(背景sub_background和add_background)
1.2、分割线的宽度(divider_width)和颜色(divider_color)
1.3、输入框的文字大小(input_text_size)、颜色(input_text_color)、宽度(input_text_width)、背景(input_background)
1.4、最大值(max_num)、当前值(num)、最小值(min_num)、输入框是否可输入(can_input)、点击加减号是否立即改变输入框内的数值(is_now_change)
1.5、改变数值时,根据当前数值设置加减号按钮是否可用
1.6、添加数量改变监听(OnNumChangeListener)
1.7、输入框设置为不可编辑时,点击输入框回调(setCenterClickListener),弹出对话框,让用户输入数值
1.8、设置点击加减号按钮后,不能立刻改变输入框的值时,设置点击加减好回调(OnSubAddClickCallBack),接口调用成功后,再更改
最终的属性如下
<declare-styleable name="QuantityView">
<!--加减号共同属性 op开头-->
<attr name="op_width" format="dimension" />
<attr name="op_text_size" format="dimension" />
<attr name="op_text_color" format="color" />
<!--减号按钮属性 sub开头-->
<attr name="sub_background" format="reference|color" />
<attr name="sub_text" format="string" />
<!--加号按钮属性 add开头-->
<attr name="add_background" format="color|reference" />
<attr name="add_text" format="string" />
<!--输入框属性 input开头-->
<attr name="input_text_size" format="dimension" />
<attr name="input_text_width" format="dimension" />
<attr name="input_text_color" format="color" />
<attr name="input_background" format="reference|color" />
<!--分割线颜色和宽度-->
<attr name="divider_width" format="dimension" />
<attr name="divider_color" format="color" />
<!--当前数量,最小值,最大值,是否可输入,点击加减号是否立刻修改输入框的值-->
<attr name="num" format="integer" />
<attr name="min_num" format="integer" />
<attr name="max_num" format="integer" />
<attr name="can_input" format="boolean" />
<attr name="is_now_change" format="boolean" />
</declare-styleable>
2、自定义控件QantityView源码
public class QuantityView extends LinearLayout {
private static final String TAG = "QuantityView";
//加减按钮公共属性
private int opPadding;
private int opWidth;
private int opTextSize;
private ColorStateList opTextColor;
//减号按钮属性
private Drawable subBackground;
private String subText;
//加号按钮属性
private Drawable addBackground;
private String addText;
//输入框属性
private int inputTextSize, inputTextWidth;
private int inputTextColor;
private Drawable inputBackground;
private TextWatcher textWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
afterTextChangedExecute = true;
if (TextUtils.isEmpty(s)) {
return;
}
setNum(Integer.parseInt(s.toString()));
}
};
//当前数量,最大值,最小值,是否可输入,点击加减号是否立刻修改输入框的值
private int num;
private int maxNum;
private int minNum;
private boolean canInput, isNowChange = true;
//分割线属性
private int dividerWidth;
private int dividerColor;
//所有控件
private ImageView addImageView, subImageView;
private TextView addTextView, subTextView;
private EditText editText;
private View clickView, leftDivider, rightDivider;
private boolean afterTextChangedExecute = false;//afterTextChanged执行了
public QuantityView(Context context) {
this(context, null);
}
public QuantityView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public QuantityView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.QuantityView, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
//加减按钮公共属性
case R.styleable.QuantityView_op_width:
opWidth = a.getLayoutDimension(attr, 0);
break;
// case R.styleable.QuantityView_op_padding:
// opPadding = a.getDimensionPixelSize(attr, 0);
// break;
case R.styleable.QuantityView_op_text_color:
opTextColor = a.getColorStateList(attr);
break;
case R.styleable.QuantityView_op_text_size:
opTextSize = a.getDimensionPixelSize(attr, 15);
break;
//减号按钮属性
case R.styleable.QuantityView_sub_background:
subBackground = a.getDrawable(attr);
break;
case R.styleable.QuantityView_sub_text:
subText = a.getString(attr);
break;
//加号按钮属性
case R.styleable.QuantityView_add_background:
addBackground = a.getDrawable(attr);
break;
case R.styleable.QuantityView_add_text:
addText = a.getString(attr);
break;
//分割线属性
case R.styleable.QuantityView_divider_color:
dividerColor = a.getColor(attr, 0);
break;
case R.styleable.QuantityView_divider_width:
dividerWidth = a.getLayoutDimension(attr, 0);
break;
//输入框属性
case R.styleable.QuantityView_input_background:
inputBackground = a.getDrawable(attr);
break;
case R.styleable.QuantityView_input_text_color:
inputTextColor = a.getColor(attr, Color.parseColor("#333333"));
break;
case R.styleable.QuantityView_input_text_size:
inputTextSize = a.getDimensionPixelSize(attr, 15);
break;
case R.styleable.QuantityView_input_text_width:
inputTextWidth = a.getLayoutDimension(attr, 0);
break;
//其他公共属性
case R.styleable.QuantityView_can_input:
canInput = a.getBoolean(attr, true);
break;
case R.styleable.QuantityView_num:
num = a.getInt(attr, 0);
break;
case R.styleable.QuantityView_min_num:
minNum = a.getInt(attr, 1);
break;
case R.styleable.QuantityView_max_num:
maxNum = a.getInt(attr, 200);
break;
case R.styleable.QuantityView_is_now_change:
isNowChange = a.getBoolean(attr, true);
break;
}
}
a.recycle();
initView();
}
private void initView() {
setFocusable(true);
setFocusableInTouchMode(true);
// inputTextSize = dip2px(inputTextSize);
// opPadding = dip2px(opPadding);
//减号按钮
// LayoutParams subParams = new LayoutParams(opWidth, ViewGroup.LayoutParams.MATCH_PARENT);
// subImageView = new ImageView(getContext());
// subImageView.setImageDrawable(subSrc);
// subImageView.setPadding(opPadding, opPadding, opPadding, opPadding);
// subImageView.setBackground(subBackground);
// subImageView.setLayoutParams(subParams);
// subImageView.setOnClickListener(new OnClickListener() {
// @Override
// public void onClick(View v) {
// if (isNowChange) {
// num--;
// setNum(num);
// } else {
// if (onSubAddClickCallBack != null) {
//
// onSubAddClickCallBack.onClickCallBack(QuantityView.this, num - 1);
// }
// }
//
// }
// });
LayoutParams subParams = new LayoutParams(opWidth, ViewGroup.LayoutParams.MATCH_PARENT);
subTextView = new TextView(getContext());
// subTextView.setPadding(opPadding, opPadding, opPadding, opPadding);
subTextView.setBackgroundDrawable(subBackground);
subTextView.setLayoutParams(subParams);
subTextView.setClickable(true);
subTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, opTextSize);
subTextView.setTextColor(opTextColor);
subTextView.setGravity(Gravity.CENTER);
subTextView.setText(subText);
subTextView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isNowChange) {
num--;
setNum(num);
} else {
if (onSubAddClickCallBack != null) {
onSubAddClickCallBack.onClickCallBack(QuantityView.this, num - 1);
}
}
}
});
//加号按钮
// LayoutParams addParams = new LayoutParams(opWidth, ViewGroup.LayoutParams.MATCH_PARENT);
// addImageView = new ImageView(getContext());
// addImageView.setImageDrawable(addSrc);
// addImageView.setPadding(opPadding, opPadding, opPadding, opPadding);
// addImageView.setBackground(addBackground);
// addImageView.setLayoutParams(addParams);
//
// addImageView.setOnClickListener(new OnClickListener() {
// @Override
// public void onClick(View v) {
// if (isNowChange) {
// if (!TextUtils.isEmpty(editText.getText() + ""))
// num++;
// setNum(num);
// } else {
// if (onSubAddClickCallBack != null) {
// onSubAddClickCallBack.onClickCallBack(QuantityView.this, num + 1);
// }
// }
//
// }
// });
LayoutParams addParams = new LayoutParams(opWidth, ViewGroup.LayoutParams.MATCH_PARENT);
addTextView = new TextView(getContext());
// addTextView.setPadding(opPadding, opPadding, opPadding, opPadding);
addTextView.setBackgroundDrawable(addBackground);
addTextView.setLayoutParams(addParams);
addTextView.setClickable(true);
addTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, opTextSize);
addTextView.setTextColor(opTextColor);
addTextView.setGravity(Gravity.CENTER);
addTextView.setText(addText);
addTextView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isNowChange) {
if (!TextUtils.isEmpty(editText.getText() + ""))
num++;
setNum(num);
} else {
if (onSubAddClickCallBack != null) {
onSubAddClickCallBack.onClickCallBack(QuantityView.this, num + 1);
}
}
}
});
//分割线
LayoutParams leftDividerParams = new LayoutParams(dividerWidth, ViewGroup.LayoutParams.MATCH_PARENT);
leftDivider = new View(getContext());
leftDivider.setBackgroundColor(dividerColor);
leftDivider.setLayoutParams(leftDividerParams);
LayoutParams rightDividerParams = new LayoutParams(dividerWidth, ViewGroup.LayoutParams.MATCH_PARENT);
rightDivider = new View(getContext());
rightDivider.setBackgroundColor(dividerColor);
rightDivider.setLayoutParams(rightDividerParams);
//输入布局
FrameLayout frameLayout = new FrameLayout(getContext());
frameLayout.setBackgroundColor(getContext().getResources().getColor(android.R.color.white));
LayoutParams inputParams = new LayoutParams(inputTextWidth, ViewGroup.LayoutParams.MATCH_PARENT);
inputParams.gravity = Gravity.CENTER;
frameLayout.setLayoutParams(inputParams);
//输入框
FrameLayout.LayoutParams editTextParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
editText = new EditText(getContext());
editText.setPadding(0, 0, 0, 0);
editText.setBackgroundColor(getResources().getColor(android.R.color.white));
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
editText.setSingleLine();
editText.setGravity(Gravity.CENTER);
editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, inputTextSize);
editText.setTextColor(inputTextColor);
editText.setLayoutParams(editTextParams);
editText.addTextChangedListener(textWatcher);
editText.setClickable(true);
editText.setEnabled(canInput);
FrameLayout.LayoutParams clickViewParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
clickView = new View(getContext());
clickView.setLayoutParams(clickViewParams);
clickView.setClickable(true);
clickView.setVisibility(canInput ? GONE : VISIBLE);
frameLayout.addView(editText);
frameLayout.addView(clickView);
//数量控件点击按钮
this.setClickable(true);
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "onClick:显示对话框界面");
}
});
addView(subTextView);
addView(leftDivider);
addView(frameLayout);
addView(rightDivider);
addView(addTextView);
setNum(num);
}
public int getNum() {
return num;
}
public int getMinNum() {
return minNum;
}
public int getMaxNum() {
return maxNum;
}
/**
* 设置当前数量
*
* @param newNum
*/
public QuantityView setNum(int newNum) {
// 设置的数量小于最小值,使用最小值作为当前数量,大于最大值,使用最大值作为当前数量,修改按钮是否可用
num = newNum;
if (newNum >= maxNum) {
num = maxNum;
addTextView.setEnabled(false);
} else {
addTextView.setEnabled(true);
}
if (newNum <= minNum) {
num = minNum;
subTextView.setEnabled(false);
} else {
subTextView.setEnabled(true);
}
//设置之前先移除,再添加避免,重复触发afterTextChanged
editText.removeTextChangedListener(textWatcher);
editText.setText(num + "");
editText.setSelection((num + "").length());
editText.addTextChangedListener(textWatcher);
if (onNumChangeListener != null)
onNumChangeListener.onNuberChange(QuantityView.this
, editText, num);
return this;
}
/**
* 设置最小值
*
* @param minNum 最小值
* @return
*/
public QuantityView setMinNum(int minNum) {
this.minNum = minNum;
setNum(num);
return this;
}
/**
* @param maxNum 设置最大值
* @return
*/
public QuantityView setMaxNum(int maxNum) {
this.maxNum = maxNum;
setNum(num);
return this;
}
/**
* 设置是否可输入
*
* @param canInput
* @return
*/
public QuantityView setCanInput(boolean canInput) {
this.canInput = canInput;
editText.setEnabled(canInput);
clickView.setVisibility(canInput ? GONE : VISIBLE);
return this;
}
/**
* @param nowChange 设置是否立即改变输入框的内容
* @return
*/
public QuantityView setNowChange(boolean nowChange) {
isNowChange = nowChange;
return this;
}
/**
* 设置中间的点击事件
*
* @param onClickListener
* @return
*/
public QuantityView setCenterClickListener(@NonNull OnClickListener onClickListener) {
clickView.setOnClickListener(onClickListener);
return this;
}
/**
* dp转像素
*
* @param dpValue
* @return
*/
public int dip2px(float dpValue) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
//数量改变监听
private OnNumChangeListener onNumChangeListener;
public QuantityView setOnNumChangeListener(OnNumChangeListener onNumChangeListener) {
this.onNumChangeListener = onNumChangeListener;
return this;
}
public interface OnNumChangeListener {
void onNuberChange(QuantityView cartQuantityView, EditText editText, int num);
}
//点击加号减号按钮回调接口
private OnSubAddClickCallBack onSubAddClickCallBack;
public QuantityView setOnSubAddClickCallBack(OnSubAddClickCallBack onSubAddClickCallBack) {
this.onSubAddClickCallBack = onSubAddClickCallBack;
return this;
}
public interface OnSubAddClickCallBack {
void onClickCallBack(QuantityView quantitySkuDesView, int num);
}
}
3、使用篇
3.1、在布局文件中使用
<com.aositeluoke.quantityviewdemo.QuantityView
android:id="@+id/quantity"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:background="@drawable/shape_jd_quantity"
android:padding="1dp"
app:add_background="@drawable/selector_jd_add_bg"
app:add_text="+"
app:can_input="false"
app:divider_color="@color/color_232326"
app:divider_width="1dp"
app:input_background="@android:color/white"
app:input_text_color="@color/color_232326"
app:input_text_size="16dp"
app:input_text_width="50dp"
app:max_num="15"
app:min_num="0"
app:num="10"
app:op_text_color="@color/selector_jg_quantity"
app:op_text_size="20dp"
app:op_width="30dp"
app:sub_background="@drawable/selector_jd_sub_bg"
app:sub_text="-" />
<!--
android:background="@drawable/shape_jd_quantity"//根布局背景(带四个圆角)
android:padding="1dp"
app:add_background="@drawable/selector_jd_add_bg" //加号按钮背景
app:add_text="+" //加号按钮文本
app:can_input="false" //是否可输入
app:divider_color="@color/color_232326" //分割线颜色
app:divider_width="1dp" 分割线宽度
app:input_background="@android:color/white" //输入框背景
app:input_text_color="@color/color_232326" 输入框文字颜色
app:input_text_size="16dp" //输入框问题大小
app:input_text_width="50dp" //输入框宽度
app:max_num="15" // 最大值
app:min_num="0" //最小值
app:num="10" // 当前值
app:op_text_color="@color/selector_jg_quantity" // 加减号按钮文字颜色
app:op_text_size="20dp" //加减按钮文字大小
app:op_width="30dp" 加减按钮宽度
app:sub_background="@drawable/selector_jd_sub_bg" //减号按钮背景
app:sub_text="-" //减号按钮文本
-->
shape_jd_quantity.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke
android:width="1dp"
android:color="@color/color_232326" />
<corners android:radius="@dimen/jd_quantity_radius" />
</shape>
selector_jd_add_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--可点击按下时-->
<item android:state_pressed="true">
<shape>
<solid android:color="@color/color_e3e5e9" />
<corners android:bottomRightRadius="@dimen/jd_quantity_radius" android:topRightRadius="@dimen/jd_quantity_radius" />
</shape>
</item>
<!--可点击 未按下时-->
<item android:state_pressed="false">
<shape>
<solid android:color="@color/white" />
<corners android:bottomRightRadius="@dimen/jd_quantity_radius" android:topRightRadius="@dimen/jd_quantity_radius" />
</shape>
</item>
</selector>
加减号文字颜色selector_jg_quantity.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--不可点击时颜色-->
<item android:color="#bfbfbf" android:state_enabled="false" />
<!--可点击时颜色-->
<item android:color="@color/color_232326" android:state_enabled="true" />
</selector>
减号按钮背景selector_jd_sub_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--可点击按下时-->
<item android:state_pressed="true">
<shape>
<solid android:color="@color/color_e3e5e9" />
<corners android:bottomLeftRadius="@dimen/jd_quantity_radius" android:topLeftRadius="@dimen/jd_quantity_radius" />
</shape>
</item>
<!--可点击 未按下时-->
<item android:state_pressed="false">
<shape>
<solid android:color="@color/white" />
<corners android:bottomLeftRadius="@dimen/jd_quantity_radius" android:topLeftRadius="@dimen/jd_quantity_radius" />
</shape>
</item>
</selector>
颜色文件colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="white">#ffffff</color>
<color name="color_e3e5e9">#e3e5e9</color>
<color name="color_232326">#232326</color>
<color name="transparent">#00000000</color>
</resources>
尺寸文件dimens.xml
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="jd_quantity_radius">2dp</dimen>
</resources>
3.2、在代码中设置监听
//点击加减号调用接口成功后,再改变数量控件的数量
quantity.setNowChange(false);//设置不能立即改变,等接口调用成功后再更新
quantity.setOnSubAddClickCallBack(new QuantityView.OnSubAddClickCallBack() {
@Override
public void onClickCallBack(QuantityView quantitySkuDesView, int num) {
//数量为0,显示删除提示对话框
if (num == 0) {
confirmDialog.show();
} else {
quantitySkuDesView.setNum(num);
}
}
});
//点击中间输入框,弹出对话框让用户输入数量
quantity.setCanInput(false);
quantity.setCenterClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialogQuantity.setMinNum(quantity.getMinNum() == 0 ? 1 : 0);
dialogQuantity.setMaxNum(quantity.getMaxNum());
dialogQuantity.setNum(quantity.getNum());
inputDialog.show();
}
});
//数量改变回调
quantity.setOnNumChangeListener(new QuantityView.OnNumChangeListener() {
@Override
public void onNuberChange(QuantityView cartQuantityView, EditText editText, int num) {
}
});
网友评论