shenliangshanghai / ViewDrag

仿高德地图底部拖拽控件

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ViewDrag

仿高德地图底部拖拽控件

效果图

图1 图2

相关知识

1、相关知识点涉及

  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

2、ViewGroup的子view实例化在onFinshInflate 中

   构造方法----->onFinshInflate()------>onMeasure()------>onLayout()

3、onMeasure的注意点

3.1、对于ViewGroup自身来说主要方法有:
           int  widthMode=MeasureSpec.getMode(widthMeasureSpec);
           int  widthSize=MeasureSpec.getSize(widthMeasureSpec);
  • 根据不同的Mode得到、设置不同size
  • 需要用到setMeasureDimension(widthSize,heightSize);
  • atMost模式需要注意,如果不处理atMost这种模式的话,即使你设置Warp_content,也是跟Math_parent是一个效果
3.2、对于ChildView来说
  • 需要调用measureChild 或者measureChildren对子view 进行测量
  • 不直接使用measureChildren处理,使用measureChild 对每个子view 单独进行测量的话
  • 根据LayoutParams得到mode,size,然后使用int measureSpec=MeasureSpec.makeSpec(size,mode)生成对应的测量工具

4、onLayout注意点

  • 摆放位置可能涉及到Margin等其他参数
4.1、如何获取Margin 值,这里需要使用到LayoutParams,但是继承ViewGroup情况下的VIewGroup.LayoutParams,只能得到width、height,无法得到Margin值,这里就涉及到定义当前LayoutParams 的类型
4.2、自定义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);
                 }

5、自定义属性

  • 两种
5.1、获取android自带属性
      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);
5.2、获取自定义属性
      TypedArray ta=context.obtainStyleAttributes(attributeSet,R.styleable.属性数组名);
      float attr1=ta.getFloat(int index(R.styleable.属性数组名_属性名),float defaultValue);

6、ViewDragHelper的使用

6.1、实例化
ViewDragHelper viewDragHelper  =   ViewDragHelper.create(ViewGroup parent,ViewDragHelper.Callback callback);
6.2、与事件分发相关的
6.2.1、public boolean shouldInterceptTouchEvent(MotionEvent ev); 是否拦截事件,在无事件冲突的情况下,直接使用
public boolean onInterceptTouchEvent(MotionEvent ev){
       return viewDragHelper.shouldInterceptTouchEvent(ev);
}
6.2.2、public void processTouchEvent(MotionEvent ev); 代替TouchEvent事件中的处理逻辑,内部调用Callback中的方法
public boolean TouchEvent(MotionEvent ev){
      viewDragHelper.processEvent(ev);
      return true;
}
6.3、ViewDragHelper.Callback相关
6.3.1、public boolean tryCaptureView(View child,int pointerId);根据手指触碰的位置处的View判断是否拦截事件,一般可写为
public boolean tryCaptureView(View child,int pointerId){
       return  child==触发拦截事件的view
}
6.3.2、public int clampViewPositionHorizontal(View view,int left,int dx)
  • 返回手指拖动过程中,left的位置,手指一旦离开屏幕将不再调用该方法
6.3.3、public int clampViewPositionVertical(View view,int top,int dy)
  • 返回手指拖动过程中,top的位置,手指一旦离开屏幕将不再调用该方法
6.3.4、public int getViewHorizontalDragRange(View child)
  • 返回横向滑动最大距离差,该值与shouldInterceptTouchEvent返回结果有关,该值不大于TouchSlop(最小滑动距离)的话,shouldInterceptTouchEvent()一直为false
6.3.5、public int getViewHorizontalDragRange(View child)
  • 返回竖向滑动最大距离差,该值与shouldInterceptTouchEvent返回结果有关,该值不大于TouchSlop(最小滑动距离)的话,shouldInterceptTouchEvent()一直为false
6.3.6、public void onViewRelease(View releaseChild,float xVel,float yVel);
  • 手指放开时候回调,用于处理惯性滑动
6.3.7、public void onViewPositionChanged(View changedView, int left,int top,float dx,float dy);
  • 整个滑动过程(包括惯性滑动)位置改变都会调用
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
6.3.9、public void settleCapturedViewAt(int finalLeft,int finalTop)
  • 与3.8类似,但是指定了CapturedView
6.4、shouldInterceptTouchEvent伪代码
public boolean shouldInterceptTouchEvent(MotionEvent ev){
      case MotionEvent.ACTION_DOWN:
          初始化些参数,这里StateIDLE
        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;
      }
6.5、processTouchEvent伪代码
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;
}

7、几个坐标相关属性之间的关系

7.1、MotionEvent事件中:
       rawx,rawy:相对于屏幕坐标点
       x,y:相对于当前控件的坐标
7.2、 View位置定位中:
       x,y 是View左上角在父布局中的坐标
       left ,top,right,bottom:分别是左边,上边,右边,底部距离x轴,y轴的距离
       translateX/Y:左上角相对于父布局的偏移量

       x=left+translateX;
       y=top+translateY;

       scrollX,scrollY:内容的偏移,与left,x都没有关系,注意:正负号方向与坐标系方向相反

About

仿高德地图底部拖拽控件


Languages

Language:Java 100.0%