Android: 如何将 spinner 绑定到自定义对象列表?

在用户界面中必须有一个 spinner,它包含一些名称(名称是可见的) ,并且每个名称都有自己的 ID (ID 不等于显示序列)。当用户从列表中选择名称时,必须更改变量 currentID。

该应用程序包含 ArrayList

User 是一个具有 ID 和名称的对象:

public class User{
public int ID;
public String name;
}

我不知道的是如何创建一个 spinner,它显示用户名列表,并将 spinner 项绑定到 ID,以便在选择/更改 spinner 项时将变量 currentID 设置为适当的值。

如果任何人能够提供所描述问题的解决方案或提供任何有用的链接来解决这个问题,我将不胜感激。

谢谢!

247867 次浏览

您可以使用 看看这个答案。您也可以使用自定义适配器,但是下面的解决方案对于简单的情况是可行的。

这里有一个转贴:

因此,如果你来这里是因为你想在 Spinner 中同时拥有标签和值-我是这样做的:

  1. 只要按照通常的方法创建你的 Spinner
  2. array.xml文件中定义2个大小相等的数组——一个数组用于标签,一个数组用于值
  3. Spinner设置为 android:entries="@array/labels"
  4. 当你需要一个值的时候,像这样做(不,你不需要链接它) :

      String selectedVal = getResources().getStringArray(R.array.values)[spinner.getSelectedItemPosition()];
    

对我来说工作得很好,getResource ()所需的代码如下:

spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {


@Override
public void onItemSelected(AdapterView<?> spinner, View v,
int arg2, long arg3) {
String selectedVal = getResources().getStringArray(R.array.compass_rate_values)[spinner.getSelectedItemPosition()];
//Do something with the value
}


@Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}


});

只需要确保(自己)两个数组中的值正确对齐!

我知道线很旧了,但以防万一。

用户对象:

public class User{


private int _id;
private String _name;


public User(){
this._id = 0;
this._name = "";
}


public void setId(int id){
this._id = id;
}


public int getId(){
return this._id;
}


public void setName(String name){
this._name = name;
}


public String getName(){
return this._name;
}
}

自定义 Spinner 适配器(ArrayAdapter)

public class SpinAdapter extends ArrayAdapter<User>{


// Your sent context
private Context context;
// Your custom values for the spinner (User)
private User[] values;


public SpinAdapter(Context context, int textViewResourceId,
User[] values) {
super(context, textViewResourceId, values);
this.context = context;
this.values = values;
}


@Override
public int getCount(){
return values.length;
}


@Override
public User getItem(int position){
return values[position];
}


@Override
public long getItemId(int position){
return position;
}




// And the "magic" goes here
// This is for the "passive" state of the spinner
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// I created a dynamic TextView here, but you can reference your own  custom layout for each spinner item
TextView label = (TextView) super.getView(position, convertView, parent);
label.setTextColor(Color.BLACK);
// Then you can get the current item using the values array (Users array) and the current position
// You can NOW reference each method you has created in your bean object (User class)
label.setText(values[position].getName());


// And finally return your dynamic (or custom) view for each spinner item
return label;
}


// And here is when the "chooser" is popped up
// Normally is the same view, but you can customize it if you want
@Override
public View getDropDownView(int position, View convertView,
ViewGroup parent) {
TextView label = (TextView) super.getDropDownView(position, convertView, parent);
label.setTextColor(Color.BLACK);
label.setText(values[position].getName());


return label;
}
}

以及实施方案:

public class Main extends Activity {
// You spinner view
private Spinner mySpinner;
// Custom Spinner adapter (ArrayAdapter<User>)
// You can define as a private to use it in the all class
// This is the object that is going to do the "magic"
private SpinAdapter adapter;


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);


// Create the Users array
// You can get this retrieving from an external source
User[] users = new User[2];


users[0] = new User();
users[0].setId(1);
users[0].setName("Joaquin");


users[1] = new User();
users[1].setId(2);
users[1].setName("Alberto");


