package com.gzsll.hupu.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.ColorDrawable; import android.support.annotation.NonNull; import android.text.InputFilter; import android.util.AttributeSet; import android.view.View; import android.widget.EditText; import android.widget.NumberPicker; import com.gzsll.hupu.R; import java.lang.reflect.Field; /** * Created by sll on 2016/4/1. */ public class MaterialNumberPicker extends NumberPicker { private static final int MIN_VALUE = 1; private static final int MAX_VALUE = 10; private static final int DEFAULT_VALUE = 1; private static final float TEXT_SIZE = 20.f; private static final int TEXT_COLOR = Color.BLACK; private static final int BACKGROUND_COLOR = Color.WHITE; private static final int SEPARATOR_COLOR = Color.TRANSPARENT; private Builder mBuilder; private int mTextColor; private float mTextSize; private int mSeparatorColor; private boolean mEnableFocusability; public MaterialNumberPicker(Context context) { super(context); initView(); } public MaterialNumberPicker(Context context, AttributeSet attributeSet) { super(context, attributeSet); initView(); TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.MaterialNumberPicker); for (int i = 0; i < a.getIndexCount(); ++i) { int attr = a.getIndex(i); if (attr == R.styleable.MaterialNumberPicker_npMinValue) { setMinValue(a.getInt(attr, MIN_VALUE)); } else if (attr == R.styleable.MaterialNumberPicker_npMaxValue) { setMaxValue(a.getInt(attr, MAX_VALUE)); } else if (attr == R.styleable.MaterialNumberPicker_npDefaultValue) { setValue(a.getInt(attr, DEFAULT_VALUE)); } else if (attr == R.styleable.MaterialNumberPicker_npTextSize) { setTextSize(a.getDimension(attr, TEXT_SIZE)); } else if (attr == R.styleable.MaterialNumberPicker_npTextColor) { setTextColor(a.getColor(attr, TEXT_COLOR)); } else if (attr == R.styleable.MaterialNumberPicker_npSeparatorColor) { setSeparatorColor(a.getColor(attr, SEPARATOR_COLOR)); } else if (attr == R.styleable.MaterialNumberPicker_npFocusValue) { setFocusability(a.getBoolean(attr, false)); } else if (attr == R.styleable.MaterialNumberPicker_npWrapValue) { setWrapSelectorWheel(a.getBoolean(attr, false)); } else if (attr == R.styleable.MaterialNumberPicker_npBackground) { setBackgroundColor(a.getColor(attr, BACKGROUND_COLOR)); } } a.recycle(); } public MaterialNumberPicker(Builder builder) { super(builder.context); initView(); mBuilder = builder; setMinValue(builder.minValue); setMaxValue(builder.maxValue); setValue(builder.defaultValue); setFormatter(builder.formatter); setBackgroundColor(builder.backgroundColor); setSeparatorColor(builder.separatorColor); setTextColor(builder.textColor); setTextSize(spToPixels(getContext(), builder.textSize)); setWrapSelectorWheel(builder.wrapSelectorWheel); setFocusability(builder.enableFocusability); } public final Builder getBuilder() { return this.mBuilder; } public int getTextColor() { return mTextColor; } public int getSeparatorColor() { return mSeparatorColor; } public boolean isFocusabilityEnabled() { return mEnableFocusability; } /** * Init number picker by disabling focusability of edit text embedded inside the number picker * We also override the edit text filter private attribute by using reflection as the formatter * is * still buggy while attempting to display the default value * This is still an open Google @see <a href="https://code.google.com/p/android/issues/detail?id=35482#c9">issue</a> * from 2012 */ private void initView() { setMinValue(MIN_VALUE); setMaxValue(MAX_VALUE); setValue(DEFAULT_VALUE); setBackgroundColor(BACKGROUND_COLOR); setSeparatorColor(SEPARATOR_COLOR); setTextColor(TEXT_COLOR); setTextSize(TEXT_SIZE); setWrapSelectorWheel(false); setFocusability(false); try { Field f = NumberPicker.class.getDeclaredField("mInputText"); f.setAccessible(true); EditText inputText = (EditText) f.get(this); inputText.setFilters(new InputFilter[0]); } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) { e.printStackTrace(); } } /** * Uses reflection to access divider private attribute and override its color * Use Color.Transparent if you wish to hide them */ public void setSeparatorColor(int separatorColor) { mSeparatorColor = separatorColor; Field[] pickerFields = NumberPicker.class.getDeclaredFields(); for (Field pf : pickerFields) { if (pf.getName().equals("mSelectionDivider")) { pf.setAccessible(true); try { pf.set(this, new ColorDrawable(separatorColor)); } catch (IllegalAccessException | IllegalArgumentException e) { e.printStackTrace(); } break; } } } /** * Uses reflection to access text color private attribute for both wheel and edit text inside the * number picker. */ public void setTextColor(int textColor) { mTextColor = textColor; updateTextAttributes(); } /** * Uses reflection to access text size private attribute for both wheel and edit text inside the * number picker. */ public void setTextSize(float textSize) { mTextSize = textSize; updateTextAttributes(); } private void updateTextAttributes() { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (child instanceof EditText) { try { Field selectorWheelPaintField = NumberPicker.class.getDeclaredField("mSelectorWheelPaint"); selectorWheelPaintField.setAccessible(true); Paint wheelPaint = ((Paint) selectorWheelPaintField.get(this)); wheelPaint.setColor(mTextColor); wheelPaint.setTextSize(mTextSize); EditText editText = ((EditText) child); editText.setTextColor(mTextColor); editText.setTextSize(pixelsToSp(getContext(), mTextSize)); invalidate(); break; } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) { e.printStackTrace(); } } } } private void setFocusability(boolean isFocusable) { mEnableFocusability = isFocusable; setDescendantFocusability(isFocusable ? FOCUS_AFTER_DESCENDANTS : FOCUS_BLOCK_DESCENDANTS); } private float pixelsToSp(Context context, float px) { return px / context.getResources().getDisplayMetrics().scaledDensity; } private float spToPixels(Context context, float sp) { return sp * context.getResources().getDisplayMetrics().scaledDensity; } public static class Builder { private Context context; private Formatter formatter; private int backgroundColor = BACKGROUND_COLOR; private int separatorColor = SEPARATOR_COLOR; private int textColor = TEXT_COLOR; private float textSize = TEXT_SIZE; private int minValue = MIN_VALUE; private int maxValue = MAX_VALUE; private int defaultValue = DEFAULT_VALUE; private boolean enableFocusability = false; private boolean wrapSelectorWheel = false; public Builder(@NonNull Context context) { this.context = context; } public Builder formatter(Formatter formatter) { this.formatter = formatter; return this; } public Builder backgroundColor(int backgroundColor) { this.backgroundColor = backgroundColor; return this; } public Builder separatorColor(int separatorColor) { this.separatorColor = separatorColor; return this; } public Builder textColor(int textColor) { this.textColor = textColor; return this; } public Builder textSize(float textSize) { this.textSize = textSize; return this; } public Builder minValue(int minValue) { this.minValue = minValue; return this; } public Builder maxValue(int maxValue) { this.maxValue = maxValue; return this; } public Builder defaultValue(int defaultValue) { this.defaultValue = defaultValue; return this; } public Builder wrapSelectorWheel(boolean wrapSelectorWheel) { this.wrapSelectorWheel = wrapSelectorWheel; return this; } public Builder enableFocusability(boolean enableFocusability) { this.enableFocusability = enableFocusability; return this; } public MaterialNumberPicker build() { return new MaterialNumberPicker(this); } } }