Android Notes (ListView Scroller)

ListView使用技巧

  • 优化技巧
    • ViewHolder-ListView 视图缓存机制
    • 设置分割线divider=”@null”透明
    • 取消点击效果,listSelector
    • smoothScrollBy/ToPosition()
    • 处理空ListView,listView.setEmptyView(ImageView)
    • 滑动监听,onTouchListener,onScrollListener,GestureDetector手势识别,VelocityTracker滑动速度检测
  • 拓展

    • 滚动到顶部,底部时有个弹性距离可以继续滑动,松开后回弹

      1
      2
      3
      4
      5
      6
      7
      8
      9
      private void init(){
      mMaxOverDistance= (int) (getResources().getDisplayMetrics().density*mMaxOverDistance);
      }

      @Override
      protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
      Log.e("overscroll","by "+mMaxOverDistance);
      return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxOverDistance, isTouchEvent);
      }
  • 聊天ListView

    • getItemViewType(position)-> mData.get(position).getType()
    • getViewTypeCount()->2
    • getView()区分type,实例化不同布局添加内容
  • listview选中未选中状态: 在getView()中判断,点击选中的item的position和当前position是否相同,添加不同的布局

Android Scroll

滑动效果是触摸事件不断改变View的坐标,移动位置,来达到滑动效果

  • 触摸事件类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 //单点触摸按下
public static final int ACTION_DOWN= 0;
//单点触摸离开
public static final int ACTION_UP= 1;
//触摸点移动
public static final int ACTION_MOVE= 2;
//触摸动作取消
public static final int ACTION_CANCEL= 3;
//触摸动作超出边界
public static final int ACTION_OUTSIDE= 4;
//多点触摸按下
public static final int ACTION_POINTER_DOWN= 5;
//多点触摸离开
public static final int ACTION_POINTER_UP= 6;

