Java 如何让我的 ArrayAdapter 遵循 ViewHolder 模式?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3832254/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 05:37:37  来源:igfitidea点击:

How can I make my ArrayAdapter follow the ViewHolder pattern?

javaandroidlistviewandroid-arrayadapter

提问by Sheehan Alam

Here is my ArrayAdapter. I would like to make this more efficient by following the ViewHolder pattern:

这是我的 ArrayAdapter。我想通过遵循 ViewHolder 模式来提高效率:

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

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

but am not sure how to accomplish this.

但我不知道如何做到这一点。

UPDATE: ViewHolder Pattern

更新: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;
    }
}

采纳答案by codelark

The ViewHolder is basically a static class instance that you associate with a view when it's created, caching the child views you're looking up at runtime. If the view already exists, retrieve the holder instance and use its fields instead of calling findViewById.

ViewHolder 基本上是一个静态类实例,您在创建时将其与视图关联,缓存您在运行时查找的子视图。如果视图已存在,则检索持有者实例并使用其字段而不是调用findViewById.

In your case:

在你的情况下:

@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;
}

caveat: I didn't try to compile this, so take with a grain of salt.

警告:我没有尝试编译这个,所以请谨慎对待。

回答by Rafael Rossignol

I just faced a similar problem here, but a little bit more complex, consider a very similar sample.

我刚刚在这里遇到了类似的问题,但稍微复杂一点,考虑一个非常相似的示例。

@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;
}

So, remember, when you use the ViewHolder Pattern, it's good to cleanup the component listeners before change the state of them.

因此,请记住,当您使用 ViewHolder 模式时,最好在更改它们的状态之前清理组件侦听器。