7

[下部に編集]

これらのタイプのアニメーションを手動でコーディングしようとしています:

Google カレンダー

これらのビューを注意深く見ると、それらは List または RecyclerView に属していますが、の境界外でアニメーション化 (サイズ アニメーション、変換アニメーション) されています。

そうしようとすると、結果として、私のビューは親の境界の下に入ります。

https://drive.google.com/file/d/0B-V0KHNRjbE_bkJEekExNGNLbDA/view?usp=sharing


これは 1 つのフレームで、子ビューが親から取得され、ビュー全体に拡大し始めていることを確認するために慎重に停止しています。

ここに画像の説明を入力

これは、ほぼ 100% 展開された場所です。

ここに画像の説明を入力


これを別の方法で再指摘したかっただけです。これはActivity Transitionsに関連するものですか? もしそうなら、私はそれを行う方法についてまったく考えていません。

4

1 に答える 1

1

この効果を得るには、次の 2 つの方法が考えられます。

1 つの方法は、Shared Element Activity Transition を使用することです。2 つのアクティビティを使用する必要があります。1 つはリサイクラー ビュー、もう 1 つは全画面表示です。アニメーションは、アクティビティ 1 とアクティビティ 2 の間の切り替えの間に自動的に適用されます。このソリューションは機能し、多くのコードは必要ありませんが、2 つのアクティビティの同期を維持するという問題が発生します (RecyclerView の正確な位置など)。カスタマイズは不可能ではありませんが、フレームワークに大きく依存しているため、難しい場合があります。

2 番目の方法は、同じアクティビティ内にとどまり、オブジェクト アニメーターを使用して、リサイクラー ビュー アイテムとフル スクリーン ビューの間を遷移することです。秘訣は、RecyclerView 内にあるビューをアニメーション化するのではなく、RecyclerView 内にあるビューの境界からフルスクリーン ビューをアニメーション化することです。このようにして、親の境界によって制限されることはありません。高度にカスタマイズ可能で、すべてのアニメーションを完全に制御できるため、先に進んで 2 番目のソリューションを実装しました。

このサンプル アプリには、変換およびスケーリングのアニメーターが含まれています。画面左側の小さな四角い位置ビューからアニメーションします。この動作は簡単に変更できます。

デモ: https://dl.dropboxusercontent.com/u/87080012/device-2016-03-25-160611.mp4

プロジェクト リポジトリへのリンク: https://dkarmazi@bitbucket.org/dkarmazi/androidrecyclerviewanimation.git

アクティビティ

public class MainActivity extends AppCompatActivity implements Adapter.ItemClickListener, CustomView.CloseButtonClickListener {
    public static final int ANIMATION_SPEED = 3000;
    private RecyclerView recyclerView;
    private CustomView customView;
    private RelativeLayout rootView;
    private Rect lastClickedRecyclerViewItemRect;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rootView = (RelativeLayout) findViewById(R.id.root_view);
        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        customView = (CustomView) findViewById(R.id.custom_view);

        recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        recyclerView.setAdapter(new Adapter(getApplicationContext(), this, getSampleData()));
    }

    @Override
    public void onItemClicked(View clickedView, int position, String title) {
        lastClickedRecyclerViewItemRect = new Rect();
        clickedView.getGlobalVisibleRect(lastClickedRecyclerViewItemRect);

        Rect targetViewRect = new Rect();
        rootView.getGlobalVisibleRect(targetViewRect);

        AnimatorSet animatorSet = getViewToViewScalingAnimator(rootView, customView, lastClickedRecyclerViewItemRect, targetViewRect, ANIMATION_SPEED, 0);

        customView.setData(position, title, this);
        customView.setVisibility(View.VISIBLE);

        animatorSet.start();
    }

    @Override
    public void onCloseButtonClicked(int position) {
        Rect clickedViewRect = new Rect();
        customView.getGlobalVisibleRect(clickedViewRect);
        AnimatorSet animatorSet = getViewToViewScalingAnimator(rootView, customView, clickedViewRect, lastClickedRecyclerViewItemRect, ANIMATION_SPEED, 0);

        animatorSet.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                // no op
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                customView.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                // no op
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                // no op
            }
        });

        animatorSet.start();
    }

    public static AnimatorSet getViewToViewScalingAnimator(final RelativeLayout parentView,
                                                           final View viewToAnimate,
                                                           final Rect fromViewRect,
                                                           final Rect toViewRect,
                                                           final long duration,
                                                           final long startDelay) {
        // get all coordinates at once
        final Rect parentViewRect = new Rect(), viewToAnimateRect = new Rect();
        parentView.getGlobalVisibleRect(parentViewRect);
        viewToAnimate.getGlobalVisibleRect(viewToAnimateRect);

        viewToAnimate.setScaleX(1f);
        viewToAnimate.setScaleY(1f);

        // rescaling of the object on X-axis
        final ValueAnimator valueAnimatorWidth = ValueAnimator.ofInt(fromViewRect.width(), toViewRect.width());
        valueAnimatorWidth.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // Get animated width value update
                int newWidth = (int) valueAnimatorWidth.getAnimatedValue();

                // Get and update LayoutParams of the animated view
                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) viewToAnimate.getLayoutParams();

                lp.width = newWidth;
                viewToAnimate.setLayoutParams(lp);
            }
        });

        // rescaling of the object on Y-axis
        final ValueAnimator valueAnimatorHeight = ValueAnimator.ofInt(fromViewRect.height(), toViewRect.height());
        valueAnimatorHeight.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // Get animated width value update
                int newHeight = (int) valueAnimatorHeight.getAnimatedValue();

                // Get and update LayoutParams of the animated view
                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) viewToAnimate.getLayoutParams();
                lp.height = newHeight;
                viewToAnimate.setLayoutParams(lp);
            }
        });

        // moving of the object on X-axis
        ObjectAnimator translateAnimatorX = ObjectAnimator.ofFloat(viewToAnimate, "X", fromViewRect.left - parentViewRect.left, toViewRect.left - parentViewRect.left);

        // moving of the object on Y-axis
        ObjectAnimator translateAnimatorY = ObjectAnimator.ofFloat(viewToAnimate, "Y", fromViewRect.top - parentViewRect.top, toViewRect.top - parentViewRect.top);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.setInterpolator(new DecelerateInterpolator(1f));
        animatorSet.setDuration(duration); // can be decoupled for each animator separately
        animatorSet.setStartDelay(startDelay); // can be decoupled for each animator separately
        animatorSet.playTogether(valueAnimatorWidth, valueAnimatorHeight, translateAnimatorX, translateAnimatorY);

        return animatorSet;
    }

    private static List<String> getSampleData() {
        List<String> dataList = new ArrayList<>();
        dataList.add("zero");
        dataList.add("one");
        dataList.add("two");
        dataList.add("three");
        dataList.add("four");
        dataList.add("five");
        dataList.add("six");
        dataList.add("seven");
        dataList.add("eight");
        dataList.add("nine");
        dataList.add("ten");
        dataList.add("eleven");
        dataList.add("twelve");
        dataList.add("thirteen");
        dataList.add("fourteen");
        dataList.add("fifteen");
        dataList.add("sixteen");
        dataList.add("seventeen");
        dataList.add("eighteen");
        dataList.add("nineteen");
        dataList.add("twenty");

        return dataList;
    }
}

活動レイアウト

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"/>

    <com.dkarmazi.android.myapplication.CustomView
        android:id="@+id/custom_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"/>
</RelativeLayout>

フルスクリーンで表示されるカスタムビュー

public class CustomView extends FrameLayout {
    public interface CloseButtonClickListener {
        void onCloseButtonClicked(int position);
    }

    private TextView positionView;
    private TextView titleView;
    private View closeView;
    private CloseButtonClickListener closeButtonClickListener;
    private int position;

    public CustomView(Context context) {
        super(context);
        init();
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        inflate(getContext(), R.layout.custom_view, this);
        positionView = (TextView) findViewById(R.id.custom_view_position);
        titleView = (TextView) findViewById(R.id.custom_view_title);
        closeView = findViewById(R.id.custom_view_close_button);

        closeView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if(closeButtonClickListener != null) {
                    closeButtonClickListener.onCloseButtonClicked(position);
                }
            }
        });
    }

    public void setData(int position, String title, CloseButtonClickListener closeButtonClickListener) {
        this.position = position;
        this.positionView.setText("" + position);
        this.titleView.setText(title);
        this.closeButtonClickListener = closeButtonClickListener;
    }
}

カスタム ビューのレイアウト

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_red_dark">

    <ImageView
        android:id="@+id/custom_view_close_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_menu_close_clear_cancel"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center"
        android:layout_marginTop="50dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:textSize="20sp"
            android:gravity="center"
            android:layout_gravity="top"
            android:text="Position:" />

        <TextView
            android:id="@+id/custom_view_position"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:textSize="25sp"
            android:gravity="center"
            android:layout_gravity="top"
            android:paddingBottom="100dp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:textSize="20sp"
            android:gravity="center"
            android:layout_gravity="top"
            android:text="Title:" />

        <TextView
            android:id="@+id/custom_view_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:gravity="center"
            android:textSize="25sp"
            android:layout_gravity="center"/>
    </LinearLayout>
</RelativeLayout>

RecyclerView アダプター

public class Adapter extends RecyclerView.Adapter {
    public interface ItemClickListener {
        void onItemClicked(View v, int position, String title);
    }

    private Context context;
    private ItemClickListener itemClickListener;
    private List<String> dataList;

    public Adapter(Context context, ItemClickListener itemClickListener, List<String> dataList) {
        this.context = context;
        this.itemClickListener = itemClickListener;
        this.dataList = dataList;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.recycler_view_item, null, false);

        return new MyViewHolder(view, new OnRecyclerItemClickListener());
    }


    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ((MyViewHolder) holder).onRecyclerItemClickListener.updatePosition(position);
        ((MyViewHolder) holder).position.setText("" + position);
        ((MyViewHolder) holder).title.setText(dataList.get(position));
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }

    private class MyViewHolder extends RecyclerView.ViewHolder {
        private OnRecyclerItemClickListener onRecyclerItemClickListener;
        private TextView position;
        private TextView title;

        public MyViewHolder(View itemView, OnRecyclerItemClickListener onRecyclerItemClickListener) {
            super(itemView);

            itemView.setOnClickListener(onRecyclerItemClickListener);
            this.onRecyclerItemClickListener = onRecyclerItemClickListener;
            this.position = (TextView) itemView.findViewById(R.id.position);
            this.title = (TextView) itemView.findViewById(R.id.title);
        }
    }


    private class OnRecyclerItemClickListener implements View.OnClickListener {
        private int position = -1;

        public void updatePosition(int position) {
            this.position = position;
        }

        @Override
        public void onClick(View v) {
            if(itemClickListener != null) {
                itemClickListener.onItemClicked(v.findViewById(R.id.position), position, dataList.get(position));
            }
        }
    }
}

リサイクラー ビュー アイテムのレイアウト

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recycler_view_item"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp">

    <TextView
        android:id="@+id/position"
        android:layout_width="30dp"
        android:layout_height="50dp"
        android:textColor="@android:color/white"
        android:gravity="center"
        android:background="@android:color/holo_green_light"
        android:layout_alignParentLeft="true"/>

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:textColor="@android:color/white"
        android:gravity="center"
        android:background="@android:color/holo_green_dark"
        android:layout_toRightOf="@id/position"
        android:layout_alignParentRight="true"/>
</RelativeLayout>
于 2016-03-25T20:12:57.050 に答える