Android自定义数值增减控件

前言

最近做一个小App的时候用到了数值增减功能,主要用途就是在将商品添加到购物车时指定购买商品数量;遇到这个需求后首先想到的就是去网上找个现成的来用,但寻找很久也没找到合适的;可能太简单没人觉得这是什么大不了的,或者根本不需要自定义一个控件,这样只能自己动手了。

我本身是个Android新手,很多地方都还是理解不清楚;这次自定义的控件也不知道怎么样,但勉强能满足自己的需求。

效果截图

这里写图片描述

上图中的“数量“即是本控件的样子,可以点击左右的加减号来控制TextView中数值的增减。

这里写图片描述

上图是在点击TextView时弹出的对话框,在对话框中的TextView中输入想要的数字后,确定即可退出对话框,然后将数字填写入控件的TextView。

在控件数值改变后可以触发监听器,用于回调。

主要代码

attrs.xml

首先在attrs.xml文件中定义所要用到的属性,如下所示:

<declare-styleable name="IncreaseReduceTextView">
    <attr name="textBackground" format="reference"/>
    <attr name="textSize" format="dimension"/>
    <attr name="verticalPadding" format="dimension"/>
    <attr name="horizontalPadding" format="dimension"/>
    <attr name="viewSpace" format="dimension"/>
</declare-styleable>

简单解释一下:

  • textBackground
    控件TextView文本域背景
  • textSize
    文本域字体大小, 默认为14sp
  • verticalPadding
    文本域垂直方向上的内边距, 默认为2dp
  • horizontalPadding
    文本域水平方向上的内边距, 默认为2dp
  • viewSpace
    TextView左右Margin

IncreaseReduceTextView

接下来是主控件代码,代码较多,但逻辑很简单,如下所示:

package com.witmoon.xmb.ui.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.support.v4.app.FragmentActivity;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.witmoon.xmb.R;
import com.witmoon.xmb.activity.common.fragment.PurchaseQuantityDialogFragment;
import com.witmoon.xmb.util.DensityUtils;

/**
 * 自定义数值增减控件, 用于购买数量
 * Created by zhyh on 2015/5/26.
 */
public class IncreaseReduceTextView extends ViewGroup implements View.OnClickListener {

    private Context mContext;
    private int number = 1;
    private int mTargetWidth;

    private ImageView mMinusImageButton;
    private TextView mNumberTextView;
    private ImageView mAddImageButton;

    private int mBackground;                // TextView背景资源
    private int mTextSize = 14;             // 字体大小, 默认为14sp
    private int mVerticalPadding = 2;       // TextView上下内边距, 默认为2dp
    private int mHorizontalPadding = 2;     // TextView左右内边距, 默认为2dp
    private int mViewSpace = 0;             // TextView左右Margin

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

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

    public IncreaseReduceTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        if (getChildCount() > 0) {
            throw new RuntimeException("IncreaseReduceTextView不允许有子元素.");
        }

        this.mContext = context;

        // 读取自定义属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.IncreaseReduceTextView);
        mBackground = ta.getResourceId(R.styleable.IncreaseReduceTextView_textBackground, 0);
        mTextSize = ta.getDimensionPixelSize(R.styleable.IncreaseReduceTextView_textSize,
                DensityUtils.sp2px(context, mTextSize));
        mVerticalPadding = ta.getDimensionPixelSize(R.styleable
                .IncreaseReduceTextView_verticalPadding, DensityUtils.dp2px(context,
                mVerticalPadding));
        mHorizontalPadding = ta.getDimensionPixelSize(R.styleable
                .IncreaseReduceTextView_horizontalPadding, DensityUtils.dp2px(context,
                mHorizontalPadding));
        mViewSpace = ta.getDimensionPixelSize(R.styleable.IncreaseReduceTextView_viewSpace,
                mViewSpace);
        ta.recycle();

