Android

How can I make my ArrayAdapter follow the ViewHolder pattern?

Как я могу заставить свой ArrayAdapter следовать шаблону ViewHolder?

Вот мой ArrayAdapter. Я хотел бы сделать это более эффективным, следуя шаблону ViewHolder:

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/List14.html

но я не уверен, как это сделать.

ОБНОВЛЕНИЕ: шаблон ViewHolder

private class QuoteAdapter extends ArrayAdapter<Quote> {

private ArrayList<Quote> items;
// used to keep selected position in ListView
private int selectedPos = -1; // init value for not-selected

public QuoteAdapter(Context context, int textViewResourceId, ArrayList<Quote> items) {
super(context, textViewResourceId, items);
this.items = items;
}

public void setSelectedPosition(int pos) {
selectedPos = pos;
// inform the view of this change
notifyDataSetChanged();
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ViewHolder holder; // to reference the child views for later actions

if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.mainrow, null);

// cache view fields into the holder
holder = new ViewHolder();
holder.nameText = (TextView) v.findViewById(R.id.nameText);
holder.priceText = (TextView) v.findViewById(R.id.priceText);
holder.changeText = (TextView) v.findViewById(R.id.changeText);

// associate the holder with the view for later lookup
v.setTag(holder);
}
else {
// view already exists, get the holder instance from the view
holder = (ViewHolder)v.getTag();
}

// change the row color based on selected state
if (selectedPos == position) {
v.setBackgroundResource(R.drawable.stocks_selected_gradient);
holder.nameText.setTextColor(Color.WHITE);
holder.priceText.setTextColor(Color.WHITE);
holder.changeText.setTextColor(Color.WHITE);
} else {
v.setBackgroundResource(R.drawable.stocks_gradient);
holder.nameText.setTextAppearance(getApplicationContext(), R.style.BlueText);
holder.priceText.setTextAppearance(getApplicationContext(), R.style.BlueText);
holder.changeText.setTextAppearance(getApplicationContext(), R.style.BlueText);
}

Quote q = items.get(position);
if (q != null) {
if (holder.nameText != null) {
holder.nameText.setText(q.getSymbol());
}
if (holder.priceText != null) {
holder.priceText.setText(q.getLastTradePriceOnly());
}
if (holder.changeText != null) {
try {
float floatedChange = Float.valueOf(q.getChange());
if (floatedChange < 0) {
if (selectedPos != position)
holder.changeText.setTextAppearance(getApplicationContext(), R.style.RedText); // red
} else {
if (selectedPos != position)
holder.changeText.setTextAppearance(getApplicationContext(), R.style.GreenText); // green
}
} catch (NumberFormatException e) {
System.out.println("not a number");
} catch (NullPointerException e) {
System.out.println("null number");
}
holder.changeText.setText(q.getChange() + " (" + q.getPercentChange() + ")");
}
}
return v;
}
}
Переведено автоматически
Ответ 1

ViewHolder - это, по сути, статический экземпляр класса, который вы связываете с представлением при его создании, кэшируя дочерние представления, которые вы просматриваете во время выполнения. Если представление уже существует, извлеките экземпляр holder и используйте его поля вместо вызова findViewById.

В вашем случае:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ViewHolder holder; // to reference the child views for later actions

if (v == null) {
LayoutInflater vi =
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.mainrow, null);
// cache view fields into the holder
holder = new ViewHolder();
holder.nameText = (TextView) v.findViewById(R.id.nameText);
holder.priceText = (TextView) v.findViewById(R.id.priceText);
holder.changeText = (TextView) v.findViewById(R.id.changeText);
// associate the holder with the view for later lookup
v.setTag(holder);
}
else {
// view already exists, get the holder instance from the view
holder = (ViewHolder) v.getTag();
}
// no local variables with findViewById here

// use holder.nameText where you were
// using the local variable nameText before

return v;
}

// somewhere else in your class definition
static class ViewHolder {
TextView nameText;
TextView priceText;
TextView changeText;
}

предостережение: я не пытался это скомпилировать, так что отнеситесь к этому с недоверием.

Ответ 2

Я только что столкнулся с аналогичной проблемой здесь, но немного сложнее, рассмотрим очень похожий пример.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ViewHolder holder;

if (v == null) {
LayoutInflater vi =
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.mainrow, null);
holder = new ViewHolder();
holder.nameText = (TextView) v.findViewById(R.id.nameText);
holder.priceText = (TextView) v.findViewById(R.id.priceText);
holder.myCheckbox = (Checkbox) v.findViewById(R.id.changeText);
v.setTag(holder);
}
else {
holder = (ViewHolder) v.getTag();
/*ATTENTION - this line below was added, because i started to face some problems with view reuse of other rows, when user changed the state, the behavior of other row was triggered*/
holder.myCheckbox.setOnCheckedChangeListener(null);
}
/*without the new line above, the line below trigger the old event and some strange behavior happens.*/
holder.myCheckbox.setChecked(myList.get(position).isChecked());
holder.myCheckbox.setOnCheckedChangeListener(()->{
//some behavior
});



return v;
}

// somewhere else in your class definition
static class ViewHolder {
TextView nameText;
TextView priceText;
Checkbox myCheckbox;
}

Итак, помните, что при использовании шаблона ViewHolder полезно очистить прослушиватели компонентов, прежде чем изменять их состояние.

java android listview