自定义 View:如何手动实现 ViewGroup 的拖拽
作者:Changing Lin
- 2021 年 11 月 19 日
本文字数:2611 字
阅读完需:约 9 分钟
1.知识点
VelocityTracker:速度跟踪器
View.scrollTo:把当前 View 移动到 具体的某个位置(x, y)
View.scrollBy:把当前 View 移动在原有位置偏移多少变化量(dx, dy)
View.computeScroll:当子 View 在发生滑动的时候,即 mScrollX 和 mScrollY 会发生变化的时候,父布局会调用这个方法
scrollTo 和 scrollBy 方法只能改变 view 内容的位置而不能改变 view 在布局中的位置。 scrollBy 是基于当前位置的相对滑动,而 scrollTo 是基于所传参数的绝对滑动。通过 View 的 getScrollX 和 getScrollY 方法可以得到滑动的距离。
2.原理
downScrollX 的含义:记录 我当前在 X 轴上滑动了的距离
滑动偏移量的计算:
float dx = downX - event.getX() + downScrollX; // 表示当前MOVE事件滑动了多少距离 + 起始的位置,得到最终的偏移
复制代码
实现父控件跟随手指滑动的方法:scrollTo(dx, 0)
3.代码
package com.besmart.components;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.OverScroller;
public class VirtualViewPager extends ViewGroup {
private static final String TAG = "SimulatorViewPager";
float downX;
float downY;
float downScrollX; // 我当前滑动
VelocityTracker velocityTracker = VelocityTracker.obtain();
ViewConfiguration viewConfiguration;
private float maxVelocity;
private float minVelocity;
OverScroller overScroller;
public VirtualViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
viewConfiguration = ViewConfiguration.get(context);
minVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
maxVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
overScroller = new OverScroller(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
Log.e(TAG, String.format("onLayout: 当前有几个子View %d-%d-%d-%d-%d", count, left, top, right, bottom));
int childWidth = getWidth();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
child.layout(left, top, right, bottom);
left += childWidth;
right += childWidth;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "onTouchEvent: "+event.toString());
if (event.getActionMasked() == MotionEvent.ACTION_DOWN){
velocityTracker.clear();
}
velocityTracker.addMovement(event);
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
downScrollX = getScrollX(); // 初始值
break;
case MotionEvent.ACTION_MOVE:
float dx = downX - event.getX() + downScrollX;
if (dx > getWidth())
dx = getWidth();
else if (dx<0){
dx = 0;
}
scrollTo((int) dx, 0);
break;
case MotionEvent.ACTION_UP:
velocityTracker.computeCurrentVelocity(1000, maxVelocity);
float vx = velocityTracker.getXVelocity(); // 横向速度
int scrollX = getScrollX();
int targetPage;
if (Math.abs(vx) < minVelocity){
targetPage = scrollX> getWidth()/2? 1: 0;
}else {
targetPage = vx <0? 1: 0;
}
int scrollDistance = targetPage == 1? getWidth()-scrollX: -scrollX;
// scrollTo(targetPage*getWidth(), 0);
overScroller.startScroll(scrollX, 0, scrollDistance, 0);
postInvalidateOnAnimation();
// getParent().requestDisallowInterceptTouchEvent(false);
break;
default:break;
}
return true;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TAG, "onInterceptTouchEvent: "+ev.toString());
boolean result = false;
switch (ev.getActionMasked()){
case MotionEvent.ACTION_DOWN:
downX = ev.getX();
downY = ev.getY();
downScrollX = getScrollX(); // 初始值
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(ev.getX()-downX) > viewConfiguration.getScaledPagingTouchSlop()){
getParent().requestDisallowInterceptTouchEvent(true); // 请求父控件禁止拦截事件
result = true; // 开始拦截事件
}
break;
default:break;
}
return result;
}
@Override
public void computeScroll() {
if (overScroller.computeScrollOffset()){
scrollTo(overScroller.getCurrX(), overScroller.getCurrY());
postInvalidateOnAnimation();
}
}
}
复制代码
划线
评论
复制
发布于: 4 小时前阅读数: 9
版权声明: 本文为 InfoQ 作者【Changing Lin】的原创文章。
原文链接:【http://xie.infoq.cn/article/0a48cf3431666d515fdde3ec9】。文章转载请联系作者。
Changing Lin
关注
获得机遇的手段远超于固有常规之上~ 2020.04.29 加入
我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。
评论