仿高德地图底部拖拽控件
1.1、自定义view流程onMeasure,onLayout
1.2、自定义属性
1.3、ViewDragHepler类的使用(重点)
1.4、事件分发
1.5、自定义设置LayoutParams类型
1.6、几个坐标相关属性之间的关系(x,y)、(rawX、rawY)、translateX translateY、left top right bottom、scrollX scrollY
构造方法----->onFinshInflate()------>onMeasure()------>onLayout()
int widthMode=MeasureSpec.getMode(widthMeasureSpec);
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
- 根据不同的Mode得到、设置不同size
- 需要用到setMeasureDimension(widthSize,heightSize);
- atMost模式需要注意,如果不处理atMost这种模式的话,即使你设置Warp_content,也是跟Math_parent是一个效果
- 需要调用measureChild 或者measureChildren对子view 进行测量
- 不直接使用measureChildren处理,使用measureChild 对每个子view 单独进行测量的话
- 根据LayoutParams得到mode,size,然后使用
int measureSpec=MeasureSpec.makeSpec(size,mode)
生成对应的测量工具
- 摆放位置可能涉及到Margin等其他参数
4.1、如何获取Margin 值,这里需要使用到LayoutParams,但是继承ViewGroup情况下的VIewGroup.LayoutParams,只能得到width、height,无法得到Margin值,这里就涉及到定义当前LayoutParams 的类型
protected LayoutParams generateDefaultLayoutParams(){
return new MarginLayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
}
protect LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp){
return new MarginLayoutParams(lp);
}
public LayoutParams generateLayoutParams(AttributeSet attr){
return new MarginLayoutParams(getContext(),attr);
}
- 两种
int attr[] =new int[]{android.R.attr.gravity,android.R.attr.layout_weight};
TypedArray ta=context.obtainStyleAttributes(attributeSet,attr);
float attr1=ta.getFloat(int index(数组下标),float defaultValue);
TypedArray ta=context.obtainStyleAttributes(attributeSet,R.styleable.属性数组名);
float attr1=ta.getFloat(int index(R.styleable.属性数组名_属性名),float defaultValue);
ViewDragHelper viewDragHelper = ViewDragHelper.create(ViewGroup parent,ViewDragHelper.Callback callback);
public boolean onInterceptTouchEvent(MotionEvent ev){
return viewDragHelper.shouldInterceptTouchEvent(ev);
}
public boolean TouchEvent(MotionEvent ev){
viewDragHelper.processEvent(ev);
return true;
}
public boolean tryCaptureView(View child,int pointerId){
return child==触发拦截事件的view
}
- 返回手指拖动过程中,left的位置,手指一旦离开屏幕将不再调用该方法
- 返回手指拖动过程中,top的位置,手指一旦离开屏幕将不再调用该方法
- 返回横向滑动最大距离差,该值与
shouldInterceptTouchEvent
返回结果有关,该值不大于TouchSlop(最小滑动距离)的话,shouldInterceptTouchEvent()
一直为false
- 返回竖向滑动最大距离差,该值与shouldInterceptTouchEvent返回结果有关,该值不大于TouchSlop(最小滑动距离)的话,shouldInterceptTouchEvent()一直为false
- 手指放开时候回调,用于处理惯性滑动
- 整个滑动过程(包括惯性滑动)位置改变都会调用
6.3.8、public void smoothSlidViewTo(View child,int finalLeft, int finalTop);一般用在onViewRelease方法中指定某个View滑动到某个位置,这是一个不断刷新的过程
public void computeScroll(){
if(viewDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
}
- continueSettling会一直回调mCallback.onViewPositionChanged
- 与3.8类似,但是指定了CapturedView
public boolean shouldInterceptTouchEvent(MotionEvent ev){
case MotionEvent.ACTION_DOWN:
初始化一些参数,这里State为IDLE
break;
case MotionEvent.ACTION_MOVE:
boolean checkTouchSlop=(mCallback.getViewVerticalDragRange(child) > 0 &&Math.abs(dy)>mTouchSlop)||
(mCallback.getViewHorizontalDragRange(child) > 0 &&Math.abs(dx)>mTouchSlop);
if(toCapture != null && checkTouchSlop&&mCallback.tryCaptureView()){
setDragState(STATE_DRAGGING);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
setDragState(STATE_IDLE);
break
return mDragState==STATE_DRAGGING;
}
public void processTouchEvent(MotionEvent ev){
case MotionEvent.ACTION_DOWN:
if(mCallback.tryCaptureView()){
setDragState(STATE_DRAGGING);
}
break;
case MotionEvent.ACTION_MOVE:
if (mDragState == STATE_DRAGGING) {
if (dx != 0) {
clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx);
ViewCompat.offsetLeftAndRight(mCapturedView, clampedX - oldLeft);
}
if (dy != 0) {
clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy);
ViewCompat.offsetTopAndBottom(mCapturedView, clampedY - oldTop);
}
if (dx != 0 || dy != 0) {
final int clampedDx = clampedX - oldLeft;
final int clampedDy = clampedY - oldTop;
mCallback.onViewPositionChanged(mCapturedView, clampedX, clampedY,clampedDx, clampedDy);
}
}
break;
case MotionEvent.ACTION_UP:
if (mDragState == STATE_DRAGGING) {
mCallback.onViewReleased(mCapturedView, xvel, yvel);
setDragState(STATE_IDLE);
}
break;
}
rawx,rawy:相对于屏幕坐标点
x,y:相对于当前控件的坐标
x,y 是View左上角在父布局中的坐标
left ,top,right,bottom:分别是左边,上边,右边,底部距离x轴,y轴的距离
translateX/Y:左上角相对于父布局的偏移量
x=left+translateX;
y=top+translateY;
scrollX,scrollY:内容的偏移,与left,x都没有关系,注意:正负号方向与坐标系方向相反