136

ListViewニュースを表示するがあります。画像、タイトル、テキストが含まれています。イメージは別のスレッド(キューとすべてを含む)にロードされ、イメージがダウンロードされたらnotifyDataSetChanged()、リストアダプターを呼び出してイメージを更新します。これは機能しますが、表示されているすべてのアイテムを呼び出すためgetView()、頻繁に呼び出されます。リスト内の1つのアイテムだけを更新したいと思います。どうすればいいですか?notifyDataSetChanged()getView()

現在のアプローチで抱えている問題は次のとおりです。

  1. スクロールが遅い
  2. リスト内の新しい画像が1つ読み込まれるたびに発生する、画像のフェードインアニメーションがあります。
4

11 に答える 11

201

あなたの情報ミシェルのおかげで、私は答えを見つけました。を使用すると、実際に正しいビューを取得できますView#getChildAt(int index)。問題は、最初に表示されているアイテムからカウントを開始することです。実際には、目に見えるアイテムしか取得できません。でこれを解決しListView#getFirstVisiblePosition()ます。

例:

private void updateView(int index){
    View v = yourListView.getChildAt(index - 
        yourListView.getFirstVisiblePosition());

    if(v == null)
       return;

    TextView someText = (TextView) v.findViewById(R.id.sometextview);
    someText.setText("Hi! I updated you manually!");
}
于 2010-09-16T14:45:44.907 に答える
72

この質問はGoogleI/ O 2010で行われたもので、ここで見ることができます。

ListViewの世界、時間52:30

基本的に、Romain Guyが説明するのは、ビューを取得するためにを呼び出しgetChildAt(int)ListView(私が思うに)getFirstVisiblePosition()位置とインデックスの間の相関関係を見つけるために呼び出すことです。

Romainは、例としてShelvesというプロジェクトも指摘しています。彼はメソッドを意味しているのではないかと思いますShelvesActivity.updateBookCovers()が、の呼び出しが見つかりませんgetFirstVisiblePosition()

今後の素晴らしいアップデート:

RecyclerViewは、近い将来これを修正する予定です。http://www.grokkingandroid.com/first-glance-androids-recyclerview/で指摘されているように、次のような変更を正確に指定するメソッドを呼び出すことができます。

void notifyItemInserted(int position)
void notifyItemRemoved(int position)
void notifyItemChanged(int position)

また、見栄えの良いアニメーションで報われるので、誰もがRecyclerViewに基づく新しいビューを使用したいと思うでしょう!未来は素晴らしいですね!:-)

于 2010-09-16T10:22:10.910 に答える
6

これが私がやった方法です:

後で更新できるように、アイテム (行) には一意の ID が必要です。リストがアダプターからビューを取得するときに、すべてのビューのタグを設定します。(デフォルトのタグが別の場所で使用されている場合は、キー タグを使用することもできます)

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    View view = super.getView(position, convertView, parent);
    view.setTag(getItemId(position));
    return view;
}

更新については、リストのすべての要素をチェックします。指定された id のビューがそこにある場合は、それが表示されるので、更新を実行します。

private void update(long id)
{

    int c = list.getChildCount();
    for (int i = 0; i < c; i++)
    {
        View view = list.getChildAt(i);
        if ((Long)view.getTag() == id)
        {
            // update view
        }
    }
}

実際には、他の方法よりも簡単で、位置ではなく ID を扱う場合に優れています。また、表示されるアイテムの更新を呼び出す必要があります。

于 2014-02-20T03:45:53.010 に答える
3

このモデル クラス オブジェクトのように、最初にモデル クラスをグローバルとして取得します

SampleModel golbalmodel=new SchedulerModel();

グローバルに初期化します

それをグローバルモデルに初期化することにより、モデルによってビューの現在の行を取得します

SampleModel data = (SchedulerModel) sampleList.get(position);
                golbalmodel=data;

変更された値を設定するグローバル モデル オブジェクト メソッドに設定し、notifyDataSetChanged を追加します。

golbalmodel.setStartandenddate(changedate);

notifyDataSetChanged();

これに関する関連する質問と良い回答があります。

于 2016-12-07T10:47:56.543 に答える
2
int wantedPosition = 25; // Whatever position you're looking for
int firstPosition = linearLayoutManager.findFirstVisibleItemPosition(); // This is the same as child #0
int wantedChild = wantedPosition - firstPosition;

if (wantedChild < 0 || wantedChild >= linearLayoutManager.getChildCount()) {
    Log.w(TAG, "Unable to get view for desired position, because it's not being displayed on screen.");
    return;
}

View wantedView = linearLayoutManager.getChildAt(wantedChild);
mlayoutOver =(LinearLayout)wantedView.findViewById(R.id.layout_over);
mlayoutPopup = (LinearLayout)wantedView.findViewById(R.id.layout_popup);

mlayoutOver.setVisibility(View.INVISIBLE);
mlayoutPopup.setVisibility(View.VISIBLE);

RecycleView の場合は、このコードを使用してください

