android侧滑删除实现分析

1.事件分发传递
2.scroller 原理
3.TouchEvent 事件
4.scrollTo()

开始时候带着的问题

 1.手指放上触发子view的 touch事件,当手指一动到下一个view的时候触发父veiw。手指不抬起时候。
 * 2.内容如何滚动移动子 item veiw的位置 
 * 3.如何缓存进行复用问题。
 * 4.有时候会同时出来两个删除?
 * 5.为什么会造成卡顿。?
 * 6.需要计算点击的item 和 缓存上一个item 进行状态复原
   7.item ui怎么写?

 * 对滑动内容的管理
 * 1.代码都写在SLiderView是否可行?
 * 2.如果只用move 事件 ,计算差值---如何进行解决呢!
 * 3.子类进行事件消耗和不消耗 区别
 * 4.过滤 水平滑动和垂直滑动的方法
 */

android侧滑删除实现分析_第1张图片

主要讲解 scroller,原理。事件分发机制暂不进行分析

侧滑的Listview和 item的关系:如图(item宽度要比屏幕宽)

android侧滑删除实现分析_第2张图片

看到这个图第一想法就是在手滑动的时候让item中的内容向左滑动,具体滑动距离就是 超出屏幕的部分宽度。

那么如何实现?

有几种方式

1.通过view的onlayou()

2.通过objectAnimator动画

3.scroller 的scrollTo( ) scrollBy().实现。

最简单最直接的方式当然是scroller,也是平时开发中接触较多的一个系统api。

当然选择使用 scroller很重要的一个原因是因为scroller是滑动的内容。而不是包裹的view滑动

当然先来了解一下scrollTo()scrollBy(),也是调用的scrollTo(),上图

引用blog地址: http://www.xuebuyuan.com/2013505.html

android侧滑删除实现分析_第3张图片

直接扫描书本上。有可能会和自己理解的不同。scrollTo(100,0),就是内容想右。(图三)而不是图上的内容向左边。(图二)

其实这个应该和android的x轴 y轴正方向理解不要混了。ScrollTo()是内容从0点 向左移动到100 点位置。
android侧滑删除实现分析_第4张图片

懂了这点。那个getscrollX() 和getscrollY() 就好理解。就是指。内容的滚动距(0,0)点距离

有了scrollto() 和 getscrollX(),这个侧滑基本上可以得完成了。

Scroller 是如何让view进行移动?

StartScroll()这个方法并没有使view进行移动。这是初始化一些参数,

public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}

invalidate(); //触发重绘

然后在ondraw()中会去调用

 @Override
public void computeScroll() {
    super.computeScroll();

}
去判断是否结束滚动。那么只要重写该方法。基本上。View的重复滚动问题就完事了

@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate(); //绘制–
}
}

对就是得上面的这个函数,是系统scroller 从开始位置滚动到终止位置。

读scrollto( ) 源码会发现里面有一个简单时间位移算法。根据当前位置和起始时间终止时间计算当前滚动到的位置。 然后不断进行view重绘。直到滚动到终止位置!

简单介绍一下实现,也是给予上面的滑动原理,进行健壮性的扩展。能符合日常的开发需求

1.自定义
class SilderListViewDemo extends ListView
SliderViewDemo extends LinearLayout

分别重写 sliderView 和 SliderViewDemo 类的,onTouch() 进行监听action_down,action_move ,action_up

这里需要将listview TouchEvent事件传递给 item中自定义SliderView的onTouch 进行操作。

直接贴出来代码和分析的注释部分:

ListView 的onTouch( );

@Override
public boolean onTouchEvent(MotionEvent event) {

    //这里为什么要写在这里,不写在acton_down中,因为这里每次都调用,而action_down只有down时候触发。(算我没说)
    int x = (int) event.getX();
    int y = (int) event.getY();

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mX = x;
            mY = y;
            //根据 x,y 确定点击的那个item
            currentPositon = this.pointToPosition((int) event.getX(), (int) event.getY());
            //未关闭状态进行复原
            if (mPosition != currentPositon) {
                mPosition = currentPositon;
                if (itemViewContent != null) {
                    //恢复上个侧滑的状态
                    itemViewContent.reset();
                }
                isScrolling = false;
            }

            break;
        case MotionEvent.ACTION_UP:
            if (itemViewContent != null) {
                //手指抬起,去判断自定滑动到起点还是终点
                itemViewContent.adjustScroller();
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if (mPosition != -1) {
                float detex = Math.abs(x - mX);
                float deteY = Math.abs(y - mY);

                if (deteY < 30 && detex > 20) {
                    isScrolling = true;
                    //这里本可以通过position拿到 点击的view,因为listview的item的缓存复用问题的,这里我们只需要考虑屏幕内显示的item
                    int firstPositon = getFirstVisiblePosition();
                    int screenPosition = currentPositon - firstPositon;
                    itemViewContent = (SliderViewDemo) getChildAt(screenPosition); 
                    //将事件传递给sliderView去处理
                    itemViewContent.onTouchEvent(event);
                }
            }
            break;
        default:
            break;
    }
    //注意当我们不主动消耗事件,交给系统去分发接下来的事件
    return super.onTouchEvent(event);

}

接下来看sliderView ( LinerLayout ) 中的onTouch代码

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            //这里没有写 down 和 up监听。不是必须可写或者不写。主要逻辑不再这里处理
            case MotionEvent.ACTION_MOVE:

                float x = event.getX();
                float y = event.getY();
                //移动的差值
                float deltaX = x - mLastX;
                float delatY = y - mLastY;
                mLastX = x;
                mLastY = y;
                if (Math.abs(deltaX) < Math.abs(delatY) * 2) {
                    //过滤 y轴时间,否则会导致无法滑动效果
                    break;
                }
                float newScrollX = getScrollX() - deltaX;
                if (newScrollX < 0) {
                    newScrollX = 0;
                } else if (newScrollX > mHolderWidth) { //滑动距离为规定的del删除的距离
                    newScrollX = mHolderWidth;
                }
                this.scrollTo((int) newScrollX, 0);
                break;
            default:
                break;

        }
        return super.onTouchEvent(event); //将事件交给父类。这点很重要。如果子类消耗。父类中onTouch事件就不触发
}

还有写异常判断。手指抬起时候。进行判断。是左滑动还是右滑动。下面是demo链接地址:

百度云盘demo:http://pan.baidu.com/s/1eRKnrOI 有作者和自己写的对比。

你可能感兴趣的:(view自定义)