ViewDragHelper解读
来源:互联网 发布:武装突袭3低配优化 编辑:程序博客网 时间:2024/05/22 13:54
精彩推荐
Android ViewDragHelper完全解析 自定义ViewGroup神器
模仿探探首页卡片左右滑动效果,滑动流畅,卡片view无限重生
案例效果图
案例注释
public class SlideLayout extends ViewGroup { private List<CardItemView> viewList = new ArrayList<>(); // 存放的是每一层的view,从顶到底 private List<View> releasedViewList = new ArrayList<>(); // 手指松开后存放的view列表 private final ViewDragHelper mDragHelper; // 拖拽工具类 private int initCenterViewX = 0, initCenterViewY = 0; // 最初时,中间View的x位置,y位置 private int allWidth = 0; // 面板的宽度 private int allHeight = 0; // 面板的高度 private int childWith = 0; // 每一个子View对应的宽度 private static final float SCALE_STEP = 0.08f; // view叠加缩放的步长 private static final int MAX_SLIDE_DISTANCE_LINKAGE = 400; // 水平距离+垂直距离,超过这个值,则下一层view完成向上一层view的过渡 private View bottomLayout; // 卡片下边的三个按钮布局 private int bottomMarginTop = 40;//底部按钮和图片的间距 private int yOffsetStep = 40; // view叠加垂直偏移量的步长 private static final int X_VEL_THRESHOLD = 900; private static final int X_DISTANCE_THRESHOLD = 300; public static final int VANISH_TYPE_LEFT = 0;//左侧按钮点击事件 public static final int VANISH_TYPE_RIGHT = 1;//右侧按钮点击事件 private Object obj1 = new Object(); private CardSwitchListener cardSwitchListener; // 回调接口 private List<CardDataItem> dataList; // 存储的数据链表 private int isShowing = 0; // 当前正在显示的小项 private View leftBtn, rightBtn; private boolean btnLock = false; private GestureDetectorCompat moveDetector; private OnClickListener btnListener; public SlideLayout(Context context) { this(context, null); } public SlideLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.card); bottomMarginTop = (int) array.getDimension(R.styleable.card_bottomMarginTop, bottomMarginTop); yOffsetStep = (int) array.getDimension(R.styleable.card_yOffsetStep, yOffsetStep); array.recycle(); mDragHelper = ViewDragHelper.create(this, 10, new DragHelperCallback()); moveDetector = new GestureDetectorCompat(context, new MoveDetector()); btnListener = new View.OnClickListener() { @Override public void onClick(View view) { if (view instanceof ImageView) { // 点击的是卡片 if (null != cardSwitchListener && view.getScaleX() > 1 - SCALE_STEP) { cardSwitchListener.onItemClick(view, isShowing); } } else { // 点击的是bottomLayout里面的一些按钮 btnLock = true; int type = -1; if (view == leftBtn) { type = VANISH_TYPE_LEFT; } else if (view == rightBtn) { type = VANISH_TYPE_RIGHT; } vanishOnBtnClick(type); } } }; } //手势监听 class MoveDetector extends GestureDetector.SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx, float dy) { // 拖动了,touch不往下传递 return Math.abs(dy) + Math.abs(dx) > 5; } } //拖动事件处理核心方法 public class DragHelperCallback extends ViewDragHelper.Callback { //是否可以拖动view @Override public boolean tryCaptureView(View child, int pointerId) { // 如果数据List为空,或者子View不可见,则不予处理 if (child == bottomLayout || dataList == null || dataList.size() == 0 || child.getVisibility() != View.VISIBLE || child.getScaleX() <= 1.0f - SCALE_STEP) { // 一般来讲,如果拖动的是第三层、或者第四层的View,则直接禁止 // 此处用getScale的用法来巧妙回避 return false; } if (btnLock) { return false; } // 只捕获顶部view(rotation=0) int childIndex = viewList.indexOf(child); if (childIndex > 0) { return false; } return true; } //松手之后View的处理 @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { animToSide(releasedChild, xvel, yvel); } //View位置发生变化时 @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { // 调用offsetLeftAndRight导致viewPosition改变,会调到此处,所以此处对index做保护处理 int index = viewList.indexOf(changedView); if (index + 2 > viewList.size()) { return; } processLinkageView(changedView); } //View滑动的X坐标 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { return left; } //View滑动的Y坐标 @Override public int clampViewPositionVertical(View child, int top, int dy) { return top; } } //重测View的大小 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); int maxWidth = MeasureSpec.getSize(widthMeasureSpec); int maxHeight = MeasureSpec.getSize(heightMeasureSpec); //设置模式和大小 setMeasuredDimension( resolveSizeAndState(maxWidth, widthMeasureSpec, 0), resolveSizeAndState(maxHeight, heightMeasureSpec, 0)); allWidth = getMeasuredWidth(); allHeight = getMeasuredHeight(); } //放置布局 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { // 布局卡片view int size = viewList.size(); for (int i = 0; i < size; i++) { View viewItem = viewList.get(i); int childHeight = viewItem.getMeasuredHeight(); viewItem.layout(left, top, right, top + childHeight); int offset = yOffsetStep * i; float scale = 1 - SCALE_STEP * i; if (i > 2) { // 备用的view offset = yOffsetStep * 2; scale = 1 - SCALE_STEP * 2; } viewItem.offsetTopAndBottom(offset); viewItem.setScaleX(scale); viewItem.setScaleY(scale); } // 布局底部按钮的View if (null != bottomLayout) { int layoutTop = viewList.get(0).getMeasuredHeight() + bottomMarginTop; bottomLayout.layout(left, layoutTop, right, layoutTop + bottomLayout.getMeasuredHeight()); } // 初始化一些中间参数 initCenterViewX = viewList.get(0).getLeft(); initCenterViewY = viewList.get(0).getTop(); childWith = viewList.get(0).getMeasuredWidth(); } //设置卡片操作回调 public void setCardSwitchListener(CardSwitchListener cardSwitchListener) { this.cardSwitchListener = cardSwitchListener; } //顶层卡片View位置改变,底层的位置需要调整 private void processLinkageView(View changedView) { int changeViewLeft = changedView.getLeft(); int changeViewTop = changedView.getTop(); int distance = Math.abs(changeViewTop - initCenterViewY) + Math.abs(changeViewLeft - initCenterViewX); float rate = distance / (float) MAX_SLIDE_DISTANCE_LINKAGE; float rate1 = rate; float rate2 = rate - 0.2f; if (rate > 1) { rate1 = 1; } if (rate2 < 0) { rate2 = 0; } else if (rate2 > 1) { rate2 = 1; } ajustLinkageViewItem(changedView, rate1, 1); ajustLinkageViewItem(changedView, rate2, 2); } //由index对应view变成index-1对应的view private void ajustLinkageViewItem(View changedView, float rate, int index) { int changeIndex = viewList.indexOf(changedView); int initPosY = yOffsetStep * index; float initScale = 1 - SCALE_STEP * index; int nextPosY = yOffsetStep * (index - 1); float nextScale = 1 - SCALE_STEP * (index - 1); int offset = (int) (initPosY + (nextPosY - initPosY) * rate); float scale = initScale + (nextScale - initScale) * rate; View ajustView = viewList.get(changeIndex + index); ajustView.offsetTopAndBottom(offset - ajustView.getTop() + initCenterViewY); ajustView.setScaleX(scale); ajustView.setScaleY(scale); } //处理是否拦截滑动事件 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev); boolean moveFlag = moveDetector.onTouchEvent(ev); if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { // ACTION_DOWN的时候就对view重新排序 orderViewStack(); // 保存初次按下时arrowFlagView的Y坐标 // action_down时就让mDragHelper开始工作,否则有时候导致异常 mDragHelper.processTouchEvent(ev); } return shouldIntercept && moveFlag; } //处理是否处理滑动事件 @Override public boolean onTouchEvent(MotionEvent event) { mDragHelper.processTouchEvent(event); return true; } //对View重新排序 private void orderViewStack() { synchronized (obj1) { if (releasedViewList.size() == 0) { return; } CardItemView changedView = (CardItemView) releasedViewList.get(0); if (changedView.getLeft() == initCenterViewX) { releasedViewList.remove(0); return; } // 1. 消失的卡片View位置重置,由于大多手机会重新调用onLayout函数,所以此处大可以不做处理,不信你注释掉看看 changedView.offsetLeftAndRight(initCenterViewX - changedView.getLeft()); changedView.offsetTopAndBottom(initCenterViewY - changedView.getTop() + yOffsetStep * 2); float scale = 1.0f - SCALE_STEP * 2; changedView.setScaleX(scale); changedView.setScaleY(scale); // 2. 卡片View在ViewGroup中的顺次调整 int num = viewList.size(); for (int i = num - 1; i > 0; i--) { View tempView = viewList.get(i); tempView.bringToFront();//调整到前面来 } // 3. changedView填充新数据 int newIndex = isShowing + 4; if (newIndex < dataList.size()) { CardDataItem dataItem = dataList.get(newIndex); changedView.fillData(dataItem); } else { changedView.setVisibility(View.INVISIBLE); } // 4. viewList中的卡片view的位次调整 viewList.remove(changedView); viewList.add(changedView); releasedViewList.remove(0); // 5. 更新showIndex、接口回调 if (isShowing + 1 < dataList.size()) { isShowing++; } if (null != cardSwitchListener) { cardSwitchListener.onShow(isShowing); } } } @Override public void computeScroll() { if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } else { // 动画结束 synchronized (this) { if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) { orderViewStack(); btnLock = false; } } } } //松手时处理滑动到边缘的动画 private void animToSide(View changedView, float xvel, float yvel) { int finalX = initCenterViewX; int finalY = initCenterViewY; int flyType = -1; // 1. 下面这一坨计算finalX和finalY,要读懂代码需要建立一个比较清晰的数学模型才能理解,不信拉倒 int dx = changedView.getLeft() - initCenterViewX; int dy = changedView.getTop() - initCenterViewY; if (dx == 0) { // 由于dx作为分母,此处保护处理 dx = 1; } if (xvel > X_VEL_THRESHOLD || dx > X_DISTANCE_THRESHOLD) { finalX = allWidth; finalY = dy * (childWith + initCenterViewX) / dx + initCenterViewY; flyType = VANISH_TYPE_RIGHT; } else if (xvel < -X_VEL_THRESHOLD || dx < -X_DISTANCE_THRESHOLD) { finalX = -childWith; finalY = dy * (childWith + initCenterViewX) / (-dx) + dy + initCenterViewY; flyType = VANISH_TYPE_LEFT; } // 如果斜率太高,就折中处理 if (finalY > allHeight) { finalY = allHeight; } else if (finalY < -allHeight / 2) { finalY = -allHeight / 2; } // 如果没有飞向两侧,而是回到了中间,需要谨慎处理 if (finalX != initCenterViewX) { releasedViewList.add(changedView); } // 2. 启动动画 if (mDragHelper.smoothSlideViewTo(changedView, finalX, finalY)) { ViewCompat.postInvalidateOnAnimation(this); } // 3. 消失动画即将进行,listener回调 if (flyType >= 0 && cardSwitchListener != null) { cardSwitchListener.onCardVanish(isShowing, flyType); } } //卡片回调接口 public interface CardSwitchListener { /** * 新卡片显示回调 * * @param index 最顶层显示的卡片的index */ public void onShow(int index); /** * 卡片飞向两侧回调 * * @param index 飞向两侧的卡片数据index * @param type 飞向哪一侧{@link #VANISH_TYPE_LEFT}或{@link #VANISH_TYPE_RIGHT} */ public void onCardVanish(int index, int type); /** * 卡片点击事件 * * @param cardImageView 卡片上的图片view * @param index 点击到的index */ public void onItemClick(View cardImageView, int index); } //本来想写成Adapter适配,想想还是算了,这种比较简单 public void fillData(List<CardDataItem> dataList) { this.dataList = dataList; int num = viewList.size(); for (int i = 0; i < num; i++) { CardItemView itemView = viewList.get(i); itemView.fillData(dataList.get(i)); itemView.setVisibility(View.VISIBLE); } if (null != cardSwitchListener) { cardSwitchListener.onShow(0); } } //布局加载完成 @Override protected void onFinishInflate() { super.onFinishInflate(); // 渲染完成,初始化卡片view列表 viewList.clear(); int num = getChildCount(); for (int i = num - 1; i >= 0; i--) { View childView = getChildAt(i); if (childView.getId() == R.id.card_bottom_layout) { bottomLayout = childView; initBottomLayout(); } else { CardItemView viewItem = (CardItemView) childView; viewItem.setTag(i + 1); viewItem.imageView.setOnClickListener(btnListener); viewList.add(viewItem); } } } //初始化底部按钮 private void initBottomLayout() { leftBtn = bottomLayout.findViewById(R.id.card_left_btn); rightBtn = bottomLayout.findViewById(R.id.card_right_btn); leftBtn.setOnClickListener(btnListener); rightBtn.setOnClickListener(btnListener); } //点击按钮消失动画 //按钮事件处理 private void vanishOnBtnClick(int type) { synchronized (obj1) { View animateView = viewList.get(0); if (animateView.getVisibility() != View.VISIBLE || releasedViewList.contains(animateView)) { return; } int finalX = 0; if (type == VANISH_TYPE_LEFT) { finalX = -childWith; } else if (type == VANISH_TYPE_RIGHT) { finalX = allWidth; } if (finalX != 0) { releasedViewList.add(animateView); if (mDragHelper.smoothSlideViewTo(animateView, finalX, initCenterViewY + allHeight)) { ViewCompat.postInvalidateOnAnimation(this); } } if (type >= 0 && cardSwitchListener != null) { cardSwitchListener.onCardVanish(isShowing, type); } } }}
0 0
- ViewDragHelper解读
- ViewDragHelper
- ViewDragHelper
- ViewDragHelper
- ViewDragHelper
- ViewDragHelper
- ViewDragHelper
- ViewDragHelper
- ViewDragHelper
- ViewDragHelper
- ViewDragHelper
- ViewDragHelper(二)- 源码及原理解读(进阶篇)
- ViewDragHelper详解
- ViewDragHelper详解
- ViewDragHelper详解
- Android: ViewDragHelper
- ViewDragHelper入门
- ViewDragHelper 使用
- 项目在eclipse运行正常,但单独部署到tomcat上不正常
- 88. Merge Sorted Array
- 存储性能优化
- 【Spring Framework 深入】—— IoC容器初始化 -> BeanDefinition的注册
- C++ string 用法详解
- ViewDragHelper解读
- gulp常用插件
- python如何支持中文注释
- 操作系统--存储管理
- HTML练习注册用户
- 同位语-名词性从句(基础)
- iOS 微信支付宝支付完成后跳转回自己的app
- java获取项目路径
- “Rescue(营救),ZOJ1649”的一种解法和疑惑