2012-10-31 15 views
9

ちょっとした初心者の質問です。 getView()ViewHolderを初期化するのはなぜですか?なぜそれをコンストラクタで初期化できないのですか?ViewHolder - good practice

+1

http://www.youtube.com/watch?v=wDBM6wVEO70の終わりにそれを更新する前にholder.textView.getText()の値がどのよう

  • 。このビデオを見てください。あなたの質問に答えてください。 – Raghunandan

  • 答えて

    29

    あなたが存在し、複数のViewHolderオブジェクトを持つことになります。

    ListViewはその性質上、その行ごとに新しいViewインスタンスを作成しません。これは、百万のものがListViewの場合、百万点のレイアウト情報を保存する必要はありません。では、何を保存する必要がありますか?画面上にあるものだけ。これらのビューを何度も何度も再利用することができます。このようにして、100万個のオブジェクトのListViewはたぶん10個の子ビューを持つことができます。カスタムアレイアダプタで

    は、あなたはこのようになります getView()と呼ばれる機能があります:

    public View getView(int position, View convertView, ViewGroup parent) { 
        //Here, position is the index in the list, the convertView is the view to be 
        //recycled (or created), and parent is the ListView itself. 
    
        //Grab the convertView as our row of the ListView 
        View row = convertView; 
    
        //If the row is null, it means that we aren't recycling anything - so we have 
        //to inflate the layout ourselves. 
        if(row == null) { 
          LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
          row = inflater.inflate(R.layout.list_item, parent, false); 
        } 
    
        //Now either row is the recycled view, or one that we've inflated. All that's left 
        //to do is set the data of the row. In this case, assume that the row is just a 
        //simple TextView 
        TextView textView = (TextView) row.findViewById(R.id.listItemTextView); 
    
        //Grab the item to be rendered. In this case, I'm just using a string, but 
        //you will use your underlying object type. 
        final String item = getItem(position); 
    
        textView.setText(item); 
    
        //and return the row 
        return row; 
    } 
    

    これは動作しますが、しかし、時間を割いて、あなたはここに非効率性を見つけることができるかどうかを確認します。どのコードが重複して呼び出されるかを考えてみてください。

    問題は、私たちが初めてそれを見た後も、決して変更されなくても、何度も何度も呼びかけているということです。あなたのリストにシンプルなTextViewしかないのであれば、複雑なレイアウトの場合や、データを設定したいビューが複数ある場合は、それほど悪くないかもしれません。もう一度。

    これをどのように修正しますか?さて、それを見た後でTextViewをどこかに保存するのは意味があります。そこで、ビューを「保持」するクラスViewHolderを導入します。それは、アダプタのためだけキャッシュメカニズムだから

    private static class ViewHolder { 
        TextView textView; 
    } 
    

    このクラスは、プライベートで、私たちはへの参照を必要としないように、それは静的である:そうのようなので、アダプターの内部で、内部クラスを導入それを使用するアダプタ。

    row.findViewByIdに何度も電話する必要がないように、このビューが保存されます。どこで設定すればいいですか?私たちが最初にビューを膨らませるとき。どこに保管しますか?ビューにはカスタムの「タグ」フィールドがあり、これはビューに関するメタ情報を保存するために使用できます。我々はすでに、このビューを見てきた場合はその後、私達はちょうど..行内の各ビューを調べるのではなく、タグをルックアップするために

    を持っているので、getView()の内側にif文は次のようになります。

    //If the row is null, it means that we aren't recycling anything - so we have 
    //to inflate the layout ourselves. 
    ViewHolder holder = null; 
    if(row == null) { 
        LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
        row = inflater.inflate(R.layout.list_item, parent, false); 
        //Now create the ViewHolder 
        holder = new ViewHolder(); 
        //and set its textView field to the proper value 
        holder.textView = (TextView) row.findViewById(R.id.listItemTextView); 
        //and store it as the 'tag' of our view 
        row.setTag(holder); 
    } else { 
        //We've already seen this one before! 
        holder = (ViewHolder) row.getTag(); 
    } 
    

    これで、すでにリサイクルされたビューへの参照であるため、holder.textViewのテキスト値を更新するだけです!したがって、最終アダプタのコードは

    public View getView(int position, View convertView, ViewGroup parent) { 
        //Here, position is the index in the list, the convertView is the view to be 
        //recycled (or created), and parent is the ListView itself. 
    
        //Grab the convertView as our row of the ListView 
        View row = convertView; 
    
        //If the row is null, it means that we aren't recycling anything - so we have 
        //to inflate the layout ourselves. 
        ViewHolder holder = null; 
        if(row == null) { 
         LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
         row = inflater.inflate(R.layout.list_item, parent, false); 
         //Now create the ViewHolder 
         holder = new ViewHolder(); 
         //and set its textView field to the proper value 
         holder.textView = (TextView) row.findViewById(R.id.listItemTextView); 
         //and store it as the 'tag' of our view 
         row.setTag(holder); 
        } else { 
         //We've already seen this one before! 
         holder = (ViewHolder) row.getTag(); 
        } 
    
        //Grab the item to be rendered. In this case, I'm just using a string, but 
        //you will use your underlying object type. 
        final String item = getItem(position); 
    
        //And update the ViewHolder for this View's text to the correct text. 
        holder.textView.setText(item); 
    
        //and return the row 
        return row; 
    } 
    

    となります。考える

    いくつかの点:あなたは、変更したい行に複数のビューを持っている場合

    1. はどのようにこの変更をしますか?
      1. ViewHolderのコンストラクタが呼び出された回数:挑戦として、各行は2つのTextViewオブジェクトを持ってListViewコントロールとあなたのListViewコントロールをデバッグするときは、あなたが本当に何が起こっているか見ることができるように、いくつかのことを確認しImageView
      2. を作ります。あなたはgetView()
    +0

    thnx、awesome answer – Gorets

    +0

    問題はありません - すべてが意味を成しているかどうかを教えてください.ViewHolderのことは私が初めて学んだときに私をしばらく混乱させました。 – mindvirus

    +0

    うわー。優れた答え。あなたのための挑戦として。いくつかの参考文献でそれを改善できますか? =) – Qw4z1

    2

    行を移入するたびにリストをスクロールし、すべての行に対して新しい行ビューを作成するので、ビューホルダーを初期化する必要があります。私はその行に2つのTextViewをしたと同じように、

    static class ViewHolder { 
         protected TextView title; 
         protected TextView type; 
    
        } 
    
    
        public View getView(int position, View convertView, ViewGroup parent) { 
          View view = null; 
          if (convertView == null) { 
           LayoutInflater inflator = context.getLayoutInflater(); 
           view = inflator.inflate(R.layout.feeds_rowview, null); 
           final ViewHolder viewHolder = new ViewHolder(); 
           view.setTag(viewHolder); 
           viewHolder.title = (TextView) view.findViewById(R.id.Title); 
           viewHolder.type = (TextView) view.findViewById(R.id.Type); 
    
          } else { 
           view = convertView; 
          } 
    
          ViewHolder holder = (ViewHolder) view.getTag(); 
          holder.title.setText(list.get(position).getTitle()); 
          holder.type.setText(list.get(position).getType()); 
    
          return view; 
        } 
    
    +0

    いいえ、すべてのビュー所有者の値が異なります。異なるデータが並んでいるので。 –

    関連する問題