如何从RecyclerView中获取子视图?

时间:2016-03-22 14:14:18

标签: android android-recyclerview activity-transition

[在底部编辑]

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

Google Calendar

如果仔细查看这些视图,它们属于List或RecyclerView,但它们是边界内的动画(大小动画,翻译动画)。

如果我尝试这样做,结果就是我的观点属于我父母的界限。

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

这是一个框架,小心地停止只是为了看到子视图是从父视图中获取的,并且正在开始扩展到整个视图:

enter image description here

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

enter image description here

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

1 个答案:

答案 0 :(得分: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));
            }
        }
    }
}

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>