web-dev-qa-db-ja.com

スクロール時にRecyclerViewをアニメートする

スクロールしたときにRecyclerViewの要素をアニメーション化する方法はありますか?

DefaultItemAnimatorと_RecyclerView.ItemAnimator_を調べましたが、そのアニメーションはデータセットが変更された場合にのみ呼び出されるようです。間違っている場合は修正してください。

RecyclerView.ItemAnimator.animateMove()がいつ呼ばれるのか少し混乱していますか?そのクラスにいくつかのブレークポイントを入れましたが、どれもアプリを停止しません。

しかし、私の質問に戻って、RecyclerViewをどのようにアニメーション化できますか?一部のカスタムルールに応じて、一部の要素に別の不透明度が必要です。


私はもう少し努力しましたが、アニメーションの動きはまさに私が探しているものです。そのメソッドはdispatchLayout()から呼び出されます。そのメソッドのjavadocは次のとおりです。

レイアウトに起因する変更のアニメーション化を処理するlayoutChildren()のラッパー。アニメーションは、5つの異なる種類のアイテムがプレイ中にあるという前提で機能します。
PERSISTENT:アイテムはレイアウトの前後に表示されます
削除:レイアウト前にアイテムが表示され、アプリによって削除されました
ADDED:アイテムはレイアウト前に存在せず、アプリによって追加されました
DISAPPEARING:アイテムはデータセット内に存在しますが、レイアウトのプロセスで表示から非表示に変更されました(他の変更の副作用として画面外に移動しました)
APPEARING:アイテムはデータセット内に存在しますが、レイアウトのプロセスで非表示から表示に変更されます(他の変更の副作用として画面上で移動されました)
全体的なアプローチでは、レイアウトの前後にどのアイテムが存在するかを把握し、各アイテムについて上記の5つの状態のいずれかを推測します。その後、それに応じてアニメーションが設定されます。
PERSISTENTビューは移動されます({@link ItemAnimator#animateMove(ViewHolder、int、int、int、int)})削除されたビューは削除されます({@link ItemAnimator#animateRemove(ViewHolder)})
追加されたビューが追加されます({@link ItemAnimator#animateAdd(ViewHolder)})
DISAPPEARINGビューは画面外に移動します
APPEARINGビューは画面上で移動します

これまでのところ、私はPERSISTENT、DISAPPEARING、およびAPPEARINGを探していますが、この行が次の理由で呼び出されることはありません。

_boolean animateChangesSimple = mItemAnimator != null && mItemsAddedOrRemoved
            && !mItemsChanged;
_

mItemsAddedOrRemovedは常に常にfalseであるため、そのコールバックに到達することはありません。フラグを正しく設定する方法はありますか?

24
rekire

OnScrollListenerを使用し、カスタムanimate()メソッドでアニメーション化することになりました。私の場合、そのコードの所要時間はわずか2msなので、60fpsでは問題ありません。

recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(int newState) {
        if(newState == RecyclerView.SCROLL_STATE_IDLE) {
            // special handler to avoid displaying half elements
            scrollToNext();
        }
        animate();
    }

    @Override
    public void onScrolled(int dx, int dy) {
        animate();
    }
});
9
rekire

私はこのようにしました。誰かを助けるかもしれない。それが最善の方法であるかどうかはわかりませんが、私にとってはうまくいきます。

PDATE:高速スクロール動作を修正するには、アダプターのonViewDetachedFromWindowメソッドをオーバーライドし、アニメーションビューでclearAnimationを呼び出します(この場合、holder.itemView.clearAnimation() )。

up_from_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:Android="http://schemas.Android.com/apk/res/Android"
 Android:shareInterpolator="@Android:anim/decelerate_interpolator">
<translate
    Android:fromXDelta="0%" Android:toXDelta="0%"
    Android:fromYDelta="100%" Android:toYDelta="0%"
    Android:duration="400" />
</set>

down_from_top.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:Android="http://schemas.Android.com/apk/res/Android"
 Android:shareInterpolator="@Android:anim/decelerate_interpolator">
<translate
    Android:fromXDelta="0%" Android:toXDelta="0%"
    Android:fromYDelta="-100%" Android:toYDelta="0%"
    Android:duration="400" />
</set>

最後に、このコードをonBindViewHolderrecyclerViewに配置します。 lastPositionというフィールドを作成し、-1に初期化します。

Animation animation = AnimationUtils.loadAnimation(context,
            (position > lastPosition) ? R.anim.up_from_bottom
                    : R.anim.down_from_top);
    holder.itemView.startAnimation(animation);
    lastPosition = position;
6
Vineet Ashtekar

良い解決策は、onBindViewHolderメソッドでアダプターのホルダーをアニメーション化することです。 Material TestプロジェクトSlidenerdから抜粋したスニペット:

@Override 
public void onBindViewHolder(ViewHolderBoxOffice holder, int position) {
    Movie currentMovie = mListMovies.get(position);
    //one or more fields of the Movie object may be null since they are fetched from the web 
    holder.movieTitle.setText(currentMovie.getTitle());


    //retrieved date may be null 
    Date movieReleaseDate = currentMovie.getReleaseDateTheater();
    if (movieReleaseDate != null) {
        String formattedDate = mFormatter.format(movieReleaseDate);
        holder.movieReleaseDate.setText(formattedDate);
    } else { 
        holder.movieReleaseDate.setText(Constants.NA);
    } 


    int audienceScore = currentMovie.getAudienceScore();
    if (audienceScore == -1) {
        holder.movieAudienceScore.setRating(0.0F);
        holder.movieAudienceScore.setAlpha(0.5F);
    } else { 
        holder.movieAudienceScore.setRating(audienceScore / 20.0F);
        holder.movieAudienceScore.setAlpha(1.0F);
    } 


    if (position > mPreviousPosition) {
        AnimationUtils.animateSunblind(holder, true);         
    } else { 
        AnimationUtils.animateSunblind(holder, false);

    } 
    mPreviousPosition = position;

リンク

0
CrazyFox