自定义侧拉控件

/*自定义侧栏控件,实现QQ5.0UI效果,更具扩展性/
package com.woojn.draglayoutdemo.view;

import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
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;

/**
* Created by woojn on 2016/11/8.
*/

public class DragLayout extends FrameLayout {

private ViewDragHelper mViewDragHelper;
private View mMenuView;
private View mMainView;
private int mMeasuredWidth;
private int mMeasuredHeight;
private int mRange;

public DragLayout(Context context) {
    this(context, null);
}

public DragLayout(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

//第四步.处理viewDragHelper回调方法

private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
    // (1) 根据返回值决定子控件是否可以拖动,返回true则可以拖动
    // child: 被拖动的子控件
    @Override
    public boolean tryCaptureView(View child, int pointerId) {
        return true;
    }

    // (2) 设置(固定住)子控件将要显示的位置
    // clamp: 固定住
    // left: 被拖动控件将要显示的位置
    // dx: 位置的偏移量: dx = left - 当前的left

    @Override
    public int clampViewPositionHorizontal(View child, int left, int dx) {

// int currentLeft = child.getLeft();
// System.out.println(“========currentLeft:”+currentLeft);
//限定主界面的滑动范围
if (child == mMainView) {
left = fixLeft(left);
}
return left;
}

    // (3) 返回水平方向拖动的最大范围
    // 注意:实际不会真正地限制滑动的范围
    // a. 如果要移动应该返回大于0的值
    // b. 内部会根据此返回值计算动画执行的时长
    // Range: 范围

    @Override
    public int getViewHorizontalDragRange(View child) {
        return mRange;//如果主界面设置了可点击,返回0就不可拖了
    }

    // (4) 位置发生改变时回调
    // [关联子控件的滑动,事件监听(打开,关闭,拖动),伴随动画]

    @Override
    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
        // (a)关联子控件的滑动
        // 当滑动菜单时,同时滑动主界面,菜单滑动了多少(dx),主界面也滑动多少(dx)
        if (changedView == mMenuView) {
            //保持菜单界面不变
            mMenuView.layout(0, 0, mMeasuredWidth, mMeasuredHeight);
            //主界面新的位置
            int newLeft = mMainView.getLeft() + dx;
            newLeft = fixLeft(newLeft);
            mMainView.layout(newLeft, 0, mMeasuredWidth + newLeft, mMeasuredHeight);
        }

        // (b)事件监听(打开,关闭,拖动)
        listenDragStatus();

        // (c)伴随动画
        animateChildren();
    }

    // (5) 拖动结束松开手时回调此方法
    // releasedChild: 当前被拖动的子控件
    // xvel: 释放时水平方向的速度, 往右甩动为正值
    // Released: 释放
    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) {
        if(xvel > 0){
            open();
        }else if(xvel==0&&mMainView.getLeft()>mRange/2){//释放且偏移量大于一半
            open();
        }else {
            close();
        }
    }
};

/**打开侧拉菜单*/
protected void open(){
    // 第一步:平滑地移动到[finalLeft, finalTop]的位置
    mViewDragHelper.smoothSlideViewTo(mMainView,mRange,0);
    // 刷新界面 invalidate()--> onDraw() --> computeScroll()
    ViewCompat.postInvalidateOnAnimation(this);
}

/**关闭侧拉菜单*/
protected void close(){
    // 第一步:平滑地移动到[finalLeft, finalTop]的位置
    mViewDragHelper.smoothSlideViewTo(mMainView,0,0);
    // 刷新界面 invalidate()--> onDraw() --> computeScroll()
    ViewCompat.postInvalidateOnAnimation(this);
}

// 此方法会多次调用
@Override
public void computeScroll() {
    super.computeScroll();
    // 返回true表示动画没有结束,需要继续刷新
    if(mViewDragHelper.continueSettling(true)){
        ViewCompat.postInvalidateOnAnimation(this);
    }
}

