How to exit Childview from RecyclerView?

[IMAGE NUMBER]

I am trying to manually code these types of animations:

Google Calendar

If you carefully look at these views, they belong to the list or RecyclerView, but they are animated (size animation, translation animation) from parent boundaries.

If I try to do this, the result will be that my presentation runs under my parental boundaries.

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


This is one frame, carefully stopped to see that the child view was taken from the parent and begins to expand to the whole view:

enter image description here

and here it expands by almost 100%:

enter image description here


I just wanted to rethink this differently. Is this material in relation to Activity Transitions ? Because if this is so, I do not know how to do it.

+7
android android-recyclerview activity-transition
source share
1 answer

I can imagine two ways to achieve this effect:

One way is to use the elementary element activity transition. This will require the use of two types of activities: one with a recycler view, the second with a full screen view. Animation will automatically apply between switching between activity 1 and two actions. This solution will work and does not require a lot of code, but you will encounter the problem of synchronizing two actions (for example, the exact position of the RecyclerView). Customization is not impossible, but it can be difficult, as you rely heavily on structure.

The second way is to stay within the same action and use object animators to switch between the recycler’s viewing object and full-screen viewing. The trick is not to animate the view that is inside the RecyclerView, but to animate the full screen from the borders of the view that is inside the RecyclerView. Thus, you will not be limited to parental boundaries. I went ahead and implemented the second solution, as it is very customizable and gives you full control over all the animations.

This sample application includes translation and scaling animators. It will be animated from a small square position on the left side of the screen. This behavior can be easily changed.

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

Project repo link: https: // dkarmazi@bitbucket.org /dkarmazi/androidrecyclerviewanimation.git

Activities

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

Action layout

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

Custom view to be displayed in full screen

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

Layout for custom view

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

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

Recycle View Element Layout

 <?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> 
+1
source share

All Articles