Smoke and Mirrors the Magic behind wonderful UI in Android
Israel Ferrer Camacho @rallat
Smoke and mirrors N. Amer. the obscuring or embellishing of the truth.
Android Framework
RecyclerView LayoutManager getViewForPosition RecyclerView bindViewHolder getViewType getViewHolderByType Adapter RecyclePool
Shared Element Transitions ActivityTransitionState startExit/startEnter ActivityTransitionCoordinator beginDelayedTransition TransitionManager
ViewOverlay LinearLayout ViewOverlay of the LinearLayout ViewOverlay.add( );
ViewOverlay LinearLayout re-layout ViewOverlay of the LinearLayout
/** * 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); }
private View.OnTouchListener touchEater = new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { return true; } };
Important attributes
ClipChildren
<?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>
<?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>
ClipPadding
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; }
Smoke and mirrors Lesson: Almost anything fast enough will look good
Smoke and mirrors Lesson: Fake it until you make it.
Demo
Pivot
/** * 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);
smallRecyclerView.setAdapter(smallAdapter); mediumRecyclerView.setAdapter(mediumAdapter); mediumRecyclerView.setVisibility( INVISIBLE );
ItemTouchListenerDispatcher dispatcher = new ItemTouchListenerDispatcher(this, galleryGestureDetector, fullScreenGestureDetector); smallRecyclerView.addOnItemTouchListener(onItemTouchListener); mediumRecyclerView.addOnItemTouchListener(onItemTouchListener);
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; } } } …
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; } } } …
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; } } } …
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; } } } …
Scaling with gesture
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); }
Scale begin @Override public boolean onScaleBegin(@NonNull ScaleGestureDetector detector) { mediumRecyclerView.setVisibility(View. VISIBLE ); smallRecyclerView.setVisibility(View. VISIBLE ); return true; }
Recommend
More recommend