如何从 RecyclerView 中取出子视图?

How to get out a childview from RecyclerView?

[在底部编辑]

我正在尝试手动编写这些类型的动画:

Google Calendar

如果你仔细看那些视图,它们属于一个List或RecyclerView,但是它们是动画(大小动画,平移动画)出来的parent'范围。

如果我尝试这样做,结果是我的视图超出了 parent 的范围。

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


这是一帧,小心地停下来只是为了看到 child 视图已从 parent 中取出,并开始扩展到整个视图:

这是几乎 100% 扩展的地方:


我只是想 re-point 换一种方式。这是与 Activity Transitions 有关的东西吗?因为如果是这样,我不知道该怎么做。

我想到了两种实现这个效果的方法:

一种方法是使用共享元素 Activity 转换。它将需要使用 2 个 Activity:一个是回收站视图,第二个是全屏视图。在 activity 一和 activity 二之间切换时将自动应用动画。此解决方案可行并且不需要太多代码,但您将 运行 陷入保持两个活动同步的问题(例如 RecyclerView 的确切位置)。定制并非不可能,但可能会很困难,因为您严重依赖框架。

第二种方法,保持在相同的 activity 内并使用 object 动画师在回收站视图项目和全屏视图之间进行转换。诀窍不是为位于 RecyclerView 内部的视图设置动画,而是为位于 RecyclerView 内部的视图边界设置全屏视图动画。这样,你就不会被parent的界限所限制。我继续实施了第二个解决方案,因为它是高度可定制的,让您可以完全控制所有动画。

此示例应用包括平移和缩放动画师。它将从屏幕左侧的小方块位置视图开始动画。这种行为很容易改变。

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

Link 到项目回购:https://dkarmazi@bitbucket.org/dkarmazi/androidrecyclerviewanimation.git

Activity

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;
    }
}

Activity布局

<?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));
            }
        }
    }
}

Recycler 查看项目布局

<?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>