diff --git a/demo/src/main/java/com/jaredrummler/materialspinner/example/MainActivity.java b/demo/src/main/java/com/jaredrummler/materialspinner/example/MainActivity.java index 7b2cdf5..5efd074 100644 --- a/demo/src/main/java/com/jaredrummler/materialspinner/example/MainActivity.java +++ b/demo/src/main/java/com/jaredrummler/materialspinner/example/MainActivity.java @@ -18,6 +18,7 @@ package com.jaredrummler.materialspinner.example; import android.content.ActivityNotFoundException; +import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -27,56 +28,119 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; import com.jaredrummler.materialspinner.MaterialSpinner; +import com.jaredrummler.materialspinner.MaterialSpinnerAdapter; + +import java.util.ArrayList; +import java.util.List; public class MainActivity extends AppCompatActivity { - private static final String[] ANDROID_VERSIONS = { - "Cupcake", - "Donut", - "Eclair", - "Froyo", - "Gingerbread", - "Honeycomb", - "Ice Cream Sandwich", - "Jelly Bean", - "KitKat", - "Lollipop", - "Marshmallow", - "Nougat", - "Oreo" - }; - - @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - - @Override public void onClick(View view) { - try { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/jaredrummler/MaterialSpinner"))); - } catch (ActivityNotFoundException ignored) { + private static final String[] ANDROID_VERSIONS = { + "Cupcake", + "Donut", + "Eclair", + "Froyo", + "Gingerbread", + "Honeycomb", + "Ice Cream Sandwich", + "Jelly Bean", + "KitKat", + "Lollipop", + "Marshmallow", + "Nougat", + "Oreo" + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View view) { + try { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/jaredrummler/MaterialSpinner"))); + } catch (ActivityNotFoundException ignored) { + } + } + }); + + setupSpinner1(); + setupSpinner2(); + } + + private void setupSpinner1() { + MaterialSpinner spinner = (MaterialSpinner) findViewById(R.id.spinner); + spinner.setItems(ANDROID_VERSIONS); + spinner.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener() { + + @Override + public void onItemSelected(MaterialSpinner view, int position, long id, String item) { + Snackbar.make(view, "Clicked " + item, Snackbar.LENGTH_SHORT).show(); + } + }); + spinner.setOnNothingSelectedListener(new MaterialSpinner.OnNothingSelectedListener() { + + @Override + public void onNothingSelected(MaterialSpinner spinner) { + Snackbar.make(spinner, "Nothing selected", Snackbar.LENGTH_SHORT).show(); + } + }); + + } + + private void setupSpinner2() { + MaterialSpinner spinner2 = (MaterialSpinner) findViewById(R.id.spinner2); + + List cheeses = new ArrayList(); + cheeses.add(new Cheese("Edam", "Ne")); + cheeses.add(new Cheese("Cheddar", "Uk")); + cheeses.add(new Cheese("Brie", "Fr")); + + MyCustomCheeseAdapter adapter = new MyCustomCheeseAdapter(this, cheeses); + spinner2.setAdapter(adapter); + spinner2.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener() { + + @Override + public void onItemSelected(MaterialSpinner view, int position, long id, Cheese item) { + Snackbar.make(view, "Cheese clicked " + item, Snackbar.LENGTH_SHORT).show(); + } + }); + } + + class Cheese { + String name; + String origin; + + public Cheese(String name, String origin) { + this.name = name; + this.origin = origin; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Cheese{"); + sb.append("name='").append(name).append('\''); + sb.append(", origin='").append(origin).append('\''); + sb.append('}'); + return sb.toString(); + } + } + + class MyCustomCheeseAdapter extends MaterialSpinnerAdapter { + + public MyCustomCheeseAdapter(Context context, List items) { + super(context, items); + } + + @Override + public String getItemText(int position) { + return super.getItem(position).name; } - } - }); - - MaterialSpinner spinner = (MaterialSpinner) findViewById(R.id.spinner); - spinner.setItems(ANDROID_VERSIONS); - spinner.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener() { - - @Override public void onItemSelected(MaterialSpinner view, int position, long id, String item) { - Snackbar.make(view, "Clicked " + item, Snackbar.LENGTH_LONG).show(); - } - }); - spinner.setOnNothingSelectedListener(new MaterialSpinner.OnNothingSelectedListener() { - - @Override public void onNothingSelected(MaterialSpinner spinner) { - Snackbar.make(spinner, "Nothing selected", Snackbar.LENGTH_LONG).show(); - } - }); - } + } } diff --git a/demo/src/main/res/layout/content_main.xml b/demo/src/main/res/layout/content_main.xml index 3c243c3..d572ec7 100644 --- a/demo/src/main/res/layout/content_main.xml +++ b/demo/src/main/res/layout/content_main.xml @@ -1,5 +1,4 @@ - - + + + android:layout_height="wrap_content" + app:ms_dropdown_height="wrap_content" + app:ms_dropdown_max_height="350dp" /> + android:id="@+id/spinner2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/spinner" + android:layout_marginTop="8dp" + app:ms_dropdown_height="wrap_content" + app:ms_dropdown_max_height="350dp" /> diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 05ef575..758de96 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3510030..2d80b69 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sun Oct 28 17:36:40 PDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip diff --git a/gradlew b/gradlew index 9d82f78..cccdd3d 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..f955316 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/library/gradle.properties b/library/gradle.properties index d2b7810..bdd8627 100644 --- a/library/gradle.properties +++ b/library/gradle.properties @@ -1,11 +1,9 @@ -VERSION_NAME=1.3.1 -VERSION_CODE=131 +VERSION_NAME=1.3.2 +VERSION_CODE=132 GROUP=com.jaredrummler - POM_NAME=Material Spinner POM_ARTIFACT_ID=material-spinner POM_PACKAGING=aar - POM_DESCRIPTION=A spinner view for Android POM_URL=https://github.com/jaredrummler/Material-Spinner POM_SCM_URL=https://github.com/jaredrummler/MaterialSpinner @@ -16,6 +14,5 @@ POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt POM_LICENCE_DIST=repo POM_DEVELOPER_ID=jaredrummler POM_DEVELOPER_NAME=Jared Rummler - SNAPSHOT_REPOSITORY_URL=https://oss.sonatype.org/content/repositories/snapshots -RELEASE_REPOSITORY_URL=https://oss.sonatype.org/service/local/staging/deploy/maven2 \ No newline at end of file +RELEASE_REPOSITORY_URL=https://oss.sonatype.org/service/local/staging/deploy/maven2 diff --git a/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinner.java b/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinner.java index 106ca23..117952a 100755 --- a/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinner.java +++ b/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinner.java @@ -31,21 +31,14 @@ import android.os.Build; import android.os.Bundle; import android.os.Parcelable; -import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.WindowManager; -import android.widget.AdapterView; -import android.widget.ListAdapter; -import android.widget.ListView; -import android.widget.PopupWindow; -import android.widget.TextView; +import android.view.*; +import android.widget.*; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; @@ -55,583 +48,583 @@ */ public class MaterialSpinner extends TextView { - private OnNothingSelectedListener onNothingSelectedListener; - private OnItemSelectedListener onItemSelectedListener; - private MaterialSpinnerBaseAdapter adapter; - private PopupWindow popupWindow; - private ListView listView; - private Drawable arrowDrawable; - private boolean hideArrow; - private boolean nothingSelected; - private int popupWindowMaxHeight; - private int popupWindowHeight; - private int selectedIndex; - private int backgroundColor; - private int backgroundSelector; - private int arrowColor; - private int arrowColorDisabled; - private int textColor; - private int hintColor; - private int popupPaddingTop; - private int popupPaddingLeft; - private int popupPaddingBottom; - private int popupPaddingRight; - private String hintText; - - public MaterialSpinner(Context context) { - super(context); - init(context, null); - } - - public MaterialSpinner(Context context, AttributeSet attrs) { - super(context, attrs); - init(context, attrs); - } - - public MaterialSpinner(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(context, attrs); - } - - private void init(Context context, AttributeSet attrs) { - TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MaterialSpinner); - int defaultColor = getTextColors().getDefaultColor(); - boolean rtl = Utils.isRtl(context); - - int paddingLeft, paddingTop, paddingRight, paddingBottom; - int defaultPaddingLeft, defaultPaddingTop, defaultPaddingRight, defaultPaddingBottom; - int defaultPopupPaddingLeft, defaultPopupPaddingTop, defaultPopupPaddingRight, defaultPopupPaddingBottom; - - Resources resources = getResources(); - defaultPaddingLeft = defaultPaddingRight = - defaultPaddingBottom = defaultPaddingTop = resources.getDimensionPixelSize(R.dimen.ms__padding_top); - if (rtl) { - defaultPaddingRight = resources.getDimensionPixelSize(R.dimen.ms__padding_left); - } else { - defaultPaddingLeft = resources.getDimensionPixelSize(R.dimen.ms__padding_left); - } - defaultPopupPaddingLeft = - defaultPopupPaddingRight = resources.getDimensionPixelSize(R.dimen.ms__popup_padding_left); - defaultPopupPaddingTop = defaultPopupPaddingBottom = resources.getDimensionPixelSize(R.dimen.ms__popup_padding_top); - - try { - backgroundColor = ta.getColor(R.styleable.MaterialSpinner_ms_background_color, Color.WHITE); - backgroundSelector = ta.getResourceId(R.styleable.MaterialSpinner_ms_background_selector, 0); - textColor = ta.getColor(R.styleable.MaterialSpinner_ms_text_color, defaultColor); - hintColor = ta.getColor(R.styleable.MaterialSpinner_ms_hint_color, defaultColor); - arrowColor = ta.getColor(R.styleable.MaterialSpinner_ms_arrow_tint, textColor); - hideArrow = ta.getBoolean(R.styleable.MaterialSpinner_ms_hide_arrow, false); - hintText = ta.getString(R.styleable.MaterialSpinner_ms_hint) == null ? "" - : ta.getString(R.styleable.MaterialSpinner_ms_hint); - popupWindowMaxHeight = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_dropdown_max_height, 0); - popupWindowHeight = ta.getLayoutDimension(R.styleable.MaterialSpinner_ms_dropdown_height, - WindowManager.LayoutParams.WRAP_CONTENT); - paddingTop = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_padding_top, defaultPaddingTop); - paddingLeft = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_padding_left, defaultPaddingLeft); - paddingBottom = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_padding_bottom, defaultPaddingBottom); - paddingRight = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_padding_right, defaultPaddingRight); - popupPaddingTop = - ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_popup_padding_top, defaultPopupPaddingTop); - popupPaddingLeft = - ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_popup_padding_left, defaultPopupPaddingLeft); - popupPaddingBottom = - ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_popup_padding_bottom, defaultPopupPaddingBottom); - popupPaddingRight = - ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_popup_padding_right, defaultPopupPaddingRight); - arrowColorDisabled = Utils.lighter(arrowColor, 0.8f); - } finally { - ta.recycle(); - } - - nothingSelected = true; - - setGravity(Gravity.CENTER_VERTICAL | Gravity.START); - setClickable(true); - setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); - - setBackgroundResource(R.drawable.ms__selector); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && rtl) { - setLayoutDirection(View.LAYOUT_DIRECTION_RTL); - setTextDirection(View.TEXT_DIRECTION_RTL); - } - - if (!hideArrow) { - arrowDrawable = Utils.getDrawable(context, R.drawable.ms__arrow).mutate(); - arrowDrawable.setColorFilter(arrowColor, PorterDuff.Mode.SRC_IN); - Drawable[] drawables = getCompoundDrawables(); - if (rtl) { - drawables[0] = arrowDrawable; - } else { - drawables[2] = arrowDrawable; - } - setCompoundDrawablesWithIntrinsicBounds(drawables[0], drawables[1], drawables[2], drawables[3]); - } - - listView = new ListView(context); - listView.setId(getId()); - listView.setDivider(null); - listView.setItemsCanFocus(true); - listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - - @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - if (position >= selectedIndex - && position < adapter.getCount() - && adapter.getItems().size() != 1 - && TextUtils.isEmpty(hintText)) { - position++; + private OnNothingSelectedListener onNothingSelectedListener; + private OnItemSelectedListener onItemSelectedListener; + private MaterialSpinnerBaseAdapter adapter; + private PopupWindow popupWindow; + private ListView listView; + private Drawable arrowDrawable; + private boolean hideArrow; + private boolean nothingSelected; + private int popupWindowMaxHeight; + private int popupWindowHeight; + private int selectedIndex; + private int backgroundColor; + private int backgroundSelector; + private int arrowColor; + private int arrowColorDisabled; + private int textColor; + private int hintColor; + private int popupPaddingTop; + private int popupPaddingLeft; + private int popupPaddingBottom; + private int popupPaddingRight; + + public MaterialSpinner(Context context) { + super(context); + init(context, null); + } + + public MaterialSpinner(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public MaterialSpinner(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MaterialSpinner); + int defaultColor = getTextColors().getDefaultColor(); + boolean rtl = Utils.isRtl(context); + + int paddingLeft, paddingTop, paddingRight, paddingBottom; + int defaultPaddingLeft, defaultPaddingTop, defaultPaddingRight, defaultPaddingBottom; + int defaultPopupPaddingLeft, defaultPopupPaddingTop, defaultPopupPaddingRight, defaultPopupPaddingBottom; + + Resources resources = getResources(); + defaultPaddingLeft = defaultPaddingRight = + defaultPaddingBottom = defaultPaddingTop = resources.getDimensionPixelSize(R.dimen.ms__padding_top); + if (rtl) { + defaultPaddingRight = resources.getDimensionPixelSize(R.dimen.ms__padding_left); + } else { + defaultPaddingLeft = resources.getDimensionPixelSize(R.dimen.ms__padding_left); + } + defaultPopupPaddingLeft = + defaultPopupPaddingRight = resources.getDimensionPixelSize(R.dimen.ms__popup_padding_left); + defaultPopupPaddingTop = defaultPopupPaddingBottom = resources.getDimensionPixelSize(R.dimen.ms__popup_padding_top); + + try { + backgroundColor = ta.getColor(R.styleable.MaterialSpinner_ms_background_color, Color.WHITE); + backgroundSelector = ta.getResourceId(R.styleable.MaterialSpinner_ms_background_selector, 0); + textColor = ta.getColor(R.styleable.MaterialSpinner_ms_text_color, defaultColor); + hintColor = ta.getColor(R.styleable.MaterialSpinner_ms_hint_color, defaultColor); + arrowColor = ta.getColor(R.styleable.MaterialSpinner_ms_arrow_tint, textColor); + hideArrow = ta.getBoolean(R.styleable.MaterialSpinner_ms_hide_arrow, false); + popupWindowMaxHeight = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_dropdown_max_height, 0); + popupWindowHeight = ta.getLayoutDimension(R.styleable.MaterialSpinner_ms_dropdown_height, + WindowManager.LayoutParams.WRAP_CONTENT); + paddingTop = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_padding_top, defaultPaddingTop); + paddingLeft = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_padding_left, defaultPaddingLeft); + paddingBottom = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_padding_bottom, defaultPaddingBottom); + paddingRight = ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_padding_right, defaultPaddingRight); + popupPaddingTop = + ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_popup_padding_top, defaultPopupPaddingTop); + popupPaddingLeft = + ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_popup_padding_left, defaultPopupPaddingLeft); + popupPaddingBottom = + ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_popup_padding_bottom, defaultPopupPaddingBottom); + popupPaddingRight = + ta.getDimensionPixelSize(R.styleable.MaterialSpinner_ms_popup_padding_right, defaultPopupPaddingRight); + arrowColorDisabled = Utils.lighter(arrowColor, 0.8f); + } finally { + ta.recycle(); + } + + nothingSelected = true; + + setGravity(Gravity.CENTER_VERTICAL | Gravity.START); + setClickable(true); + setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); + + setBackgroundResource(R.drawable.ms__selector); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && rtl) { + setLayoutDirection(View.LAYOUT_DIRECTION_RTL); + setTextDirection(View.TEXT_DIRECTION_RTL); + } + + if (!hideArrow) { + arrowDrawable = Utils.getDrawable(context, R.drawable.ms__arrow).mutate(); + arrowDrawable.setColorFilter(arrowColor, PorterDuff.Mode.SRC_IN); + Drawable[] drawables = getCompoundDrawables(); + if (rtl) { + drawables[0] = arrowDrawable; + } else { + drawables[2] = arrowDrawable; + } + setCompoundDrawablesWithIntrinsicBounds(drawables[0], drawables[1], drawables[2], drawables[3]); + } + + listView = new ListView(context); + listView.setId(getId()); + listView.setDivider(null); + listView.setItemsCanFocus(true); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + selectedIndex = position; + nothingSelected = false; + Object item = adapter.getItem(position); + adapter.notifyItemSelected(position); + setTextColor(textColor); + setText(adapter.getItemText(position)); + collapse(); + if (onItemSelectedListener != null) { + onItemSelectedListener.onItemSelected(MaterialSpinner.this, position, id, item); + } + } + }); + + popupWindow = new PopupWindow(context); + popupWindow.setContentView(listView); + popupWindow.setOutsideTouchable(true); + popupWindow.setFocusable(true); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + popupWindow.setElevation(16); + popupWindow.setBackgroundDrawable(Utils.getDrawable(context, R.drawable.ms__drawable)); + } else { + popupWindow.setBackgroundDrawable(Utils.getDrawable(context, R.drawable.ms__drop_down_shadow)); + } + + if (backgroundColor != Color.WHITE) { // default color is white + setBackgroundColor(backgroundColor); + } else if (backgroundSelector != 0) { + setBackgroundResource(backgroundSelector); } - selectedIndex = position; - nothingSelected = false; - Object item = adapter.get(position); - adapter.notifyItemSelected(position); - setTextColor(textColor); - setText(item.toString()); - collapse(); - if (onItemSelectedListener != null) { - //noinspection unchecked - onItemSelectedListener.onItemSelected(MaterialSpinner.this, position, id, item); + if (textColor != defaultColor) { + setTextColor(textColor); } - } - }); - popupWindow = new PopupWindow(context); - popupWindow.setContentView(listView); - popupWindow.setOutsideTouchable(true); - popupWindow.setFocusable(true); + popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - popupWindow.setElevation(16); - popupWindow.setBackgroundDrawable(Utils.getDrawable(context, R.drawable.ms__drawable)); - } else { - popupWindow.setBackgroundDrawable(Utils.getDrawable(context, R.drawable.ms__drop_down_shadow)); + @Override + public void onDismiss() { + if (nothingSelected && onNothingSelectedListener != null) { + onNothingSelectedListener.onNothingSelected(MaterialSpinner.this); + } + if (!hideArrow) { + animateArrow(false); + } + } + }); } - if (backgroundColor != Color.WHITE) { // default color is white - setBackgroundColor(backgroundColor); - } else if (backgroundSelector != 0) { - setBackgroundResource(backgroundSelector); + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + popupWindow.setWidth(MeasureSpec.getSize(widthMeasureSpec)); + popupWindow.setHeight(calculatePopupWindowHeight()); + if (adapter != null) { + CharSequence currentText = getText(); + String longestItem = currentText.toString(); + for (int i = 0; i < adapter.getCount(); i++) { + String itemText = adapter.getItemText(i); + if (itemText.length() > longestItem.length()) { + longestItem = itemText; + } + } + setText(longestItem); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + setText(currentText); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } } - if (textColor != defaultColor) { - setTextColor(textColor); + + @Override + public boolean onTouchEvent(@NonNull MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP) { + togglePopup(); + } + return super.onTouchEvent(event); } - popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { + togglePopup(); + } + return super.onKeyUp(keyCode, event); + } - @Override public void onDismiss() { - if (nothingSelected && onNothingSelectedListener != null) { - onNothingSelectedListener.onNothingSelected(MaterialSpinner.this); + private void togglePopup() { + if (isEnabled() && isClickable()) { + if (!popupWindow.isShowing()) { + expand(); + } else { + collapse(); + } } - if (!hideArrow) { - animateArrow(false); + } + + @Override + public void setBackgroundColor(int color) { + backgroundColor = color; + Drawable background = getBackground(); + if (background instanceof StateListDrawable) { // pre-L + try { + Method getStateDrawable = StateListDrawable.class.getDeclaredMethod("getStateDrawable", int.class); + if (!getStateDrawable.isAccessible()) getStateDrawable.setAccessible(true); + int[] colors = {Utils.darker(color, 0.85f), color}; + for (int i = 0; i < colors.length; i++) { + ColorDrawable drawable = (ColorDrawable) getStateDrawable.invoke(background, i); + drawable.setColor(colors[i]); + } + } catch (Exception e) { + Log.e("MaterialSpinner", "Error setting background color", e); + } + } else if (background != null) { // 21+ (RippleDrawable) + background.setColorFilter(color, PorterDuff.Mode.SRC_IN); } - } - }); - } - - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - popupWindow.setWidth(MeasureSpec.getSize(widthMeasureSpec)); - popupWindow.setHeight(calculatePopupWindowHeight()); - if (adapter != null) { - CharSequence currentText = getText(); - String longestItem = currentText.toString(); - for (int i = 0; i < adapter.getCount(); i++) { - String itemText = adapter.getItemText(i); - if (itemText.length() > longestItem.length()) { - longestItem = itemText; + popupWindow.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + + @Override + public void setTextColor(int color) { + textColor = color; + if (adapter != null) { + adapter.setTextColor(textColor); + adapter.notifyDataSetChanged(); } - } - setText(longestItem); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - setText(currentText); - } else { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - - @Override public boolean onTouchEvent(@NonNull MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_UP) { - if (isEnabled() && isClickable()) { - if (!popupWindow.isShowing()) { - expand(); + super.setTextColor(color); + } + + public void setHintColor(int color) { + hintColor = color; + super.setTextColor(color); + } + + @Override + public Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable("state", super.onSaveInstanceState()); + bundle.putInt("selected_index", selectedIndex); + bundle.putBoolean("nothing_selected", nothingSelected); + if (popupWindow != null) { + bundle.putBoolean("is_popup_showing", popupWindow.isShowing()); + collapse(); } else { - collapse(); + bundle.putBoolean("is_popup_showing", false); + } + return bundle; + } + + @Override + public void onRestoreInstanceState(Parcelable savedState) { + if (savedState instanceof Bundle) { + Bundle bundle = (Bundle) savedState; + selectedIndex = bundle.getInt("selected_index"); + nothingSelected = bundle.getBoolean("nothing_selected"); + if (adapter != null) { + setTextColor(textColor); + setText(adapter.getItemText(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() { + expand(); + } + }); + } + } + savedState = bundle.getParcelable("state"); } - } - } - return super.onTouchEvent(event); - } - - @Override public void setBackgroundColor(int color) { - backgroundColor = color; - Drawable background = getBackground(); - if (background instanceof StateListDrawable) { // pre-L - try { - Method getStateDrawable = StateListDrawable.class.getDeclaredMethod("getStateDrawable", int.class); - if (!getStateDrawable.isAccessible()) getStateDrawable.setAccessible(true); - int[] colors = { Utils.darker(color, 0.85f), color }; - for (int i = 0; i < colors.length; i++) { - ColorDrawable drawable = (ColorDrawable) getStateDrawable.invoke(background, i); - drawable.setColor(colors[i]); + super.onRestoreInstanceState(savedState); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + if (arrowDrawable != null) { + arrowDrawable.setColorFilter(enabled ? arrowColor : arrowColorDisabled, PorterDuff.Mode.SRC_IN); } - } catch (Exception e) { - Log.e("MaterialSpinner", "Error setting background color", e); - } - } else if (background != null) { // 21+ (RippleDrawable) - background.setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - popupWindow.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - - @Override public void setTextColor(int color) { - textColor = color; - if (adapter != null) { - adapter.setTextColor(textColor); - adapter.notifyDataSetChanged(); - } - super.setTextColor(color); - } - - public void setHintColor(int color) { - hintColor = color; - super.setTextColor(color); - } - - @Override public Parcelable onSaveInstanceState() { - Bundle bundle = new Bundle(); - bundle.putParcelable("state", super.onSaveInstanceState()); - bundle.putInt("selected_index", selectedIndex); - bundle.putBoolean("nothing_selected", nothingSelected); - if (popupWindow != null) { - bundle.putBoolean("is_popup_showing", popupWindow.isShowing()); - collapse(); - } else { - bundle.putBoolean("is_popup_showing", false); - } - return bundle; - } - - @Override public void onRestoreInstanceState(Parcelable savedState) { - if (savedState instanceof Bundle) { - Bundle bundle = (Bundle) savedState; - selectedIndex = bundle.getInt("selected_index"); - nothingSelected = bundle.getBoolean("nothing_selected"); - if (adapter != null) { - if (nothingSelected && !TextUtils.isEmpty(hintText)) { - setHintColor(hintColor); - setText(hintText); + } + + /** + * @return the selected item position + */ + public int getSelectedIndex() { + return selectedIndex; + } + + /** + * 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.getCount()) { + adapter.notifyItemSelected(position); + selectedIndex = position; + setText(adapter.getItemText(position)); + } else { + throw new IllegalArgumentException("Position must be lower than adapter count!"); + } + } + } + + /** + * Register a callback to be invoked when an item in the dropdown is selected. + * + * @param onItemSelectedListener The callback that will run + */ + public void setOnItemSelectedListener(@Nullable OnItemSelectedListener onItemSelectedListener) { + this.onItemSelectedListener = onItemSelectedListener; + } + + /** + * Register a callback to be invoked when the {@link PopupWindow} is shown but the user didn't select an item. + * + * @param onNothingSelectedListener the callback that will run + */ + public void setOnNothingSelectedListener(@Nullable OnNothingSelectedListener onNothingSelectedListener) { + this.onNothingSelectedListener = onNothingSelectedListener; + } + + /** + * Set the dropdown items + * + * @param items A list of items + * @param The item type + */ + public void setItems(@NonNull T... items) { + setItems(Arrays.asList(items)); + } + + /** + * Set the dropdown items + * + * @param items A list of items + * @param The item type + */ + public void setItems(@NonNull List items) { + adapter = new MaterialSpinnerAdapter<>(getContext(), items) + .setPopupPadding(popupPaddingLeft, popupPaddingTop, popupPaddingRight, popupPaddingBottom) + .setBackgroundSelector(backgroundSelector) + .setTextColor(textColor); + setAdapterInternal(adapter); + } + + /** + * Set a custom adapter for the dropdown items + * + * @param adapter The list adapter + */ + public void setAdapter(@NonNull ListAdapter adapter) { + this.adapter = new MaterialSpinnerAdapterWrapper(getContext(), adapter) + .setPopupPadding(popupPaddingLeft, popupPaddingTop, popupPaddingRight, popupPaddingBottom) + .setBackgroundSelector(backgroundSelector) + .setTextColor(textColor); + setAdapterInternal(this.adapter); + } + + /** + * Set the custom adapter for the dropdown items + * + * @param adapter The adapter + * @param The type + */ + public void setAdapter(MaterialSpinnerAdapter adapter) { + this.adapter = adapter; + this.adapter.setPopupPadding(popupPaddingLeft, popupPaddingTop, popupPaddingRight, popupPaddingBottom); + this.adapter.setTextColor(textColor); + this.adapter.setBackgroundSelector(backgroundSelector); + setAdapterInternal(adapter); + } + + private void setAdapterInternal(@NonNull MaterialSpinnerBaseAdapter adapter) { + boolean shouldResetPopupHeight = listView.getAdapter() != null; + listView.setAdapter(adapter); + if (selectedIndex >= adapter.getCount()) { + selectedIndex = 0; + } + if (adapter.getItems().size() > 0) { + setTextColor(textColor); + setText(adapter.getItemText(selectedIndex)); } else { - setTextColor(textColor); - setText(adapter.get(selectedIndex).toString()); + setText(""); } - adapter.notifyItemSelected(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() { + if (shouldResetPopupHeight) { + popupWindow.setHeight(calculatePopupWindowHeight()); + } + } + + /** + * Get the list of items in the adapter + * + * @param The item type + * @return A list of items or {@code null} if no items are set. + */ + public List getItems() { + if (adapter == null) { + return null; + } + //noinspection unchecked + return adapter.getItems(); + } + + /** + * Show the dropdown menu + */ + public void expand() { + if (canShowPopup()) { + if (!hideArrow) { + animateArrow(true); + } + nothingSelected = true; + popupWindow.showAsDropDown(this); + } + } + + /** + * Closes the dropdown menu + */ + public void collapse() { + if (!hideArrow) { + animateArrow(false); + } + popupWindow.dismiss(); + } + + /** + * Set the tint color for the dropdown arrow + * + * @param color the color value + */ + public void setArrowColor(@ColorInt int color) { + arrowColor = color; + arrowColorDisabled = Utils.lighter(arrowColor, 0.8f); + if (arrowDrawable != null) { + arrowDrawable.setColorFilter(arrowColor, PorterDuff.Mode.SRC_IN); + } + } + + private boolean canShowPopup() { + Activity activity = getActivity(); + if (activity == null || activity.isFinishing()) { + return false; + } + boolean isLaidOut; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + isLaidOut = isLaidOut(); + } else { + isLaidOut = getWidth() > 0 && getHeight() > 0; + } + return isLaidOut; + } - @Override public void run() { - expand(); + private Activity getActivity() { + Context context = getContext(); + while (context instanceof ContextWrapper) { + if (context instanceof Activity) { + return (Activity) context; } - }); + context = ((ContextWrapper) context).getBaseContext(); } - } - savedState = bundle.getParcelable("state"); - } - super.onRestoreInstanceState(savedState); - } - - @Override public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - if (arrowDrawable != null) { - arrowDrawable.setColorFilter(enabled ? arrowColor : arrowColorDisabled, PorterDuff.Mode.SRC_IN); - } - } - - /** - * @return the selected item position - */ - public int getSelectedIndex() { - return selectedIndex; - } - - /** - * 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.getCount()) { - adapter.notifyItemSelected(position); - selectedIndex = position; - setText(adapter.get(position).toString()); - } else { - throw new IllegalArgumentException("Position must be lower than adapter count!"); - } - } - } - - /** - * Register a callback to be invoked when an item in the dropdown is selected. - * - * @param onItemSelectedListener The callback that will run - */ - public void setOnItemSelectedListener(@Nullable OnItemSelectedListener onItemSelectedListener) { - this.onItemSelectedListener = onItemSelectedListener; - } - - /** - * Register a callback to be invoked when the {@link PopupWindow} is shown but the user didn't select an item. - * - * @param onNothingSelectedListener the callback that will run - */ - public void setOnNothingSelectedListener(@Nullable OnNothingSelectedListener onNothingSelectedListener) { - this.onNothingSelectedListener = onNothingSelectedListener; - } - - /** - * Set the dropdown items - * - * @param items A list of items - * @param The item type - */ - public void setItems(@NonNull T... items) { - setItems(Arrays.asList(items)); - } - - /** - * Set the dropdown items - * - * @param items A list of items - * @param The item type - */ - public void setItems(@NonNull List items) { - adapter = new MaterialSpinnerAdapter<>(getContext(), items) - .setPopupPadding(popupPaddingLeft, popupPaddingTop, popupPaddingRight, popupPaddingBottom) - .setBackgroundSelector(backgroundSelector) - .setTextColor(textColor); - setAdapterInternal(adapter); - } - - /** - * Set a custom adapter for the dropdown items - * - * @param adapter The list adapter - */ - public void setAdapter(@NonNull ListAdapter adapter) { - this.adapter = new MaterialSpinnerAdapterWrapper(getContext(), adapter) - .setPopupPadding(popupPaddingLeft, popupPaddingTop, popupPaddingRight, popupPaddingBottom) - .setBackgroundSelector(backgroundSelector) - .setTextColor(textColor); - setAdapterInternal(this.adapter); - } - - /** - * Set the custom adapter for the dropdown items - * - * @param adapter The adapter - * @param The type - */ - public void setAdapter(MaterialSpinnerAdapter adapter) { - this.adapter = adapter; - this.adapter.setTextColor(textColor); - this.adapter.setBackgroundSelector(backgroundSelector); - this.adapter.setPopupPadding(popupPaddingLeft, popupPaddingTop, popupPaddingRight, popupPaddingBottom); - setAdapterInternal(adapter); - } - - private void setAdapterInternal(@NonNull MaterialSpinnerBaseAdapter adapter) { - boolean shouldResetPopupHeight = listView.getAdapter() != null; - adapter.setHintEnabled(!TextUtils.isEmpty(hintText)); - listView.setAdapter(adapter); - if (selectedIndex >= adapter.getCount()) { - selectedIndex = 0; - } - if (adapter.getItems().size() > 0) { - if (nothingSelected && !TextUtils.isEmpty(hintText)) { - setText(hintText); - setHintColor(hintColor); - } else { - setTextColor(textColor); - setText(adapter.get(selectedIndex).toString()); - } - } else { - setText(""); - } - if (shouldResetPopupHeight) { - popupWindow.setHeight(calculatePopupWindowHeight()); - } - } - - /** - * Get the list of items in the adapter - * - * @param The item type - * @return A list of items or {@code null} if no items are set. - */ - public List getItems() { - if (adapter == null) { - return null; - } - //noinspection unchecked - return adapter.getItems(); - } - - /** - * Show the dropdown menu - */ - public void expand() { - if (canShowPopup()) { - if (!hideArrow) { - animateArrow(true); - } - nothingSelected = true; - popupWindow.showAsDropDown(this); - } - } - - /** - * Closes the dropdown menu - */ - public void collapse() { - if (!hideArrow) { - animateArrow(false); - } - popupWindow.dismiss(); - } - - /** - * Set the tint color for the dropdown arrow - * - * @param color the color value - */ - public void setArrowColor(@ColorInt int color) { - arrowColor = color; - arrowColorDisabled = Utils.lighter(arrowColor, 0.8f); - if (arrowDrawable != null) { - arrowDrawable.setColorFilter(arrowColor, PorterDuff.Mode.SRC_IN); - } - } - - private boolean canShowPopup() { - Activity activity = getActivity(); - if (activity == null || activity.isFinishing()) { - return false; - } - boolean isLaidOut; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - isLaidOut = isLaidOut(); - } else { - isLaidOut = getWidth() > 0 && getHeight() > 0; - } - return isLaidOut; - } - - private Activity getActivity() { - Context context = getContext(); - while (context instanceof ContextWrapper) { - if (context instanceof Activity) { - return (Activity) context; - } - context = ((ContextWrapper) context).getBaseContext(); - } - return null; - } - - private void animateArrow(boolean shouldRotateUp) { - int start = shouldRotateUp ? 0 : 10000; - int end = shouldRotateUp ? 10000 : 0; - ObjectAnimator animator = ObjectAnimator.ofInt(arrowDrawable, "level", start, end); - animator.start(); - } - - /** - * Set the maximum height of the dropdown menu. - * - * @param height the height in pixels - */ - public void setDropdownMaxHeight(int height) { - popupWindowMaxHeight = height; - popupWindow.setHeight(calculatePopupWindowHeight()); - } - - /** - * Set the height of the dropdown menu - * - * @param height the height in pixels - */ - public void setDropdownHeight(int height) { - popupWindowHeight = height; - popupWindow.setHeight(calculatePopupWindowHeight()); - } - - private int calculatePopupWindowHeight() { - if (adapter == null) { - return WindowManager.LayoutParams.WRAP_CONTENT; - } - float itemHeight = getResources().getDimension(R.dimen.ms__item_height); - float listViewHeight = adapter.getCount() * itemHeight; - if (popupWindowMaxHeight > 0 && listViewHeight > popupWindowMaxHeight) { - return popupWindowMaxHeight; - } else if (popupWindowHeight != WindowManager.LayoutParams.MATCH_PARENT - && popupWindowHeight != WindowManager.LayoutParams.WRAP_CONTENT - && popupWindowHeight <= listViewHeight) { - return popupWindowHeight; - } else if (listViewHeight == 0 && adapter.getItems().size() == 1) { - return (int) itemHeight; - } - return WindowManager.LayoutParams.WRAP_CONTENT; - } - - /** - * Get the {@link PopupWindow}. - * - * @return The {@link PopupWindow} that is displayed when the view has been clicked. - */ - public PopupWindow getPopupWindow() { - return popupWindow; - } - - /** - * Get the {@link ListView} that is used in the dropdown menu - * - * @return the ListView shown in the PopupWindow. - */ - public ListView getListView() { - return listView; - } - - /** - * Interface definition for a callback to be invoked when an item in this view has been selected. - * - * @param Adapter item type - */ - public interface OnItemSelectedListener { + return null; + } + + private void animateArrow(boolean shouldRotateUp) { + int start = shouldRotateUp ? 0 : 10000; + int end = shouldRotateUp ? 10000 : 0; + ObjectAnimator animator = ObjectAnimator.ofInt(arrowDrawable, "level", start, end); + animator.start(); + } /** - *

