<declare-styleable name="NiceSpinner">
<attr name="arrowTint" format="color" />
<attr name="hideArrow" format="boolean" />
<attr name="arrowDrawable" format="reference|color" />
<attr name="dropDownListPaddingBottom" format="dimension" />
<attr name="backgroundSelector" format="integer" />
<attr name="textTint" format="color" />
<attr name="popupTextAlignment" format="enum">
<enum name="start" value="0" />
<enum name="end" value="1" />
<enum name="center" value="2" />
<attr name="isPutByDef" format="boolean" />
<attr name="isMultiple" format="boolean"/>
<attr name="delimiter" format="string"/>
</declare-styleable>
public class NiceSpinnerextends AppCompatTextView {
private static final int MAX_LEVEL =10000;
private static final int DEFAULT_ELEVATION =16;
private static final StringINSTANCE_STATE ="instance_state";
private static final StringSELECTED_INDEX ="selected_index";
private static final StringIS_POPUP_SHOWING ="is_popup_showing";
private static final StringIS_ARROW_HIDDEN ="is_arrow_hidden";
private static final StringARROW_DRAWABLE_RES_ID ="arrow_drawable_res_id";
public static final int VERTICAL_OFFSET =1;
private int selectedIndex = -1;
private DrawablearrowDrawable;
private PopupWindowpopupWindow;
private ListViewlistView;
private NiceSpinnerBaseAdapteradapter;
private AdapterView.OnItemClickListeneronItemClickListener;
private AdapterView.OnItemSelectedListeneronItemSelectedListener;
private OnItemCheckedListeneronItemCheckedListener;
private boolean isArrowHidden;
private int textColor;
private int backgroundSelector;
private int arrowDrawableTint;
private int displayHeight;
private int parentVerticalOffset;
private int dropDownListPaddingBottom;
private @DrawableRes
int arrowDrawableResId;
private SpinnerTextFormatterspinnerTextFormatter =new SimpleSpinnerTextFormatter<>();
private SpinnerTextFormatterselectedTextFormatter =new SimpleSpinnerTextFormatter<>();
private PopUpTextAlignmenthorizontalAlignment;
private boolean isPutByDef =true;
private StringdefText;
private boolean isMultiple =false;
private Stringdelimiter =",";
public NiceSpinner(Context context) {
super(context);
init(context, null);
}
public NiceSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public NiceSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@Override
public ParcelableonSaveInstanceState() {
Bundle bundle =new Bundle();
bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState());
bundle.putInt(SELECTED_INDEX, selectedIndex);
bundle.putBoolean(IS_ARROW_HIDDEN, isArrowHidden);
bundle.putInt(ARROW_DRAWABLE_RES_ID, arrowDrawableResId);
if (popupWindow !=null) {
bundle.putBoolean(IS_POPUP_SHOWING, popupWindow.isShowing());
}
return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable savedState) {
if (savedStateinstanceof Bundle) {
Bundle bundle = (Bundle) savedState;
selectedIndex = bundle.getInt(SELECTED_INDEX);
if (adapter !=null) {
setSelectText();
adapter.setSelectedIndex(selectedIndex);
}
if (bundle.getBoolean(IS_POPUP_SHOWING)) {
if (popupWindow !=null) {
// Post the show request into the looper to avoid bad token exception
post(new Runnable() {
@Override
public void run() {
showDropDown();
}
});
}
}
isArrowHidden = bundle.getBoolean(IS_ARROW_HIDDEN, false);
arrowDrawableResId = bundle.getInt(ARROW_DRAWABLE_RES_ID);
savedState = bundle.getParcelable(INSTANCE_STATE);
}
super.onRestoreInstanceState(savedState);
}
private void init(Context context, AttributeSet attrs) {
Resources resources = getResources();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NiceSpinner);
/* setPadding(resources.getDimensionPixelSize(R.dimen.dp_10), 0, 0,
0);*/
setClickable(true);
backgroundSelector = typedArray.getResourceId(R.styleable.NiceSpinner_backgroundSelector, R.drawable.selector);
// setBackgroundResource(backgroundSelector);
textColor = typedArray.getColor(R.styleable.NiceSpinner_textTint, getDefaultTextColor(context));
isPutByDef = typedArray.getBoolean(R.styleable.NiceSpinner_isPutByDef, true);
isMultiple = typedArray.getBoolean(R.styleable.NiceSpinner_isMultiple, false);
delimiter = typedArray.getString(R.styleable.NiceSpinner_delimiter);
if (delimiter ==null) {
delimiter =",";
}
defText = getText().toString();
setTextColor(textColor);
listView =new ListView(context);
// Set the spinner's id into the listview to make it pretend to be the right parent in
// onItemClick
listView.setId(getId());
listView.setDivider(null);
listView.setItemsCanFocus(true);
listView.setVerticalScrollBarEnabled(true);
listView.setHorizontalScrollBarEnabled(false);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
/* if (position >= selectedIndex && position < adapter.getCount()) {
position++;
}*/
// Need to set selected index before calling listeners or getSelectedIndex() can be
// reported incorrectly due to race conditions.
if (isMultiple) {
adapter.setCheckedIndex(position);
}else {
adapter.setSelectedIndex(position);
selectedIndex = position;
}
if (onItemClickListener !=null) {
onItemClickListener.onItemClick(parent, view, position, id);
}
if (onItemSelectedListener !=null && !isMultiple) {
onItemSelectedListener.onItemSelected(parent, view, position, id);
}
if (onItemCheckedListener !=null &&isMultiple) {
onItemCheckedListener.onItemChecked(parent, view, adapter.getChecked());
}
setSelectText();
if (!isMultiple) {
dismissDropDown();
}
}
});
popupWindow =new PopupWindow(context);
popupWindow.setContentView(listView);
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
popupWindow.setElevation(DEFAULT_ELEVATION);
popupWindow.setBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.spinner_drawable));
}else {
popupWindow.setBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.drop_down_shadow));
}
popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
if (!isArrowHidden) {
animateArrow(false);
}
}
});
isArrowHidden = typedArray.getBoolean(R.styleable.NiceSpinner_hideArrow, false);
arrowDrawableTint = typedArray.getColor(R.styleable.NiceSpinner_arrowTint, Integer.MAX_VALUE);
arrowDrawableResId = typedArray.getResourceId(R.styleable.NiceSpinner_arrowDrawable, R.drawable.arrow);
dropDownListPaddingBottom =
typedArray.getDimensionPixelSize(R.styleable.NiceSpinner_dropDownListPaddingBottom, 0);
horizontalAlignment = PopUpTextAlignment.fromId(
typedArray.getInt(R.styleable.NiceSpinner_popupTextAlignment, PopUpTextAlignment.CENTER.ordinal())
);
switch (horizontalAlignment) {
case END:
setGravity(Gravity.CENTER_VERTICAL | Gravity.END);
break;
case START:
setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
break;
case CENTER:
setGravity(Gravity.CENTER);
break;
}
typedArray.recycle();
measureDisplayHeight();
}
private void measureDisplayHeight() {
displayHeight = getContext().getResources().getDisplayMetrics().heightPixels;
}
private int getParentVerticalOffset() {
if (parentVerticalOffset >0) {
return parentVerticalOffset;
}
int[] locationOnScreen =new int[2];
getLocationOnScreen(locationOnScreen);
return parentVerticalOffset = locationOnScreen[VERTICAL_OFFSET];
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
arrowDrawable = initArrowDrawable(arrowDrawableTint);
setArrowDrawableOrHide(arrowDrawable);
}
private DrawableinitArrowDrawable(int drawableTint) {
Drawable drawable = ContextCompat.getDrawable(getContext(), arrowDrawableResId);
if (drawable !=null) {
drawable = DrawableCompat.wrap(drawable);
if (drawableTint != Integer.MAX_VALUE && drawableTint !=0) {
DrawableCompat.setTint(drawable, drawableTint);
}
}
return drawable;
}
private void setArrowDrawableOrHide(Drawable drawable) {
if (!isArrowHidden && drawable !=null) {
setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null);
}else {
setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
}
}
private int getDefaultTextColor(Context context) {
TypedValue typedValue =new TypedValue();
context.getTheme()
.resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
TypedArray typedArray = context.obtainStyledAttributes(typedValue.data,
new int[]{android.R.attr.textColorPrimary});
int defaultTextColor = typedArray.getColor(0, Color.BLACK);
typedArray.recycle();
return defaultTextColor;
}
public int getSelectedIndex() {
return selectedIndex;
}
public SetgetCheckIndex() {
return adapter !=null ?adapter.getChecked() :null;
}
public T getSelectItem() {
if (adapter !=null &&selectedIndex > -1 &&selectedIndex
return adapter.getItem(selectedIndex);
}
return null;
}
public ListgetCheckItem() {
List checkItem =new ArrayList<>();
if (isMultiple &&adapter !=null) {
for (Integer integer :adapter.getChecked()) {
if (integer > -1 && integer
checkItem.add(adapter.getItem(integer));
}
}
}
return checkItem;
}
public int getCount() {
if (adapter !=null) {
return adapter.getCount();
}else {
return 0;
}
}
public void setArrowDrawable(@DrawableRes @ColorRes int drawableId) {
arrowDrawableResId = drawableId;
arrowDrawable = initArrowDrawable(R.drawable.arrow);
setArrowDrawableOrHide(arrowDrawable);
}
public void setArrowDrawable(Drawable drawable) {
arrowDrawable = drawable;
setArrowDrawableOrHide(arrowDrawable);
}
private void setSelectText() {
if (adapter ==null) {
return;
}
if (isMultiple) {
List selectItem =new ArrayList<>();
for (Integer integer :adapter.getChecked()) {
selectItem.add(adapter.getItem(integer));
}
setMultipleTextInternal(selectItem);
}else {
if (selectedIndex >=0 &&selectedIndex
setTextInternal(adapter.getItemInDataset(selectedIndex));
}
}
public void setMultipleTextInternal(List data) {
StringBuilder selectText =new StringBuilder();
for (T datum : data) {
CharSequence text ="";
if (selectedTextFormatter !=null) {
text =selectedTextFormatter.format(datum);
}else if (spinnerTextFormatter !=null) {
text =spinnerTextFormatter.format(datum);
}
selectText.append(",").append(text);
}
if (selectText.length() >0) {
selectText.deleteCharAt(0);
}
setText(selectText);
}
public void setTextInternal(T data) {
if (selectedTextFormatter !=null) {
setText(selectedTextFormatter.format(data));
}else if (spinnerTextFormatter !=null) {
setText(spinnerTextFormatter.format(data));
}else {
setText(data.toString());
}
}
/**
* Set the default spinner item using its index
*
* @param position the item's position
*/
public void setSelectedIndex(int position) {
if (adapter !=null) {
if (position >=0 && position
adapter.setSelectedIndex(position);
selectedIndex = position;
if (onItemSelectedListener !=null &&listView !=null) {
onItemSelectedListener.onItemSelected(listView,
listView.getChildAt(position -listView.getFirstVisiblePosition()),
position, adapter.getItemId(position));
}
setSelectText();
}else {
throw new IllegalArgumentException("Position must be lower than adapter count!");
}
}
}
/**
* Set the default spinner item using its index
*
* @param position the item's position
*/
public void setCheckedIndex(int position) {
if (adapter !=null) {
if (position >=0 && position <=adapter.getCount()) {
adapter.setCheckedIndex(position);
if (onItemCheckedListener !=null &&listView !=null) {
onItemCheckedListener.onItemChecked(listView,
listView.getChildAt(position -listView.getFirstVisiblePosition()),
adapter.getChecked());
}
setSelectText();
}else {
throw new IllegalArgumentException("Position must be lower than adapter count!");
}
}
}
public void addOnItemClickListener(AdapterView.OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener onItemSelectedListener) {
this.onItemSelectedListener = onItemSelectedListener;
}
public OnItemCheckedListenergetOnItemCheckedListener() {
return onItemCheckedListener;
}
public void setOnItemCheckedListener(OnItemCheckedListener onItemCheckedListener) {
this.onItemCheckedListener = onItemCheckedListener;
}
public boolean isMultiple() {
return isMultiple;
}
public void setMultiple(boolean multiple) {
isMultiple = multiple;
}
public void attachData(List list) {
adapter =new NiceSpinnerAdapter<>(getContext(), list, textColor, backgroundSelector, spinnerTextFormatter, horizontalAlignment);
setAdapterInternal(adapter);
}
public boolean isPutByDef() {
return isPutByDef;
}
public void attachDataSource(List list) {
if (isPutByDef) {
if (!list.contains(getText().toString())) {
list.add(0, getText().toString());
}
}
attachData(list);
}
@Override
public void setText(CharSequence text, BufferType type) {
if (TextUtils.isEmpty(text) &&isPutByDef) {
text =defText;
}
super.setText(text, type);
}
public void setAdapter(ListAdapter adapter) {
this.adapter =new NiceSpinnerAdapterWrapper(getContext(), adapter, textColor, backgroundSelector,
spinnerTextFormatter, horizontalAlignment);
setAdapterInternal(this.adapter);
}
public PopUpTextAlignmentgetPopUpTextAlignment() {
return horizontalAlignment;
}
private void setAdapterInternal(NiceSpinnerBaseAdapter adapter) {
// If the adapter needs to be settled again, ensure to reset the selected index as well
if (isMultiple) {
adapter.setChecked(new HashSet<>());
}else {
if (adapter.getCount() >0) {
selectedIndex =0;
adapter.setSelectedIndex(0);
}
}
listView.setAdapter(adapter);
setSelectText();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isEnabled() && event.getAction() == MotionEvent.ACTION_UP) {
if (!popupWindow.isShowing()) {
showDropDown();
}else {
dismissDropDown();
}
}
return super.onTouchEvent(event);
}
private void animateArrow(boolean shouldRotateUp) {
//由于多个下拉单选框会互相影响,暂时关闭动画
if (true) {
return;
}
int start = shouldRotateUp ?0 :MAX_LEVEL;
int end = shouldRotateUp ?MAX_LEVEL :0;
ObjectAnimator animator = ObjectAnimator.ofInt(arrowDrawable, "level", start, end);
animator.setInterpolator(new LinearOutSlowInInterpolator());
animator.start();
}
public void dismissDropDown() {
if (!isArrowHidden) {
animateArrow(false);
}
popupWindow.dismiss();
}
public void showDropDown() {
if (!isArrowHidden) {
animateArrow(true);
}
measurePopUpDimension();
popupWindow.showAsDropDown(this);
}
private void measurePopUpDimension() {
int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(getPopUpHeight(), MeasureSpec.AT_MOST);
listView.measure(widthSpec, heightSpec);
popupWindow.setWidth(listView.getMeasuredWidth());
popupWindow.setHeight(listView.getMeasuredHeight() -dropDownListPaddingBottom);
}
private int getPopUpHeight() {
return Math.max(verticalSpaceBelow(), verticalSpaceAbove());
}
private int verticalSpaceAbove() {
return getParentVerticalOffset();
}
private int verticalSpaceBelow() {
return displayHeight - getParentVerticalOffset() - getMeasuredHeight();
}
public void setTintColor(@ColorRes int resId) {
if (arrowDrawable !=null && !isArrowHidden) {
DrawableCompat.setTint(arrowDrawable, ContextCompat.getColor(getContext(), resId));
}
}
public void setArrowTintColor(int resolvedColor) {
if (arrowDrawable !=null && !isArrowHidden) {
DrawableCompat.setTint(arrowDrawable, resolvedColor);
}
}
public void hideArrow() {
isArrowHidden =true;
setArrowDrawableOrHide(arrowDrawable);
}
public void showArrow() {
isArrowHidden =false;
setArrowDrawableOrHide(arrowDrawable);
}
public boolean isArrowHidden() {
return isArrowHidden;
}
public void setDropDownListPaddingBottom(int paddingBottom) {
dropDownListPaddingBottom = paddingBottom;
}
public int getDropDownListPaddingBottom() {
return dropDownListPaddingBottom;
}
public void setSpinnerTextFormatter(SpinnerTextFormatter textFormatter) {
this.spinnerTextFormatter = textFormatter;
}
public void setSelectedTextFormatter(SpinnerTextFormatter textFormatter) {
this.selectedTextFormatter = textFormatter;
}
public interface OnItemCheckedListener {
void onItemChecked(AdapterView parent, View view, Set positions);
}
}
public class UIRoundButtonextends UIAlphaButton {
public UIRoundButton(Context context) {
super(context);
init(context, null, 0);
}
public UIRoundButton(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.UIButtonStyle);
init(context, attrs, R.attr.UIButtonStyle);
}
public UIRoundButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
UIRoundButtonDrawable bg = UIRoundButtonDrawable.fromAttributeSet(context, attrs, defStyleAttr);
ViewUtil.setBackgroundKeepingPadding(this, bg);
setChangeAlphaWhenDisable(false);
setChangeAlphaWhenPress(false);
}
}
public class GsonTypeAdaptorextends TypeAdapter {
@Override
public Objectread(JsonReader in)throws IOException {
JsonToken token = in.peek();
switch (token) {
case BEGIN_ARRAY:
List list =new ArrayList();
in.beginArray();
while (in.hasNext()) {
list.add(read(in));
}
in.endArray();
return list;
case BEGIN_OBJECT:
Map map =new LinkedTreeMap();
in.beginObject();
while (in.hasNext()) {
map.put(in.nextName(), read(in));
}
in.endObject();
return map;
case STRING:
return in.nextString();
case NUMBER:
/**
* 改写数字的处理逻辑,将数字值分为整型与浮点型。
*/
double dbNum = in.nextDouble();
// 数字超过long的最大值,返回浮点类型
if (dbNum > Long.MAX_VALUE) {
return dbNum;
}
// 判断数字是否为整数值
long lngNum = (long) dbNum;
if (dbNum == lngNum) {
return lngNum;
}else {
return dbNum;
}
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
default:
throw new IllegalStateException();
}
}
@Override
public void write(JsonWriter out, Object value)throws IOException {
// 序列化无需实现
}
}
public class GsonTypeAdaptorextends TypeAdapter {
@Override
public Objectread(JsonReader in)throws IOException {
JsonToken token = in.peek();
switch (token) {
case BEGIN_ARRAY:
List list =new ArrayList();
in.beginArray();
while (in.hasNext()) {
list.add(read(in));
}
in.endArray();
return list;
case BEGIN_OBJECT:
Map map =new LinkedTreeMap();
in.beginObject();
while (in.hasNext()) {
map.put(in.nextName(), read(in));
}
in.endObject();
return map;
case STRING:
return in.nextString();
case NUMBER:
/**
* 改写数字的处理逻辑,将数字值分为整型与浮点型。
*/
double dbNum = in.nextDouble();
// 数字超过long的最大值,返回浮点类型
if (dbNum > Long.MAX_VALUE) {
return dbNum;
}
// 判断数字是否为整数值
long lngNum = (long) dbNum;
if (dbNum == lngNum) {
return lngNum;
}else {
return dbNum;
}
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
default:
throw new IllegalStateException();
}
}
@Override
public void write(JsonWriter out, Object value)throws IOException {
// 序列化无需实现
}
}
网友评论