[英]Custom adapter to make listview with different items
我想要的是在列表視圖的每一行中都有一個帶有不同類型項的列表視圖。 我知道我需要使用擴展BaseAdapter而不是ArrayAdapter的自定義適配器,但是此后我不知道如何解決該問題。 以前,我嘗試過這樣的適配器:
public class MyArrayAdapter<T> extends ArrayAdapter<T> {
LayoutInflater mInflater;
int[] mLayoutResourceIds;
private final Context context;
public MyArrayAdapter(Context context, int[] textViewResourceId, List<T> objects) {
super(context, textViewResourceId[0], objects);
this.context = context;
mInflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE);
mLayoutResourceIds = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
int type = getItemViewType(position);
// instead of if else you can use a case
if (row == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (type == TYPE_ITEM1) {
//infalte layout of type1
row = inflater.inflate(R.layout.item1, parent, false);
}
if (type == TYPE_ITEM2) {
//infalte layout of type2
} else {
//infalte layout of normaltype
}
}
return super.getView(position, convertView, parent);
}
@Override
public int getItemViewType(int position) {
if (position== 0){
type = TYPE_ITEM1;
} else if (position == 1){
type = TYPE_ITEM2;
}
else
{
type= TYPE_ITEM3 ;
}
return type; //To change body of overridden methods use File | Settings | File Templates.
}
@Override
public int getViewTypeCount() {
return 3; //To change body of overridden methods use File | Settings | File Templates.
}
}
但是當我運行活動時,出現錯誤:
java.lang.IllegalStateException: ArrayAdapter requires the resource ID to be a TextView
而且我確實知道為什么,但是我不打算在所有布局中使用textView。 我需要一些有關如何進行的指導。 謝謝
在我的item1.xml中更改linearLayout父級的背景會導致空指針異常。 如果我離開背景,它確實可以在沒有空指針異常的情況下工作
您的ArrayAdapter
實現可以正常工作,但是您需要返回在getView()
方法中誇大的行View
,而不是委托給super實現。
出現此錯誤的原因是因為ArrayAdapters需要TextView的資源ID,該ID將存在於列表中的每個項目中。 它將嘗試查找具有此資源ID的textview,然后根據ArrayAdapter中的內容將其設置為某些文本。 但是,如果您重寫適配器的getView()
方法,則將忽略此操作。
要實現您想要的,有幾種方法。 如果您的列表中有可重用的組件(例如,您顯示的項目類型不同,但是您可能多次顯示相同類型的項目),那么我建議您執行以下操作:
public class MultiTypeAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private Context mContext;
private List<IAdapterItem> mItems;
public enum RowType {
ITEM_TYPE_ONE, ITEM_TYPE_TWO
}
/**
* @param context
* @param itemsList
*/
private MultiTypeAdapter(Context context, List<IAdapterItem> itemsList) {
mItems = itemsList;
mContext = context;
mInflater = LayoutInflater.from(context);
}
@Override
public int getViewTypeCount() {
return RowType.values().length;
}
@Override
public int getItemViewType(int position) {
return getItem(position).getViewType().ordinal();
}
//View is generated by children
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getItem(position).getView(mInflater, convertView, mContext);
}
@Override
public boolean isEnabled(int position) {
return getItem(position).isEnabled();
}
@Override
public long getItemId(int position) {
return getItem(position).getItemId();
}
@Override
public IAdapterItem getItem(int position) {
return mItems.get(position);
}
...
}
然后,您將擁有實現其自己的getView()
方法的子組件。
public class ItemTypeOne implements IAdapterItem {
@Override
public RowType getViewType() {
return RowType.ITEM_TYPE_ONE;
}
@Override
public View getView(LayoutInflater inflater, View convertView, Context context) {
View view = convertView;
view = inflater.inflate(R.layout.item_type_one, null);
// Set the view elements
return view;
}
}
使用這種方法,您可以創建一個新類來為列表中的每個“類型”實現IAdapterItem。 這種方法非常干凈,因為每個孩子都知道如何正確顯示自己。 如果您未使用其他類型,則我不建議您使用這種方法。
corsair992是正確的。 當您返回row
不是super.getView ...時,可能會起作用。 在處理具有不同行的列表時,請務必小心。 變量convertView將無法正常工作! 對於使用TYPE_ITEM1的行,您可以獲取TYPE_ITEM2的convertView,我想在您的情況下它看起來不太好:)您必須檢查convertView的類型,如果要使用它,則可以使用它,否則必須膨脹阿吉安人。
我不確定為什么會出現錯誤。 如果您給我初始化適配器的代碼,我可能會有所幫助。 但是我有一個包含兩個textviews和一個復選框的自定義適配器。 因此它可能會有所幫助。
這是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="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="@+id/toptext"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center_vertical"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:id="@+id/bottomtext"
android:singleLine="true"
android:ellipsize="marquee"
/>
</LinearLayout>
<CheckBox
android:id="@+id/cbIsTaskCompleted"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="false"/>
</LinearLayout>
自定義適配器文件...
package com.isaac.jeff;
import java.util.ArrayList;
import com.isaac.jeff.screen.GoalActivity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;
public class OurTasksArrayAdapter extends ArrayAdapter<Task> {
private Context context;
public OurTasksArrayAdapter(Context context, int textResourceId, ArrayList<Task> tasks) {
super(context, textResourceId, tasks);
this.context = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
LayoutInflater layoutInflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = layoutInflater.inflate(R.layout.task_row, null);
}
Task task = getItem(position);
if (task != null) {
// CheckBox
CheckBox cbIsTaskCompleted = (CheckBox) view.findViewById(R.id.cbIsTaskCompleted);
cbIsTaskCompleted.setTag(position);
cbIsTaskCompleted.setChecked(task.isTaskCompleted());
cbIsTaskCompleted.setOnCheckedChangeListener(mListener);
TextView tt = (TextView) view.findViewById(R.id.toptext);
TextView bt = (TextView) view.findViewById(R.id.bottomtext);
if (tt != null) {
tt.setText("Name: " + task.getTaskDescription());
}
if (bt != null) {
bt.setText("Status: " + task.getTaskDescription());
}
}
GoalActivity ga = (GoalActivity) context;
ga.updateTaskProgress();
return view;
}
OnCheckedChangeListener mListener = new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
GoalActivity ga = (GoalActivity) context;
getItem((Integer)buttonView.getTag()).setTaskCompleted(isChecked); // get the tag so we know the row and store the status
ga.updateTaskProgress();
//notifyDataSetChanged();
}
};
}
Hope this helps!
這就是我用2種不同的布局完成ListView的方式:
public class Adapter extends BaseAdapter{
private static final int VIEW_TYPE_COUNT = 2;
private static final int ITEM_1 = 0;
private static final int ITEM_2= 1;
private final int VIEWTYPES[] = {Item_1, ITEM_2};
private LayoutInflater mInflater;
public Adapter(Context context){
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
}
@Override
public int getItemViewType(int position) {
return VIEWTYPES[mPostList.get(position).getViewType()];
}
@Override
public int getViewTypeCount(){
return VIEW_TYPE_COUNT;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
ViewHolder2 holder2;
int type = getItemViewType(position);
if (convertView == null) {
holder = new ViewHolder();
holder2 = new ViewHolder2();
switch(type){
case ITEM_1:
convertView = mInflater.inflate(R.layout.ITEM_1_LAYOUT, null);
holder.text1 = (TextView)convertView.findViewById();
convertView.setTag(R.layout.ITEM_1_LAYOUT, holder);
break;
case ITEM_2:
convertView = mInflater.inflate(R.layout.iTEM_2_LAYOUT, null);
holder2.text2 = (TextView)convertView.findViewById();
convertView.setTag(R.layout.ITEM_2_LAYOUT, holder2);
break;
}
} else{
// Get the ViewHolder back to get fast access to the TextViews
holder = (ViewHolder) convertView.getTag(R.layout.ITEM_1_LAYOUT);
holder2 = (ViewHolder2)convertView.getTag(R.layout.ITEM_2_LAYOUT);
}
switch(type){
case ITEM_1:
holder.text1.setText("1");
break;
case ITEM_2:
holder.text2.setText("2");
break;
}
return convertView;
}
}
static class ViewHolder {
//Store layout1 views here
TextView text1;
}
static class ViewHolder2{
//Store layout2 views here
TextView text2;
}
您已經很接近目標了。
首先,您應該使您的類擴展BaseAdapter。 ArrayAdapter旨在用於使用數組中一項的toString()值在每行中填充單個TextView。 您看到的錯誤似乎暗示您正在將布局傳遞給不僅包含TextView的ArrayAdapter構造函數,而且ArrayAdapter構造函數引發錯誤。
您正在根據要顯示的視圖類型正確生成不同的視圖。 您應該考慮convertView不為null的可能性,也就是說,ListView正在回收較舊的View以消耗更少的內存。 您可以將else添加到if (row == null)
條件中,然后相應地重用或重新創建該行。
您可以查看以下教程,以在ListView上顯示不同類型的項目:
http://logc.at/2011/10/10/handling-listviews-with-multiple-row-types/
作為變體-您可以通過以下示例在模式xml文件中顯示或隱藏元素:
if (position<1) { imageView.setImageResource(R.drawable.ok); }
else { imageView.setImageResource(R.drawable.no);
imageView.setVisibility(View.GONE); }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.