Android 实现加减自定义控件

✍️作者简介:小北编程(专注于HarmonyOS、Android、Java、Web、TCP/IP等技术方向)
🐳博客主页:开源中国稀土掘金51cto博客博客园知乎简书慕课网CSDN
🔔如果文章对您有一定的帮助请👉关注✨、点赞👍、收藏📂、评论💬。
🔥如需转载请参考【转载须知】

简介

本文将介绍一个自定义的CounterView控件,展示了如何创建一个可定制的数字输入控件,该控件可以设置最小值、最大值、步进值以及其他属性。

在这里插入图片描述

控件结构

CounterView控件由以下几个元素组成:
减少按钮(decreaseButton):用于递减数值。
增加按钮(increaseButton):用于递增数值。
数值编辑框(valueEditText):用于显示和编辑数值。
自定义视图(customView):用于显示自定义内容(如果需要)。

控件属性

CounterView控件具有以下属性:
最小值(minValue):控制数值的最小允许值。
最大值(maxValue):控制数值的最大允许值。
步进值(increment):控制递增或递减时的步长。
默认值(defaultValue):控件的初始数值。
小数位数控制(decimalEnabled和decimalPlaces):控制是否允许输入小数以及小数位数的限制。

控件功能

CounterView控件具有以下功能:
支持递增和递减操作。
限制输入数值的范围在最小值和最大值之间。
支持小数输入,可以根据需要设置小数位数。
提供数值变化的监听器,可以在数值变化时进行相应的操作。
主要方法
以下是CounterView控件中的一些重要方法的简要说明:

方法功能
init初始化控件的各个元素,包括按钮、编辑框和自定义视图
applyAttributes用于应用从XML布局中传递的属性
setDefaultValue设置控件的默认数值
setMinValue设置数值的最小
setMaxValue设置最大值限制
setIncrement设置递增或递减的步长
setValue设置控件显示的数值
decreaseValue递减数值,并根据设置的范围进行限制和提示
increaseValue递增数值,并根据设置的范围进行限制和提示
setDecimalEnabled用于启用或禁用小数输入,并根据设置的小数位数进行限制
applyDecimalFilter应用小数过滤器,以确保输入的数值符合设置的小数位数要求
notifyValueChanged在数值发生变化时通知监听器
setOnValueChangedListener设置数值变化的监听器,以便在数值发生变化时进行相应的操作
控件代码可根据你的需求自行修改
import android.content.Context;
import android.icu.math.BigDecimal;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Spanned;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;

/**
 * Copyright (C) 2023-2024 Author
 *
 * 数量加减控件
 * 前面减号后面加号中间输入
 *
 * @author   xiaolu
 * @date     2023/11/8
 * @version  1.0.0
 */
public class CounterView extends LinearLayout {
    private Button decreaseButton;  // 减少按钮
    private Button increaseButton;  // 增加按钮
    private EditText valueEditText; // 数值编辑框
    private View customView;        // 自定义视图

    private BigDecimal minValue = BigDecimal.ZERO;             // 最小值
    private BigDecimal maxValue = BigDecimal.valueOf(100); // 最大值
    private BigDecimal increment = BigDecimal.ONE;             // 步进值
    private BigDecimal defaultValue = BigDecimal.ZERO;         // 默认值
    private OnValueChangedListener valueChangedListener;       // 数值变化监听器
    private boolean decimalEnabled = false;                    // 是否允许小数
    private int decimalPlaces = 0;                             // 小数位数

    /**
     * 构造方法,用于创建 CustomCounterView 实例。
     *
     * @param context 上下文参数,不能为空
     */
    public CounterView(Context context) {
        super(context);
        init(context);
    }

