2016-05-12 12 views
2

RecyclerViewには、複数のビュータイプがあり、それぞれ背景が異なるレンダリングがあります。当然ながら、私はこれらすべてのコンポーネントのオーバードローを避けたいので、RecyclerViewとすべてのビューを階層の中にバックグラウンドなしで表示します。RecyclerViewのレイアウトを過剰描画で最適化する

これは正常に動作します。アイテムのアニメーションを開始したり終了したりするまでです。 DefaultItemAnimatorはもちろん、素早くアイテムを出し入れするため、RecyclerViewの「穴」が開き、その背景がすぐに見えるようになります。

アニメーションが実際に実行されているときにRecyclerViewに背景のみを与え、それ以外の場合は背景を削除して、スクロールが高いFPSレートでスムーズに動作するようにしてみましょう。しかし、具体的な "アニメーションが始まりません"と対応する "アニメーションが終了する"という信号がRecyclerViewItemAnimatorまたは関連クラスに存在しないため、これは当初の考えよりも実際には難しくなります。

RecyclerView.ItemAnimator.ItemAnimatorFinishedListener finishListener = 
    new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 
     @Override 
     public void onAnimationsFinished() { 
      recycler.setBackgroundResource(0); 
     } 
    }; 

recycler.getAdapter().registerAdapterDataObserver(
    new RecyclerView.AdapterDataObserver() { 
     @Override 
     public void onItemRangeInserted(int positionStart, int itemCount) { 
      start(); 
     } 

     @Override 
     public void onItemRangeRemoved(int positionStart, int itemCount) { 
      start(); 
     } 

     @Override 
     public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { 
      start(); 
     } 

     private void start() { 
      recycler.setBackgroundResource(R.color.white); 
      if (!recycler.getItemAnimator().isRunning()) { 
       return; 
      } 
      recycler.getItemAnimator().isRunning(finishListener); 
     } 
    } 
); 

ここでの問題は、アダプタの範囲のコールバックは、実際のアニメーションを実行するより仕方早く走っているということである、なぜなら私が最近試した何

は成功せず、このようなItemAnimatorFinishedListenerAdapterDataObserverを組み合わせることでしたが、アニメーションはの内部で発生する前にスケジュールされません。つまり、start()メソッドではrecycler.getItemAnimator().isRunning()が常にfalseを返します。そのため、背景は決して削除されません。

私はさらにViewTreeObserver.OnGlobalLayoutListenerを試してみる前に、それをミックスに持ち込むことができます。誰かがこの問題の解決策を見つけるのは簡単ですか?

答えて

2

[OK]を、私は道をさらに行って、ViewTreeObserver.OnGlobalLayoutListenerを含む - これは動作しているようだ:

/** 
* This is a utility class that monitors a {@link RecyclerView} for changes and temporarily 
* gives the view a background so we do not see any artifacts while items are animated in or 
* out of the view, and, at the same time prevent the overdraw that would occur when we'd 
* give the {@link RecyclerView} a permanent opaque background color. 
* <p> 
* Created by Thomas Keller <[email protected]> on 12.05.16. 
*/ 
public class RecyclerBackgroundSaver { 

    private RecyclerView mRecyclerView; 
    @ColorRes 
    private int mBackgroundColor; 

    private boolean mAdapterChanged = false; 

    private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener 
      = new ViewTreeObserver.OnGlobalLayoutListener() { 
     @Override 
     public void onGlobalLayout() { 
      // ignore layout changes until something actually changed in the adapter 
      if (!mAdapterChanged) { 
       return; 
      } 
      mRecyclerView.setBackgroundResource(mBackgroundColor); 

      // if no animation is running (which should actually only be the case if 
      // we change the adapter without animating anything, like complete dataset changes), 
      // do not do anything either 
      if (!mRecyclerView.getItemAnimator().isRunning()) { 
       return; 
      } 

      // remove this view tree observer, i.e. do not react on further layout changes for 
      // one and the same dataset change and give control to the ItemAnimatorFinishedListener 
      mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); 
      mRecyclerView.getItemAnimator().isRunning(finishListener); 
     } 
    }; 

    RecyclerView.ItemAnimator.ItemAnimatorFinishedListener finishListener 
      = new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 
     @Override 
     public void onAnimationsFinished() { 
      // the animation ended, reset the adapter changed flag so the next change kicks off 
      // the cycle again and add the layout change listener back 
      mRecyclerView.setBackgroundResource(0); 
      mAdapterChanged = false; 
      mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener); 
     } 
    }; 

    RecyclerView.AdapterDataObserver mAdapterDataObserver = new RecyclerView.AdapterDataObserver() { 
     @Override 
     public void onItemRangeInserted(int positionStart, int itemCount) { 
      mAdapterChanged = true; 
     } 

     @Override 
     public void onItemRangeRemoved(int positionStart, int itemCount) { 
      mAdapterChanged = true; 
     } 

     @Override 
     public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { 
      mAdapterChanged = true; 
     } 
    }; 


    public RecyclerBackgroundSaver(RecyclerView recyclerView, @ColorRes int backgroundColor) { 
     mRecyclerView = recyclerView; 
     mBackgroundColor = backgroundColor; 
    } 

    /** 
    * Enables the background saver, i.e for the next item change, the RecyclerView's background 
    * will be temporarily set to the configured background color. 
    */ 
    public void enable() { 
     checkNotNull(mRecyclerView.getAdapter(), "RecyclerView has no adapter set, yet"); 
     mRecyclerView.getAdapter().registerAdapterDataObserver(mAdapterDataObserver); 
     mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener); 
    } 

    /** 
    * Disables the background saver, i.e. for the next animation, 
    * the RecyclerView's parent background will again shine through. 
    */ 
    public void disable() { 
     mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener); 
     if (mRecyclerView.getAdapter() != null) { 
      mRecyclerView.getAdapter().unregisterAdapterDataObserver(mAdapterDataObserver); 
     } 
    } 
} 
関連する問題