仿写Social Steps的ToolBar效果

泡在网上的日子 / 文 发表于2017-11-03 19:49 第次阅读 动画

前段时间在medium上看到一篇比较有意思的文章Toolbar Delight。该篇文章讲解了如何实现下面这种效果:

Untitled.gif

gif效果不好,想看清晰的版本请看原始文章的视频。

文章虽好,但是代码不全,有些细节作者其实也没有透露。于是我大致看了之后决定自己实现一个类似的效果,相似程度95以上吧。

其实这种还是很简单的,都是些细节问题,大致可以分解为:

  1. 从左到右边的渐变,这个很简单。

  2. 滚动的时候弧度随着 AppBarLayout 的 verticalOffset 发生变化,当折叠的时候,颜色逐渐过渡到colorPrimary,同时云彩也在折叠的时候往边界跑。

  3. 不同时间颜色是不一样的,太阳或者月亮的位置也尽量模拟真实世界。这个不难,把一天的时间分段处理就好了。

  4. 当打开界面的时候,有一个从上一个时间段状态过渡到当前状态的动画。我这里的实现效果跟原文略有区别,但是要做到跟文章完全吻合也很简单。

至于太阳,星星,云彩,都是bitmap,反编译Social Steps得到的。

好吧,编不下去了,直接看我最终实现的效果:

Untitled.gif

以上是晚上19.44的效果,其它时间段就不一一上图了。

大部分效果都是在一个叫做ToolbarArcBackground的自定义view中实现的:

ToolbarArcBackground.java

public?class?ToolbarArcBackground?extends?View?{
????private?float?scale?=?1;

????private?float?timeRate;

????private?int?gradientColor1?=?0xff4CAF50;
????private?int?gradientColor2?=?0xFF0E3D10;
????private?int?lastGradientColor1?=?0xff4CAF50;
????private?int?lastGradientColor2?=?0xFF0E3D10;
????private?Context?context;

????private?Bitmap?sun;
????private?Bitmap?sunMorning;
????private?Bitmap?sunNoon;
????private?Bitmap?sunEvening;

????private?Bitmap?cloud1;
????private?Bitmap?cloud2;
????private?Bitmap?cloud3;

????private?Bitmap?moon;
????private?Bitmap?star;

????private?int?cloud1X?=?50;
????private?int?cloud2X?=?450;
????private?int?cloud3X?=?850;

????int?waveHeight?=?60;

????private?int?cloud1Y?=?waveHeight?+?150;
????private?int?cloud2Y?=?waveHeight?+?120;
????private?int?cloud3Y?=?waveHeight?+?20;



????private?int?sunHeight;

????private?Day?now?=?Day.MORNING;

????enum?Day?{
????????MORNING,?NOON,?AFTERNOON,?EVENING,
????????MIDNIGHT
????}

????public?ToolbarArcBackground(Context?context)?{
????????super(context);
????????this.context?=?context;
????????init();
????}

????public?ToolbarArcBackground(Context?context,?AttributeSet?attrs)?{
????????super(context,?attrs);
????????this.context?=?context;
????????init();
????}

????public?ToolbarArcBackground(Context?context,?AttributeSet?attrs,?int?defStyleAttr)?{
????????super(context,?attrs,?defStyleAttr);
????????this.context?=?context;
????????init();
????}

????public?void?setScale(float?scale)?{
????????this.scale?=?scale;
????????invalidate();
????}

????private?void?init()?{
????????calculateTimeLine();
????????createBitmaps();
????????initGradient();
????}

????private?void?initGradient(){
????????switch?(now)?{
????????????case?MORNING:
????????????????lastGradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_midnight);
????????????????lastGradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_midnight);

????????????????gradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_morning);
????????????????gradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_morning);
????????????????break;
????????????case?NOON:
????????????????lastGradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_morning);
????????????????lastGradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_morning);

????????????????gradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_noon);
????????????????gradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_noon);
????????????????break;
????????????case?AFTERNOON:
????????????????lastGradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_noon);
????????????????lastGradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_noon);

????????????????gradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_noon_evening);
????????????????gradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_noon_evening);
????????????????break;
????????????case?EVENING:
????????????????lastGradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_noon_evening);
????????????????lastGradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_noon_evening);

????????????????gradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_evening);
????????????????gradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_evening);
????????????????break;
????????????case?MIDNIGHT:
????????????????lastGradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_evening);
????????????????lastGradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_evening);

????????????????gradientColor1?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_1_midnight);
????????????????gradientColor2?=?ContextCompat.getColor(context,?R.color.toolbar_gradient_2_midnight);
????????????????break;
????????}
????}

????private?void?calculateTimeLine()?{
????????Date?d?=?new?Date();
????????if?(d.getHours()?>?5?&&?d.getHours()?=?11)?{
????????????now?=?Day.NOON;
????????}?else?if?(d.getHours()?=?13)?{
????????????now?=?Day.AFTERNOON;
????????}?else?if?(d.getHours()?=?18)?{
????????????now?=?Day.EVENING;
????????}?else?if?(d.getHours()?<=?5?||?d.getHours()?>=?22?&&?d.getHours()??5?&&?d.getHours()?=?18)?{
????????????????timeRate?=?(float)(d.getHours()?-?17)?/?12;
????????????}?else?{
????????????????timeRate?=?(float)(d.getHours()?+?6)?/?12;
????????????}
????????}
????}

