views:

91

answers:

2

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;
        }
    }
+1  A: 

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.

Konstantin Burov
I would handle the selection background with a stateful drawable, however I want the background of the view to remain selected. I don't know of any other way to do this, than to change the background of the view.
Sheehan Alam
That makes sense. But anyhow, why do you call notifyDatasetChanged()? I believe there should be a way to change just one view without repainting all of them.
Konstantin Burov
Yeah, it seems if I don't call notifyDatasetChanged() multiple views can have the selection background. I only want one view to have the selection background at a time. Do you know of any other way I can update the view, without having to repaint all of them?
Sheehan Alam
How do you get the position of view to select? If you're using OnItemClickListener interface, it provides view parameter for the onItemClick method. Keep a reference to previously selected view. On next clicks first clear the saved view background, then update background of newly clicked item.
Konstantin Burov
I am using OnItemClickListener, but I am not sure how to fill in the parameters for getView(position, view, viewgroup)?
Sheehan Alam
why invoke getView? Just take view parameter and modify the view.
Konstantin Burov
im not sure how I can access the view? can you show me in code?
Sheehan Alam
How about doing it same way as you do in getView snippet?
Konstantin Burov
But View v is a member of the QuotesAdapter class. I can't access it via quotesAdapter.v.setBackgroundResource() in OnItemClickListener. I can't make it a public variable either because only final is permitted.
Sheehan Alam
As Konstantin mentioned earlier, the View that receives the click is passed as a parameter to `OnItemClickListener.onItemClick`. Use that reference to set the background.
codelark
while that works -- how can I set the background back to the original when I click on another item?
Sheehan Alam
Keep a reference to previously clicked item, use the reference to set background back.
Konstantin Burov
GREAT! Almost there. How can I turn the my text white like I am currently doing through the adapter?
Sheehan Alam
I guess cast view parameter to ViewGroup, then use findViewById to get reference to text views..
Konstantin Burov
That works for changing the text white. When I want to change it back to blue it doesn't do anything. Check out the updated `OnListItemClick()`. `nameText.setTextAppearance(getApplicationContext(), R.style.BlueText);` doesnt work
Sheehan Alam
That's because you're setting it to wrong views, see the code carefully, you're setting blue style and then white color to the same views.
Konstantin Burov
A: 

Well I will admit I don't know how large your dataset is nor do I know how many visibile 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)

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

Greg
@Greg - check out my latest update. I have implemented the ViewHolder pattern, but I'm not sure if I am setting the background resource appropriately?
Sheehan Alam