【问题标题】:How to get out a childview from RecyclerView?如何从 RecyclerView 中取出子视图?
【发布时间】:2016-03-22 14:14:17
【问题描述】:

[在底部编辑]

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

Google Calendar

如果您仔细查看这些视图,它们属于 List 或 RecyclerView,但它们的动画(大小动画、平移动画)超出了 父级 的范围。

如果我尝试这样做,结果是我的观点超出了我父母的界限。

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


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

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


我只是想以另一种方式重新指出这一点。这是与Activity Transitions 有关的东西吗?因为如果是这样,我不知道该怎么做。

【问题讨论】:

    标签: android android-recyclerview activity-transition


    【解决方案1】:

    我可以想到两种方法来达到这个效果:

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

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

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

    演示: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>
    

    【讨论】:

    • (当 SO 允许我这样做时,将奖励赏金)。我认为没有办法通过一项活动来做到这一点,这就是我认为它正在填充的内容。所以这终于是诀窍了!非常感谢mcuh
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多