web-dev-qa-db-ja.com

PagerAdapterのビューリサイクルメカニズムを実装するにはどうすればよいですか?

カレンダーを表す複雑なビューを膨らませることを想定したポケットベルアダプタがあります。

カレンダーの毎年の膨張には約350ミリ秒かかります。

パフォーマンスを向上させるために、ビューをリサイクルするListView配列アダプター(getView()convertViewパラメーター)に存在するのと同じメカニズムを実装したいと思います。

これが、アダプターからの現在のgetView()です。

@Override
protected View getView(VerticalViewPager pager, final DateTileGrid currentDataItem, int position)
{
    mInflater = LayoutInflater.from(pager.getContext());

        // This is were i would like to understand weather is should use a recycled view or create a new one.
    View datesGridView = mInflater.inflate(R.layout.fragment_dates_grid_page, pager, false);


    DateTileGridView datesGrid = (DateTileGridView) datesGridView.findViewById(R.id.datesGridMainGrid);
    TextView yearTitle = (TextView) datesGridView.findViewById(R.id.datesGridYearTextView);
    yearTitle.setText(currentDataItem.getCurrentYear() + "");
    DateTileView[] tiles = datesGrid.getTiles();

    for (int i = 0; i < 12; i++)
    {
        String pictureCount = currentDataItem.getTile(i).getPictureCount().toString();
        tiles[i].setCenterLabel(pictureCount);
        final int finalI = i;
        tiles[i].setOnCheckedChangeListener(new DateTileView.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(DateTileView tileChecked, boolean isChecked)
            {
                DateTile tile = currentDataItem.getTile(finalI);
                tile.isSelected(isChecked);
            }
        });
    }

    return datesGridView;
}

そのような振る舞いを実装するための指針や方向性はありますか?特に、アダプタでDateTileGridViewsの1つが画面からスワイプされていることをどのように知ることができるので、次回再利用するためにメモリに保存できます。

29
Mortalus

だから私はそれを理解しました。

  1. destroyItem(ViewGroup container, int position, Object view)を上書きしてキャッシュビューを保存します
  2. 別のメソッドを作成して、リサイクルされたビューを使用する機会があるかどうか、または新しいビューを拡張する必要があるかどうかを確認します。
  3. 同じビューがページャーにアタッチされないように、使用後にリサイクルされたビューをキャッシュから削除することを忘れないでください。

これがコードです。ビューのスタックを使用して、ページャーから削除されたすべてのビューをキャッシュしました。

private View inflateOrRecycleView(Context context)
{

    View viewToReturn;
    mInflater = LayoutInflater.from(context);
    if (mRecycledViewsList.isEmpty())
    {
        viewToReturn = mInflater.inflate(R.layout.fragment_dates_grid_page, null, false);
    }
    else
    {
        viewToReturn = mRecycledViewsList.pop();
        Log.i(TAG,"Restored recycled view from cache "+ viewToReturn.hashCode());
    }


    return viewToReturn;
}

@Override
public void destroyItem(ViewGroup container, int position, Object view)
{
    VerticalViewPager pager = (VerticalViewPager) container;
    View recycledView = (View) view;
    pager.removeView(recycledView);
    mRecycledViewsList.Push(recycledView);
    Log.i(TAG,"Stored view in cache "+ recycledView.hashCode());
}

アダプタコンストラクタでスタックをインスタンス化することを忘れないでください。

32
Mortalus

このようにRecycleCacheを定義することでこれを解決しました

protected static class RecycleCache {

  private final RecyclerPagerAdapter mAdapter;

  private final ViewGroup mParent;

  private final int mViewType;

  private List<ViewHolder> mCaches;

  public RecycleCache(RecyclerPagerAdapter adapter, ViewGroup parent, int viewType) {
    mAdapter = adapter;
    mParent = parent;
    mViewType = viewType;
    mCaches = new ArrayList<>();
  }

  public ViewHolder getFreeViewHolder() {
    int i = 0;
    ViewHolder viewHolder;
    for (int n = mCaches.size(); i < n; i++) {
      viewHolder = mCaches.get(i);
      if (!viewHolder.mIsAttached) {
        return viewHolder;
      }
    }
    viewHolder = mAdapter.onCreateViewHolder(mParent, mViewType);
    mCaches.add(viewHolder);
    return viewHolder;
  }
}

