您的位置:首页 > 其它

仿QQ侧滑面板(二)

2016-01-28 23:23 274 查看

1.3 结束动画

1.3.1 跳转的结束动画

1.3.2 平滑的结束动画

1.4 伴随动画

1.4.1 分解伴随动画

1.4.2 实现伴随动画

1.3 结束动画

拖拽过程中当手指抬起时,需要实现一个打开,关闭面板的动画,结束动画可以在 onViewReleased()方法实现

1.3.1 跳转的结束动画

onViewReleased()方法在松手之后会被调用,此时可以做结束动画,结束动画只需要考虑需要打开的情况,其它则为需要关闭情况

1.当水平方向的速度等于 0,并且主面板此时左边的位置在拖拽范围中轴线的右边则需要执行打开动画,即 mMainContent.getLeft() > mRange*0.5f

2.当水平方向的速度大于 0 时,则需要执行打开动画

3.其它情况则需要执行关闭动画

//5. 决定松手后要做的事件,结束动画
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//releasedChild 被释放的孩子 xvel 水平方向的速度, 向左为-,向右为+
Log.e(TAG, "onViewReleased: xvel:" + xvel);
//考虑开启的情况,其它情况则关闭的情况
if (xvel == 0 && mainContent.getLeft() > range * 0.5f) {
//在允许滑动的范围的中轴线右边,则打开
open();
} else if (xvel > 0) {
//速度向右时,则打开
open();
} else {
//关闭
close();
}
}


open(),close()创建为 DragLayout 的方法,这样方便外界调用

protected void open() {
mainContent.layout(range, 0, range + viewWidth, viewHeight);
}

protected void close() {
mainContent.layout(0, 0, viewWidth, viewHeight);
}


1.3.2 平滑的结束动画

首先实现平滑的打开动画,在这里需要用到 ViewDragHelper 提供的一个方法smoothSlideViewTo(child,finalLeft,finalTop),三个参数的意思分别是:

child 需要平滑移动的 view

finalLeft 需要移动到的终点左边位置

finalTop 需要移动到的终点的上边位置

smoothSlideViewTo()方法的返回值为 true,表示位置不是最终位置,需要重绘界面

重载一个 open(boolean isSmooth)方法, 用参数 isSmooth 标识是调用平滑动画还是跳转动画, open()方法则直接调用 open(true),默认为平滑动画

protected void open() {
open(true);
}

protected void open(boolean isSmooth) {
int finalLeft = range;
if (isSmooth) {
//触发一个平滑动画
if (viewDragHelper.smoothSlideViewTo(mainContent, finalLeft, 0)) {
//invalidate(); 可能会漏帧
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
//直接跳转
mainContent.layout(finalLeft, 0, finalLeft + viewWidth, viewHeight);
}
}


注意: smoothSlideViewTo()方法返回 true,需要重绘界面, 此时不建议使用 invalidate(),因为在动画的过程中可能会丢帧,推荐使用 ViewCompat.postInvalidateOnAnimation(this),参数一定要传子 view 所在的容器,因为只有容器才知道子 view 的具体位置

重绘命令调用后, 还需要重写 computScroll()方法, 重绘时, 系统会在 draw()方法后调用 computScroll(),在该方法中调用 ViewDragHelper 的维持动画的方法

continueSettling(deferCallbacks)参数 deferCallbacks 表示是否延迟画下一帧,此处传入 true,返回值表示是否已经移动到最终位置,如果为 true,还没有移动到最终位置,需要重绘界面,这样 computeScroll()方法就会不断的调用,界面也就会不断的重绘,直到移动到最终位置

@Override
public void computeScroll() {
super.computeScroll();
//调用完后会调用 draw()
if (viewDragHelper.continueSettling(true)) {
//参数传入 true,表示延迟画下一帧
//mViewDragHelper.continueSettling(true)
ViewCompat.postInvalidateOnAnimation(this);
}
}


同样的道理,关闭的平滑动画只需要修改 finalLeft = 0 即可

protected void close() {
close(true);
}

protected void close(boolean isSmooth) {
int finalLeft = 0;
if (isSmooth) {
//触发一个平滑动画
if (viewDragHelper.smoothSlideViewTo(mainContent, finalLeft, 0)) {
//invalidate(); 可能会漏帧
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
//直接跳转
mainContent.layout(finalLeft, 0, finalLeft + viewWidth, viewHeight);
}
}


1.4 伴随动画

1.4.1 分解伴随动画

伴随动画是拖拽的过程中,左面板,主面板会跟随拖拽百分比所做的动画,该动画需要在onViewPositionChanged()回调方法中实现

左面板:缩放动画,平移动画,透明度动画

主面板:缩放动画

背景: 亮度变化

1.4.2 实现伴随动画

创建一个方法 dispatchDragEvent(),在 onViewPositionChanged()方法中调用

// 当控件位置变化时调用,可以做伴随动画,状态更新,事件回调
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
// left 最新的水平位置 dx 刚刚发生的水平变化量
Log.e(TAG, "onViewPositionChanged: left:" + left + " dx:" + dx);
if (changedView == leftContent) {
// 如果滑动的是左面板
// 1. 放回到原来的位置
leftContent.layout(0, 0, viewWidth, viewHeight);
// 2. 把变化量传递给主面板, 主面板旧的值+变化量
int newLeft = mainContent.getLeft() + dx;
// 需要修正左边值
newLeft = fixLeft(newLeft);
mainContent.layout(newLeft, 0, newLeft + viewWidth, viewHeight);
}
dispatchDragEvent();
// offsetLeftAndRight 在低版本中没有重绘界面,手动调用重绘
invalidate();
}


实现左面板的缩放动画

private void dispatchDragEvent() {
//0.0f->1.0f 获取动画的百分比,主面板左边的位置引起的一系列变化
float percent = mainContent.getLeft() * 1.0f / range;
Log.e(TAG, "dispatchDragEvent: percent:" + percent);

//左面板:缩放动画,平移动画,透明度动画
//0.0f ->1.0f percent*0.5f => 0.0f -> 0.5f
//寻找规律->拷贝 FloatEvaluator.java 中的估值方法
//percent*0.5f + 0.5f => 0.5f -> 1.0f
//percent*(1.0f -0.6f)+0.6f => 0.6f -> 1.0f => start + percent(end - start)

//1. 缩放动画
leftContent.setScaleX(evaluate(percent, 0.5f, 1.0f));
leftContent.setScaleY(evaluate(percent, 0.5f, 1.0f));

}

//源码 FloatEvaluator.java 中拷贝的估值方法
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}


