getView() for ArrayAdapter is slow
-
26-09-2019 - |
Question
When a user selects a ListViewItem
, I am changing that row's background image. This seems to happen very slowly. I'm not sure why?
OnItemClickListener
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
//quotesAdapter.setSelectedPosition(position);
setupDetailView(position);
setupChartView(position);
setupARView(position);
emptyView.setVisibility(View.INVISIBLE);
ViewGroup vg = (ViewGroup)v;
TextView nameText = (TextView) vg.findViewById(R.id.nameText);
TextView priceText = (TextView) vg.findViewById(R.id.priceText);
TextView changeText = (TextView) vg.findViewById(R.id.changeText);
//change the old row back to normal
if(oldView != null){
oldView.setBackgroundResource(R.drawable.stocks_gradient);
nameText.setTextAppearance(getApplicationContext(), R.style.BlueText);
priceText.setTextAppearance(getApplicationContext(), R.style.BlueText);
changeText.setTextAppearance(getApplicationContext(), R.style.BlueText);
}
//change the selected row
v.setBackgroundResource(R.drawable.stocks_selected_gradient);
nameText.setTextColor(Color.WHITE);
priceText.setTextColor(Color.WHITE);
changeText.setTextColor(Color.WHITE);
//keep a reference to the old row, for the next time user clicks
oldView = v;
}
});
}
Original Code:
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;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.mainrow, null);
}
TextView nameText = (TextView) v.findViewById(R.id.nameText);
TextView priceText = (TextView) v.findViewById(R.id.priceText);
TextView changeText = (TextView) v.findViewById(R.id.changeText);
// change the row color based on selected state
if (selectedPos == position) {
v.setBackgroundResource(R.drawable.stocks_selected_gradient);
nameText.setTextColor(Color.WHITE);
priceText.setTextColor(Color.WHITE);
changeText.setTextColor(Color.WHITE);
} else {
v.setBackgroundResource(R.drawable.stocks_gradient);
nameText.setTextAppearance(getApplicationContext(), R.style.BlueText);
priceText.setTextAppearance(getApplicationContext(), R.style.BlueText);
changeText.setTextAppearance(getApplicationContext(), R.style.BlueText);
}
Quote q = items.get(position);
if (q != null) {
if (nameText != null) {
nameText.setText(q.getSymbol());
}
if (priceText != null) {
priceText.setText(q.getLastTradePriceOnly());
}
if (changeText != null) {
try {
float floatedChange = Float.valueOf(q.getChange());
if (floatedChange < 0) {
if (selectedPos != position)
changeText.setTextAppearance(getApplicationContext(), R.style.RedText); // red
} else {
if (selectedPos != position)
changeText.setTextAppearance(getApplicationContext(), R.style.GreenText); // green
}
} catch (NumberFormatException e) {
System.out.println("not a number");
} catch (NullPointerException e) {
System.out.println("null number");
}
changeText.setText(q.getChange() + " (" + q.getPercentChange() + ")");
}
}
return v;
}
}
UPDATE: Adapter with ViewHolder pattern
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;
}
}
Solution
Your getView()
is o.k. (even though it can be made faster). I think problem is with setSelectedPosition()
. You're invoking notifyDataSetChanged() which causes too many views to be repainted. You should handle selection background with stateful drawable.
OTHER TIPS
Well I will admit I don't know how large your dataset is nor do I know how many visible elements are on screen at a time, but if you can guarantee your list is in single selection mode and have a stateful drawable with the correct state having a different color
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="true" >
<item
android:state_pressed="true"
android:state_enabled="true"
android:drawable="@color/my_color" />
<item
android:state_selected="true"
android:drawable="@color/my_color" />
</item>
<item
android:state_focused="true"
android:state_enabled="true"
android:drawable="@android:drawable/list_selector_background" />
</selector>
This should work automatically.
If you want the selected state to appear as if it is sticky, you might also try making the listView
multichoice and intercepting the call to set selection to unset previous ones.
If you are still intent on setting the background though manually, I would optimize your getView
method by using the ViewHolder
pattern that is used by others. This will allow your repaint to be less expensive and appear as if it didn't actually happen (again depending on the number of elements on screen at the time)