Skip to content

Instantly share code, notes, and snippets.

@gautier-levert
Created August 24, 2017 12:50
Show Gist options
  • Save gautier-levert/e0fb2109eaa4fead456eb888236b3db5 to your computer and use it in GitHub Desktop.
Save gautier-levert/e0fb2109eaa4fead456eb888236b3db5 to your computer and use it in GitHub Desktop.
Android spinner without default value
import android.content.Context;
import android.support.v7.widget.AppCompatSpinner;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* A modified Spinner that doesn't automatically select the first entry in the list.
* <p>
* Shows the prompt if nothing is selected.
* <p>
* Limitations: does not display prompt if the entry list is empty.
* <p>
* Froms Stack Overflow : https://stackoverflow.com/questions/867518/how-to-make-an-android-spinner-with-initial-text-select-one
*/
public class NoDefaultSpinner extends AppCompatSpinner {
public NoDefaultSpinner(Context context) {
super(context);
}
public NoDefaultSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setAdapter(SpinnerAdapter orig) {
final SpinnerAdapter adapter = newProxy(orig);
super.setAdapter(adapter);
try {
final Method m = AdapterView.class.getDeclaredMethod("setNextSelectedPositionInt", int.class);
m.setAccessible(true);
m.invoke(this, -1);
final Method n = AdapterView.class.getDeclaredMethod("setSelectedPositionInt", int.class);
n.setAccessible(true);
n.invoke(this, -1);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected SpinnerAdapter newProxy(SpinnerAdapter obj) {
return (SpinnerAdapter) Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
new Class[]{SpinnerAdapter.class},
new SpinnerAdapterProxy(obj)
);
}
/**
* Intercepts getViewMethod() to display the prompt if position < 0
*/
private class SpinnerAdapterProxy implements InvocationHandler {
SpinnerAdapter obj;
Method getViewMethod;
SpinnerAdapterProxy(SpinnerAdapter obj) {
this.obj = obj;
try {
this.getViewMethod = SpinnerAdapter.class.getMethod("getView", int.class, View.class, ViewGroup.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
try {
return m.equals(getViewMethod) && (Integer) (args[0]) < 0
? getView((int) args[0], (View) args[1], (ViewGroup) args[2])
: m.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
View getView(int position, View convertView, ViewGroup parent) {
if (position < 0) {
final TextView v = (TextView) LayoutInflater.from(getContext())
.inflate(android.R.layout.simple_spinner_item, parent, false);
v.setText(getPrompt());
return v;
}
return obj.getView(position, convertView, parent);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment