@TryLoveCatch
2017-04-20T16:03:27.000000Z
字数 7590
阅读 1804
android
自定义View
实例
ViewPager
实现ViewPager滑动的时候,背景渐变的过程。
主要需要用到一下的知识点
Android提供的Color属性估值器,它可以估算两个颜色值之间,任意部分的色值。所以背景的颜色渐变,可以用这个来实现。
这个方法,我的理解就是,会根据传入的时间,来得到这个时间对应的Value
,并且会触发onAnimationUpdate()
回调,然后就会停止,除非再次调用setCurrentPlayTime()
方法。
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
先说一下onPageScrolled()
方法:
假设有A和B两个pager,A-->B,打印如下:
onPageScrolled====position: 0, positionOffset: 0.05185185, positionOffsetPixels: 55
onPageScrolled====position: 0, positionOffset: 0.13703704, positionOffsetPixels: 148
onPageScrolled====position: 0, positionOffset: 0.25277779, positionOffsetPixels: 273
onPageScrolled====position: 0, positionOffset: 0.37222221, positionOffsetPixels: 402
onPageScrolled====position: 0, positionOffset: 0.43333334, positionOffsetPixels: 468
onPageScrolled====position: 0, positionOffset: 0.46944445, positionOffsetPixels: 507
onPageScrolled====position: 0, positionOffset: 0.63055557, positionOffsetPixels: 681
onPageScrolled====position: 0, positionOffset: 0.9990741, positionOffsetPixels: 1079
onPageScrolled====position: 1, positionOffset: 0.0, positionOffsetPixels: 0
然后,B-->A,打印如下:
onPageScrolled====position: 0, positionOffset: 0.962963, positionOffsetPixels: 1040
onPageScrolled====position: 0, positionOffset: 0.9, positionOffsetPixels: 972
onPageScrolled====position: 0, positionOffset: 0.83518517, positionOffsetPixels: 902
onPageScrolled====position: 0, positionOffset: 0.79907405, positionOffsetPixels: 863
onPageScrolled====position: 0, positionOffset: 0.7814815, positionOffsetPixels: 844
onPageScrolled====position: 0, positionOffset: 0.64074075, positionOffsetPixels: 692
onPageScrolled====position: 0, positionOffset: 0.5277778, positionOffsetPixels: 570
onPageScrolled====position: 0, positionOffset: 0.42037037, positionOffsetPixels: 454
onPageScrolled====position: 0, positionOffset: 9.259259E-4, positionOffsetPixels: 1
onPageScrolled====position: 0, positionOffset: 0.0, positionOffsetPixels: 0
根据以上打印,我们可以推断出来:
1、滑动过程中不断被调用
2、无论从左到右,还是从右到左滑动,onPageScrolled
的参数都表示前一页的数据。A->B还是B->A都表述的是A的数据,只有在A->B翻页成功并且结束的时候,会打印B的数据:position: 1, positionOffset: 0.0, positionOffsetPixels: 0
3、position
: 表示后面两个参数对应的是那一页。并不一定是当前的页面。例如,A->B的时候,position
打印的是0,但是B->A的时候,position
打印的依然是0。
4、positionOffset
:position
对应页面已经滑动了多少,也可以理解为,该页面现在看不见的地方所占的比例,取值范围[0~1],当为0的时候,表明显示出来了,当为1的时候,表示完全看不见了。
5、positionOffsetPixels
:同positionOffset
,只是单位变成了像素。
再来看一下onPageScrollStateChanged()
方法:
A->B和B->A都是一样的,也无论翻页成功与否,基本上打印如下:
onPageScrollStateChanged====state: 1
onPageScrollStateChanged====state: 2
onPageScrollStateChanged====state: 0
参数
state
有三个取值:
SCROLL_STATE_IDLE
,即0,表示页面处于闲置、稳定状态,即没被拖动也没惯性滑动;
SCROLL_STATE_DRAGGING
,即1,表示页面正在被用户拖动,即手指正在拖动状态,还未抬起;
SCROLL_STATE_SETTLING
,即2,表示页面处于即将到达最终状态的过程,即手指松开后惯性滑动状态。
最后说一下onPageSelected()
方法:
A->B和B->A都是一样的,完整打印如下:
onPageScrollStateChanged====state: 1
onPageScrolled====position: 0, positionOffset: 0.01759255, positionOffsetPixels: 18
onPageScrolled====position: 0, positionOffset: 0.1907407, positionOffsetPixels: 205
onPageScrollStateChanged====state: 2
onPageSelected====position: 1
onPageScrolled====position: 0, positionOffset: 0.2805556, positionOffsetPixels: 303
onPageScrolled====position: 0, positionOffset: 0.9990741, positionOffsetPixels: 1079
onPageScrolled====position: 1, positionOffset: 0.0, positionOffsetPixels: 0
onPageScrollStateChanged====state: 0
1、只有在翻页成功才会调用
2、在onPageScrollStateChanged()
为SCROLL_STATE_SETTLING
之后调用,当符合1的时候。
3、onPageSelected()
调用之后,onPageScrolled()
依然会执行一段时间
4、onPageScrollStateChanged()
最先执行,而且也会在最后再执行一次。
5、只有在手指抬起,才会调用onPageScrollStateChanged()
为SCROLL_STATE_SETTLING
和onPageSelected()
public void seek(long seekTime) {
colorAnim.setCurrentPlayTime(seekTime);
}
private void createAnimation(Object[] pColors) {
if (colorAnim == null) {
colorAnim = ValueAnimator.ofObject(new ArgbEvaluator(), pColors);
colorAnim.setDuration(DURATION);
colorAnim.addUpdateListener(this);
}
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentColor = (int) animation.getAnimatedValue();
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mCurrentColor == -1){
return;
}
setBackgroundColor(mCurrentColor);
}
根据传入的colors
创建ValueAnimator
,其中用到了ArgbEvaluator
Color属性估值器,注意我们并没有start
这个动画,我们通过外部调用seek()
方法,来触发ValueAnimator.setCurrentPlayTime()
方法,然后这个方法会调用onAnimationUpdate()
回调,我们就刷新一次,重新画出背景。调用代码如下:
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
handleBgAnim(position, positionOffset);
}
});
private void handleBgAnim(int pPosition, float pPositionOffset) {
int count = mArrViews.size() - 1;
if (count != 0) {
float length = (pPosition + pPositionOffset) / count;
int progress = (int) (length * ColorAnimationView.DURATION);
mColorAnimationView.seek(progress);
}
}
seek()
方法需要传入一个时间进度,所以我们通过onPageScrolled()
方法的参数除以pager的个数,得到一个比值,然后乘以动画持续时间,得到当前的时间进度。
后来需求更改了,背景改成渐变色了,不再是纯色,所以就不能用ArgbEvaluator
了。其实,渐变色无非就是起始色和结束色,多了一个颜色而已,我们可以自定义TypeEvaluator来实现,代码如下:
public static class Color {
public int startColor;
public int endColor;
}
private class MyEvaluator implements TypeEvaluator<Color> {
public Color evaluate(float fraction, Color startValue, Color endValue) {
Color tColor = new Color();
tColor.startColor = createColor(fraction, startValue.startColor, endValue.startColor);
tColor.endColor = createColor(fraction, startValue.startColor, endValue.startColor);
return tColor;
}
private int createColor(float fraction, int pStartColor, int pEndColor) {
int startA = (pStartColor >> 24) & 0xff;
int startR = (pStartColor >> 16) & 0xff;
int startG = (pStartColor >> 8) & 0xff;
int startB = pStartColor & 0xff;
int endA = (pEndColor >> 24) & 0xff;
int endR = (pEndColor >> 16) & 0xff;
int endG = (pEndColor >> 8) & 0xff;
int endB = pEndColor & 0xff;
return (int) ((startA + (int) (fraction * (endA - startA))) << 24)
| (int) ((startR + (int) (fraction * (endR - startR))) << 16)
| (int) ((startG + (int) (fraction * (endG - startG))) << 8)
| (int) ((startB + (int) (fraction * (endB - startB))));
}
}
MyEvaluator
类其实就是ArgbEvaluator
的代码,只是ArgbEvaluator
返回一个color,我们返回了一个对象,具体逻辑一模一样。
然后修改原来的使用逻辑:
private void createAnimation(Object[] pColors) {
if (colorAnim == null) {
colorAnim = ValueAnimator.ofObject(new MyEvaluator(), pColors);
colorAnim.setDuration(DURATION);
colorAnim.addUpdateListener(this);
}
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentColor = (Color) animation.getAnimatedValue();
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mCurrentColor == null) {
return;
}
mDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
new int[] {mCurrentColor.startColor, mCurrentColor.endColor});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
setBackground(mDrawable);
} else {
setBackgroundDrawable(mDrawable);
}
}
关于这两个,我后面会专门写动画相关的文章来详细展开介绍。
Interpolator
时间插值器,作用是根据时间流逝的百分比来计算当前属性值改变的百分比
Evaluator
类型估值算法,也叫估值器.作用是根据当前属性改变的百分比来计算改变后的属性值.系统预置的估值器有:
编译的时候有一个警告:
警告: 最后一个参数使用了不准确的变量类型的 varargs 方法的非 varargs 调用;
对于 varargs 调用, 应使用 Object
对于非 varargs 调用, 应使用 Object[], 这样也可以抑制此警告
虽然不影响运行,但是确实很不爽,报错的位置如下:
private void createAnimation(Color[] pColors) {
if (colorAnim == null) {
colorAnim = ValueAnimator.ofObject(new MyEvaluator(), pColors);
colorAnim.setDuration(DURATION);
colorAnim.addUpdateListener(this);
}
}
因为ValueAnimator.ofObject
第二参数是可变参数,我传入的是Color[]
数组,所以就报错了,其实,修改办法也很简单,改为Object[]
即可,如下:
private void createAnimation(Object[] pColors) {
if (colorAnim == null) {
colorAnim = ValueAnimator.ofObject(new MyEvaluator(), pColors);
colorAnim.setDuration(DURATION);
colorAnim.addUpdateListener(this);
}
}
Android特效专辑(二)——ViewPager渲染背景颜色渐变(引导页)
Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法
Android-开发艺术探索》-07-Andriod动画深入分析
[Parallax Animation]实现知乎 Android 客户端启动页视差滚动效果