int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
break;
}
  • 实项滑动的七种方法

    • layout()

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      相对于视图坐标系的获取坐标getX,getY,而lastX,lastY不需要重新设置坐标,因为相对于视图坐标,点击屏幕获取到的坐标会重新设置
      对于Android屏幕坐标系,getRawX,getRawY,lastRawX,lastRawY需要重新设置,获取准确的偏移量,第一次坐标位置getRawX,getRawY在(10,10),在移动到(20,20),偏移量为10,此时不重新设置,下个点是(50,50),偏移量是50-10=40,而实际正确的偏移量为50-20=30
      @Override
      public boolean onTouchEvent(MotionEvent event) {
      int x = (int) event.getX();
      int y = (int) event.getY();
      int rx = (int) event.getRawX();
      int ry = (int) event.getRawY();
      switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
      lastX = x;
      lastY = y;
      lastrX = rx;
      lastrY = ry;
      break;
      case MotionEvent.ACTION_MOVE:
      int dx = x - lastX;
      int dy = y - lastY;
      int rdx = rx - lastrX;
      int rdy = ry - lastrY;
      Log.e("drag", "x=" + x + " y=" + y
      + " lastX=" + lastX + " lastY=" + lastY);
      Log.e("drag", "rx=" + rx + " ry=" + ry
      + " lastrX=" + lastrX + " lastrY=" + lastrY);
      Log.e("drag", "dx=" + dx + " dy=" + dy
      + " rdx=" + rdx + " rdy=" + rdy);
      layout(getLeft() + dx, getTop() + dy,
      getRight() + dx, getBottom() + dy);
      // lastrX = x;
      // lastrY = y;
      break;
      case MotionEvent.ACTION_UP:
      break;
      }
      return true;
      }
    • offsetLeftAndrRight(),offsetTopAndBottom()

      1
      2
      offsetLeftAndRight(dx);
      offsetTopAndBottom(dy);
    • LayoutParams(),ViewGroup.MarginLayoutParams

      1
      2
      3
      4
      5
      6
      7
      8
      9
      FrameLayout.LayoutParams layoutParams= (FrameLayout.LayoutParams) getLayoutParams();
      layoutParams.leftMargin=getLeft()+dx;
      layoutParams.topMargin=getTop()+dy;
      setLayoutParams(layoutParams);

      ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
      layoutParams.leftMargin = getLeft() + dx;
      layoutParams.topMargin = getTop() + dy;
      setLayoutParams(layoutParams);
    • ScrollTo(x,y)/ScrollBy(dx,dy)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
       ViewGroup 移动的是子View
      View移动的是内容

      //移动的是屏幕下方画布,会造成内容向相反方向移动
      //scrollBy(dx,dy);
      //所以移动父视图,并且值为负,相反方向scrollby才有正确的效果
      case MotionEvent.ACTION_DOWN:
      lastX = x;
      lastY = y;
      if (!isScroll) {
      lastrX = rx;
      lastrY = ry;
      }
      break;
      case MotionEvent.ACTION_MOVE:
      int dx = x - lastX;
      int dy = y - lastY;
      //移动的是屏幕下方画布,会造成内容向相反方向移动
      // scrollBy(dx,dy);
      //((View) getParent()).scrollBy(-dx, -dy);
      ((View) getParent()).scrollTo(-(int) event.getRawX() + lastrX, -(int) event.getRawY() + lastrY);
      isScroll = true;
      break;
  • Scroller 实现平移滑动

    • 初始化Scroller

      1
      mScroller=new Scroller(context);
    • 重写computeScroll()模拟滑动

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
       @Override
      public void computeScroll() {
      super.computeScroll();
      //判断Scroll是否执行完毕,true 没有执行完
      if(mScroller.computeScrollOffset()){
      ((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
      //只能通过invalidate()—>draw()->computeScroll()间接调用computeScroll(),
      //结束后computeScrollOffset()返回false,中断循环
      postInvalidate();
      }
      }
    • startScroll 开始模拟滑动

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      @Override
      public boolean onTouchEvent(MotionEvent event) {
      int x = (int) event.getX();
      int y = (int) event.getY();
      int rx = (int) event.getRawX();
      int ry = (int) event.getRawY();
      switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
      lastX = x;
      lastY = y;
      lastRx = rx;
      lastRy = ry;
      break;
      case MotionEvent.ACTION_MOVE:
      ((View) getParent()).scrollTo(-rx + lastRx, -ry + lastRy);
      break;
      case MotionEvent.ACTION_UP:
      View parent = (View) getParent();
      //模拟滑动,起始坐标和偏移量,滑动偏移为滑动距离的负数,相反方向滑动
      mScroller.startScroll(parent.getScrollX(), parent.getScrollY(),
      -parent.getScrollX(), -parent.getScrollY());
      //通知重绘
      invalidate();
      break;
      }
      return true;
      }
  • ViewDragHelper实项Drawlayout菜单侧滑 youtubelayout

    • 回调

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
        private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
      /**重写tryCaptureView,何时开始检测触摸事件,
      * 当前触摸的是MainView时开始检测*/
      @Override
      public boolean tryCaptureView(View child, int pointerId) {
      return mMainView == child;
      }

      /**水平垂直方向上的滑动,默认返回0,不滑动*/
      @Override
      public int clampViewPositionHorizontal(View child, int left, int dx) {
      // return super.clampViewPositionHorizontal(child, left, dx);
      return left;
      }

      @Override
      public int clampViewPositionVertical(View child, int top, int dy) {
      // return super.clampViewPositionVertical(child, top, dy);
      return top;
      }

      /**拖动结束后调用,自动滑动打开或关闭菜单*/
      @Override
      public void onViewReleased(View releasedChild, float xvel, float yvel) {
      super.onViewReleased(releasedChild, xvel, yvel);
      if (mMainView.getTop() > mHeight / 2) {
      mViewDragHelper.smoothSlideViewTo(mMainView, 0, (int) (mHeight * 1.2f));
      } else {
      if (mMainView.getLeft() < mWidth / 2) {
      //关闭菜单
      mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
      } else {
      mViewDragHelper.smoothSlideViewTo(mMainView, (int) (mWidth * 1.2f), 0);
      }
      }
      ViewCompat.postInvalidateOnAnimation(ViewDragHelperView.this);
      }

      /**更改scale进行缩放*/
      @Override
      public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
      super.onViewPositionChanged(changedView, left, top, dx, dy);
      }

      /**状态改变*/
      @Override
      public void onViewDragStateChanged(int state) {
      super.onViewDragStateChanged(state);
      }

      /**触摸后回调*/
      @Override
      public void onViewCaptured(View capturedChild, int activePointerId) {
      super.onViewCaptured(capturedChild, activePointerId);
      }
      };
    • 获取宽度

      1
      2
      3
      4
      5
      6
      @Override
      protected void onSizeChanged(int w, int h, int oldw, int oldh) {
      super.onSizeChanged(w, h, oldw, oldh);
      mWidth = mMenuView.getMeasuredWidth();
      mHeight = mMenuView.getMeasuredHeight();
      }
    • 获取MenuView,MainView

      1
      2
      3
      4
      5
      6
      @Override
      protected void onFinishInflate() {
      super.onFinishInflate();
      mMenuView = getChildAt(0);
      mMainView = getChildAt(1);
      }
  • 触摸事件拦截,并传递给VieDragHelper

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 触摸事件传递给ViewDragHelper,必须写
    */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
    mViewDragHelper.processTouchEvent(event);
    return true;
    }

    /**
    * 重写事件拦截方法,把事件传递给ViewDragHelper
    */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }
  • 重写computeScroll()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /**
    * 平滑移动
    */
    @Override
    public void computeScroll() {
    super.computeScroll();
    if (mViewDragHelper.continueSettling(true)) {
    ViewCompat.postInvalidateOnAnimation(this);
    }
    }
willkernel wechat
关注微信公众号
帅哥美女们,请赐予我力量吧!