    /**
     * 构造方法,用于创建 CustomCounterView 实例。
     *
     * @param context 上下文参数,不能为空
     * @param attrs   属性参数
     */
    public CounterView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
        applyAttributes(context, attrs);
    }

    /**
     * 构造方法,用于创建 CustomCounterView 实例。
     *
     * @param context  上下文参数,不能为空
     * @param attrs    属性参数
     * @param defStyle 默认样式参数
     */
    public CounterView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
        applyAttributes(context, attrs);
    }

    // 初始化方法
    private void init(Context context) {
        decreaseButton = new Button(context);
        decreaseButton.setText("-");
        addView(decreaseButton);

        valueEditText = new EditText(context);
        valueEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
        addView(valueEditText);

        increaseButton = new Button(context);
        increaseButton.setText("+");
        addView(increaseButton);

        customView = new View(context);
        addView(customView);

        decreaseButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                decreaseValue();
            }
        });

        increaseButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                increaseValue();
            }
        });

        valueEditText.addTextChangedListener(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) {
                try {
                    BigDecimal currentValue = new BigDecimal(s.toString());
                    if (currentValue.compareTo(minValue) < 0) {
                        ToastUtil.showShort("输入数字不能小于最小值 " + minValue);
                        setEditTextValue(minValue.toString());
                    } else if (currentValue.compareTo(maxValue) > 0) {
                        ToastUtil.showShort("输入数字不能大于最大值 " + maxValue);
                        setEditTextValue(maxValue.toString());
                    }
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
    }

    // 应用属性方法
    private void applyAttributes(Context context, AttributeSet attrs) {

    }

    // 设置默认值方法
    public void setDefaultValue(BigDecimal defaultValue) {
        this.defaultValue = defaultValue;
        setValue(defaultValue);
    }

    // 设置最小值方法
    public void setMinValue(BigDecimal minValue) {
        this.minValue = minValue;
    }

    // 设置最大值方法
    public void setMaxValue(BigDecimal maxValue) {
        this.maxValue = maxValue;
    }

    // 设置步进值方法
    public void setIncrement(BigDecimal increment) {
        this.increment = increment;
    }

    // 获取当前数值方法
    public BigDecimal getValue() {
        return BigDecimal.ZERO;
    }

    // 设置数值方法
    public void setValue(BigDecimal value) {
        valueEditText.setText(value.toString());
        // 将光标移动到文本末尾
        valueEditText.setSelection(valueEditText.getText().length());
    }

    // 更新文本数据方法
    private void setEditTextValue(String value) {
        valueEditText.setText(value);
        // 将光标移动到文本末尾
        valueEditText.setSelection(valueEditText.getText().length());
    }

    // 减少数值方法
    private void decreaseValue() {
        try {
            String input = valueEditText.getText().toString().trim();
            if (!input.isEmpty()) {
                BigDecimal currentValue = new BigDecimal(input);
                BigDecimal newValue = currentValue.subtract(increment);
                if (newValue.compareTo(minValue) < 0) {
                    ToastUtil.showShort("输入数字不能小于最小值 " + minValue);
                    newValue = minValue;
                }
                setEditTextValue(newValue.toString());
                notifyValueChanged();
            }
        } catch (NumberFormatException e) {
            setEditTextValue("0");
            notifyValueChanged();
        }
    }

    // 增加数值方法
    private void increaseValue() {
        try {
            String input = valueEditText.getText().toString().trim();
            if (!input.isEmpty()) {
                BigDecimal currentValue = new BigDecimal(input);
                BigDecimal newValue = currentValue.add(increment);
                if (newValue.compareTo(maxValue) > 0) {
                    ToastUtil.showShort("输入数字不能大于最大值 " + maxValue);
                    newValue = maxValue;
                }
                setEditTextValue(newValue.toString());
                notifyValueChanged();
            }
        } catch (NumberFormatException e) {
            setEditTextValue("0");
            notifyValueChanged();
        }
    }

    /**
     * 开启小数输入
     * @param decimalEnabled 是否开启  开:true 关:false
     * @param decimalPlaces  小数位数控制,如果是-1不控制位数,否则就是小数位数
     */
    public void setDecimalEnabled(boolean decimalEnabled, int decimalPlaces) {
        this.decimalEnabled = decimalEnabled;
        this.decimalPlaces = decimalPlaces;
        if (decimalEnabled) {
            if (decimalPlaces == -1) {
                valueEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
            } else {
                valueEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL | InputType.TYPE_NUMBER_FLAG_SIGNED);
            }
        } else {
            valueEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
        }
        applyDecimalFilter();
    }

    private void applyDecimalFilter() {
        valueEditText.setFilters(new InputFilter[]{new InputFilter() {
            @Override
            public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
                if (decimalEnabled) {
                    String value = dest.toString().substring(0, dstart) + source.toString() + dest.toString().substring(dend);
                    if (!value.isEmpty() && !value.equals(".") && !value.equals("-")) {
                        try {
                            BigDecimal newValue = new BigDecimal(value);
                            if (decimalPlaces >= 0 && newValue.scale() > decimalPlaces) {
                                return "";
                            }
                        } catch (NumberFormatException | ArithmeticException e) {
                            return "";
                        }
                    }
                }
                return null;
            }
        }});
    }

    // 通知值已经改变方法
    private void notifyValueChanged() {
        if (valueChangedListener != null) {
            valueChangedListener.onValueChanged(new BigDecimal(valueEditText.getText().toString()));
        }
    }

    // 设置数值变化监听器方法
    public void setOnValueChangedListener(OnValueChangedListener listener) {
        this.valueChangedListener = listener;
    }

    // 数值变化监听器接口
    public interface OnValueChangedListener {
        void onValueChanged(BigDecimal newValue);
    }
}

xml引用
    <你的包名.CounterView
        android:id="@+id/counterView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

代码中设置
	binding.counterView.setMinValue(BigDecimal.ZERO);
        binding.counterView.setMaxValue(BigDecimal.TEN);
        binding.counterView.setIncrement(BigDecimal.ONE);
        binding.counterView.setDefaultValue(BigDecimal.ZERO);
        binding.counterView.setDecimalEnabled(true,2);

        binding.counterView.setOnValueChangedListener(new CounterView.OnValueChangedListener() {
            @Override
            public void onValueChanged(BigDecimal newValue) {
                // 在这里处理数值变化事件
                Logger.d("CounterViewActivity CounterView onValueChanged 数据变化 " + newValue.toString());
            }
        });

本文介绍了CounterView控件的基本结构、功能和使用方法,希望对开发人员在创建定制化数字输入控件时有所帮助。通过灵活应用这些方法和功能,可以根据具体的应用需求进行定制和调整,为用户提供更好的应用体验。

无论是哪个阶段,坚持努力都是成功的关键。不要停下脚步,继续前行,即使前路崎岖,也请保持乐观和勇气。相信自己的能力,你所追求的目标定会在不久的将来实现。加油!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小北编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值