Droidkaigi Smoke and Mirrors

7ace70cd355db1983dea895fbe01a4ef?s=47 rallat
March 10, 2017
26

Droidkaigi Smoke and Mirrors

7ace70cd355db1983dea895fbe01a4ef?s=128

rallat

March 10, 2017
Tweet

Transcript

  1. Smoke and Mirrors the Magic behind wonderful UI in Android

  2. Israel Ferrer Camacho @rallat

  3. Smoke and mirrors N. Amer. the obscuring or embellishing of

    the truth.
  4. None
  5. None
  6. Smoke and Mirrors the Magic behind wonderful UI in Android

  7. Android Framework

  8. None
  9. None
  10. RecyclerView RecyclePool Adapter RecyclerView LayoutManager

  11. RecyclerView RecyclePool Adapter RecyclerView LayoutManager

  12. RecyclerView RecyclePool Adapter RecyclerView LayoutManager getViewForPosition

  13. RecyclerView RecyclePool Adapter RecyclerView LayoutManager getViewForPosition getViewType

  14. RecyclerView RecyclePool Adapter RecyclerView LayoutManager getViewForPosition getViewHolderByType getViewType

  15. RecyclerView RecyclePool Adapter RecyclerView LayoutManager getViewForPosition getViewHolderByType

  16. RecyclerView RecyclePool Adapter RecyclerView LayoutManager getViewForPosition getViewHolderByType bindViewHolder

  17. RecyclerView RecyclePool Adapter RecyclerView LayoutManager getViewForPosition getViewHolderByType

  18. RecyclerView RecyclePool Adapter RecyclerView LayoutManager getViewForPosition getViewHolderByType

  19. RecyclerView RecyclePool Adapter RecyclerView LayoutManager getViewHolderByType

  20. RecyclerView RecyclePool Adapter RecyclerView LayoutManager getViewHolderByType

  21. None
  22. None
  23. None
  24. Shared Element Transitions TransitionManager ActivityTransitionCoordinator ActivityTransitionState

  25. Shared Element Transitions TransitionManager ActivityTransitionCoordinator ActivityTransitionState

  26. Shared Element Transitions TransitionManager ActivityTransitionCoordinator ActivityTransitionState startExit/startEnter

  27. Shared Element Transitions TransitionManager ActivityTransitionCoordinator ActivityTransitionState startExit/startEnter beginDelayedTransition

  28. ViewOverlay

  29. ViewOverlay LinearLayout

  30. ViewOverlay LinearLayout ViewOverlay of the LinearLayout

  31. ViewOverlay LinearLayout ViewOverlay of the LinearLayout ViewOverlay.add( );

  32. ViewOverlay

  33. ViewOverlay LinearLayout re-layout

  34. ViewOverlay LinearLayout re-layout ViewOverlay of the LinearLayout

  35. None
  36. None
  37. /**
 * A transition listener receives notifications from a transition.


    * Notifications indicate transition lifecycle events.
 */
 public static interface TransitionListener {
 
 void onTransitionStart(Transition transition);
 
 void onTransitionEnd(Transition transition);
 
 void onTransitionCancel(Transition transition);
 
 void onTransitionPause(Transition transition);
 
 void onTransitionResume(Transition transition);
 }

  38. private View.OnTouchListener touchEater = new View.OnTouchListener() {
 @Override
 public boolean

    onTouch(View view, MotionEvent motionEvent) {
 return true;
 }
 };
  39. Important attributes

  40. ClipChildren

  41. None
  42. None
  43. <?xml version="1.0" encoding="utf-8"?>
 <FrameLayout
 android:id="@+id/parent"
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:padding="@dimen/activity_vertical_margin">
 <FrameLayout


    android:layout_width="300dp"
 android:layout_height="300dp"
 android:background="@color/colorAccent"
 android:clipChildren="false">
 <FrameLayout
 android:layout_width="200dp"
 android:layout_height="200dp"
 android:background="@color/colorPrimary">
 <ImageView
 android:id="@+id/imageview"
 android:layout_width="100dp"
 android:layout_height="100dp"
 android:src="@drawable/profile"/>
 </FrameLayout>
 </FrameLayout>
 </FrameLayout>

  44. <?xml version="1.0" encoding="utf-8"?>
 <FrameLayout
 android:id="@+id/parent"
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:padding="@dimen/activity_vertical_margin">
 <FrameLayout


    android:layout_width="300dp"
 android:layout_height="300dp"
 android:background="@color/colorAccent"
 android:clipChildren="false">
 <FrameLayout
 android:layout_width="200dp"
 android:layout_height="200dp"
 android:background="@color/colorPrimary">
 <ImageView
 android:id="@+id/imageview"
 android:layout_width="100dp"
 android:layout_height="100dp"
 android:src="@drawable/profile"/>
 </FrameLayout>
 </FrameLayout>
 </FrameLayout>

  45. <?xml version="1.0" encoding="utf-8"?>
 <FrameLayout
 android:id="@+id/parent"
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:clipChildren=“false"
 android:padding="@dimen/activity_vertical_margin">


    <FrameLayout
 android:layout_width="300dp"
 android:layout_height="300dp"
 android:background="@color/colorAccent"
 android:clipChildren="false">
 <FrameLayout
 android:layout_width="200dp"
 android:layout_height="200dp"
 android:background="@color/colorPrimary">
 <ImageView
 android:id="@+id/imageview"
 android:layout_width="100dp"
 android:layout_height="100dp"
 android:src="@drawable/profile"/>
 </FrameLayout>
 </FrameLayout>
 </FrameLayout>
  46. <?xml version="1.0" encoding="utf-8"?>
 <FrameLayout
 android:id="@+id/parent"
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:clipChildren=“false"
 android:padding="@dimen/activity_vertical_margin">


    <FrameLayout
 android:layout_width="300dp"
 android:layout_height="300dp"
 android:background="@color/colorAccent"
 android:clipChildren="false">
 <FrameLayout
 android:layout_width="200dp"
 android:layout_height="200dp"
 android:background="@color/colorPrimary">
 <ImageView
 android:id="@+id/imageview"
 android:layout_width="100dp"
 android:layout_height="100dp"
 android:src="@drawable/profile"/>
 </FrameLayout>
 </FrameLayout>
 </FrameLayout>
  47. ClipPadding

  48. None
  49. None
  50. Utils public static void disableParentsClip(@NonNull View view) {
 while (view.getParent()

    != null && view.getParent() instanceof ViewGroup) {
 ViewGroup viewGroup = (ViewGroup) view.getParent();
 viewGroup.setClipChildren(false);
 viewGroup.setClipToPadding(false);
 view = viewGroup;
 }
 }
 
 public static void enableParentsClip(@NonNull View view) {
 while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
 ViewGroup viewGroup = (ViewGroup) view.getParent();
 viewGroup.setClipChildren(true);
 viewGroup.setClipToPadding(true);
 view = viewGroup;
 }

  51. None
  52. None
  53. None
  54. None
  55. None
  56. None
  57. None
  58. None
  59. None
  60. None
  61. None
  62. None
  63. None
  64. None
  65. None
  66. None
  67. Smoke and mirrors Lesson: Almost anything fast enough will look

    good
  68. None
  69. None
  70. None
  71. None
  72. None
  73. None
  74. None
  75. None
  76. None
  77. None
  78. None
  79. Smoke and mirrors Lesson: Fake it until you make it.

  80. None
  81. Demo

  82. None
  83. None
  84. None
  85. None
  86. Pivot

  87. Pivot

  88. None
  89. None
  90. /**
 * Sets the x location of the point around

    which the view * is {@link #setRotation(float) rotated} and * {@link #setScaleX(float) scaled}.
 * By default, the pivot point is centered on the object. */ smallRecyclerView.setPivotX(0); smallRecyclerView.setPivotY(0); mediumRecyclerView.setPivotX(0);
 mediumRecyclerView.setPivotY(0);
  91. /**
 * Sets the x location of the point around

    which the view * is {@link #setRotation(float) rotated} and * {@link #setScaleX(float) scaled}.
 * By default, the pivot point is centered on the object. */ smallRecyclerView.setPivotX(0); smallRecyclerView.setPivotY(0); mediumRecyclerView.setPivotX(0);
 mediumRecyclerView.setPivotY(0);
  92. smallRecyclerView.setAdapter(smallAdapter); 
 mediumRecyclerView.setAdapter(mediumAdapter);
 mediumRecyclerView.setVisibility(INVISIBLE);


  93. ItemTouchListenerDispatcher dispatcher = new ItemTouchListenerDispatcher(this, galleryGestureDetector, fullScreenGestureDetector); 
 smallRecyclerView.addOnItemTouchListener(onItemTouchListener);
 mediumRecyclerView.addOnItemTouchListener(onItemTouchListener);

  94. public class ItemTouchListenerDispatcher implements RecyclerView.OnItemTouchListener {
 …
 @Override
 public void

    onTouchEvent(RecyclerView rv, MotionEvent e) {
 currentSpan = getSpan(e);
 switch (rv.getId()) {
 case R.id.mediumRecyclerView: {
 if (currentSpan < 0) {
 galleryGestureDetector.onTouchEvent(e);
 } else if (currentSpan == 0) {
 final View childViewUnder = rv.findChildViewUnder(e.getX(), e.getY());
 if (childViewUnder != null) {
 childViewUnder.performClick();
 }
 }
 break;
 }
 case R.id.smallRecyclerView: {
 galleryGestureDetector.onTouchEvent(e);
 break;
 }
 default: {
 break;
 }
 
 }
 }
 …
  95. public class ItemTouchListenerDispatcher implements RecyclerView.OnItemTouchListener {
 …
 @Override
 public void

    onTouchEvent(RecyclerView rv, MotionEvent e) {
 currentSpan = getSpan(e);
 switch (rv.getId()) {
 case R.id.mediumRecyclerView: {
 if (currentSpan < 0) {
 galleryGestureDetector.onTouchEvent(e);
 } else if (currentSpan == 0) {
 final View childViewUnder = rv.findChildViewUnder(e.getX(), e.getY());
 if (childViewUnder != null) {
 childViewUnder.performClick();
 }
 }
 break;
 }
 case R.id.smallRecyclerView: {
 galleryGestureDetector.onTouchEvent(e);
 break;
 }
 default: {
 break;
 }
 
 }
 }
 …
  96. public class ItemTouchListenerDispatcher implements RecyclerView.OnItemTouchListener {
 …
 @Override
 public void

    onTouchEvent(RecyclerView rv, MotionEvent e) {
 currentSpan = getSpan(e);
 switch (rv.getId()) {
 case R.id.mediumRecyclerView: {
 if (currentSpan < 0) {
 galleryGestureDetector.onTouchEvent(e);
 } else if (currentSpan == 0) {
 final View childViewUnder = rv.findChildViewUnder(e.getX(), e.getY());
 if (childViewUnder != null) {
 childViewUnder.performClick();
 }
 }
 break;
 }
 case R.id.smallRecyclerView: {
 galleryGestureDetector.onTouchEvent(e);
 break;
 }
 default: {
 break;
 }
 
 }
 }
 …
  97. public class ItemTouchListenerDispatcher implements RecyclerView.OnItemTouchListener {
 …
 @Override
 public void

    onTouchEvent(RecyclerView rv, MotionEvent e) {
 currentSpan = getSpan(e);
 switch (rv.getId()) {
 case R.id.mediumRecyclerView: {
 if (currentSpan < 0) {
 galleryGestureDetector.onTouchEvent(e);
 } else if (currentSpan == 0) {
 final View childViewUnder = rv.findChildViewUnder(e.getX(), e.getY());
 if (childViewUnder != null) {
 childViewUnder.performClick();
 }
 }
 break;
 }
 case R.id.smallRecyclerView: {
 galleryGestureDetector.onTouchEvent(e);
 break;
 }
 default: {
 break;
 }
 
 }
 }
 …
  98. Scaling with gesture

  99. None
  100. None
  101. public interface OnScaleGestureListener {
 /**
 * Responds to scaling events

    for a gesture in progress.
 * Reported by pointer motion.
 */
 public boolean onScale(ScaleGestureDetector detector);
 
 /**
 * Responds to the beginning of a scaling gesture. Reported by
 * new pointers going down.
 */
 public boolean onScaleBegin(ScaleGestureDetector detector);
 
 /**
 * Responds to the end of a scale gesture. Reported by existing
 * pointers going up.
 
 * @param detector The detector reporting the event - use this to
 * retrieve extended info about event state.
 */
 public void onScaleEnd(ScaleGestureDetector detector);
 }
  102. Scale begin @Override
 public boolean onScaleBegin(@NonNull ScaleGestureDetector detector) {
 mediumRecyclerView.setVisibility(View.VISIBLE);


    smallRecyclerView.setVisibility(View.VISIBLE);
 return true;
 }
  103. During Scale @Override
 public boolean onScale(@NonNull ScaleGestureDetector detector) {
 if

    (gestureTolerance(detector)) {
 //small
 scaleFactor *= detector.getScaleFactor();
 scaleFactor = Math.max(1f, Math.min(scaleFactor, SMALL_MAX_SCALE_FACTOR));
 isInProgress = scaleFactor > 1;
 smallRecyclerView.setScaleX(scaleFactor);
 smallRecyclerView.setScaleY(scaleFactor);
 
 //medium
 scaleFactorMedium *= detector.getScaleFactor();
 scaleFactorMedium = Math.max(0.8f, Math.min(scaleFactorMedium, 1f));
 mediumRecyclerView.setScaleX(scaleFactorMedium);
 mediumRecyclerView.setScaleY(scaleFactorMedium);
 
 //alpha
 mediumRecyclerView.setAlpha((scaleFactor - 1) / (0.25f));
 smallRecyclerView.setAlpha(1 - (scaleFactor - 1) / (0.25f));
 
 }
 return true;
 }
  104. During Scale @Override
 public boolean onScale(@NonNull ScaleGestureDetector detector) {
 if

    (gestureTolerance(detector)) {
 //small
 scaleFactor *= detector.getScaleFactor();
 scaleFactor = Math.max(1f, Math.min(scaleFactor, SMALL_MAX_SCALE_FACTOR));
 isInProgress = scaleFactor > 1;
 smallRecyclerView.setScaleX(scaleFactor);
 smallRecyclerView.setScaleY(scaleFactor);
 
 //medium
 scaleFactorMedium *= detector.getScaleFactor();
 scaleFactorMedium = Math.max(0.8f, Math.min(scaleFactorMedium, 1f));
 mediumRecyclerView.setScaleX(scaleFactorMedium);
 mediumRecyclerView.setScaleY(scaleFactorMedium);
 
 //alpha
 mediumRecyclerView.setAlpha((scaleFactor - 1) / (0.25f));
 smallRecyclerView.setAlpha(1 - (scaleFactor - 1) / (0.25f));
 
 }
 return true;
 }
  105. During Scale @Override
 public boolean onScale(@NonNull ScaleGestureDetector detector) {
 if

    (gestureTolerance(detector)) {
 //small
 scaleFactor *= detector.getScaleFactor();
 scaleFactor = Math.max(1f, Math.min(scaleFactor, 1.25f));
 isInProgress = scaleFactor > 1;
 smallRecyclerView.setScaleX(scaleFactor);
 smallRecyclerView.setScaleY(scaleFactor);
 
 //medium
 scaleFactorMedium *= detector.getScaleFactor();
 scaleFactorMedium = Math.max(0.8f, Math.min(scaleFactorMedium, 1f));
 mediumRecyclerView.setScaleX(scaleFactorMedium);
 mediumRecyclerView.setScaleY(scaleFactorMedium);
 
 //alpha
 mediumRecyclerView.setAlpha((scaleFactor - 1) / (0.25f));
 smallRecyclerView.setAlpha(1 - (scaleFactor - 1) / (0.25f));
 
 }
 return true;
 }
  106. During Scale @Override
 public boolean onScale(@NonNull ScaleGestureDetector detector) {
 if

    (gestureTolerance(detector)) {
 //small
 scaleFactor *= detector.getScaleFactor();
 scaleFactor = Math.max(1f, Math.min(scaleFactor, SMALL_MAX_SCALE_FACTOR));
 isInProgress = scaleFactor > 1;
 smallRecyclerView.setScaleX(scaleFactor);
 smallRecyclerView.setScaleY(scaleFactor);
 
 //medium
 scaleFactorMedium *= detector.getScaleFactor();
 scaleFactorMedium = Math.max(0.8f, Math.min(scaleFactorMedium, 1f));
 mediumRecyclerView.setScaleX(scaleFactorMedium);
 mediumRecyclerView.setScaleY(scaleFactorMedium);
 
 //alpha
 mediumRecyclerView.setAlpha((scaleFactor - 1) / (0.25f));
 smallRecyclerView.setAlpha(1 - (scaleFactor - 1) / (0.25f));
 
 }
 return true;
 }
  107. Scale ends @Override
 public void onScaleEnd(@NonNull ScaleGestureDetector detector) {
 if

    (IsScaleInProgress()) {
 if (scaleFactor < TRANSITION_BOUNDARY) {
 transitionFromMediumToSmall();
 scaleFactor = 0;
 scaleFactorMedium = 0;
 } else {
 transitionFromSmallToMedium();
 scaleFactor = SMALL_MAX_SCALE_FACTOR;
 scaleFactorMedium = 1f;
 }
 }
 }
  108. Scale ends @Override
 public void onScaleEnd(@NonNull ScaleGestureDetector detector) {
 if

    (IsScaleInProgress()) {
 if (scaleFactor < TRANSITION_BOUNDARY) {
 transitionFromMediumToSmall();
 scaleFactor = 0;
 scaleFactorMedium = 0;
 } else {
 transitionFromSmallToMedium();
 scaleFactor = SMALL_MAX_SCALE_FACTOR;
 scaleFactorMedium = 1f;
 }
 }
 }
  109. Scale ends @Override
 public void onScaleEnd(@NonNull ScaleGestureDetector detector) {
 if

    (IsScaleInProgress()) {
 if (scaleFactor < TRANSITION_BOUNDARY) {
 transitionFromMediumToSmall();
 scaleFactor = 0;
 scaleFactorMedium = 0;
 } else {
 transitionFromSmallToMedium();
 scaleFactor = SMALL_MAX_SCALE_FACTOR;
 scaleFactorMedium = 1f;
 }
 }
 }
  110. Scale ends @Override
 public void onScaleEnd(@NonNull ScaleGestureDetector detector) {
 if

    (IsScaleInProgress()) {
 if (scaleFactor < TRANSITION_BOUNDARY) {
 transitionFromMediumToSmall();
 scaleFactor = 0;
 scaleFactorMedium = 0;
 } else {
 transitionFromSmallToMedium();
 scaleFactor = SMALL_MAX_SCALE_FACTOR;
 scaleFactorMedium = 1f;
 }
 }
 }
  111. None
  112. None
  113. None
  114. Recycler View elevation

  115. Recycler View elevation • Custom LayoutManager

  116. Recycler View elevation • Custom LayoutManager • RecyclerView.ChildDrawingOrderCallback

  117. Recycler View elevation • Custom LayoutManager • RecyclerView.ChildDrawingOrderCallback • Easier!

    Fake it with ViewOverlay!
  118. Transition to full screen 
 @Override
 public void onClick(@NonNull final

    View itemView) {
 ViewGroupOverlay overlay = fullScreenContainer.getOverlay();
 overlay.clear();
 overlay.add(itemView);
 fullScreenContainer.setBackgroundColor(TRANSPARENT);
 fullScreenContainer.setVisibility(View.VISIBLE);
 itemView.animate() .x(DELTA_TO_CENTER_X).y(DELTA_TO_CENTER_Y)
 .scaleX(DELTA_SCALE).scaleY(DELTA_SCALE) .withEndAction(setTransitionToRecyclerView()).start();
 }
 }
  119. Transition to full screen 
 @Override
 public void onClick(@NonNull final

    View itemView) {
 ViewGroupOverlay overlay = fullScreenContainer.getOverlay();
 overlay.clear();
 overlay.add(itemView);
 fullScreenContainer.setBackgroundColor(TRANSPARENT);
 fullScreenContainer.setVisibility(View.VISIBLE);
 itemView.animate() .x(DELTA_TO_CENTER_X).y(DELTA_TO_CENTER_Y)
 .scaleX(DELTA_SCALE).scaleY(DELTA_SCALE) .withEndAction(setTransitionToRecyclerView()).start();
 }
 }
  120. Transition to full screen 
 @Override
 public void onClick(@NonNull final

    View itemView) {
 ViewGroupOverlay overlay = fullScreenContainer.getOverlay();
 overlay.clear();
 overlay.add(itemView);
 fullScreenContainer.setBackgroundColor(TRANSPARENT);
 fullScreenContainer.setVisibility(View.VISIBLE);
 itemView.animate() .x(DELTA_TO_CENTER_X).y(DELTA_TO_CENTER_Y)
 .scaleX(DELTA_SCALE).scaleY(DELTA_SCALE) .withEndAction(setTransitionToRecyclerView()).start();
 }
 }
  121. Transition to full screen 
 @Override
 public void onClick(@NonNull final

    View itemView) {
 ViewGroupOverlay overlay = fullScreenContainer.getOverlay();
 overlay.clear();
 overlay.add(itemView);
 fullScreenContainer.setBackgroundColor(TRANSPARENT);
 fullScreenContainer.setVisibility(View.VISIBLE);
 itemView.animate() .x(DELTA_TO_CENTER_X).y(DELTA_TO_CENTER_Y)
 .scaleX(DELTA_SCALE).scaleY(DELTA_SCALE) .withEndAction(setTransitionToRecyclerView()).start();
 }
 }
  122. Transition to full screen 
 @Override
 public void onClick(@NonNull final

    View itemView) {
 ViewGroupOverlay overlay = fullScreenContainer.getOverlay();
 overlay.clear();
 overlay.add(itemView);
 fullScreenContainer.setBackgroundColor(TRANSPARENT);
 fullScreenContainer.setVisibility(View.VISIBLE);
 itemView.animate() .x(DELTA_TO_CENTER_X).y(DELTA_TO_CENTER_Y)
 .scaleX(DELTA_SCALE).scaleY(DELTA_SCALE) .withEndAction(setTransitionToRecyclerView()).start();
 }
 }
  123. Transition to full screen private Runnable setTransitionToRecyclerView() {
 return new

    Runnable() {
 @Override
 public void run() {
 fullScreenContainer.setBackgroundColor(BLACK);
 fullScreenContainer.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 overlay.add(itemView);
 fullScreenContainer.setBackgroundColor(TRANSPARENT);
 itemView.animate().x(originX).y(originY) .scaleY(1).scaleX(1).withEndAction(
 new Runnable() {
 @Override
 public void run() {
 overlay.remove(itemView);
 fullScreenContainer.setVisibility(View.GONE);
 }
 }).start();
 }
 });
 }
 };
 }
  124. Transition to full screen private Runnable setTransitionToRecyclerView() {
 return new

    Runnable() {
 @Override
 public void run() {
 fullScreenContainer.setBackgroundColor(BLACK);
 fullScreenContainer.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 overlay.add(itemView);
 fullScreenContainer.setBackgroundColor(TRANSPARENT);
 itemView.animate().x(originX).y(originY) .scaleY(1).scaleX(1).withEndAction(
 new Runnable() {
 @Override
 public void run() {
 overlay.remove(itemView);
 fullScreenContainer.setVisibility(View.GONE);
 }
 }).start();
 }
 });
 }
 };
 }
  125. Transition to full screen private Runnable setTransitionToRecyclerView() {
 return new

    Runnable() {
 @Override
 public void run() {
 fullScreenContainer.setBackgroundColor(BLACK);
 fullScreenContainer.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 overlay.add(itemView);
 fullScreenContainer.setBackgroundColor(TRANSPARENT);
 itemView.animate().x(originX).y(originY) .scaleY(1).scaleX(1).withEndAction(
 new Runnable() {
 @Override
 public void run() {
 overlay.remove(itemView);
 fullScreenContainer.setVisibility(View.GONE);
 }
 }).start();
 }
 });
 }
 };
 }
  126. Transition to full screen private Runnable setTransitionToRecyclerView() {
 return new

    Runnable() {
 @Override
 public void run() {
 fullScreenContainer.setBackgroundColor(BLACK);
 fullScreenContainer.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 overlay.add(itemView);
 fullScreenContainer.setBackgroundColor(TRANSPARENT);
 itemView.animate().x(originX).y(originY) .scaleY(1).scaleX(1).withEndAction(
 new Runnable() {
 @Override
 public void run() {
 overlay.remove(itemView);
 fullScreenContainer.setVisibility(View.GONE);
 }
 }).start();
 }
 });
 }
 };
 }
  127. None
  128. None
  129. None
  130. None
  131. Magic tricks

  132. Magic tricks • ClipPadding, ClipChildren to draw over parents&paddings

  133. Magic tricks • ViewOverlay for animations • ClipPadding, ClipChildren to

    draw over parents&paddings
  134. Magic tricks • ViewOverlay for animations • Single Activity allows

    control of the transition by the user touch • ClipPadding, ClipChildren to draw over parents&paddings
  135. Magic tricks • ViewOverlay for animations • Single Activity allows

    control of the transition by the user touch • Fast animations to hide implementation details and make it look cool • ClipPadding, ClipChildren to draw over parents&paddings
  136. More examples

  137. github.com/nickbutcher/plaid More examples

  138. ͋Γ͕ͱ͏͍͟͝·͢ github.com/rallat/smokeAndMirrors Israel Ferrer Camacho @rallat