Десятичные знаки Android Numberpicker

Я пытаюсь создать набор номера в Android, но колесо увеличивается только на 1. Я хочу увеличить на 0,1. Я поискал в сети, но нашел сформированный массив поплавков, отключающих колесо. Прошу помощи и извините за грамматику, учусь.


person edusandovall    schedule 31.05.2016    source источник
comment
Или у вас может быть 2 средства выбора номера. Один для единиц, а другой для десятичных знаков.   -  person    schedule 01.06.2016


Ответы (3)


Вы можете создать собственное представление DecimalPicker на основе ElegantNumberButton. Добавьте в свой проект следующие классы:

/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>

В /values/colors.xml добавьте <color name="colorText">#FFFFFF</color>

Пример использования:

В вашем месте макета деятельности

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

На вашем Activity#onCreate() месте

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.
    }
});

Результат усилий:  введите описание изображения здесь Нажмите +/-, чтобы изменить целую часть числа. Если вы хотите изменить дробную часть - нажмите на число и отредактируйте его вручную. Если дробной части нет, вы увидите только целую часть без нулей.

person isabsent    schedule 05.09.2017

Вы можете сделать это с помощью настраиваемых строк:

NumberPicker picker = new NumberPicker(this);
picker.setMinValue(0);
picker.setMaxValue(100);
picker.setDisplayedValues( new String[] { "0.0", "0.1", ..., "10.0" } );
double = picker.getValue() / 10.0;
person Eugen Martynov    schedule 31.05.2016
comment
Но если максимальное значение равно 100, нужно ли мне создавать настраиваемую строку до 100, например 0,1, 0,2 ... 99,8,99,9,100? - person edusandovall; 01.06.2016
comment
Тогда я бы пошел с двумя прядильщиками или даже полем редактирования. Было бы очень плохо с одним счетчиком - person Eugen Martynov; 01.06.2016
comment
Это решение, но numberpicker нельзя отформатировать для увеличения десятичных знаков? - person edusandovall; 01.06.2016
comment
Я не понимаю, вы это уже просили. Нет, это только для целых чисел - person Eugen Martynov; 01.06.2016
comment
Хорошо, я предложу ваше решение. - person edusandovall; 01.06.2016

В качестве альтернативы вы можете использовать это удобное диалоговое окно расширения NumberPicker Kotlin, которое масштабирует ваши Double значения в подходящий Int диапазон и преобразует значения Int обратно в Doubles перед вызовом любого обратного вызова. Он в основном скрывает тот факт, что NumberPicker поддерживает только Int, и добавляет поддержку Double!

Вот расширение фрагмента, которое вам нужно скопировать и вставить:

fun Fragment.showNumberPickerDialog(
    title: String,
    value: Double,
    range: ClosedRange<Double>,
    stepSize: Double,
    formatToString: (Double) -> String,
    valueChooseAction: (Double) -> Unit
) {
    val numberPicker = NumberPicker(context).apply {
        setFormatter { formatToString(it.toDouble() * stepSize) }
        wrapSelectorWheel = false

        minValue = (range.start / stepSize).toInt()
        maxValue = (range.endInclusive / stepSize).toInt()
        this.value = (value.toDouble() / stepSize).toInt()

        // NOTE: workaround for a bug that rendered the selected value wrong until user scrolled, see also: https://stackoverflow.com/q/27343772/3451975
        (NumberPicker::class.java.getDeclaredField("mInputText").apply { isAccessible = true }.get(this) as EditText).filters = emptyArray()
    }

    MaterialAlertDialogBuilder(context)
        .setTitle(title)
        .setView(numberPicker)
        .setPositiveButton("OK") { _, _ -> valueChooseAction(numberPicker.value.toDouble() * stepSize) }
        .setNeutralButton("Cancel") { _, _ -> /* do nothing, closes dialog automatically */ }
        .show()
}

Затем используйте вот так:

showNumberPickerDialog(
    title = "Your Weight",
    value = 75.0, // in kilograms
    range = 10.0 .. 300.0,
    formatToString = { "$it kg" },
    valueChooseAction = { saveNewWeight(it) }
)
person Jeehut    schedule 12.01.2020
comment
Это почти сработало для меня, за исключением случаев, когда stepSize = 0.1 я получал значения, которые были намного длиннее, например, 4.5 выходило нормально, но 4.6 выходило как 4.6000000000000005, и тогда 4.7 было бы хорошо, а 4.8 - нет. Я также не совсем знал, как заставить NumberPickerDialog появиться, кроме как с помощью button, было бы неплохо сделать это из spinner или другого поля ввода - person Devnsyde; 18.11.2020
comment
@Devnsyde Не стесняйтесь проверить, как я использовал свой код в своем проекте с открытым исходным кодом: github.com/Flinesoft/FitnessTracker-Android/ Обратите внимание, что у меня не было подобных проблем в моем проекте. Я надеюсь, что это помогает! - person Jeehut; 18.11.2020