ここで私のサンプルコードをチェックしてください RecyclerPagerAdapter

3
Henry Tao

私はこのようにしました..最初に抽象softCacheクラスを作成します:

public abstract class SoftCache<T> {
    private Stack<Reference<T>> mRecyclingStack;
    final Class<T> classType;

    public SoftCache(Class<T> typeParameterClass) {
        this.classType = typeParameterClass;
        mRecyclingStack = new Stack<Reference<T>>();
    }

    /* implement this to create new object of type T if cache is empty */
    public abstract T runWhenCacheEmpty();

    /*
     * retrieves last item from cache or creates a new T object if cache is
     * empty
     */
    public T get() {
        T itemCached = null;

        if (mRecyclingStack.isEmpty()) {
            itemCached = runWhenCacheEmpty();

        } else {
            SoftReference<T> softRef = (SoftReference<T>) mRecyclingStack
                    .pop();

            Object obj = softRef.get();
            /*
             * if referent object is empty(due to GC) then create a new
             * object
             */
            if (obj == null) {
                itemCached = runWhenCacheEmpty();

            }
            /*
             * otherwise restore from cache by casting the referent as the
             * class Type that was passed to constructor
             */
            else {
                itemCached = (classType.cast(softRef.get()));
            }
        }
        return itemCached;
    }

softCacheから継承するようになったため、runWhenCacheEmptyメソッドを実装できます。

public class ViewCache extends SoftCache<View>{

            public ViewCache(Class<View> typeParameterClass) {
                super(typeParameterClass);
            }

            @Override
            public View runWhenCacheEmpty() {   
                return mFragment.getActivity().getLayoutInflater()
                        .inflate(R.layout.mypagelayout, null);
            }   
        }

次に、コンストラクターで、たとえばViewクラス用にする場合は、次のようにインスタンス化します(ただし、どのタイプのクラスでも機能します)。

 SoftCache<View> myViewCache = new ViewCache(View.class);

現在destroyItemで、ビューをキャッシュに保存します。

@Override
    public void destroyItem(final ViewGroup container, final int position, final Object object) {
        final View v = (View) object;

        if(v.getId() == R.id.mypagelayout)
            myViewCache.put(v);  //this saves it


   }

これで、メソッドinstantiateItemは次のように単純にそれを利用します。

@Override
    public Object instantiateItem(final ViewGroup container, final int position) {
       View MyPageView=myViewCache.get();

}

更新:異なるレイアウトにキャッシュを使用したい場合、またはそれを拡張したくない場合は、レイアウトIDを使用して入力したレイアウトを取得する複数のレイアウトに同じキャッシュを使用できるソリューションを思いつきました:

public class SoftViewCache {

    private HashMap<Integer,ArrayList<SoftReference<View>>> multiMap;

    public SoftViewCache() {
        multiMap= new HashMap<Integer, ArrayList<SoftReference<View>>>();           
    }

    /*
     * retrieves cached item  or return null if cache is
     * empty
     */
    public View get(int id) {
        View itemCached = null;
        if (!multiMap.containsKey(id)) {
            return null;
        } 
        else {
            /*get the referent object and check if its already been GC if not we re-use*/
            SoftReference<View> softRef =multiMap.get(id).get(0);
            Object obj = softRef.get();
            /*
             * if referent object is empty(due to GC) then caller must create a new
             * object
             */
            if (null == obj) {
                return null;
            }
            /*
             * otherwise restore from cache 
             */
            else {
                itemCached = (softRef.get());
            }
        }
        return itemCached;
    }

    /* saves a view object to the cache by reference, we use a multiMap to allow
     * duplicate IDs*/
    public void put(View item) {
        SoftReference<View> ref = new SoftReference<View>(item);
        int key = item.getId();
          /*check if we already have a reuseable layouts saved if so just add to the list
           * of reusable layouts*/
        if (multiMap.containsKey(key)) {
            multiMap.get(key).add(ref);
        } else {
            /*otherwise we have no reusable layouts lets create a list of reusable layouts
             * and add it to the multiMap*/
            ArrayList<SoftReference<View>> list = new ArrayList<SoftReference<View>>();
            list.add(ref);
            multiMap.put(key, list);
        }
    }
}
3
j2emanue