Search code examples
androidandroid-spinnernumberpicker

Android Numberpicker decimals


I'm trying to create a numberpicker in Android but the wheel only increase by 1. I want to increase by 0.1. I've looked up on the net but I've found a formated array of floats disabling the wheel. Please help and sorry for the grammar, I'm learning.


Solution

  • You can create your own DecimalPicker view based on ElegantNumberButton. Add following classes to your project:

    /path/to/your/DecimalPicker.java

    import android.content.Context;
    import android.content.res.Resources;
    import android.content.res.TypedArray;
    import android.graphics.PorterDuff;
    import android.graphics.PorterDuffColorFilter;
    import android.graphics.drawable.Drawable;
    import android.os.Build;
    import android.text.Editable;
    import android.text.TextWatcher;
    import android.util.AttributeSet;
    import android.view.KeyEvent;
    import android.view.View;
    import android.view.inputmethod.EditorInfo;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.LinearLayout;
    import android.widget.RelativeLayout;
    import android.widget.TextView;
    
    import java.text.DecimalFormat;
    import java.text.DecimalFormatSymbols;
    import java.text.NumberFormat;
    import java.util.Locale;
    
    import ru.alanov.cashbox.R;
    import ru.alanov.cashbox.Utils;
    
    public class DecimalPicker extends RelativeLayout {
        private Context context;
        private AttributeSet attrs;
        private int styleAttr;
        private OnClickListener mListener;
        private double initialNumber, finalNumber, lastNumber, currentNumber;
        private EditText editText;
        private String format;
        private OnValueChangeListener onValueChangeListener;
    
        public DecimalPicker(Context context) {
            super(context);
            this.context = context;
            initView();
        }
    
        public DecimalPicker(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.context = context;
            this.attrs = attrs;
            initView();
        }
    
        public DecimalPicker(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            this.context = context;
            this.attrs = attrs;
            this.styleAttr = defStyleAttr;
            initView();
        }
    
        private void initView(){
            inflate(context, R.layout.decimal_picker, this);
    
            final Resources res = getResources();
            final int defaultColor = res.getColor(R.color.colorPrimary);
            final int defaultTextColor = res.getColor(R.color.colorText);
            final Drawable defaultDrawable = res.getDrawable(R.drawable.decimal_picker_shape);
    
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DecimalPicker, styleAttr, 0);
    
            initialNumber = a.getInt(R.styleable.DecimalPicker_initialNumber, 0);
            finalNumber = a.getInt(R.styleable.DecimalPicker_finalNumber, Integer.MAX_VALUE);
            float textSize = a.getDimension(R.styleable.DecimalPicker_textSize, 24);
            int color = a.getColor(R.styleable.DecimalPicker_backGroundColor,defaultColor);
            int textColor = a.getColor(R.styleable.DecimalPicker_textColor,defaultTextColor);
            Drawable drawable = a.getDrawable(R.styleable.DecimalPicker_backgroundDrawable);
    
            Button buttonMinus = (Button) findViewById(R.id.subtract_btn);
            Button buttonPlus = (Button) findViewById(R.id.add_btn);
            editText = (EditText) findViewById(R.id.number_counter);
            editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
    
                @Override
                public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                    if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT) {
                        String num = ((EditText) v).getText().toString();
                        setNumber(num, true);
                    }
                    return false;
                }
            });
            editText.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) {
    
                }
    
                @Override
                public void afterTextChanged(Editable s) {
                    String value = s.toString().trim();
                    double valueDouble = -1;
                    try {
                        valueDouble = parseDouble(value.isEmpty() ? "0" : value);
                    } catch (NumberFormatException e) {
                        e.printStackTrace();
                    }
                    if (valueDouble >= 0){
                        lastNumber = currentNumber;
                        currentNumber = valueDouble;
                        callListener(DecimalPicker.this);
                    }
                }
            });
    
            LinearLayout mLayout = (LinearLayout) findViewById(R.id.decimal_picker_layout);
    
            buttonMinus.setTextColor(textColor);
            buttonPlus.setTextColor(textColor);
            editText.setTextColor(textColor);
            buttonMinus.setTextSize(textSize);
            buttonPlus.setTextSize(textSize);
            editText.setTextSize(textSize);
    
            if (drawable == null){
                drawable = defaultDrawable;
            }
            assert drawable != null;
            drawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC));
            if (Build.VERSION.SDK_INT > 16)
                mLayout.setBackground(drawable);
            else
                mLayout.setBackgroundDrawable(drawable);
    
            editText.setText(String.valueOf(initialNumber));
    
            currentNumber = initialNumber;
            lastNumber = initialNumber;
    
            buttonMinus.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View mView) {
                    double num = parseDouble(editText.getText().toString());
                    setNumber(String.valueOf(num - 1)/*, true*/);
                }
            });
            buttonPlus.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View mView) {
                    double num = parseDouble(editText.getText().toString());
                    setNumber(String.valueOf(num + 1)/*, true*/);
                }
            });
            a.recycle();
        }
    
        private void callListener(View view){
            if (mListener != null)
                mListener.onClick(view);
    
            if (onValueChangeListener != null && lastNumber != currentNumber)
                onValueChangeListener.onValueChange(this, lastNumber, currentNumber);
        }
    
        public String getNumber(){
            return String.valueOf(currentNumber);
        }
    
        public void setNumber(String number) {
            try {
                double n = parseDouble(number);
                if (n > finalNumber)
                    n = finalNumber;
    
                if (n < initialNumber)
                    n = initialNumber;
    
                if (format != null) {
                    String num = String.format(Utils.getCurrentLocale(getContext()), format, n);
                    num = removeTrailingZeroes(num);
                    editText.setText(num);
                } else
                    editText.setText(String.valueOf(number));
    
                lastNumber = currentNumber;
                currentNumber = parseDouble(editText.getText().toString());
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
    
        private double parseDouble(String str) throws NumberFormatException {
            return Double.parseDouble(str.replace(",","."));
        }
    
    
        private String removeTrailingZeroes(String num) {
            NumberFormat nf = NumberFormat.getInstance();
            if (nf instanceof DecimalFormat) {
                DecimalFormatSymbols sym = ((DecimalFormat) nf).getDecimalFormatSymbols();
                char decSeparator = sym.getDecimalSeparator();
                String[] split = num.split((decSeparator == '.' ? "\\" : "") + String.valueOf(decSeparator));
                if (split.length == 2 && split[1].replace("0", "").isEmpty())
                    num = split[0];
            }
            return num;
        }
    
        public void setNumber(String number, boolean notifyListener){
            setNumber(number);
            if (notifyListener)
                callListener(this);
        }
    
        public void setOnClickListener(OnClickListener onClickListener) {
            mListener = onClickListener;
        }
    
        public void setOnValueChangeListener(OnValueChangeListener onValueChangeListener){
            this.onValueChangeListener = onValueChangeListener;
        }
    
        public interface OnClickListener {
            void onClick(View view);
        }
    
        public interface OnValueChangeListener {
            void onValueChange(DecimalPicker view, double oldValue, double newValue);
        }
    
        public void setRange(Double startingNumber, Double endingNumber) {
            initialNumber = startingNumber;
            finalNumber = endingNumber;
        }
    
        public void setFormat(String format){
            this.format = format;
        }
    }
    

    /layout/decimal_picker.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal"
        android:layout_gravity="center_horizontal"
        android:layout_width="wrap_content"
        android:id="@+id/decimal_picker_layout"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/subtract_btn"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:text="-"
            android:background="@android:color/transparent" />
        <EditText
            android:id="@+id/number_counter"
            android:gravity="center"
            android:imeOptions="flagNoExtractUi"
            android:inputType="numberDecimal"
            android:text = "1"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:background="@android:color/transparent" />
        <Button
            android:id="@+id/add_btn"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:text="+"
            android:background="@android:color/transparent" />
    </LinearLayout>
    

    /drawable/decimal_picker_shape.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
        <corners android:radius="4dp" />
    </shape>
    

    /values/attrs.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="DecimalPicker">
            <attr name="backGroundColor" format="color"/>
            <attr name="initialNumber" format="integer"/>
            <attr name="finalNumber" format="integer" />
            <attr name="textColor" format="color"/>
            <attr name="textSize" format="dimension"/>
            <attr name="backgroundDrawable" format="reference"/>
            <attr name="decimalFormat" format="reference"/>
        </declare-styleable>
    </resources>
    

    In /values/colors.xml add <color name="colorText">#FFFFFF</color>

    Usage example:

    In your activity layout place

    <path.to.your.DecimalPicker
         android:id="@+id/decimal_picker"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textSize="16sp"/>
    

    In your Activity#onCreate() place

    DecimalPicker decimalPicker = (DecimalPicker) view.findViewById(R.id.decimal_picker);
    decimalPicker.setFormat("%.3f");//Weight format
    decimalPicker.setOnValueChangeListener(new DecimalPicker.OnValueChangeListener() {
    
        @Override
        public void onValueChange(DecimalPicker picker, double oldValue, double newValue) {
            //Do what you want to handle value change.
        }
    });
    

    The result of efforts: enter image description here Click on +/- will change integer part of number. If you want to change fractional part - click on number and edit it by hands. If there is no fractional part you will see a integer part only without zeroes.