- 自定义FrameLayout,在布局里面引用,并添加两个LinearLayout作为子view。
- 导入最新support-v4包,使用ViewDragHelper辅助类,构造方法初始化。
ViewDragHelper.create(this, 1.0f, callback);
实例化callback
// 1. 返回值, 决定了child是否可以被拖拽
@Override
public boolean tryCaptureView(View child, int pointerId) {
return false;
}
@Override
public void onViewDragStateChanged(int state) {
super.onViewDragStateChanged(state);
}
3.转交触摸事件给ViewDragHelper。
public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
// 由 ViewDragHelper 判断触摸事件是否该拦截
return mHelper.shouldInterceptTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent event) {
// 由 ViewDragHelper 处理事件
try {
mHelper.processTouchEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
4.在onSizedChange去获取宽高,getMeasuredWidth,getMeasuredHeight,设置拖拽范围。
5.
// 2. 返回拖拽的范围. 返回一个 >0 的值, 决定了动画的执行时长, 水平方向是否可以被滑开
@Override
public int getViewHorizontalDragRange(View child) {
return mRange;
};
// 3. 修正子View水平方向的位置. 此时还没有发生真正的移动.
// 返回值决定了View将会移动到的位置
public int clampViewPositionHorizontal(View child, int left, int dx) {
// child 被拖拽的子View
// left 建议移动到的位置
// dx 跟旧的位置的差值
if(child == mMainContent){
left = fixLeft(left);
}
return left;
}
6.
// 4. 当控件位置变化时 调用, 可以做 : 伴随动画, 状态的更新, 事件的回调.
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
// left最新的水平方向的位置
// dx 刚刚发生的水平变化量
if(changedView == mLeftContent){
// 如果移动的是左面板
// 1. 放回原来的位置
mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight);
// 2. 把左面板发生的变化量dx转递给主面板
int newLeft = mMainContent.getLeft() + dx;
// 修正左边值.
newLeft = fixLeft(newLeft);
mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight);
}
dispatchDragEvent();
// 为了兼容低版本, 手动重绘界面所有内容.
invalidate();
}
7.
//5. 决定了松手之后要做的事情, 结束的动画
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
System.out.println("onViewReleased: xvel: " + xvel);
// releasedChild 被释放的孩子
// xvel 水平方向的速度 向右为+, 向左为-
if(xvel == 0 && mMainContent.getLeft() > mRange * 0.5f){
open();
} else if (xvel > 0) {
open();
} else {
close();
}
}
8.做平滑移动开关动画
if(mHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)){
// 如果当前位置不是指定的最终位置. 返回true
// 需要重绘界面, 一定要传 子View 所在的容器
ViewCompat.postInvalidateOnAnimation(this);
}
//2. 维持动画的继续, 高频率调用.
@Override
public void computeScroll() {
super.computeScroll();
if(mHelper.continueSettling(true)){
// 如果当前位置还没有移动到最终位置. 返回true.需要继续重绘界面
ViewCompat.postInvalidateOnAnimation(this);
}
}
9.拖拽伴随动画
/**
* 分发拖拽事件, 伴随动画,更新状态.
*/
protected void dispatchDragEvent() {
// 0.0 -> 1.0
float percent = mMainContent.getLeft() * 1.0f / mRange;
System.out.println("percent: " + percent);
if(onDragChangeListener != null){
onDragChangeListener.onDraging(percent);
}
// 更新状态
Status lastStatus = status;
status = updateStatus(percent);
if(lastStatus != status && onDragChangeListener != null){
if(status == Status.Close){
onDragChangeListener.onClose();
}else if (status == Status.Open) {
onDragChangeListener.onOpen();
}
}
// 执行动画
animViews(percent);
}
private void animViews(float percent) {
// - 左面板: 缩放动画, 平移动画, 透明度动画
// 缩放动画 0.0 -> 1.0 >>> 0.0 -> 0.5 >>>0.5 -> 1.0
// percent * 0.5 + 0.5
// mLeftContent.setScaleX(percent * 0.5f + 0.5f);
// mLeftContent.setScaleY(percent * 0.5f + 0.5f);
ViewHelper.setScaleX(mLeftContent, evaluate(percent, 0.5f, 1.0f));
ViewHelper.setScaleY(mLeftContent, evaluate(percent, 0.5f, 1.0f));
// 平移动画 -mWidth / 2.0f -> 0
ViewHelper.setTranslationX(mLeftContent, evaluate(percent, -mWidth / 2.0f, 0));
// 透明度动画 0.2f -> 1.0
ViewHelper.setAlpha(mLeftContent, evaluate(percent, 0.2f, 1.0f));
// - 主面板: 缩放动画 1.0 -> 0.8
ViewHelper.setScaleX(mMainContent, evaluate(percent, 1.0f, 0.8f));
ViewHelper.setScaleY(mMainContent, evaluate(percent, 1.0f, 0.8f));
// - 背 景: 亮度变化
getBackground().setColorFilter((Integer)evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), PorterDuff.Mode.SRC_OVER);
}
10.侧边栏状态
public static enum Status {
Close, Open, Draging
}
public interface OnDragChangeListener {
void onClose();
void onOpen();
void onDraging(float percent);
}
private OnDragChangeListener onDragChangeListener;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public OnDragChangeListener getOnDragChangeListener() {
return onDragChangeListener;
}
public void setOnDragChangeListener(OnDragChangeListener onDragChangeListener) {
this.onDragChangeListener = onDragChangeListener;
}
/**
* 更新状态
* @param percent 当前动画执行的百分比
* @return
*/
private Status updateStatus(float percent) {
if(percent == 0){
return Status.Close;
}else if (percent == 1) {
return Status.Open;
}
return Status.Draging;
}