/*自定义侧栏控件,实现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;
}
}