Callback method to be invoked when an item in this view has been selected. This callback is invoked only when - * the newly selected position is different from the previously selected position or if there was no selected - * item.

+ * Set the maximum height of the dropdown menu. * - * @param view The {@link MaterialSpinner} view - * @param position The position of the view in the adapter - * @param id The row id of the item that is selected - * @param item The selected item + * @param height the height in pixels */ - void onItemSelected(MaterialSpinner view, int position, long id, T item); - } + public void setDropdownMaxHeight(int height) { + popupWindowMaxHeight = height; + popupWindow.setHeight(calculatePopupWindowHeight()); + } - /** - * Interface definition for a callback to be invoked when the dropdown is dismissed and no item was selected. - */ - public interface OnNothingSelectedListener { + /** + * Set the height of the dropdown menu + * + * @param height the height in pixels + */ + public void setDropdownHeight(int height) { + popupWindowHeight = height; + popupWindow.setHeight(calculatePopupWindowHeight()); + } + + private int calculatePopupWindowHeight() { + if (adapter == null) { + return WindowManager.LayoutParams.WRAP_CONTENT; + } + float itemHeight = getResources().getDimension(R.dimen.ms__item_height); + float listViewHeight = adapter.getCount() * itemHeight; + if (popupWindowMaxHeight > 0 && listViewHeight > popupWindowMaxHeight) { + return popupWindowMaxHeight; + } else if (popupWindowHeight != WindowManager.LayoutParams.MATCH_PARENT + && popupWindowHeight != WindowManager.LayoutParams.WRAP_CONTENT + && popupWindowHeight <= listViewHeight) { + return popupWindowHeight; + } else if (listViewHeight == 0 && adapter.getItems().size() == 1) { + return (int) itemHeight; + } + return WindowManager.LayoutParams.WRAP_CONTENT; + } + + /** + * Get the {@link PopupWindow}. + * + * @return The {@link PopupWindow} that is displayed when the view has been clicked. + */ + public PopupWindow getPopupWindow() { + return popupWindow; + } /** - * Callback method to be invoked when the {@link PopupWindow} is dismissed and no item was selected. + * Get the {@link ListView} that is used in the dropdown menu * - * @param spinner the {@link MaterialSpinner} + * @return the ListView shown in the PopupWindow. */ - void onNothingSelected(MaterialSpinner spinner); - } -} \ No newline at end of file + public ListView getListView() { + return listView; + } + + /** + * Interface definition for a callback to be invoked when an item in this view has been selected. + * + * @param Adapter item type + */ + public interface OnItemSelectedListener { + + /** + *

Callback method to be invoked when an item in this view has been selected. This callback is invoked only when + * the newly selected position is different from the previously selected position or if there was no selected + * item.

+ * + * @param view The {@link MaterialSpinner} view + * @param position The position of the view in the adapter + * @param id The row id of the item that is selected + * @param item The selected item + */ + void onItemSelected(MaterialSpinner view, int position, long id, T item); + } + + /** + * Interface definition for a callback to be invoked when the dropdown is dismissed and no item was selected. + */ + public interface OnNothingSelectedListener { + + /** + * Callback method to be invoked when the {@link PopupWindow} is dismissed and no item was selected. + * + * @param spinner the {@link MaterialSpinner} + */ + void onNothingSelected(MaterialSpinner spinner); + } +} diff --git a/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerAdapter.java b/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerAdapter.java index 1e6ceb9..e6436f1 100644 --- a/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerAdapter.java +++ b/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerAdapter.java @@ -18,38 +18,30 @@ package com.jaredrummler.materialspinner; import android.content.Context; + import java.util.List; public class MaterialSpinnerAdapter extends MaterialSpinnerBaseAdapter { - private final List items; - - public MaterialSpinnerAdapter(Context context, List items) { - super(context); - this.items = items; - } - - @Override public int getCount() { - int size = items.size(); - if (size == 1 || isHintEnabled()) return size; - return size - 1; - } - - @Override public T getItem(int position) { - if (isHintEnabled()) { - return items.get(position); - } else if (position >= getSelectedIndex() && items.size() != 1) { - return items.get(position + 1); - } else { - return items.get(position); + private final List items; + + public MaterialSpinnerAdapter(Context context, List items) { + super(context); + this.items = items; + } + + @Override + public int getCount() { + return items.size(); } - } - @Override public T get(int position) { - return items.get(position); - } + @Override + public T getItem(int position) { + return items.get(position); + } - @Override public List getItems() { - return items; - } -} \ No newline at end of file + @Override + public List getItems() { + return items; + } +} diff --git a/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerAdapterWrapper.java b/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerAdapterWrapper.java index 77ae21b..7dd946c 100644 --- a/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerAdapterWrapper.java +++ b/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerAdapterWrapper.java @@ -19,43 +19,36 @@ import android.content.Context; import android.widget.ListAdapter; + import java.util.ArrayList; import java.util.List; final class MaterialSpinnerAdapterWrapper extends MaterialSpinnerBaseAdapter { - private final ListAdapter listAdapter; - - public MaterialSpinnerAdapterWrapper(Context context, ListAdapter toWrap) { - super(context); - listAdapter = toWrap; - } - - @Override public int getCount() { - int size = listAdapter.getCount(); - if (size == 1 || isHintEnabled()) return size; - return size - 1; - } - - @Override public Object getItem(int position) { - if (isHintEnabled()) { - return listAdapter.getItem(position); - } else if (position >= getSelectedIndex() && listAdapter.getCount() != 1) { - return listAdapter.getItem(position + 1); - } else { - return listAdapter.getItem(position); + private final ListAdapter listAdapter; + + public MaterialSpinnerAdapterWrapper(Context context, ListAdapter toWrap) { + super(context); + listAdapter = toWrap; } - } - @Override public Object get(int position) { - return listAdapter.getItem(position); - } + @Override + public int getCount() { + int size = listAdapter.getCount(); + return size; + } + + @Override + public Object getItem(int position) { + return listAdapter.getItem(position); + } - @Override public List getItems() { - List items = new ArrayList<>(); - for (int i = 0; i < listAdapter.getCount(); i++) { - items.add(listAdapter.getItem(i)); + @Override + public List getItems() { + List items = new ArrayList<>(); + for (int i = 0; i < listAdapter.getCount(); i++) { + items.add(listAdapter.getItem(i)); + } + return items; } - return items; - } -} \ No newline at end of file +} diff --git a/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerBaseAdapter.java b/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerBaseAdapter.java index 9b61400..9293103 100755 --- a/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerBaseAdapter.java +++ b/library/src/main/java/com/jaredrummler/materialspinner/MaterialSpinnerBaseAdapter.java @@ -27,140 +27,101 @@ import android.widget.TextView; import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; + import java.util.List; public abstract class MaterialSpinnerBaseAdapter extends BaseAdapter { - private final Context context; - private int selectedIndex; - private int textColor; - private int backgroundSelector; - private int popupPaddingTop; - private int popupPaddingLeft; - private int popupPaddingBottom; - private int popupPaddingRight; - private boolean isHintEnabled; - - public MaterialSpinnerBaseAdapter(Context context) { - this.context = context; - } - - @Override public View getView(int position, View convertView, ViewGroup parent) { - final TextView textView; - if (convertView == null) { - LayoutInflater inflater = LayoutInflater.from(context); - convertView = inflater.inflate(R.layout.ms__list_item, parent, false); - textView = (TextView) convertView.findViewById(R.id.tv_tinted_spinner); - textView.setTextColor(textColor); - - textView.setPadding(popupPaddingLeft, popupPaddingTop, popupPaddingRight, popupPaddingBottom); -// -// int left, right, bottom, top; -// if (popupPaddingTop != -1) { -// left = textView.getPaddingLeft(); -// right = textView.getPaddingRight(); -// bottom = textView.getPaddingBottom(); -// -// textView.setPadding(left, popupPaddingTop, right, bottom); -// } -// -// if (popupPaddingLeft != -1) { -// top = textView.getPaddingTop(); -// right = textView.getPaddingRight(); -// bottom = textView.getPaddingBottom(); -// -// textView.setPadding(popupPaddingLeft, top, right, bottom); -// } -// -// if (popupPaddingBottom != -1) { -// left = textView.getPaddingLeft(); -// top = textView.getPaddingTop(); -// right = textView.getPaddingRight(); -// -// textView.setPadding(left, top, right, popupPaddingBottom); -// } -// -// if (popupPaddingRight != -1) { -// left = textView.getPaddingLeft(); -// top = textView.getPaddingTop(); -// bottom = textView.getPaddingBottom(); -// -// textView.setPadding(left, top, popupPaddingRight, bottom); -// } - - if (backgroundSelector != 0) { - textView.setBackgroundResource(backgroundSelector); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - Configuration config = context.getResources().getConfiguration(); - if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { - textView.setTextDirection(View.TEXT_DIRECTION_RTL); - } - } - convertView.setTag(new ViewHolder(textView)); - } else { - textView = ((ViewHolder) convertView.getTag()).textView; + private final Context context; + private int selectedIndex; + private int textColor; + private int backgroundSelector; + private int popupPaddingTop; + private int popupPaddingLeft; + private int popupPaddingBottom; + private int popupPaddingRight; + + public MaterialSpinnerBaseAdapter(Context context) { + this.context = context; } - textView.setText(getItemText(position)); - return convertView; - } - - public String getItemText(int position) { - return getItem(position).toString(); - } - - public int getSelectedIndex() { - return selectedIndex; - } - public void notifyItemSelected(int index) { - selectedIndex = index; - } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final TextView textView; + if (convertView == null) { + LayoutInflater inflater = LayoutInflater.from(context); + convertView = inflater.inflate(R.layout.ms__list_item, parent, false); + textView = (TextView) convertView.findViewById(R.id.tv_tinted_spinner); + textView.setTextColor(textColor); + + textView.setPadding(popupPaddingLeft, popupPaddingTop, popupPaddingRight, popupPaddingBottom); + + if (backgroundSelector != 0) { + textView.setBackgroundResource(backgroundSelector); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + Configuration config = context.getResources().getConfiguration(); + if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + textView.setTextDirection(View.TEXT_DIRECTION_RTL); + } + } + convertView.setTag(new ViewHolder(textView)); + } else { + textView = ((ViewHolder) convertView.getTag()).textView; + } + textView.setText(getItemText(position)); + return convertView; + } - @Override public long getItemId(int position) { - return position; - } + public String getItemText(int position) { + return getItem(position).toString(); + } - @Override public abstract T getItem(int position); + public int getSelectedIndex() { + return selectedIndex; + } - @Override public abstract int getCount(); + public void notifyItemSelected(int index) { + selectedIndex = index; + } - public abstract T get(int position); + @Override + public long getItemId(int position) { + return position; + } - public abstract List getItems(); + @Override + public abstract T getItem(int position); - public void setHintEnabled(boolean isHintEnabled) { - this.isHintEnabled = isHintEnabled; - } + @Override + public abstract int getCount(); - public boolean isHintEnabled() { - return this.isHintEnabled; - } + public abstract List getItems(); - public MaterialSpinnerBaseAdapter setTextColor(@ColorInt int textColor) { - this.textColor = textColor; - return this; - } + public MaterialSpinnerBaseAdapter setTextColor(@ColorInt int textColor) { + this.textColor = textColor; + return this; + } - public MaterialSpinnerBaseAdapter setBackgroundSelector(@DrawableRes int backgroundSelector) { - this.backgroundSelector = backgroundSelector; - return this; - } + public MaterialSpinnerBaseAdapter setBackgroundSelector(@DrawableRes int backgroundSelector) { + this.backgroundSelector = backgroundSelector; + return this; + } - public MaterialSpinnerBaseAdapter setPopupPadding(int left, int top, int right, int bottom) { - this.popupPaddingLeft = left; - this.popupPaddingTop = top; - this.popupPaddingRight = right; - this.popupPaddingBottom = bottom; - return this; - } + public MaterialSpinnerBaseAdapter setPopupPadding(int left, int top, int right, int bottom) { + this.popupPaddingLeft = left; + this.popupPaddingTop = top; + this.popupPaddingRight = right; + this.popupPaddingBottom = bottom; + return this; + } - private static class ViewHolder { + private static class ViewHolder { - private TextView textView; + private TextView textView; - private ViewHolder(TextView textView) { - this.textView = textView; + private ViewHolder(TextView textView) { + this.textView = textView; + } } - } -} \ No newline at end of file +}