????private?void?createBitmaps()?{
????????final?BitmapFactory.Options?options?=?new?BitmapFactory.Options();
????????options.inPreferredConfig?=?Bitmap.Config.RGB_565;

????????cloud1?=?BitmapFactory.decodeResource(getContext().getResources(),?R.drawable.bg_cloud_01);
????????cloud2?=?BitmapFactory.decodeResource(getContext().getResources(),?R.drawable.bg_cloud_02);
????????cloud3?=?BitmapFactory.decodeResource(getContext().getResources(),?R.drawable.bg_cloud_03);

????????sun?=?BitmapFactory.decodeResource(getContext().getResources(),?R.drawable.bg_sun);
????????sunMorning?=?BitmapFactory.decodeResource(getContext().getResources(),?R.drawable.bg_sun_morning);
????????sunNoon?=?BitmapFactory.decodeResource(getContext().getResources(),?R.drawable.bg_sun_noon);
????????sunEvening?=?BitmapFactory.decodeResource(getContext().getResources(),?R.drawable.bg_sun_evening);

????????moon?=?BitmapFactory.decodeResource(getContext().getResources(),?R.drawable.bg_moon);
????????star?=?BitmapFactory.decodeResource(getContext().getResources(),?R.drawable.bg_stars);
????}

????public?void?startAnimate(){
????????Log.i("ToolbarArcBackground",?"timeRate?=?"?+?timeRate);
????????ValueAnimator?anim?=?ValueAnimator.ofFloat(0f,?1f);
????????anim.setDuration(3000);
????????//anim.setInterpolator();
????????anim.addUpdateListener(new?ValueAnimator.AnimatorUpdateListener()?{
????????????float?temp?=?timeRate;

????????????int?currentGradientColor1?=gradientColor1;
????????????int?currentGradientColor2?=gradientColor2;

????????????@Override
????????????public?void?onAnimationUpdate(ValueAnimator?animation)?{
????????????????//时间段的过渡
????????????????float?currentValue?=?(float)?animation.getAnimatedValue();
????????????????timeRate?=?currentValue?*?temp;

????????????????//由上一个时间段的颜色过渡到下一个时间段的颜色
????????????????ArgbEvaluator?argbEvaluator?=?new?ArgbEvaluator();//渐变色计算类
????????????????gradientColor1?=?(int)?(argbEvaluator.evaluate(currentValue,?lastGradientColor1,?currentGradientColor1));
????????????????gradientColor2?=?(int)?(argbEvaluator.evaluate(currentValue,?lastGradientColor2,?currentGradientColor2));
????????????????invalidate();
????????????}
????????});
????????anim.start();
????}

????@Override
????protected?void?onDraw(Canvas?canvas)?{
????????super.onDraw(canvas);
????????drawGradient(canvas);
????????drawCloud(canvas);
????????if?(now?==?Day.MIDNIGHT?||?now?==?Day.EVENING)?{
????????????drawStar(canvas);
????????????drawMoon(canvas);
????????}?else?{
????????????drawSun(canvas);
????????}
????????drawOval(canvas);
????}

????private?void?drawOval(Canvas?canvas)?{

????????Paint?ovalPaint?=?new?Paint();
????????final?Path?path?=?new?Path();
????????ovalPaint.setColor(Color.WHITE);
????????ovalPaint.setAntiAlias(true);

????????path.moveTo(0,?getMeasuredHeight());

????????path.quadTo(getMeasuredWidth()?/?2,?getMeasuredHeight()?-?waveHeight?*?scale,?getMeasuredWidth(),?getMeasuredHeight());

????????path.lineTo(0,?getMeasuredHeight());
????????path.close();
????????canvas.drawPath(path,?ovalPaint);
????}

????private?void?drawCloud(Canvas?canvas)?{
????????canvas.drawBitmap(cloud1,?cloud1X?*?scale,?cloud1Y?*?scale,?null);
????????canvas.drawBitmap(cloud2,?cloud2X?*?scale,?cloud2Y?*?scale,?null);
????????canvas.drawBitmap(cloud3,?cloud3X?+?(1?-?scale)?*?getMeasuredWidth(),?cloud3Y?*?scale,?null);
????}

????private?void?drawStar(Canvas?canvas)?{
????????canvas.drawBitmap(star,?0,?0,?null);
????}

????private?void?drawMoon(Canvas?canvas)?{
????????int?passed?=??(int)(getMeasuredWidth()?*?timeRate);
????????int?xpos?=?passed?-?moon.getWidth()?/?2;
????????canvas.drawBitmap(moon,?xpos?+?(1?-?scale)?*?getMeasuredWidth(),?-50,?null);
????}

????private?void?drawGradient(Canvas?canvas)?{
????????Paint?paint?=?new?Paint();
????????ArgbEvaluator?argbEvaluator?=?new?ArgbEvaluator();//渐变色计算类
????????int?changedColor1?=?(int)?(argbEvaluator.evaluate(1?-?scale,?gradientColor1,?ContextCompat.getColor(context,?R.color.colorPrimary)));
????????int?changedColor2?=?(int)?(argbEvaluator.evaluate(1?-?scale,?gradientColor2,?ContextCompat.getColor(context,?R.color.colorPrimary)));
????????LinearGradient?linearGradient?=?new?LinearGradient(0f,?0f,?getMeasuredWidth(),?getMeasuredHeight(),?changedColor1,?changedColor2,?Shader.TileMode.CLAMP);
????????paint.setShader(linearGradient);
????????canvas.drawRect(0,?0,?getMeasuredWidth(),?getMeasuredHeight(),?paint);
//
//????????LinearGradient?linearGradient1?=?new?LinearGradient(0f,?0f,?getMeasuredWidth(),?getMeasuredHeight(),?0xff00d9ff,?0xff00b0ff,?Shader.TileMode.CLAMP);
//????????paint.setShader(linearGradient1);
//????????canvas.drawRect(0,?0,?getMeasuredWidth(),?getMeasuredHeight(),?paint);
????}

????private?void?drawSun(Canvas?canvas)?{
????????Log.e("rate",?"timeRate?=?"?+?timeRate);
????????Log.e("rate",?"sun.getWidth()?=?"?+?sun.getWidth());
????????int?passed?=??(int)(getMeasuredWidth()?*?timeRate);
????????int?xpos?=?passed?-?sun.getWidth()?/?2;
????????if?(now?==?Day.MORNING)?{
????????????canvas.drawBitmap(sunMorning,?xpos?+?(1?-?scale)?*?getMeasuredWidth(),?-50,?null);
????????}?else?if?(now?==?Day.NOON)?{
????????????canvas.drawBitmap(sunNoon,?xpos?+?(1?-?scale)?*?getMeasuredWidth(),?-50,?null);
????????}?else?if?(now?==?Day.AFTERNOON)?{
????????????canvas.drawBitmap(sunEvening,?xpos?+?(1?-?scale)?*?getMeasuredWidth(),?-50,?null);
????????}
????}
}

布局activity_main.xml:




????

????????
????????????
????????????

????????????
????????

????

????

????????

????

?在MainActivity中这样使用:

package?com.jcodecraeer.day;

import?android.support.design.widget.AppBarLayout;
import?android.support.v7.app.ActionBar;
import?android.support.v7.app.AppCompatActivity;
import?android.os.Bundle;
import?android.support.v7.widget.Toolbar;

public?class?MainActivity?extends?AppCompatActivity?{
????ToolbarArcBackground?mToolbarArcBackground;
????AppBarLayout?mAppBarLayout;

????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(R.layout.activity_main);

????????Toolbar?toolbar?=?(Toolbar)?findViewById(R.id.toolbar);
????????setSupportActionBar(toolbar);
????????final?ActionBar?ab?=?getSupportActionBar();
????????setTitle("");

????????mAppBarLayout?=?(AppBarLayout)?findViewById(R.id.appbar);
????????mToolbarArcBackground?=?(ToolbarArcBackground)?findViewById(R.id.toolbarArcBackground);
????????mAppBarLayout.addOnOffsetChangedListener(new?AppBarLayout.OnOffsetChangedListener()?{
????????????int?scrollRange?=?-1;
????????????@Override
????????????public?void?onOffsetChanged(AppBarLayout?appBarLayout,?int?verticalOffset)?{
????????????????if?(scrollRange?==?-1)?{
????????????????????scrollRange?=?appBarLayout.getTotalScrollRange();
????????????????}

????????????????float?scale?=?(float)?Math.abs(verticalOffset)?/?scrollRange;


????????????????mToolbarArcBackground.setScale(1?-?scale);

????????????}
????????});
????????getWindow().getDecorView().post(new?Runnable()?{
????????????@Override
????????????public?void?run()?{
????????????????mToolbarArcBackground.startAnimate();
????????????}
????????});
????}
}

颜色资源:



????#4CAF50
????#4CAF50
????#FF4081

????#ff00d9ff
????#341c61
????#ff416eb2
????#fff0ecb3
????#ff00d9ff
????#ffa976ed
????#ff00b0ff
????#1e1918
????#ff2a2569
????#ff00b3ff
????#ff00b0ff
????#704343

图片资源就自己反编译吧。

上一篇:DataBining用法<一>
使用databinding也有一段时间了,网上的用法也是层出不群,当初用的时候也是借鉴了不少,今天就分享一下我自己的使用方法 第一步:在这个位置加入如下代码 第二步:在xml文件里布局的最外层套一个layout标签 第三步:在Activity里面的写法 第四步:通过bindi
下一篇:正式发布 Android 架构组件 1.0 稳定版 | 附带中文介绍视频
几十亿的设备都在用 Android 系统,从高端手机到飞机上的影音娱乐系统,应有尽有,不一而足。而 Android OS 则为这几十亿的设备保驾护航,高效管理资源,保障运行流畅,然而有时候却增加了开发卓越 App 的难度。为了简化开发流程,我们在 Google I/O 大会上