// Initialize the adapter sending the current context
// Send the simple_spinner_item layout
// And finally send the Users array (Your data)
adapter = new SpinAdapter(Main.this,
android.R.layout.simple_spinner_item,
users);
mySpinner = (Spinner) findViewById(R.id.miSpinner);
mySpinner.setAdapter(adapter); // Set the custom adapter to the spinner
// You can create an anonymous listener to handle the event when is selected an spinner item
mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {


@Override
public void onItemSelected(AdapterView<?> adapterView, View view,
int position, long id) {
// Here you get the current item (a User object) that is selected by its position
User user = adapter.getItem(position);
// Here you can do the action you want to...
Toast.makeText(Main.this, "ID: " + user.getId() + "\nName: " + user.getName(),
Toast.LENGTH_SHORT).show();
}
@Override
public void onNothingSelected(AdapterView<?> adapter) {  }
});
}
}

为了理解这个技巧,我们必须知道适配器是如何工作的,特别是 ArrayAdapter。

适配器: 是能够将数据结构绑定到小部件的对象,然后这些小部件在 List 或 Spinner 中显示该数据。

因此 Adapter 回答的两个问题是:

  1. 对于某个索引,哪个小部件或复合视图需要与数据结构(类的对象)关联?
  2. 如何从数据结构(类的对象)中提取数据,以及如何根据这些数据设置小部件或组合视图的字段(即 EditText) ?

ArrayAdapter 的答案是:

  • 任何索引的每个小部件(即 row.xmlandroid.R.layout.simple_spinner_item)都是相同的,都是从给 ArrayAdapter 的构造函数提供 ID 的资源中膨胀而来的。
  • 每个小部件都应该是 TextView (或 部件的 .setText()方法将与 支持数据结构中的项的字符串格式 字符串格式将通过对该项调用 .toString()来获得。

Java

public class CustomListViewDemo extends ListActivity {
private EfficientAdapter adap;


private static String[] data = new String[] { "0", "1", "2", "3", "4" };


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
adap = new EfficientAdapter(this);
setListAdapter(adap);
}


@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
Toast.makeText(this, "Click-" + String.valueOf(position), Toast.LENGTH_SHORT).show();
}


public static class EfficientAdapter extends BaseAdapter implements Filterable {
private LayoutInflater mInflater;
private Bitmap mIcon1;
private Context context;
int firstpos=0;


public EfficientAdapter(Context context) {
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
this.context = context;
}


public View getView(final int position, View convertView, ViewGroup parent) {


ViewHolder holder;


if (convertView == null) {
convertView = mInflater.inflate(R.layout.adaptor_content, null);


holder = new ViewHolder();
holder.sp = (Spinner) convertView.findViewById(R.id.spinner1);


holder.ArrayAdapter_sp = new ArrayAdapter(parent.getContext(),android.R.layout.simple_spinner_item,data);
holder.ArrayAdapter_sp.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);


holder.sp.setAdapter( holder.ArrayAdapter_sp);
holder.sp.setOnItemSelectedListener(new OnItemSelectedListener()
{
private int pos = position;
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int p, long arg3)
{
// TODO Auto-generated method stub
Toast.makeText(context, "select spinner " + String.valueOf(pos)+" with value ID "+p, Toast.LENGTH_SHORT).show();


}


@Override
public void onNothingSelected(AdapterView<?> arg0)
{
// TODO Auto-generated method stub


}
});








convertView.setTag(holder);
} else {


holder = (ViewHolder) convertView.getTag();
}




return convertView;
}


static class ViewHolder
{


Spinner sp;
ArrayAdapter ArrayAdapter_sp;


}


@Override
public Filter getFilter() {
// TODO Auto-generated method stub
return null;
}


@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}


@Override
public int getCount() {
// TODO Auto-generated method stub
return data.length;
}


@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return data[position];
}


}


}

Adaptor _ content. xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/lineItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical" >


<Spinner
android:id="@+id/spinner1"
android:layout_width="314dp"
android:layout_height="wrap_content" />


</LinearLayout>

Xml

<?xml version="1.0" encoding="utf-8"?>


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent" android:layout_width="fill_parent"
>


<ListView
android:id="@+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginBottom="60dip"
android:layout_marginTop="10dip"
android:cacheColorHint="#00000000"
android:drawSelectorOnTop="false" />


</RelativeLayout>

它工作正常,我希望它是有用的。

受 Joaquin Alberto 的启发,这个方法对我很有效:

public class SpinAdapter extends ArrayAdapter<User>{




public SpinAdapter(Context context, int textViewResourceId,
User[] values) {
super(context, textViewResourceId, values);
}






@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView label = (TextView) super.getView(position, convertView, parent);
label.setTextColor(Color.BLACK);
label.setText(this.getItem(position).getName());
return label;
}


@Override
public View getDropDownView(int position, View convertView,ViewGroup parent) {
TextView label = (TextView) super.getView(position, convertView, parent);
label.setTextColor(Color.BLACK);
label.setText(this.getItem(position).getName());
return label;
}
}

基于 Joaquin Alberto (谢谢)示例,但它适用于任何类型(您应该在类型中实现 toString () ,这样就可以格式化输出。

import java.util.List;


import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;


public class SpinAdapter<T> extends ArrayAdapter<T> {
private Context context;
private List<T> values;


public SpinAdapter(Context context, int textViewResourceId, List<T> values) {
super(context, textViewResourceId, values);
this.context = context;
this.values = values;
}


public int getCount() {
return values.size();
}


public T getItem(int position) {
return values.get(position);
}


public long getItemId(int position) {
return position;
}


@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView label = new TextView(context);
label.setTextColor(Color.BLACK);
label.setText(values.toArray(new Object[values.size()])[position]
.toString());
return label;
}


@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
TextView label = new TextView(context);
label.setTextColor(Color.BLACK);
label.setText(values.toArray(new Object[values.size()])[position]
.toString());


return label;
}
}

另外我认为你可以用 Array 替换 List,所以你不需要在 List 中做 toArray,但是我有一个 List... ... :)

最简单的解决方案

在搜索了 SO 上的不同解决方案之后,我发现以下是用定制的 Objects填充 Spinner的最简单和最干净的解决方案。下面是完整的实现:

用户.java

public class User{
public int ID;
public String name;


@Override
public String toString() {
return this.name; // What to display in the Spinner list.
}
}

Res/layp/spinner.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="14sp"
android:textColor="#FFFFFF"
android:spinnerMode="dialog" />

Res/layp/your _ activity _ view. xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">


<Spinner android:id="@+id/user" />


</LinearLayout>

在你的活动中

List<User> users = User.all(); // This example assumes you're getting all Users but adjust it for your Class and needs.
ArrayAdapter userAdapter = new ArrayAdapter(this, R.layout.spinner, users);


Spinner userSpinner = (Spinner) findViewById(R.id.user);
userSpinner.setAdapter(userAdapter);
userSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// Get the value selected by the user
// e.g. to store it as a field or immediately call a method
User user = (User) parent.getSelectedItem();
}


@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});

我认为最好的解决方案是 乔什 · 品特“最简单的解决方案”

这对我很有效:

//Code of the activity
//get linearLayout
LinearLayout linearLayout = (LinearLayout ) view.findViewById(R.id.linearLayoutFragment);


LinearLayout linearLayout = new LinearLayout(getActivity());
//display css
RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);


//create the spinner in a fragment activiy
Spinner spn = new Spinner(getActivity());


// create the adapter.
ArrayAdapter<ValorLista> spinner_adapter = new ArrayAdapter<ValorLista>(getActivity(), android.R.layout.simple_spinner_item, meta.getValorlistaList());
spinner_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spn.setAdapter(spinner_adapter);


//set the default according to value
//spn.setSelection(spinnerPosition);


linearLayout.addView(spn, params2);
//Code of the class ValorLista


import java.io.Serializable;
import java.util.List;


public class ValorLista implements Serializable{




/**
*
*/
private static final long serialVersionUID = 4930195743192929192L;
private int id;
private String valor;
private List<Metadato> metadatoList;




public ValorLista() {
super();
// TODO Auto-generated constructor stub
}


public int getId() {
return id;
}


public void setId(int id) {
this.id = id;
}


public String getValor() {
return valor;
}
public void setValor(String valor) {
this.valor = valor;
}
public List<Metadato> getMetadatoList() {
return metadatoList;
}
public void setMetadatoList(List<Metadato> metadatoList) {
this.metadatoList = metadatoList;
}


@Override
public String toString() {
return getValor();
}


}

对于简单的解决方案,只需覆盖对象中的“ toString”即可

public class User{
public int ID;
public String name;


@Override
public String toString() {
return name;
}
}

然后你可以使用:

ArrayAdapter<User> dataAdapter = new ArrayAdapter<User>(mContext, android.R.layout.simple_spinner_item, listOfUsers);

这样,您的微调器将只显示用户名。

我的定制对象是

/**
* Created by abhinav-rathore on 08-05-2015.
*/
public class CategoryTypeResponse {
private String message;


private int status;


private Object[] object;


public String getMessage() {
return message;
}


public void setMessage(String message) {
this.message = message;
}


public int getStatus() {
return status;
}


public void setStatus(int status) {
this.status = status;
}


public Object[] getObject() {
return object;
}


public void setObject(Object[] object) {
this.object = object;
}


@Override
public String toString() {
return "ClassPojo [message = " + message + ", status = " + status + ", object = " + object + "]";
}


public static class Object {
private String name;
private String _id;
private String title;
private String desc;
private String xhdpi;
private String hdpi;
private String mdpi;
private String hint;
private String type;
private Brands[] brands;




public String getId() {
return _id;
}


public void setId(String id) {
this._id = id;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


public String getXhdpi() {
return xhdpi;
}


public void setXhdpi(String xhdpi) {
this.xhdpi = xhdpi;
}


public String getHdpi() {
return hdpi;
}


public void setHdpi(String hdpi) {
this.hdpi = hdpi;
}


public String getMdpi() {
return mdpi;
}


public void setMdpi(String mdpi) {
this.mdpi = mdpi;
}


public String get_id() {
return _id;
}


public void set_id(String _id) {
this._id = _id;
}


public String getTitle() {
return title;
}


public void setTitle(String title) {
this.title = title;
}


public String getDesc() {
return desc;
}


public void setDesc(String desc) {
this.desc = desc;
}


public String getHint() {
return hint;
}


public void setHint(String hint) {
this.hint = hint;
}


public String getType() {
return type;
}


public void setType(String type) {
this.type = type;
}


public Brands[] getBrands() {
return brands;
}


public void setBrands(Brands[] brands) {
this.brands = brands;
}


@Override
public String toString() {
return "ClassPojo [name = " + name + "]";
}
}


public static class Brands {


private String _id;
private String name;
private String value;
private String categoryid_ref;


public String get_id() {
return _id;
}


public void set_id(String _id) {
this._id = _id;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


public String getValue() {
return value;
}


public void setValue(String value) {
this.value = value;
}


public String getCategoryid_ref() {
return categoryid_ref;
}


public void setCategoryid_ref(String categoryid_ref) {
this.categoryid_ref = categoryid_ref;
}


@Override
public String toString() {
return  name;


}
}
}

我还希望在不扩展 数组适配器的情况下将这个对象设置为我的转发器的适配器源,这样我所做的就是。

brandArray = mCategoryTypeResponse.getObject()[fragPosition].getBrands();


ArrayAdapter brandAdapter = new ArrayAdapter< CategoryTypeResponse.Brands>(getActivity(),
R.layout.item_spinner, brandArray);

现在您将能够在您的 spinner 中看到结果,诀窍是在您的 自定义对象中覆盖 toString(),所以无论您想在 spinner 中显示什么值,只需在这个方法中返回它。

到目前为止,我发现的最简单的方法是:

@Override
public String toString() {
return this.label;
}

现在您可以将任何对象粘贴到您的微调器中,它将显示指定的标签。

只要对阿尔贝托的回答稍作调整就可以解决风格问题。在自定义适配器中替换 getDropDownView 函数,如下所示,

@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View v = super.getDropDownView(position, convertView, parent);
TextView tv = ((TextView) v);
tv.setText(values[position].getName());
tv.setTextColor(Color.BLACK);
return v;
}

做:

spinner.adapter = object: ArrayAdapter<Project>(
container.context,
android.R.layout.simple_spinner_dropdown_item,
state.projects
) {
override fun getDropDownView(
position: Int,
convertView: View?,
parent: ViewGroup
): View {
val label = super.getView(position, convertView, parent) as TextView
label.text = getItem(position)?.title
return label
}


override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val label = super.getView(position, convertView, parent) as TextView
label.text = getItem(position)?.title
return label
}
}

