【问题标题】:How can I make my ArrayAdapter follow the ViewHolder pattern?如何让我的 ArrayAdapter 遵循 ViewHolder 模式?
【发布时间】:2011-04-19 10:28:23
【问题描述】:

这是我的 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;
    }
}

【问题讨论】:

标签: java android listview android-arrayadapter


【解决方案1】:

ViewHolder 基本上是一个静态类实例,您在创建视图时将其与视图相关联,缓存您在运行时查找的子视图。如果视图已经存在,则检索持有者实例并使用其字段而不是调用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;
}

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

【讨论】:

  • 这与我之前提出的关于回收视图的问题有关。您知道对创建视图的开销进行的任何性能分析吗?
  • @Falmarri 不具体,不。这种特殊的模式是为了避免findViewById 的低效率而不是视图的创建。但是,制作一个测试用例来对视图重用与创建进行自己的分析会很简单。我怀疑仅解析 XML 布局就足以保证重用。如果 View 非常简单并且进行程序化布局,我对创建所涉及的开销没有第一手知识。
  • 很好的示例代码。我已经实现了视图持有者模式。查看 v.setBackgroundResource() 我是否正确地执行了与 View Holder 模式相关的操作?
  • @Sheehan Alam 设置背景资源和使用 ViewHolder 模式无关。但是,您的原始代码似乎是根据选择状态设置背景。为此,您通常希望使用选择器资源。
  • @codelark 为什么我们在这里为 ViewHolder 使用内部静态类?因为我知道我们可以在这里使用普通的私有内部类。有人知道使用静态内部类给我们带来了什么改进吗?
【解决方案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 模式时,最好在更改组件侦听器的状态之前清理它们。

【讨论】:

    猜你喜欢
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-26
    • 2016-01-11
    • 1970-01-01
    相关资源
    最近更新 更多