2023年7月15日发(作者:)
Android实现探探图⽚滑动效果之前⼀段时间,在朋友的推荐下,玩了探探这⼀款软件,初玩的时候,就发现,这款软件与⼀般的社交软件如陌陌之类的⼤相径庭,让我⽿⽬⼀新,特别是探探⾥关于图⽚滑动操作让⼈觉得⾮常新鲜。所以在下通过⽹上之前的前辈的经历加上⾃⼰的理解,也来涉涉⽔。下⾯是⽹上找的探探的原界⾯
当时就⾮常想通过⾃⼰来实现这种仿探探式的效果,然⽽却没什么思路。不过⽏庸置疑的是,这种效果的原理肯定和 ListView/RecyclerView 类似,涉及到 Item View 的回收和重⽤,否则早就因为⼤量的 Item View ⽽ OOM 了。
从View⼊⼿,RecyclerView 是⾃带 Item View 回收和重⽤功能的,⽽且,RecyclerView 的布局⽅式是通过设置 LayoutManager 来实现的,这样就充分地把布局和 RecyclerView 搞定了。
继承 Manager , 显⽰⾃⼰管理布局, ⽐如最多显⽰4个view, 并且都是居中显⽰.
底部的View还需要进⾏缩放,平移操作.public class OverLayCardLayoutManager extends Manager { private static final String TAG = "swipecard"; public static int MAX_SHOW_COUNT = 4; public static float SCALE_GAP = 0.05f; public static int TRANS_Y_GAP; public OverLayCardLayoutManager(Context context) { //平移时, 需要⽤到的参考值 TRANS_Y_GAP = (int) (20 * ources().getDisplayMetrics().density); } @Override public Params generateDefaultLayoutParams() { //必须要实现的⽅法 return new Params(_CONTENT, _CONTENT); } @Override public void onLayoutChildren(er recycler, state) { //在这个⽅法中进⾏View的布局操作.此⽅法会被调⽤多次. detachAndScrapAttachedViews(recycler); int itemCount = getItemCount(); if (itemCount < 1) { return; } //top-3View的position int bottomPosition; //边界处理 if (itemCount < MAX_SHOW_COUNT) { bottomPosition = 0; } else { bottomPosition = itemCount - MAX_SHOW_COUNT; } //从可见的最底层View开始layout,依次层叠上去 for (int position = bottomPosition; position < itemCount; position++) { //1:重recycler的缓存机制中拿到⼀个View View view = wForPosition(position); //2:和⾃定义ViewGroup⼀样, 需要先addView addView(view); //3:和⾃定义ViewGroup⼀样, 也需要测量View的⼤⼩ measureChildWithMargins(view, 0, 0); int widthSpace = getWidth() - getDecoratedMeasuredWidth(view); int heightSpace = getHeight() - getDecoratedMeasuredHeight(view); //4:和⾃定义ViewGroup的onLayout⼀样, 需要layout View.对View进⾏布局
//我们在布局时,将childView居中处理,这⾥也可以改为只⽔平居中 layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2, widthSpace / 2 + getDecoratedMeasuredWidth(view), heightSpace / 2 + getDecoratedMeasuredHeight(view)); /** * TopView的Scale 为1,translationY 0 * 每⼀级Scale相差0.05f,translationY相差7dp左右 * * 观察⼈⼈影视的UI,拖动时,topView被拖动,Scale不变,⼀直为1. * top-1View 的Scale慢慢变化⾄1,translation也慢慢恢复0 * top-2View的Scale慢慢变化⾄ top-1View的Scale,translation 也慢慢变化只top-1View的translation * top-3View的Scale要变化,translation岿然不动 */ //第⼏层,举例⼦,count =7, 最后⼀个TopView(6)是第0层, int level = itemCount - position - 1; //如果不需要缩放平移, 那么下⾯的代码可以注释掉... //除了顶层不需要缩⼩和位移 if (level > 0 /*&& level < mShowCount - 1*/) { //每⼀层都需要X⽅向的缩⼩ leX(1 - SCALE_GAP * level); //前N层,依次向下位移和Y⽅向的缩⼩ if (level < MAX_SHOW_COUNT - 1) { nslationY(TRANS_Y_GAP * level); leY(1 - SCALE_GAP * level); } else {//第N层在 向下位移和Y⽅向的缩⼩的成都与 N-1层保持⼀致 nslationY(TRANS_Y_GAP * (level - 1)); leY(1 - SCALE_GAP * (level - 1)); } } } }}⾕歌官⽅提供了⼀个ItemTouchHelper⼯具类, 对滑动进⾏了优越封装
使⽤⽅法: new ItemTouchHelper(callback).attachToRecyclerView(recyclerView);就这么简单,
接下来的操作, 都在回调callback⾥⾯进⾏.public class RenRenCallback extends Callback { private static final String TAG = "RenRen"; private static final int MAX_ROTATION = 15; OnSwipeListener mSwipeListener; boolean isSwipeAnim = false; public RenRenCallback() { //第⼀个参数决定可以拖动排序的⽅向, 这⾥由于不需要拖动排序,所以传0 //第⼆个参数决定可以⽀持滑动的⽅向,这⾥设置了上下左右都可以滑动. super(0, | | | ); } public void setSwipeListener(OnSwipeListener swipeListener) { mSwipeListener = swipeListener; } //⽔平⽅向是否可以被回收掉的阈值 public float getThreshold(RecyclerView recyclerView, lder viewHolder) { //2016 12 26 考虑 探探垂直上下⽅向滑动,不删除卡⽚,这⾥参照源码写死0.5f return th() * /*getSwipeThreshold(viewHolder)*/ 0.5f; } @Override public boolean onMove(RecyclerView recyclerView, lder viewHolder, lder target) { //由于不⽀持滑动排序, 所以不需要处理此⽅法 return false; } @Override public void onSwiped(lder viewHolder, int direction) { //当view需要滑动的时候,会回调此⽅法 //但是这个⽅法只是告诉你View需要滑动, 并不是对View和Adapter进⾏额外的操作, //所以, 如果你需要实现滑动删除, 那么需要在此⽅法中remove item等. //我们这⾥需要对滑动过后的View,进⾏恢复操作.
ation(0);//恢复最后⼀次的旋转状态 if (mSwipeListener != null) { eTo(viewHolder, 0); } notifyListener(pterPosition(), direction); } private void notifyListener(int position, int direction) { Log.w(TAG, "onSwiped: " + position + " " + direction); if (mSwipeListener != null) { ed(position, direction); } } @Override public float getSwipeThreshold(lder viewHolder) { //滑动的⽐例达到多少之后, 视为滑动 return 0.3f; } @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, lder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { dDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); //当你在滑动的过程中, 此⽅法⼀直会被回调, 就跟onTouch事件⼀样... //先根据滑动的dx dy 算出现在动画的⽐例系数fraction float swipeValue = (float) (dX * dX + dY * dY); final float threshold = getThreshold(recyclerView, viewHolder); float fraction = swipeValue / threshold; //边界修正 最⼤为1 if (fraction > 1) { fraction = 1; } else if (fraction < -1) { fraction = -1; } //对每个ChildView进⾏缩放 位移 int childCount = ldCount(); for (int i = 0; i < childCount; i++) { View child = ldAt(i); //第⼏层,举例⼦,count =7, 最后⼀个TopView(6)是第0层, int level = childCount - i - 1; if (level > 0) { leX(1 - SCALE_GAP * level + fraction * SCALE_GAP); if (level < MAX_SHOW_COUNT - 1) { leY(1 - SCALE_GAP * level + fraction * SCALE_GAP); nslationY(TRANS_Y_GAP * level - fraction * TRANS_Y_GAP); } else { //nslationY((float) (mTranslationYGap * (level - 1) - fraction * mTranslationYGap)); } } else { //最上层 //rotate if (dX < -50) { ation(-fraction * MAX_ROTATION); } else if (dX > 50) { ation(fraction * MAX_ROTATION); } else { ation(0); } if (mSwipeListener != null) { Params params = (Params) outParams(); final int adapterPosition = wAdapterPosition(); eTo(ewHolderForAdapterPosition(adapterPosition), dX); } } } } //扩展实现:点击按钮实现左滑效果 public void toLeft(RecyclerView recyclerView) { if (check(recyclerView)) { animTo(recyclerView, false); } } //扩展实现:点击按钮实现右滑效果 public void toRight(RecyclerView recyclerView) { if (check(recyclerView)) { animTo(recyclerView, true); } } private void animTo(final RecyclerView recyclerView, boolean right) { final int position = pter().getItemCount() - 1; final View view = ewHolderForAdapterPosition(position).itemView; TranslateAnimation translateAnimation = new TranslateAnimation(VE_TO_SELF, 0, VE_TO_SELF, right ? 1f : -1f, VE_TO_SELF, 0f, VE_TO_SELF, 1.3f); lAfter(true); ation(300); erpolator(new DecelerateInterpolator()); mationListener(new ionListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { isSwipeAnim = false; View(view); notifyListener(position, x > suredWidth() / 2
: ); } @Override public void onAnimationRepeat(Animation animation) { } }); nimation(translateAnimation); } private boolean check(RecyclerView recyclerView) { if (isSwipeAnim) { return false; } if (recyclerView == null || pter() == null) { return false; } if (pter().getItemCount() == 0) { return false; } isSwipeAnim = true; return true; } public interface OnSwipeListener { /** * @param direction {@link ItemTouchHelper#LEFT} / {@link ItemTouchHelper#RIGHT} * {@link ItemTouchHelper#UP} or {@link ItemTouchHelper#DOWN}). */ void onSwiped(int adapterPosition, int direction); /** * 最上层View滑动时回调. * * @param viewHolder 最上层的ViewHolder * @param offset 距离原始位置的偏移量 */ void onSwipeTo(lder viewHolder, float offset); } public static class SimpleSwipeCallback implements OnSwipeListener { /** * {@inheritDoc} */ @Override public void onSwiped(int adapterPosition, int direction) { } /** * {@inheritDoc} */ @Override public void onSwipeTo(lder viewHolder, float offset) { } }}布局⽂件:卡⽚内容
接下来,我们看看效果:以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
发布者:admin,转转请注明出处:http://www.yc00.com/web/1689430172a246940.html
评论列表(0条)