        Paint paint = new Paint();
        paint.setTextSize(mTextSize);
        mTargetWidth = (int) paint.measureText("00");
        initializeView();
    }

    // 初始化视图
    private void initializeView() {
        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.MATCH_PARENT);

        mMinusImageButton = new ImageView(mContext);
        mMinusImageButton.setLayoutParams(params);
        mMinusImageButton.setImageResource(R.mipmap.icon_minus_rounded_square);
        mMinusImageButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (number > 1) {
                    number--;
                    numberChanged();
                }
            }
        });
        addView(mMinusImageButton);

        mNumberTextView = new TextView(mContext);
        LayoutParams editLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams
                .WRAP_CONTENT);
        mNumberTextView.setLayoutParams(editLayoutParams);
        mNumberTextView.setBackgroundResource(mBackground);
        mNumberTextView.setPadding(mHorizontalPadding, mVerticalPadding, mHorizontalPadding,
                mVerticalPadding);
        mNumberTextView.setGravity(Gravity.CENTER);
        mNumberTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
        mNumberTextView.setText(String.valueOf(number));
        mNumberTextView.setOnClickListener(this);
        addView(mNumberTextView);

        mAddImageButton = new ImageView(mContext);
        mAddImageButton.setLayoutParams(params);
        mAddImageButton.setImageResource(R.mipmap.icon_add_rounded_square);
        mAddImageButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (number < 999) {
                    number++;
                    numberChanged();
                }
            }
        });
        addView(mAddImageButton);

        numberChanged();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int widthSum;   // 总宽度, 最终结果为自定义组件的宽度

        // 处理中间的TextView
        View middleView = getChildAt(1);
        int childHeight = middleView.getMeasuredHeight();
        int childWidth = middleView.getMeasuredWidth();
        widthSum = childWidth + mTargetWidth;
        middleView.measure(MeasureSpec.makeMeasureSpec(childWidth + mTargetWidth, MeasureSpec
                .EXACTLY), MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));

        // 处理左右ImageView,重新计算其尺寸
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            if (i == 1) continue;
            View child = getChildAt(i);
            int ms = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY);
            child.measure(ms, ms);
            widthSum = widthSum + childHeight;
        }

        // 设置组件自身尺寸, 总宽度再加上两个间距(间距默认为0)
        setMeasuredDimension(widthSum + mViewSpace * 2, childHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();

        int left = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();
            childView.layout(left, 0, left + childWidth, childHeight);
            left += childWidth;
            if (i != childCount - 1) {
                left += mViewSpace;
            }
        }
    }

    public void setNumber(int number) {
        this.number = number;
        numberChanged(false);
    }

    private void numberChanged() {
        numberChanged(true);
    }

    private void numberChanged(boolean isTriggerChanged) {
        mNumberTextView.setText(String.valueOf(number));
        if (mOnNumberChangeListener != null && isTriggerChanged) {
            mOnNumberChangeListener.onNumberChange(number);
        }
        if (number <= 1) {
            mMinusImageButton.setImageResource(R.mipmap.icon_minus_rounded_square_grey);
        } else {
            mMinusImageButton.setImageResource(R.mipmap.icon_minus_rounded_square);
        }
    }

    public int getNumber() {
        return this.number;
    }

    @Override
    public void onClick(View v) {
        PurchaseQuantityDialogFragment pqFragment = new PurchaseQuantityDialogFragment();
        pqFragment.setInitNumber(this.number);
        pqFragment.setOnInputCompleteListener(new PurchaseQuantityDialogFragment
                .OnInputCompleteListener() {
            @Override
            public void onInputComplete(String data) {
                number = Integer.parseInt(data);
                numberChanged();
            }
        });
        pqFragment.show(((FragmentActivity) mContext).getSupportFragmentManager(),
                "DialogFragment");
    }

    // ------------------- 数值更改回调接口 -------------------
    private OnNumberChangeListener mOnNumberChangeListener;

    public void setOnNumberChangeListener(OnNumberChangeListener listener) {
        mOnNumberChangeListener = listener;
    }

    public interface OnNumberChangeListener {
        void onNumberChange(int number);
    }
}

代码还算清晰,首先是读取自定义属性;然后初始化控件内容,测量各内部控件的尺寸,并将它们摆放好;最后注册事件监听器。

弹出对话框DialogFragment

在点击控件TextView时将弹出一个AlertDialog,用户可以直接输入一个数字;点击确定可以将用户输入更新到控件的TextView中,弹出对话框在这里使用DialogFragment来实现,代码如下所示:

package com.witmoon.xmb.activity.common.fragment;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.widget.AppCompatEditText;
import android.text.InputFilter;
import android.text.InputType;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;

import com.witmoon.xmb.R;
import com.witmoon.xmb.util.DensityUtils;

/**
 * 数量增减对话框
 * Created by zhyh on 2015/6/6.
 */
public class PurchaseQuantityDialogFragment extends DialogFragment {

    private OnInputCompleteListener mOnInputCompleteListener;

    public void setOnInputCompleteListener(OnInputCompleteListener listener) {
        mOnInputCompleteListener = listener;
    }

