我怎样才能动画状态栏和工具栏的颜色变化(就像新的日历应用程序一样)
How can I Animate the color change of the statusbar and toolbar (like the new Calendar app does)
新的 Google 日历应用程序有一个我想在我的应用程序中做的动画。当您创建一个新事件时,您可以为该事件选择一种颜色。当您这样做时,状态栏和工具栏会更改为该颜色,并带有覆盖它们的圆形效果。
这是我想做的一个例子:
我可以更改状态栏和工具栏的颜色,但是如何在更改颜色时将圆形动画效果(或类似效果)应用于它们?
我不知道他们是如何实现涟漪效果的,但是您可以使用以下代码同时对两个条形图进行平滑的 color 过渡。
private void tintSystemBars() {
// Initial colors of each system bar.
final int statusBarColor = getResources().getColor(R.color.status_bar_color);
final int toolbarColor = getResources().getColor(R.color.toolbar_color);
// Desired final colors of each bar.
final int statusBarToColor = getResources().getColor(R.color.status_bar_to_color);
final int toolbarToColor = getResources().getColor(R.color.toolbar_to_color);
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Use animation position to blend colors.
float position = animation.getAnimatedFraction();
// Apply blended color to the status bar.
int blended = blendColors(statusBarColor, statusBarToColor, position);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow.setStatusBarColor(blended);
}
// Apply blended color to the ActionBar.
blended = blendColors(toolbarColor, toolbarToColor, position);
ColorDrawable background = new ColorDrawable(blended);
getSupportActionBar().setBackgroundDrawable(background);
}
});
anim.setDuration(500).start();
}
private int blendColors(int from, int to, float ratio) {
final float inverseRatio = 1f - ratio;
final float r = Color.red(to) * ratio + Color.red(from) * inverseRatio;
final float g = Color.green(to) * ratio + Color.green(from) * inverseRatio;
final float b = Color.blue(to) * ratio + Color.blue(from) * inverseRatio;
return Color.rgb((int) r, (int) g, (int) b);
}
经过大量研究,
我找到了您想要的答案。
此动画称为 21.0 Android API - lollipop 中引入的显示动画。不幸的是,它不向后兼容。
我找到了一个库,它做同样的显示动画,但不完全是一个 backport,但是你想要的效果可以实现 API 14 以后用这个库
https://github.com/markushi/android-ui
谢谢,
如果您只想将此动画与棒棒糖一起使用,那么只需 google "Android Reveal Colour Animation implementation".
我不知道日历应用程序是否正是这样做的,但对我来说已经足够接近了。
注意事项
- 该方法使用了棒棒糖中引入的
ViewAnimationUtils.createCircularReveal
方法。
- 它需要知道状态栏和工具栏操作栏的高度。您仍然可以对操作栏使用
?attr/actionBarSize
并动态获取两者,但为了简单起见,我假设操作栏高度为 56dp
,状态栏高度为 24dp
。
总体思路
总体思路是将操作栏和状态栏设置为透明。这会将您的操作栏向上移动到状态栏下方,因此您必须调整操作栏的大小和填充以进行补偿。然后使用它后面的视图和 ViewAnimationUtils.createCircularReveal
来显示新的背景颜色。你需要在它后面多一个视图来显示旧的背景颜色,因为中间的视图正在显示新的背景颜色。
动画
动画要求:
- 透明工具栏 actionbar 覆盖了常规 actionbar 和 statusbar space。在这种情况下,硬编码高度为 56dp(操作栏)+ 24dp(状态栏)= 80dp。您还需要将顶部填充设置为 24dp,以防止操作栏内容位于状态栏下方。
- 中间视图(我将其称为 显示视图)大小相同(高度为 80dp)但位于操作栏后面。这将是
ViewAnimationUtils.createCircularReveal
作用的视图。
- 底部视图(我将其称为 显示背景 视图)与显示视图大小相同但在其后面。此视图用于显示旧背景颜色,而显示视图则显示其顶部的新颜色。
代码
以下是我使用的关键代码。请参阅 https://github.com/shaun-blake-experiments/example-toolbar-animation.
中的示例项目
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="@+id/revealBackground"
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingTop="24dp"
android:background="@color/primary"
android:elevation="4dp">
</View>
<View
android:id="@+id/reveal"
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingTop="24dp"
android:background="@color/primary"
android:elevation="4dp">
</View>
<Toolbar
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingTop="24dp"
android:background="@android:color/transparent"
android:elevation="4dp"
android:theme="@style/TranslucentActionBar">
</Toolbar>
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Invert Toolbar Colors"
android:textOn="Invert Toolbar Colors On"
android:textOff="Invert Toolbar Colors Off"
android:id="@+id/toggleButton"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
styles.xml
<resources>
<style name="AppTheme" parent="@android:style/Theme.Material.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<style name="TranslucentActionBar" parent="@android:style/Widget.Material.ActionBar">
<item name="android:textColorPrimary">@color/primary_text_dark_background</item>
</style>
</resources>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primary">#2196F3</color>
<color name="primary_dark">#1976D2</color>
<color name="primary_light">#BBDEFB</color>
<color name="accent">#009688</color>
<color name="primary_text">#DD000000</color>
<color name="primary_text_dark_background">#FFFFFF</color>
<color name="secondary_text">#89000000</color>
<color name="icons">#FFFFFF</color>
<color name="divider">#30000000</color>
</resources>
MainActivity.java
package com.example.android.toolbaranimation;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.widget.ToggleButton;
import android.widget.Toolbar;
public class MainActivity extends Activity {
private View mRevealView;
private View mRevealBackgroundView;
private Toolbar mToolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.appbar);
mToolbar.setTitle(getString(R.string.app_name));
mRevealView = findViewById(R.id.reveal);
mRevealBackgroundView = findViewById(R.id.revealBackground);
ToggleButton toggleButton = (ToggleButton) findViewById(R.id.toggleButton);
toggleButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean on = ((ToggleButton) v).isChecked();
if (on) {
animateAppAndStatusBar(R.color.primary, R.color.accent);
} else {
animateAppAndStatusBar(R.color.accent, R.color.primary);
}
}
});
setActionBar(mToolbar);
}
private void animateAppAndStatusBar(int fromColor, final int toColor) {
Animator animator = ViewAnimationUtils.createCircularReveal(
mRevealView,
mToolbar.getWidth() / 2,
mToolbar.getHeight() / 2, 0,
mToolbar.getWidth() / 2);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mRevealView.setBackgroundColor(getResources().getColor(toColor));
}
});
mRevealBackgroundView.setBackgroundColor(getResources().getColor(fromColor));
animator.setStartDelay(200);
animator.setDuration(125);
animator.start();
mRevealView.setVisibility(View.VISIBLE);
}
}
备注
- 注意工具栏上的
android:elevation
属性,显示,显示背景视图。如果工具栏上的高度较低,其他的将覆盖按钮和文本。
试试这个,它对我来说效果很好,而且它得到了相同的效果 Google 日历应用程序。
private void reveal(CollapsingToolbarLayout toolbarLayout, int colorPrimary, int colorPrimaryDark){
// get the center for the clipping circle
int cx = toolbarLayout.getWidth() / 2;
int cy = toolbarLayout.getHeight() / 2;
// get the final radius for the clipping circle
float finalRadius = (float) Math.hypot(cx, cy);
// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(toolbarLayout, cx, cy, 0, finalRadius);
// make the view visible and start the animation
toolbarLayout.setBackgroundColor(colorPrimary);
anim.start();
Window window = getWindow();
window.setStatusBarColor(colorPrimaryDark);
toolbarLayout.setContentScrimColor(colorPrimary);
}
新的 Google 日历应用程序有一个我想在我的应用程序中做的动画。当您创建一个新事件时,您可以为该事件选择一种颜色。当您这样做时,状态栏和工具栏会更改为该颜色,并带有覆盖它们的圆形效果。
这是我想做的一个例子:
我可以更改状态栏和工具栏的颜色,但是如何在更改颜色时将圆形动画效果(或类似效果)应用于它们?
我不知道他们是如何实现涟漪效果的,但是您可以使用以下代码同时对两个条形图进行平滑的 color 过渡。
private void tintSystemBars() {
// Initial colors of each system bar.
final int statusBarColor = getResources().getColor(R.color.status_bar_color);
final int toolbarColor = getResources().getColor(R.color.toolbar_color);
// Desired final colors of each bar.
final int statusBarToColor = getResources().getColor(R.color.status_bar_to_color);
final int toolbarToColor = getResources().getColor(R.color.toolbar_to_color);
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Use animation position to blend colors.
float position = animation.getAnimatedFraction();
// Apply blended color to the status bar.
int blended = blendColors(statusBarColor, statusBarToColor, position);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow.setStatusBarColor(blended);
}
// Apply blended color to the ActionBar.
blended = blendColors(toolbarColor, toolbarToColor, position);
ColorDrawable background = new ColorDrawable(blended);
getSupportActionBar().setBackgroundDrawable(background);
}
});
anim.setDuration(500).start();
}
private int blendColors(int from, int to, float ratio) {
final float inverseRatio = 1f - ratio;
final float r = Color.red(to) * ratio + Color.red(from) * inverseRatio;
final float g = Color.green(to) * ratio + Color.green(from) * inverseRatio;
final float b = Color.blue(to) * ratio + Color.blue(from) * inverseRatio;
return Color.rgb((int) r, (int) g, (int) b);
}
经过大量研究,
我找到了您想要的答案。
此动画称为 21.0 Android API - lollipop 中引入的显示动画。不幸的是,它不向后兼容。
我找到了一个库,它做同样的显示动画,但不完全是一个 backport,但是你想要的效果可以实现 API 14 以后用这个库
https://github.com/markushi/android-ui
谢谢,
如果您只想将此动画与棒棒糖一起使用,那么只需 google "Android Reveal Colour Animation implementation".
我不知道日历应用程序是否正是这样做的,但对我来说已经足够接近了。
注意事项
- 该方法使用了棒棒糖中引入的
ViewAnimationUtils.createCircularReveal
方法。 - 它需要知道状态栏和工具栏操作栏的高度。您仍然可以对操作栏使用
?attr/actionBarSize
并动态获取两者,但为了简单起见,我假设操作栏高度为56dp
,状态栏高度为24dp
。
总体思路
总体思路是将操作栏和状态栏设置为透明。这会将您的操作栏向上移动到状态栏下方,因此您必须调整操作栏的大小和填充以进行补偿。然后使用它后面的视图和 ViewAnimationUtils.createCircularReveal
来显示新的背景颜色。你需要在它后面多一个视图来显示旧的背景颜色,因为中间的视图正在显示新的背景颜色。
动画
动画要求:
- 透明工具栏 actionbar 覆盖了常规 actionbar 和 statusbar space。在这种情况下,硬编码高度为 56dp(操作栏)+ 24dp(状态栏)= 80dp。您还需要将顶部填充设置为 24dp,以防止操作栏内容位于状态栏下方。
- 中间视图(我将其称为 显示视图)大小相同(高度为 80dp)但位于操作栏后面。这将是
ViewAnimationUtils.createCircularReveal
作用的视图。 - 底部视图(我将其称为 显示背景 视图)与显示视图大小相同但在其后面。此视图用于显示旧背景颜色,而显示视图则显示其顶部的新颜色。
代码
以下是我使用的关键代码。请参阅 https://github.com/shaun-blake-experiments/example-toolbar-animation.
中的示例项目activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="@+id/revealBackground"
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingTop="24dp"
android:background="@color/primary"
android:elevation="4dp">
</View>
<View
android:id="@+id/reveal"
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingTop="24dp"
android:background="@color/primary"
android:elevation="4dp">
</View>
<Toolbar
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingTop="24dp"
android:background="@android:color/transparent"
android:elevation="4dp"
android:theme="@style/TranslucentActionBar">
</Toolbar>
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Invert Toolbar Colors"
android:textOn="Invert Toolbar Colors On"
android:textOff="Invert Toolbar Colors Off"
android:id="@+id/toggleButton"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
styles.xml
<resources>
<style name="AppTheme" parent="@android:style/Theme.Material.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<style name="TranslucentActionBar" parent="@android:style/Widget.Material.ActionBar">
<item name="android:textColorPrimary">@color/primary_text_dark_background</item>
</style>
</resources>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primary">#2196F3</color>
<color name="primary_dark">#1976D2</color>
<color name="primary_light">#BBDEFB</color>
<color name="accent">#009688</color>
<color name="primary_text">#DD000000</color>
<color name="primary_text_dark_background">#FFFFFF</color>
<color name="secondary_text">#89000000</color>
<color name="icons">#FFFFFF</color>
<color name="divider">#30000000</color>
</resources>
MainActivity.java
package com.example.android.toolbaranimation;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.widget.ToggleButton;
import android.widget.Toolbar;
public class MainActivity extends Activity {
private View mRevealView;
private View mRevealBackgroundView;
private Toolbar mToolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.appbar);
mToolbar.setTitle(getString(R.string.app_name));
mRevealView = findViewById(R.id.reveal);
mRevealBackgroundView = findViewById(R.id.revealBackground);
ToggleButton toggleButton = (ToggleButton) findViewById(R.id.toggleButton);
toggleButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean on = ((ToggleButton) v).isChecked();
if (on) {
animateAppAndStatusBar(R.color.primary, R.color.accent);
} else {
animateAppAndStatusBar(R.color.accent, R.color.primary);
}
}
});
setActionBar(mToolbar);
}
private void animateAppAndStatusBar(int fromColor, final int toColor) {
Animator animator = ViewAnimationUtils.createCircularReveal(
mRevealView,
mToolbar.getWidth() / 2,
mToolbar.getHeight() / 2, 0,
mToolbar.getWidth() / 2);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mRevealView.setBackgroundColor(getResources().getColor(toColor));
}
});
mRevealBackgroundView.setBackgroundColor(getResources().getColor(fromColor));
animator.setStartDelay(200);
animator.setDuration(125);
animator.start();
mRevealView.setVisibility(View.VISIBLE);
}
}
备注
- 注意工具栏上的
android:elevation
属性,显示,显示背景视图。如果工具栏上的高度较低,其他的将覆盖按钮和文本。
试试这个,它对我来说效果很好,而且它得到了相同的效果 Google 日历应用程序。
private void reveal(CollapsingToolbarLayout toolbarLayout, int colorPrimary, int colorPrimaryDark){
// get the center for the clipping circle
int cx = toolbarLayout.getWidth() / 2;
int cy = toolbarLayout.getHeight() / 2;
// get the final radius for the clipping circle
float finalRadius = (float) Math.hypot(cx, cy);
// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(toolbarLayout, cx, cy, 0, finalRadius);
// make the view visible and start the animation
toolbarLayout.setBackgroundColor(colorPrimary);
anim.start();
Window window = getWindow();
window.setStatusBarColor(colorPrimaryDark);
toolbarLayout.setContentScrimColor(colorPrimary);
}