Android 侧栏A-Z的快速滑动搜索(二)

在前面的文章中已经介绍了侧栏字母#A-Z以及搜索框的实现。
这里写图片描述
这次主要是来实现以下侧滑的功能,侧滑也是很多应用里面都有的,我们所熟悉的QQ里面的消息条目就是使用的侧滑功能。下面我们就来说说侧滑功能的实现吧。
侧滑删除当然也是离不了自定义控件的而且还要有滑动的动画出现,所以我们会使用到ViewDragHelper,使用是需要以下几个步骤

 1.使用静态方法来构ViewDragHelper,需要传入一个ViewDragHelper.Callback对象.
 这个一般就是在我们初始化的时候使用静态方法构造ViewDragHelper,其中需要传入一个ViewDragHelper.Callback回调对象.
 2.重写onInterceptTouchEvent和onTouchEvent回调ViewDragHelper中对应方法.
 3.在ViewDragHelper.Callback中对视图做操作.
 4.使用ViewDragHelper.smoothSlideViewTo()方法平滑滚动.
 5.自定义一些交互逻辑的自由实现.

“`
package lyx.robert.quicksearch.view;

import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;

import lyx.robert.quicksearch.utils.SwipeManager;

public class SwipeLayout extends FrameLayout {

private View contentView;// 默认显示内容的view
private View slipView;// 侧滑控件的view
private int slipHeight;//  侧滑控件的高度
private int slipWidth;// 侧滑控件的宽度
private int contentWidth;// 默认显示内容的宽度
private ViewDragHelper mDrag;

public SwipeLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
}

public SwipeLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public SwipeLayout(Context context) {
    super(context);
    init();
}

enum SwipeState{
    Open,Close;
}

private SwipeState currentState = SwipeState.Close;//默认是关闭状态

private void init() {
    //使用静态方法构造ViewDragHelper,其中需要传入一个ViewDragHelper.Callback回调对象.
    mDrag = ViewDragHelper.create(this, callback);
}

@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    contentView = getChildAt(0);
    slipView = getChildAt(1);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    slipHeight = slipView.getMeasuredHeight();
    slipWidth = slipView.getMeasuredWidth();
    contentWidth = contentView.getMeasuredWidth();
}

@Override
protected void onLayout(boolean changed, int left, int top, int right,
        int bottom) {
    contentView.layout(0, 0, contentWidth, slipHeight);
    slipView.layout(contentView.getRight(), 0, contentView.getRight()
            + slipWidth, slipHeight);
}
//重写此方法回调ViewDragHelper中对应的方法
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    boolean result = mDrag.shouldInterceptTouchEvent(ev);

    //如果当前有打开的,则需要直接拦截,交给onTouch处理
    if(!SwipeManager.getInstance().isSwipe(this)){
        //先关闭已经打开的layout
        SwipeManager.getInstance().closeCurrentLayout();

        result = true;
    }

    return result;
}

private float downX,downY;
//重写此方法回调ViewDragHelper中对应的方法.
@Override
public boolean onTouchEvent(MotionEvent event) {
    //如果当前有打开的,调用requestDisallowInterceptTouchEvent进行拦截,触摸事件的逻辑将不会执行
    if(!SwipeManager.getInstance().isSwipe(this)){
        requestDisallowInterceptTouchEvent(true);
        return true;
    }

    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        downX = event.getX();
        downY = event.getY();
        break;
    case MotionEvent.ACTION_MOVE:
        //1.获取x和y方向移动的距离
        float moveX = event.getX();
        float moveY = event.getY();
        float delatX = moveX - downX;//x方向移动的距离
        float delatY = moveY - downY;//y方向移动的距离
        if(Math.abs(delatX)>Math.abs(delatY)){
            //表示移动是偏向于水平方向,那么应该SwipeLayout应该处理,请求listview不要拦截
            requestDisallowInterceptTouchEvent(true);
        }
        //更新downX,downY
        downX = moveX;
        downY = moveY;
        break;
    case MotionEvent.ACTION_UP:

        break;
    }
    mDrag.processTouchEvent(event);
    return true;
}

private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
    //返回值就是当前捕捉到的也就是你当前拖拽的某个子View
    @Override
    public boolean tryCaptureView(View child, int pointerId) {
        return child==contentView||child==slipView;
    }
    //返回值是可以水平拖拽的范围
    @Override
    public int getViewHorizontalDragRange(View child) {
        return slipWidth;
    }
    //手指触摸移动时实时回调, left表示要移动到的x位置,dx表示移动的距离
    @Override
    public int clampViewPositionHorizontal(View child, int left, int dx) {
        if(child==contentView){
            if(left>0)left = 0;
            if(left<-slipWidth)left = -slipWidth;
        }else if (child==slipView) {
            if(left>contentWidth)left = contentWidth;
            if(left<(contentWidth-slipWidth))left = contentWidth-slipWidth;
        }
        return left;
    }
    //拖拽的子View完全挪出屏幕则防止过度绘制
    @Override
    public void onViewPositionChanged(View changedView, int left, int top,
            int dx, int dy) {
        super.onViewPositionChanged(changedView, left, top, dx, dy);
        if(changedView==contentView){
            //手动移动slipView
            slipView.layout(slipView.getLeft()+dx,slipView.getTop()+dy,
                    slipView.getRight()+dx, slipView.getBottom()+dy);
        }else if (slipView==changedView) {
            //手动移动contentView
            contentView.layout(contentView.getLeft()+dx,contentView.getTop()+dy,
                    contentView.getRight()+dx, contentView.getBottom()+dy);
        }

        //判断开和关闭的逻辑
        if(contentView.getLeft()==0 && currentState!=SwipeState.Close){
            //说明应该将state更改为关闭
            currentState = SwipeState.Close;

            //回调接口关闭的方法
            if(swipeListener!=null){
                swipeListener.onClose(getTag());
            }

            //说明当前的SwipeLayout已经关闭,需要让Manager清空一下
            SwipeManager.getInstance().clearCurrentLayout();
        }else if (contentView.getLeft()==-slipWidth && currentState!=SwipeState.Open) {
            //说明应该将state更改为开
            currentState = SwipeState.Open;

            //回调接口打开的方法
            if(swipeListener!=null){
                swipeListener.onOpen(getTag());
            }
            //当前的Swipelayout已经打开,需要让Manager记录一下下
            SwipeManager.getInstance().setSwipeLayout(SwipeLayout.this);
        }
    }
    //手指释放时回调此方法
    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) {
        super.onViewReleased(releasedChild, xvel, yvel);
        if(contentView.getLeft()<-slipWidth/2){
            //如果滑动的内容大于侧滑内容的一半应当打开
            open();
        }else {
            //否则就是关闭
            close();
        }
    }
};
/**
 * 打开的方法
 */
public void open() {
    mDrag.smoothSlideViewTo(contentView,-slipWidth,contentView.getTop());
    ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
}
/**
 * 关闭的方法
 */
public void close() {
    mDrag.smoothSlideViewTo(contentView,0,contentView.getTop());
    ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
}
public void computeScroll() {
    if(mDrag.continueSettling(true)){
        ViewCompat.postInvalidateOnAnimation(this);
    }
}

private SwipeListener swipeListener;
public void setOnSwipeListener(SwipeListener listener){
    this.swipeListener = listener;
}

public interface SwipeListener{
    void onOpen(Object obj);
    void onClose(Object obj);
}

}
回true(因为ACTION_DOWN时如果子View没有消费事件,我们需要在onTouchEvent()中返回true,否则收不到后续的事件,从而不会产生拖动等效果)。
在代码中我们会发现requestDisallowInterceptTouchEvent(true),他的作用是什么呢?Android事件机制是从父View传向子View的,可以去检测你当前子View是不是在有可滑动控件等,决定事件是否拦截,但是这个麻烦,而且并不能解决所有的问题(必须检测触摸点是否在这个控件上面),其实有比较简单的方法,在你嵌套的控件中注入ViewPager实例(调用控件的getParent()方法),然后在onTouchEvent,onInterceptTouchEvent,dispatchTouchEvent里面告诉父View,也就是ViewPager不要拦截该控件上的触摸事件。调用该方法,一旦底层View收到touch的action后调用这个方法, 那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action。
onFinishInflate()的作用相当于我们一般使用View的流程是在onCreate中使用setContentView来设置要显示Layout文件或直接创建一个View,在当设置了ContentView之后系统会对这个View进行解析,然后回调当前视图View中的onFinishInflate方法。只有解析了这个View我们才能在这个View容器中获取到拥有Id的组件,同样因为系统解析完View之后才会调用onFinishInflate方法,所以我们自定义组件时可以onFinishInflate方法中获取指定子View的引用。
模糊搜索悬浮提示点击前往Android 侧栏A-Z的快速滑动搜索(三)

点击前往csdn下载源码

点击前往GitHub下载源码

帅哥/美女,如果对您有帮助在GitHub上面点下star呗哦!再csdn上面给个好评也行啊!也不枉费我忙活了这些时间了。谢谢!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值