    private EditText mEditText;
    private String mInitInput;

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View view = inflater.inflate(R.layout.layout_purchase_quantity, null);

//        View view = createView(getActivity());
        mEditText = (EditText) view.findViewById(R.id.edit);
        mEditText.setText(mInitInput);
        mEditText.selectAll();

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setView(view).setTitle("修改购买数量").setPositiveButton(R.string.text_confirm, new
                DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        String data = mEditText.getText().toString();
                        if (isInputValid(data)) {
                            if (mOnInputCompleteListener != null) {
                                mOnInputCompleteListener.onInputComplete(data);
                            }
                            dialog.dismiss();
                        }
                    }
                }).setNegativeButton(R.string.text_cancel, null);

        return builder.create();
    }

    // 创建对话框View
    private View createView(Context context) {
        LinearLayout layout = new LinearLayout(context);
        layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        layout.setGravity(Gravity.CENTER);

        AppCompatEditText editText = new AppCompatEditText(context);
        editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(3)});
        editText.setInputType(InputType.TYPE_CLASS_NUMBER);
        editText.setId(R.id.edit);
        editText.setGravity(Gravity.CENTER_HORIZONTAL);
        editText.setPadding(12, 0, 12, 0);
        editText.setMinWidth(DensityUtils.dp2px(context, 40));
        layout.addView(editText, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));

        return layout;
    }

    // 设置初始值
    public void setInitNumber(int number) {
        this.mInitInput = String.valueOf(number);
    }

    private boolean isInputValid(String input) {
        if (TextUtils.isEmpty(input)) {
            return false;
        }
        if (!input.matches("\\d+")) {
            return false;
        }
        try {
            return Integer.parseInt(input) > 0;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    public interface OnInputCompleteListener {
        void onInputComplete(String data);
    }
}

DiaglogFragment所使用的xml布局文件

接下来还有一个上面DialogFragment要使用的XML布局文件,内容很简单,仅仅是一个EditText控件而已,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:gravity="center"
              android:paddingTop="12dp"
              android:orientation="vertical">

    <android.support.v7.widget.AppCompatEditText
        android:id="@+id/edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:inputType="number"
        android:maxLength="3"
        android:background="@drawable/bg_input_area"
        android:minWidth="48dp"
        android:padding="4dp"/>
</LinearLayout>
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt是一个跨平台的软件开发框架,它的主要目标是提供开发者一个高效、易于学习和使用的工具,使他们能够创建运行于不同平台上的应用程序。Qt不仅仅是一个GUI(图形用户界面)工具,它还提供了多种实用的功能和类库,其中之一就是控件增减。 在Qt中,通过创建一个按钮并添加一个槽函数来实现控件增减。首先,我们需要在Qt程序中创建一个按钮。在代码中,可以使用QPushButton类来实现按钮的创建,并且需要给这个按钮添加一个名称,在这个例子中为“Button1”: QPushButton *Button1 = new QPushButton(“Add Control”); 接下来,我们需要为这个按钮添加一个槽函数,用于响应按钮的点击事件。这个槽函数可以在主窗口的类中定义,可以在“mainwindow.h”文件中进行定义,然后在“mainwindow.cpp”文件中进行实现。 void MainWindow::on_Button1_clicked() { //button clicked } 现在我们已经创建了按钮并为它添加了槽函数。在槽函数中,我们可以定义如何增加或减少控件。增加控件的方法可以是创建一个新的控件并在窗口中添加它,或者是将一个已有的控件“展开”,使它变得可见。 例如,我们可以添加一个文本框控件: QLineEdit *Edit1 = new QLineEdit(this); Edit1->setGeometry(10,10,200,30); 我们可以在按钮的点击事件中调用这个控件来实现“增加控件”的功能: QLineEdit *Edit1 = new QLineEdit(this); Edit1->setGeometry(10,10,200,30); 或者我们可以使用布局管理器来自动管理控件的布局,而不是手工设置位置和大小。在这个例子中使用QVBoxLayout布局管理器: QVBoxLayout *layout = new QVBoxLayout; QWidget *widget = new QWidget; QLineEdit *Edit1 = new QLineEdit(widget); layout->addWidget(Edit1); widget->setLayout(layout); setCentralWidget(widget); 这样,每当我们点击按钮,它就会自动将一个新文本框控件添加到布局中。 总之,Qt提供了多种实现控件增减的方法,开发者可以根据自己的需求来选择最适合自己的方法。无论是手工布局还是使用布局管理器,Qt 的灵活性和易用性都可以为我们带来方便和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值