于 2015-07-15T09:56:27.637 に答える
2

答えは明確で正しいので、CursorAdapterここでケースのアイデアを追加します。

サブクラス化CursorAdapter(またはResourceCursorAdapter、または) している場合、およびメソッドをSimpleCursorAdapter実装ViewBinderまたはオーバーライドすることができます。これらは引数で現在のリスト項目インデックスを受け取りません。したがって、何らかのデータが到着し、関連する可視リスト項目を更新したい場合、それらのインデックスをどのように知るのでしょうか?bindView()newView()

私の回避策は次のとおりです。

  • 作成されたすべてのリスト アイテム ビューのリストを保持し、このリストにアイテムを追加します。newView()
  • notifyDatasetChanged()データが到着したら、それらを繰り返し、どれを更新する必要があるかを確認します。それらすべてを実行して更新するよりも優れています

ビューのリサイクルにより、保存して反復する必要があるビュー参照の数は、画面に表示されるリスト項目の数とほぼ同じになります。

于 2010-12-21T19:44:57.347 に答える
1

Erik を提供するコードを使用しましたが、うまく機能しますが、リストビュー用の複雑なカスタム アダプターがあり、UI を更新するコードの 2 回の実装に直面しました。アダプターの getView メソッドから新しいビューを取得しようとしました (listview データを保持する arraylist は既に更新/変更されています):

View cell = lvOptim.getChildAt(index - lvOptim.getFirstVisiblePosition());
if(cell!=null){
    cell = adapter.getView(index, cell, lvOptim); //public View getView(final int position, View convertView, ViewGroup parent)
    cell.startAnimation(animationLeftIn());
}

うまく機能していますが、これが良い習慣かどうかはわかりません。したがって、リスト項目を 2 回更新するコードを実装する必要はありません。

于 2013-03-25T12:20:08.883 に答える
1

まさに私はこれを使用しました

private void updateSetTopState(int index) {
        View v = listview.getChildAt(index -
                listview.getFirstVisiblePosition()+listview.getHeaderViewsCount());

        if(v == null)
            return;

        TextView aa = (TextView) v.findViewById(R.id.aa);
        aa.setVisibility(View.VISIBLE);
    }
于 2016-04-13T13:01:20.540 に答える
0

私の解決策: 正しい場合*、リスト全体を再描画せずにデータと表示可能なアイテムを更新します。それ以外の場合は、notifyDataSetChanged.

正しい - oldData サイズ == 新しいデータ サイズ、および古いデータ ID とその順序 == 新しいデータ ID と順序

どのように:

/**
 * A View can only be used (visible) once. This class creates a map from int (position) to view, where the mapping
 * is one-to-one and on.
 * 
 */
    private static class UniqueValueSparseArray extends SparseArray<View> {
    private final HashMap<View,Integer> m_valueToKey = new HashMap<View,Integer>();

    @Override
    public void put(int key, View value) {
        final Integer previousKey = m_valueToKey.put(value,key);
        if(null != previousKey) {
            remove(previousKey);//re-mapping
        }
        super.put(key, value);
    }
}

@Override
public void setData(final List<? extends DBObject> data) {
    // TODO Implement 'smarter' logic, for replacing just part of the data?
    if (data == m_data) return;
    List<? extends DBObject> oldData = m_data;
    m_data = null == data ? Collections.EMPTY_LIST : data;
    if (!updateExistingViews(oldData, data)) notifyDataSetChanged();
    else if (DEBUG) Log.d(TAG, "Updated without notifyDataSetChanged");
}


/**
 * See if we can update the data within existing layout, without re-drawing the list.
 * @param oldData
 * @param newData
 * @return
 */
private boolean updateExistingViews(List<? extends DBObject> oldData, List<? extends DBObject> newData) {
    /**
     * Iterate over new data, compare to old. If IDs out of sync, stop and return false. Else - update visible
     * items.
     */
    final int oldDataSize = oldData.size();
    if (oldDataSize != newData.size()) return false;
    DBObject newObj;

    int nVisibleViews = m_visibleViews.size();
    if(nVisibleViews == 0) return false;

    for (int position = 0; nVisibleViews > 0 && position < oldDataSize; position++) {
        newObj = newData.get(position);
        if (oldData.get(position).getId() != newObj.getId()) return false;
        // iterate over visible objects and see if this ID is there.
        final View view = m_visibleViews.get(position);
        if (null != view) {
            // this position has a visible view, let's update it!
            bindView(position, view, false);
            nVisibleViews--;
        }
    }

    return true;
}

そしてもちろん:

@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
    final View result = createViewFromResource(position, convertView, parent);
    m_visibleViews.put(position, result);

    return result;
}

bindView の最後のパラメーターを無視します (これを使用して、ImageDrawable のビットマップをリサイクルする必要があるかどうかを判断します)。

前述のように、「表示」ビューの総数は、おおよそ画面に収まる量 (向きの変更などを無視) であるため、メモリに関しては大したことはありません。

于 2013-07-24T09:53:04.513 に答える