场景
- 最近在实现一个需求:全屏的Map组件上增加一个抽屉效果,抽屉里面要求嵌套一个list并实现下拉刷新,上拉加载更多。
- 这个需求里面设计到,在屏幕不同的位置触发不同的view滑动:map滑动放大缩小,抽屉上下滑动同时list联动滑动;在抽屉里面list组件滑动,触发上下拉刷新组件刷新。
View移动方案总结
- ViewDragHelper 通过处理ViewGroup中对子View的拖拽处理,本质是对触摸事件的解析类:
ViewDragHelper主要用于处理ViewGroup中对子View的拖拽处理
主要封装了对View的触摸位置,触摸速度,移动距离等的检测和Scroller,通过接口回调的
方式告诉我们;只需要我们指定是否需要移动,移动多少等;
- 改变view在父View的layout位置来移动,但是只能移动指定的View:
view.layout(l,t,r,b);
view.offsetLeftAndRight(offset);//同时改变left和right
view.offsetTopAndBottom(offset);//同时改变top和bottom
- 改变scrollX和scrollY来移动,但是可以移动所有的子View:
scrollTo(x,y);
scrollBy(xOffset,yOffset);
- 改变Canvas绘制的位置来移动View的内容:
canvas.drawBitmap(bitmap, left, top, paint)
- 改变LayoutParams来移动View的内容:
//必须获取父View的LayoutParams
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)getLayoutParams();
layoutParams.leftMargin = getLeft() + dx;
layoutParams.topMargin = getTop() + dy;
setLayoutParams(layoutParams);
View核心事件
view移动事件处理两大核心回调接口
- onInterceptTouchEvent:处理事件的分发机制
- onTouchEvent:处理滑动事件响应
View移动案例实现
public class DrawerLayout extends RelativeLayout {
public DrawerLayout(@NonNull Context context) {
super(context);
init(context);
}
public DrawerLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public DrawerLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
mMinHeight = dip2px(MIN_HEIGHT);
mTouchScope = dip2px(TOUCH_SCOPE);
}
private int dip2px(float dpValue) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
private View mDragView;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
int childCount = getChildCount();
if (childCount > 0) {
mDragView = getChildAt(0);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int interval = (int) (event.getY() - mDragView.getY());
if (interval > 0 && interval < mTouchScope) {
mTouchState = TOUCH_STATE_SCROLLING;
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (mTouchState == TOUCH_STATE_SCROLLING) {
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
break;
}
return false;
}
private static final int MIN_HEIGHT = 180; //DP 最小高度
private static final int TOUCH_SCOPE = 50; //DP 触摸区域
private int mMinHeight = 200;
private int mTouchScope = 150;
private float mDownY;
private float mLastY;
private int mTouchState = TOUCH_STATE_REST;
private static final int TOUCH_STATE_REST = 0x01;
private static final int TOUCH_STATE_SCROLLING = 0x02;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownY = event.getY();
mLastY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float curMove = event.getY() - mLastY;
if (mTouchState == TOUCH_STATE_SCROLLING) {
ViewGroup.LayoutParams layoutParams = mDragView.getLayoutParams();
int height = (int) (layoutParams.height - curMove);
if (height >= mMinHeight) {
layoutParams.height = height;
mDragView.setLayoutParams(layoutParams);
}
}
mLastY = event.getY();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
break;
}
return mTouchState == TOUCH_STATE_SCROLLING;
}
}