17

を使用して連絡先のリスト (名前 + 写真) を表示していListViewます。初期ロードを高速化するために、最初に名前のみをロードし、画像のロードを遅らせます。これで、バックグラウンド スレッドが画像の読み込みを完了するたびに、アダプターnotifyDataSetChanged()が UI スレッドで呼び出されるようにスケジュールされます。残念ながら、これが発生した場合、は既に画面に表示されているアイテムをListView再レンダリング (つまり、呼び出し) しません。getView()このため、ビューが再利用されるように、ユーザーがスクロールして同じ項目のセットに戻らない限り、新しく読み込まれた画像は表示されません。関連するコードの一部:

private final Map<Long, Bitmap> avatars = new HashMap<Long, Bitmap>();

// this is called *on the UI thread* by the background thread
@Override
public void onAvatarLoaded(long contactId, Bitmap avatar) {
    avatars.put(requestCode, avatar);
    notifyDataSetChanged();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // snip...
    final Bitmap avatar = avatars.get(contact.id);
    if (avatar != null) {
        tag.avatar.setImageBitmap(avatar);
        tag.avatar.setVisibility(View.VISIBLE);
        tag.defaultAvatar.setVisibility(View.GONE);
    } else {
        tag.avatar.setVisibility(View.GONE);
        tag.defaultAvatar.setVisibility(View.VISIBLE);
        if (!avatars.containsKey(contact.id)) {
            avatars.put(contact.id, null);
            // schedule the picture to be loaded
            avatarLoader.addContact(contact.id, contact.id);
        }
    }
}

notifyDataSetChanged()AFAICT、画面上のアイテムが再作成されると仮定すると、私のコードは正しいです。しかし、それは真実ではないようです。または、何かが足りないのかもしれません。どうすればこれをスムーズに機能させることができますか?

4

3 に答える 3

21

ここで、私が解決したハックアラウンドで自分の質問に答えます。どうやら、notifyDataSetChanged()アイテムを追加/削除する場合にのみ使用されます。すでに表示されている項目に関する情報を更新すると、表示項目が視覚的な外観を更新しない (アダプターで呼び出されない)ことになる可能性があります。getView()

さらに、 の呼び出しは宣伝どおりinvalidateViews()にはListView機能しないようです。getView()画面上のアイテムを更新するために呼び出されない場合でも、同じ問題のある動作が発生します。

notifyDataSetChanged()最初は、 /を呼び出す頻度が原因で問題が発生したと考えていましたinvalidateViews()(さまざまなソースから更新が行われるため、非常に高速です)。そのため、これらのメソッドへの呼び出しを調整しようとしましたが、それでも役に立ちません。

これがプラットフォームのせいだとまだ 100% 確信しているわけではありませんが、私のハックアラウンドが機能するという事実は、そのことを示唆しているようです。したがって、これ以上苦労することはありませんが、私のハックアラウンドは、表示されているListViewアイテムを更新するために を拡張することです。convertViewこれは、アダプターで を適切に使用しており、 aが渡されたViewときに新しいを返さない場合にのみ機能することに注意してください。convertView明らかな理由:

public class ProperListView extends ListView {

    private static final String TAG = ProperListView.class.getName();

    @SuppressWarnings("unused")
    public ProperListView(Context context) {
        super(context);
    }

    @SuppressWarnings("unused")
    public ProperListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @SuppressWarnings("unused")
    public ProperListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    class AdapterDataSetObserver extends DataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();

            refreshVisibleViews();
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();

            refreshVisibleViews();
        }
    }

    private DataSetObserver mDataSetObserver = new AdapterDataSetObserver();
    private Adapter mAdapter;

    @Override
    public void setAdapter(ListAdapter adapter) {
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }
        mAdapter = adapter;

        mAdapter.registerDataSetObserver(mDataSetObserver);
    }

    void refreshVisibleViews() {
        if (mAdapter != null) {
            for (int i = getFirstVisiblePosition(); i <= getLastVisiblePosition(); i ++) {
                final int dataPosition = i - getHeaderViewsCount();
                final int childPosition = i - getFirstVisiblePosition();
                if (dataPosition >= 0 && dataPosition < mAdapter.getCount()
                        && getChildAt(childPosition) != null) {
                    Log.v(TAG, "Refreshing view (data=" + dataPosition + ",child=" + childPosition + ")");
                    mAdapter.getView(dataPosition, getChildAt(childPosition), this);
                }
            }
        }
    }

}
于 2013-10-29T10:42:20.057 に答える
4

次の行を onResume() に追加します listview.setAdapter(listview.getAdapter());

于 2014-12-03T16:43:10.410 に答える