Smoke and mirrors

7ace70cd355db1983dea895fbe01a4ef?s=47 rallat
August 10, 2016

Smoke and mirrors

magic tricks in Android UI development

7ace70cd355db1983dea895fbe01a4ef?s=128

rallat

August 10, 2016
Tweet

Transcript

  1. Smoke and Mirrors Israel Ferrer Camacho

  2. None
  3. None
  4. None
  5. None
  6. None
  7. Smoke and mirrors Lesson: Almost anything fast enough will look

    good
  8. None
  9. Smoke and mirrors Lesson: Fake it until you make it.

  10. Demo

  11. None
  12. None
  13. None
  14. None
  15. /**
 * 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);
  16. smallRecyclerView.setAdapter(smallAdapter); 
 mediumRecyclerView.setAdapter(mediumAdapter);
 mediumRecyclerView.setVisibility(INVISIBLE);


  17. • 2 RecyclerViews with two different adapters, same collection backing

    them up. • 1 GestureDetectors, 1 for scaling from small to medium recycler view. • 1 OnItemClickListener to do the transition from medium to full screen mode (can be another gesture listener too). • 1 OnItemTouchListener for both RecyclerViews and that will dispatch the TouchEvents to the GestureListener of the visible RecyclerView.
  18. ItemTouchListenerDispatcher dispatcher = new ItemTouchListenerDispatcher(this, galleryGestureDetector, fullScreenGestureDetector); 
 smallRecyclerView.addOnItemTouchListener(onItemTouchListener);
 mediumRecyclerView.addOnItemTouchListener(onItemTouchListener);

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

  20. None
  21. ClipChildren

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

  23. <?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>
  24. ClipChildren=false • Allows child to be drawn outside is parent.

    • Spoiler alert, you need to add this to all the parents of the current child.
  25. ClipPadding

  26. None
  27. ClipPadding=false • The animated view will be clipped by the

    padding of any parent ViewGroup • Only need to be set to false in the parents with padding.
  28. 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;
 }

  29. Scaling with gesture

  30. Scale begin @Override
 public boolean onScaleBegin(@NonNull ScaleGestureDetector detector) {
 mediumRecyclerView.setVisibility(View.VISIBLE);


    smallRecyclerView.setVisibility(View.VISIBLE);
 return true;
 }
  31. 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;
 }
  32. 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;
 }
 }
 }
  33. None
  34. Other examples • Activity Transitions: shared element • RecyclerView, ListView:

    recycling views • Image fast loading: by preloading a blurry thumbnail
  35. Questions? @rallat https://github.com/rallat/smokeAndMirrors