【android-自定义控件】实现计数器效果分析及源码

本文将介绍如何使用自定义控件实现计数器,效果如下所示:
主要功能:
1、实现计数功能
2、到达数值上限/下限无法点击
3、自定义控件暴露方法供外部使用
【android-自定义控件】实现计数器效果分析及源码_第1张图片

实现思路:
1、创建一个自定义控件的布局
2、写一个自定义组合控件的类,继承自LinearLayout/RelativLayout,实现前三个方法,统一构造函数的入口
3、使用时候将该类名copy references然后在布局中使用即可
4、利用自定义属性设置max,min,step

部分代码:
InputNumberView.class

/**
 * description 自定义组合控件
 * 使用步骤:
 * 1、继承自LinearLayout/RelativeLayout,实现前3个构造方法,确保统一入口
 * 2、把其他的子view加载进来
 * 3、使用时候将该类名copy references然后在布局中使用即可
 * 4、处理数据,处理事件
 * 5、暴露接口
 * create by xiaocai on 2020/6/21
 */
public class InputNumberView extends RelativeLayout {

    private static final String TAG = "InputNumberView";
    //当前的数值为0
    private int mCurrentNumber = 0;
    private View mMinusBtn;
    private View mPlusBtn;
    private EditText mNumberEt;
    private OnNumberNumberChangeListener mOnNumberNumberChangeListener = null;
    private int mMax;
    private int mMin;
    private int mStep;
    private boolean mDisable;
    private int mResourceId;

    public InputNumberView(Context context) {
        this(context, null);
    }

    public InputNumberView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public InputNumberView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
        //获取相关属性
        initAttrs(context, attrs);

        //设置事件
        sertUpEvent();
    }

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.InputNumberView);
        mMax = typedArray.getInt(R.styleable.InputNumberView_max, 0);
        mMin = typedArray.getInt(R.styleable.InputNumberView_min, 0);
        mStep = typedArray.getInt(R.styleable.InputNumberView_step, 1);
        mDisable = typedArray.getBoolean(R.styleable.InputNumberView_disable, false);
        mResourceId = typedArray.getResourceId(R.styleable.InputNumberView_btnBackground, -1);
        Log.d(TAG, "mMax----->" + mMax);
        Log.d(TAG, "mMin----->" + mMin);
        Log.d(TAG, "mStep----->" + mStep);
        Log.d(TAG, "mDisable----->" + mDisable);
        Log.d(TAG, "mResourceId----->" + mResourceId);
    }

    public int getMax() {
        return mMax;
    }

    public void setMax(int max) {
        mMax = max;
    }

    public int getMin() {
        return mMin;
    }

    public void setMin(int min) {
        mMin = min;
    }

    public int getStep() {
        return mStep;
    }

    public void setStep(int step) {
        mStep = step;
    }

    public boolean isDisable() {
        return mDisable;
    }

    public void setDisable(boolean disable) {
        mDisable = disable;
    }

    public int getResourceId() {
        return mResourceId;
    }

    public void setResourceId(int resourceId) {
        mResourceId = resourceId;
    }

    private void sertUpEvent() {
        //-
        mMinusBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //在-到上限的时候设置可以+
                mPlusBtn.setEnabled(true);
                mCurrentNumber -= mStep;
                if (mCurrentNumber <= mMin) {
                    mCurrentNumber = mMin;
                    Log.d(TAG, "超出下限");
                    //设置button为不可点击
                    mMinusBtn.setEnabled(!mDisable);
                }
                updateText();
            }
        });

        //+
        mPlusBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mCurrentNumber += mStep;
                //在+到上限的时候设置可以-
                mMinusBtn.setEnabled(true);
                if (mCurrentNumber >= mMax) {
                    mCurrentNumber = mMax;
                    Log.d(TAG, "超出上限");
                    //设置button为不可点击
                    mPlusBtn.setEnabled(!mDisable);
                }
                updateText();
            }
        });
    }

    /**
     * 更新数字
     */
    private void updateText() {
        mNumberEt.setText(mCurrentNumber + "");
        //更新数据,让listen监听数据变化
        if (mOnNumberNumberChangeListener != null) {
            mOnNumberNumberChangeListener.onNumberChange(mCurrentNumber);
        }
    }


    private void initView(Context context) {
        //将子view加载进来
        //inflate中的参数:布局文件,根布局(此处的根布局就是当前这个布局即this),attach(true:把从布局中加载的view绑定到当前布局中去;false:需要自己手动添加一下)
        //attach:true的情况(默认为true,可以不填内容)
        LayoutInflater.from(context).inflate(R.layout.input_number_view, this, true);
        //attach:false
//        View view = LayoutInflater.from(context).inflate(R.layout.input_number_view, this, false);
//        this.addView(view);

        //找到控件
        mMinusBtn = findViewById(R.id.minus_btn);
        mPlusBtn = findViewById(R.id.plus_btn);
        mNumberEt = findViewById(R.id.number);

        //初始化控件值
        updateText();
    }

    /**
     * 暴露方法让外部去设置数值
     *
     * @return
     */
    public int getNumber() {
        return mCurrentNumber;
    }

    public void setNumver(int number) {
        mCurrentNumber = number;
        updateText();
    }

    public void setOnNumberNumberChangeListener(OnNumberNumberChangeListener listener) {
        this.mOnNumberNumberChangeListener = listener;
    }

    public interface OnNumberNumberChangeListener {
        void onNumberChange(int value);
    }

}

MainActivity.class:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        InputNumberView inputNumberView=findViewById(R.id.number_view);
        inputNumberView.setOnNumberNumberChangeListener(new InputNumberView.OnNumberNumberChangeListener() {
            @Override
            public void onNumberChange(int value) {
                Toast.makeText(MainActivity.this, "num is "+value, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:xiaocai="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal"
    android:gravity="center"
    tools:context=".MainActivity">

    <com.example.customview.customView.InputNumberView
        android:id="@+id/number_view"
        android:layout_width="wrap_content"
        xiaocai:max="10"
        xiaocai:min="-10"
        xiaocai:disable="true"
        xiaocai:step="2"
        xiaocai:btnBackground="@drawable/shape_num_btn_bg_right"
        android:layout_height="wrap_content" />
</LinearLayout>

input_number_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="40dp"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/minus_btn"
        android:layout_width="80dp"
        android:layout_height="match_parent"
        android:background="@drawable/selector_num_btn_bg_left"
        android:gravity="center"
        android:text="-"
        android:textSize="30sp" />

    <EditText
        android:id="@+id/number"
        android:layout_width="80dp"
        android:layout_height="match_parent"
        android:background="@drawable/shape_num_inp_bg"
        android:focusable="false"
        android:gravity="center"
        android:text="0"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/plus_btn"
        android:layout_width="80dp"
        android:layout_height="match_parent"
        android:background="@drawable/selector_num_btn_bg_right"
        android:gravity="center"
        android:text="+"
        android:textSize="18sp" />

</LinearLayout>

你可能感兴趣的:(【android-自定义控件】实现计数器效果分析及源码)