private void animateChildren() {
    float percent = ((float) mMainView.getLeft())/mRange;
    //1.主界面的的缩放
    mMainView.setScaleX(valuate(1.0f,0.8f,percent));
    mMainView.setScaleY(valuate(1.0f,0.8f,percent));

    //2.菜单界面的缩放和平移和透明度
    mMenuView.setScaleX(valuate(0.5f,1.0f,percent));
    mMenuView.setScaleY(valuate(0.5f,1.0f,percent));
    mMenuView.setTranslationX(valuate(-mRange,0,percent));
    mMenuView.setAlpha(valuate(0.5f,1.0f,percent));
    //3.背景图片,亮度变化
    Drawable background = getBackground();
    if(background!=null){
        //过渡颜色
        int color = (int) new ArgbEvaluator().evaluate(percent, Color.BLACK, Color.TRANSPARENT);
        background.setColorFilter(color, PorterDuff.Mode.SRC_OVER);
    }
}

/**
 * 估值器: 根据开始值,结束值和百分比,计算变化的值
 */
public float valuate(float start, float end, float percent){
    return start + (end - start)*percent;
}

private void listenDragStatus() {
    int left = mMainView.getLeft();
    if (left == 0) {//关闭
        currentStatus = DragStatus.COLSE;

    } else if (left == mRange) {//打开
        currentStatus = DragStatus.OPEN;

    } else {//正在拖动
        currentStatus = DragStatus.DRAGGING;

    }
    //当事件状态改变时,调用监听器
    if (mOnDragListener != null) {
        if (currentStatus == DragStatus.OPEN) {
            mOnDragListener.onOpen();
        } else if (currentStatus == DragStatus.COLSE) {
            mOnDragListener.onClose();
        } else {
            float percent = ((float)mMainView.getLeft())/mRange;//拉动的距离比最大拖动距离
            mOnDragListener.onDragingg(percent);
        }
    }
}

public enum DragStatus {
    OPEN, COLSE, DRAGGING
}

private DragStatus currentStatus = DragStatus.COLSE;

//提供返回当前状态的方法
public DragStatus getCurrentStatus() {
    return currentStatus;
}

//定义接口,当状态变化时通知外面
public interface OnDragListener {
    void onOpen();

    void onClose();

    void onDragingg(float percent);
}

private OnDragListener mOnDragListener;

//提供设置监听的方法
public void setOnDragListener(OnDragListener onDragListener) {
    mOnDragListener = onDragListener;
}

private int fixLeft(int left) {
    if (left < 0) {
        left = 0;
    } else if (left > mRange) {
        left = mRange;
    }
    return left;
}


//第一步.获取viewDragHelper对象
private void init() {
    mViewDragHelper = ViewDragHelper.create(this, mCallback);
}

//填充结束后获取子控件, 注意:在此方法中还获取不到控件的宽高
@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    if (getChildCount() < 2) {//健壮性判断
        //非法状态异常
        throw new IllegalStateException("DragLayout至少需要两个子控件");
    }
    mMenuView = getChildAt(0);
    mMainView = getChildAt(1);
}

//第二步.决定viewDraghelper是否拦截事件

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

    return mViewDragHelper.shouldInterceptTouchEvent(ev);
}

//第三步.触摸事件传递给viewDragHelper

@Override
public boolean onTouchEvent(MotionEvent event) {
    //处理触摸事件
    mViewDragHelper.processTouchEvent(event);
    //当按下时要返回true才能接收后续move和up事件
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        return true;
    }
    return super.onTouchEvent(event);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //获取dragLayout的宽高

// int measuredWidth = getMeasuredWidth();
// int measuredHeight = getMeasuredHeight();
}

// 控件尺寸发生变化时回调, 执行此方法时已经调用了onMeasure
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mMeasuredWidth = getMeasuredWidth();
    mMeasuredHeight = getMeasuredHeight();
    mRange = (int) (mMeasuredWidth * 0.5f);
}

//对外提供判断打开关闭状态
public boolean isOpen(){
    return currentStatus == DragStatus.OPEN;
}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值