主面板左边位置与拖拽范围的相除可以得到一个 0.0f->1.0f的比例值,因为在整个拖拽过程中,主面板左边位置的变化是引起一系列变化的原因

推出一个公式 start + percent(end - start),即通过 percent 的变化可以计算出 start 到 end 间的任意值。源码 FloatEvaluator.java 中已经提供了这么一个方法,将其拷贝到代码中

同理可以实现其它伴随动画

private void dispatchDragEvent() {
//0.0f->1.0f 获取动画的百分比,主面板左边的位置引起的一系列变化
float percent = mainContent.getLeft() * 1.0f / range;
Log.e(TAG, "dispatchDragEvent: percent:" + percent);

//左面板:缩放动画,平移动画,透明度动画
//0.0f ->1.0f percent*0.5f => 0.0f -> 0.5f
//寻找规律->拷贝 FloatEvaluator.java 中的估值方法
//percent*0.5f + 0.5f => 0.5f -> 1.0f
//percent*(1.0f -0.6f)+0.6f => 0.6f -> 1.0f => start + percent(end - start)

//1. 缩放动画, 从 50%->100%
leftContent.setScaleX(evaluate(percent, 0.5f, 1.0f));
leftContent.setScaleY(evaluate(percent, 0.5f, 1.0f));

//2. 平移动画, 从宽度一半在屏幕外->全部移到屏幕内
leftContent.setTranslationX(evaluate(percent, -viewWidth * 0.5f, 0f));

//3. 透明度动画, 从 20%->100%
leftContent.setAlpha(evaluate(percent, 0.2f, 1.0f));

//主面板:缩放动画, 从 100%->80%
mainContent.setScaleY(evaluate(percent, 1.0f, 0.8f));

//背景亮度变化,PorterDuff.Mode.SRC_OVER 叠加模式,直接叠加在上面
getBackground().setColorFilter((Integer)  evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), PorterDuff.Mode.SRC_OVER);
}

//源码 ArgbEvaluator.java 中拷贝的估值方法
public Object evaluateColor(float fraction, Object startValue, Object endValue) {
//api18 以上的代码才有透明度的过滤
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;

int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;

return (startA + (int) (fraction * (endA - startA)) << 24) |
(startR + (int) (fraction * (endR - startR)) << 16) |
(startG + (int) (fraction * (endG - startG)) << 8) |
(startB + (int) (fraction * (endB - startB)));
}


模式 PorterDuff.Mode.SRC_OVER 表示直接叠加在上面

ArgbEvaluator.java 源码中拷贝的估值方法, api18 以上的代码才有透明度的过滤
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: