如何在 Android Spinner 中隐藏一个项目

我正在寻找一种方法,以隐藏一个项目在一个 Android 旋转小部件。这将允许您模拟一个没有选择任何项目的微调器,并确保对选中的每个项目(如果隐藏项目是“当前”项目)始终调用 onItemSelected ()回调。通常,在 spinner 中总有一个项目不会生成回调,即当前项目。

有一些关于 stackoverflow 的代码可以用来禁用(灰度)项,但是不能用来完全隐藏项,就好像它们不存在一样。

经过大量的实验,我想出了一个有点黑客风格的解决方案,可以在各种新旧 Android 平台上运行。它有一些很难注意到的外观上的小缺点。我仍然希望听到一个更正式的解决方案,而不是“不要用旋转器”。

这总是隐藏微调器中的第一个项目,但是可以相当容易地扩展以隐藏任意项目或多个项目。在 spinner 项列表的开头添加一个包含空字符串的虚拟项。您可能希望在旋转器对话框打开之前将当前的旋转器选择设置为项0,这将模拟未选择的旋转器。

具有 ArrayAdapter 方法覆盖的旋转器设置示例:

List<String> list = new ArrayList<String>();
list.add("");   //  Initial dummy entry
list.add("string1");
list.add("string2");
list.add("string3");


// Populate the spinner using a customized ArrayAdapter that hides the first (dummy) entry
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, list) {
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent)
{
View v = null;


// If this is the initial dummy entry, make it hidden
if (position == 0) {
TextView tv = new TextView(getContext());
tv.setHeight(0);
tv.setVisibility(View.GONE);
v = tv;
}
else {
// Pass convertView as null to prevent reuse of special case views
v = super.getDropDownView(position, null, parent);
}


// Hide scroll bar because it appears sometimes unnecessarily, this does not prevent scrolling
parent.setVerticalScrollBarEnabled(false);
return v;
}
};


dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);
60288 次浏览

I think it will be better to put validation on the Array List rather than on Spinner because once the item is filtered, it will be safe to add in Spinner

To hide an arbitrary item or more than one item I think that you can implement your own adapter and set the index (or array list of indexes) that you want to hide.

public class CustomAdapter extends ArrayAdapter<String> {


private int hidingItemIndex;


public CustomAdapter(Context context, int textViewResourceId, String[] objects, int hidingItemIndex) {
super(context, textViewResourceId, objects);
this.hidingItemIndex = hidingItemIndex;
}


@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View v = null;
if (position == hidingItemIndex) {
TextView tv = new TextView(getContext());
tv.setVisibility(View.GONE);
v = tv;
} else {
v = super.getDropDownView(position, null, parent);
}
return v;
}
}

And use your custom adapter when you create the list of items.

List<String> list = new ArrayList<String>();
list.add("");   //  Initial dummy entry
list.add("string1");
list.add("string2");
list.add("string3");


int hidingItemIndex = 0;


CustomAdapter dataAdapter = new CustomAdapter(this, android.R.layout.simple_spinner_item, list, hidingItemIndex);


dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);

(I have not tested the code) hope that helps.

It's easier to hide an item at the end of the list by truncating the list.

But you have to select it first so it appears in the spinner, and then check if the selection has been changed to one of the items displayed.

List<String> list = new ArrayList<String>();
list.add("string1");
list.add("string2");
list.add("string3");
list.add("[Select one]");
final int listsize = list.size() - 1;


ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, list) {
@Override
public int getCount() {
return(listsize); // Truncate the list
}
};


dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);
mySpinner.setSelection(listsize); // Hidden item to appear in the spinner

Just for interest, I made a solution to use "Prompt" as a hint. This code is made for Xamarin.Android, but it could be perfectly ported to Java in 10 minutes. Use it like a simple ArrayAdapter without adding 0-indexed or count-indexed items to source array. It also set SpinnerGeolocation.SelectedItemId to -1 when nothing is chosen (hint is the current item).

public class ArrayAdapterWithHint<T>: ArrayAdapter<T>
{
protected bool HintIsSet = false;
protected int HintResource = 0;


public ArrayAdapterWithHint(Context context, int textViewResourceId,
T[] objects)
: base(context, textViewResourceId, objects)
{
}
public ArrayAdapterWithHint(Context context, int hintResource,
int textViewResourceId, T[] objects)
: base(context, textViewResourceId, objects)
{
HintResource = hintResource;
}
public ArrayAdapterWithHint(Context context, int textViewResourceId,
IList<T> objects)
: base(context, textViewResourceId, objects)
{
}
public ArrayAdapterWithHint(Context context, int hintResource,
int textViewResourceId, IList<T> objects)
: base(context, textViewResourceId, objects)
{
HintResource = hintResource;
}


public override View GetDropDownView(int position, View convertView,
ViewGroup parent)
{
if (HintIsSet)
return base.GetDropDownView(position + 1,
convertView, parent);
return base.GetDropDownView(position, convertView, parent);
}


public override View GetView(int position, View convertView,
ViewGroup parent)
{
if (!HintIsSet && parent is Spinner &&
!string.IsNullOrWhiteSpace((parent as Spinner).Prompt))
{
Insert((parent as Spinner).Prompt, 0);
HintIsSet = true;
(parent as Spinner).SetSelection(base.Count - 1);
}
if (HintIsSet && position >= base.Count - 1)
{
View hintView = base.GetView(0, convertView, parent);
if (hintView is TextView)
(hintView as TextView).SetTextAppearance(
Context, HintResource);
return hintView;
}
if (HintIsSet && position < base.Count - 1)
return base.GetView(position + 1, convertView, parent);
return base.GetView(position, convertView, parent);
}


public override long GetItemId(int position)
{
if (HintIsSet)
{
if (position >= base.Count - 1)
return -1;
return position;
}
return base.GetItemId(position);
}


public override int Count
{
get
{
return base.Count > 0 && HintIsSet ? base.Count - 1 : base.Count;
}
}
}

I found this solution which solved my problem.

final Spinner mySpinner = (Spinner)findViewById(R.id.spinner_triptype);


final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.spinner_item, R.id.weekofday, triptype_initial);


final ArrayAdapter<String> adapter_temp = new ArrayAdapter<String>
(this,R.layout.spinner_item, R.id.weekofday, triptype_array);




mySpinner.setAdapter(adapter);
mySpinner.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// display your error popup here
if(flag_spinner_isFirst){
mySpinner.setAdapter(adapter_temp);
flag_spinner_isFirst = false;
}
v.onTouchEvent(event);
return true;


}
});

Another approach which worked best for me is to return a new blank view object. This is considerably a clean approach as you are not playing with array elements.

Create your adapter class extending ArrayAdapter

inside your method

public View getView(int position, View convertView, ViewGroup parent) {
View row = getCustomView();
if(position==0) // put the desired check here.
{
row  = new View(context);
}
}
return row;
}

To hide any item in the spinner dropdown you need to pass the position of item which needs to hided based on criteria required. I have achieved this in a use case of hiding the item which is selected from dropdown

public class CustomAdapter extends ArrayAdapter<String> {


private List<String> dates;
private int hideItemPostion;


public CustomAdapter (Context context, int resource, List<String> dates) {
super(context, resource,dates);
this.dates=dates;
}
public void setItemToHide(int itemToHide)
{
this.hideItemPostion =itemToHide;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View v = null;
if (position == hideItemPostion) {
TextView tv = new TextView(getContext());
tv.setVisibility(View.GONE);
tv.setHeight(0);
v = tv;
v.setVisibility(View.GONE);
}
else
v = super.getDropDownView(position, null, parent);
return v;
}}

And setting the adapter is something like this

final CustomAdapter dataAdapter = new CustomAdapter(this,R.layout.spinner_item,dates);
dataAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
spinner.setAdapter(dataAdapter);
dataAdapter.setItemToHide(0);

On selecting some items from the dropdown also the position needs to changed

 spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, final int i, long l) {
dataAdapter.notifyDataSetChanged();
mEPGDateSelector.setSelection(i);
dataAdapter.setItemToHide(i);}


@Override
public void onNothingSelected(AdapterView<?> adapterView) {


}
});

This is very old question but I've found a nice (and probably) clean way to not show first items as well. Inspired by @Romich's answer I've added similar logic to skip first item(s).

This effectively hides arbitrary number of items (1 by default). The code only reports the size of the objects to render to be shorter than it actually is and also changes index of items to be rendered so we skip arbitrary number of items.

To keep things simple I've excluded solution that I'm currently using which supports hiding list of random items, but that can easily be managed by few tweaks to the code.

class ArrayAdapterCustom(context: Context, textViewResourceId: Int, vararg objects: String)
: ArrayAdapter<String>(context, textViewResourceId, objects) {


//Can skip first n items (skip 1 as default)
var hideFirstItemsCount = 1


override fun getCount(): Int {
return super.getCount() - hideFirstItemsCount
}


override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
return super.getDropDownView(position + hideFirstItemsCount, convertView, parent)
}
}

A better approach with no need to adjust the model.

public class HidableSpinnerArrayAdapter<T> extends ArrayAdapter<T> {


...


@Override
public boolean isEnabled(int position) {
// override this check what items are enabled/disabled
}


// Change color item
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
// when hiding items, cannot reuse views
View view = super.getDropDownView(position,
null /* convertView usually */, parent);
if (!isEnabled(position)) {
TextView tv = (TextView) view;
tv.setEnabled(false);
tv.setMaxHeight(0);
tv.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
return tv;
}
else {
return view;
}
}
}