如果你不需要一个分离的类,我的意思是一个简单的适配器映射到你的对象。 下面是基于提供的 ArrayAdapter 函数的代码。

并且因为您可能需要在适配器创建后添加项(例如数据库项异步加载)。

简单但高效。

editCategorySpinner = view.findViewById(R.id.discovery_edit_category_spinner);


// Drop down layout style - list view with radio button
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);


// attaching data adapter to spinner, as you can see i have no data at this moment
editCategorySpinner.setAdapter(dataAdapter);
final ArrayAdapter<Category> dataAdapter = new ArrayAdapter<Category>


(getActivity(), android.R.layout.simple_spinner_item, new ArrayList<Category>(0)) {




// And the "magic" goes here
// This is for the "passive" state of the spinner
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// I created a dynamic TextView here, but you can reference your own  custom layout for each spinner item
TextView label = (TextView) super.getView(position, convertView, parent);
label.setTextColor(Color.BLACK);
// Then you can get the current item using the values array (Users array) and the current position
// You can NOW reference each method you has created in your bean object (User class)
Category item = getItem(position);
label.setText(item.getName());


// And finally return your dynamic (or custom) view for each spinner item
return label;
}


// And here is when the "chooser" is popped up
// Normally is the same view, but you can customize it if you want
@Override
public View getDropDownView(int position, View convertView,
ViewGroup parent) {
TextView label = (TextView) super.getDropDownView(position, convertView, parent);
label.setTextColor(Color.BLACK);
Category item = getItem(position);
label.setText(item.getName());


return label;
}
};

然后您可以使用这段代码(我不能在适配器构造函数中放入 Category [] ,因为数据是分开加载的)。

注意,通过在内部调用 notifyDataSetChanged ()来刷新 spinner。

categoryRepository.getAll().observe(this, new Observer<List<Category>>() {


@Override
public void onChanged(@Nullable final List<Category> items) {
dataAdapter.addAll(items);
}
});

以下是 Kotlin 的完整流程:

旋转适配器类:

class CustomSpinnerAdapter(
context: Context,
textViewResourceId: Int,
val list: List<User>
) : ArrayAdapter<User>(
context,
textViewResourceId,
list
) {
override fun getCount() = list.size


override fun getItem(position: Int) = list[position]


override fun getItemId(position: Int) = list[position].report.toLong()


override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
return (super.getDropDownView(position, convertView, parent) as TextView).apply {
text = list[position].name
}
}


override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
return (super.getDropDownView(position, convertView, parent) as TextView).apply {
text = list[position].name
}
}
}

并像下面这样在你的活动/片段中使用它:

spinner.adapter = CustomerSalesSpinnerAdapter(
context,
R.layout.cuser_spinner_item,
userList
)

Kotlin:

data class User(
var id: Long,
var name : String? ){


override fun toString(): String {
return name
}
}

实施

 mBinding.ddUser.setOnItemClickListener { parent, view, position, id ->
val user = adapter.getItem(position)
Log.i("idUser","${user?.idtoString()} - ${user?.name}")
}
**Easy & Simple Solution**


overide the below method in model class.




@NonNull
@Override
public String toString() {
return name; //whatever the value which you want to show in spinner list.
}


Add the below line to set adapter.


spinner.setAdapter(new ArrayAdapter<ModelClassName>(getContext(),R.layout.item_spinner,list));