Changing ActionBar background affects Navigation drawer - android

I'm changing the ActionBar background in a fragment with a ScrollView. I have a ScrollView listener which changes the alpha of the items in the layered-list based on the scroll distance. The action bar actually works great. However, the Navigation Drawer background gets affected by this alpha change and I have no idea why; the nav drawer background becomes invisible. I can reproduce this easily on a Nexus 7 running Android L but it doesn't always happen for some 4.4 devices.
The main part is:
ActionBar actionBar = activity.getSupportActionBar();
LayerDrawable mActionBarBg = (LayerDrawable) getResources().getDrawable(R.drawable.background_actionbar);
if (mActionBarBg != null) {
setActionBarBgAlpha(0);
actionBar.setBackgroundDrawable(mActionBarBg);
}
Alpha calculator is this:
scrollView.setOnScrollChangedLitstener(new OnScrollChangedListener() {
#Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {
t = Math.max(t, 0);
endpointHeader.setTranslationY(endpointHeader.getTop() + (t * 0.25f));
mScrollAlpha = (int) Math.min(alphaToHeightRatio * t, 255);
updateActionBarElemsAlpha(mScrollAlpha);
}
});
Couple of other methods:
private void updateActionBarElemsAlpha(int updatedAlpha){
if(mActionBarTitleColor != null){
mActionBarTitleColor.setAlpha(updatedAlpha);
mActionBarTitleView.setTextColor(mActionBarTitleColor.getColor());
}
setActionBarBgAlpha(updatedAlpha);
}
private void setActionBarBgAlpha(int alpha){
if(mActionBarBg != null){
mActionBarBg.getDrawable(1).setAlpha(alpha);
mActionBarBg.getDrawable(2).setAlpha(alpha);
}
}
background_actionbar.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/gradient" />
<item android:drawable="#color/grey_divider"/>
<item android:bottom="2dp"
android:drawable="#color/White"/>
</layer-list>

You should always mutate drawables before changing any attributes, otherwise you'll end up modifying the cached state which is by default shared across all instances.
if (mActionBarBg != null) {
mActionBarBg.mutate();
mActionBarBg.getDrawable(1).setAlpha(alpha);
mActionBarBg.getDrawable(2).setAlpha(alpha);
}

Related

How to control NavigationView margin between menu items

I have a navigation drawer with many items so the user needs to scroll up and down in order to see all the items.
I would like to reduce the margins between individual menu items, so that all items fit within a standard screen with no need to scroll.
Is there a way to control the margins in between the menu items?
Answer here helped me to reduce space at least between groups. I used the following dimen
<dimen tools:override="true" name="design_navigation_separator_vertical_padding">1dp</dimen>
We can create a drawable and set it as the NavigationView's itemBackground attribute. I will explain below:
If we walk through the NavigationMenuAdapter, we will see there are four types of items:
private static final int VIEW_TYPE_NORMAL = 0;
private static final int VIEW_TYPE_SUBHEADER = 1;
private static final int VIEW_TYPE_SEPARATOR = 2;
private static final int VIEW_TYPE_HEADER = 3;
What we want to work with is VIEW_TYPE_NORMAL. The attributes exposed to developers can be found in the below code:
case VIEW_TYPE_NORMAL:
{
NavigationMenuItemView itemView = (NavigationMenuItemView) holder.itemView;
itemView.setIconTintList(iconTintList);
if (textAppearanceSet) {
itemView.setTextAppearance(textAppearance);
}
if (textColor != null) {
itemView.setTextColor(textColor);
}
ViewCompat.setBackground(
itemView,
itemBackground != null ? itemBackground.getConstantState().newDrawable() : null);
NavigationMenuTextItem item = (NavigationMenuTextItem) items.get(position);
itemView.setNeedsEmptyIcon(item.needsEmptyIcon);
itemView.setHorizontalPadding(itemHorizontalPadding);
itemView.setIconPadding(itemIconPadding);
if (hasCustomItemIconSize) {
itemView.setIconSize(itemIconSize);
}
itemView.setMaxLines(itemMaxLines);
itemView.initialize(item.getMenuItem(), 0);
break;
}
Unfortunately,there is no interface for us to add margins between the NavigationMenuItemView. However, it allows us to set a background. So we can set a customer drawable to the NavigationView with a specific height. We only set a height in that drawable, like:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle">
<size android:height="60dp"/>
</shape>
Then apply this drawable to the NavigationView in the layout.xml, like:
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:itemBackground="#drawable/bk_menu_item"/>
I understand it is not a perfect solution, but it seems the only solution working in my case.
Paste this in styles.xml
<style name="NavigationTheme" parent="AppTheme">
<item name="android:textSize">16sp</item>
<item name="android:layout_marginBottom">02dp</item>
</style>
In navigation drawer put this in each item:
android:theme="#style/NavigationTheme"
add below code in dimens.xml
<dimen tools:override="true" name="design_navigation_icon_padding">16dp</dimen>

Android - ActionBar not resizing with onConfigurationChanged ( AppCompat )

In my application manifest I've add android:configChanges to prevent activity reload/restart on rotate
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize" >
it works, but supportActionBar ( I'm using AppCompat ) preserves his height with small font size.
ActionBar should be bigger in portrait and smaller in landscape, but it keeps the initial value:
if I start in landscape, the actionbar stay thin in portrait
if I start in portrait, the actionbar stay big in landscape
Removing android:configChanges="orientation|keyboardHidden|screenSize" is the only solution I've found, but the app restart on rotate, and I need to preserve application content
Starting in portrait
Starting in landscape
Starting in landscape and rotating screen to portrait (small action bar and small font height)
By setting android:configChanges="orientation|keyboardHidden|screenSize"
You declare that you will handle these config changes by yourself. In normal cases, you should not set that, and let Android recreate your Activity.
Edit:
If you want to keep the line android:configChanges, you have to override onConfigChanged() and change everything needed by yourself, e.g. the size of the ActionBar/ToolBar.
As others have pointed out you should save and restore the instance state instead of handling configuration changes yourself if possible. If you have good reason not to do that you can try to update the toolbar's height and text appearance after the configuration change.
The following code should work for the support library version of Toolbar. The attributes actionBarSize, titleTextAppearance and subtitleTextAppearance are provided by the support library.
The code assumes that you have a custom attribute appToolbarStyle declared in attrs.xml. If you don't need that you can adapt the code to use R.style.Widget_AppCompat_Toolbar directly instead.
import android.support.v7.widget.Toolbar;
...
private Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
toolbar = findViewById(R.id.toolbar);
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateToolbar();
}
private void updateToolbar() {
if (toolbar == null)
return;
final Context context = toolbar.getContext();
int[] attr = new int[] { R.attr.actionBarSize, R.attr.appToolbarStyle };
int idxActionBarSize = 0;
int idxAppToolbarStyle = 1;
TypedArray a = context.obtainStyledAttributes(attr);
int actionBarSize = a.getDimensionPixelSize(idxActionBarSize, 0);
int appToolbarStyle = a.getResourceId(idxAppToolbarStyle, R.style.Widget_AppCompat_Toolbar);
a.recycle();
if (actionBarSize != 0) {
ViewGroup.LayoutParams layoutParams = toolbar.getLayoutParams();
if (layoutParams != null) {
layoutParams.height = actionBarSize;
}
toolbar.setMinimumHeight(actionBarSize);
}
attr = new int[] { R.attr.titleTextAppearance, R.attr.subtitleTextAppearance };
int idxTitleTextAppearance = 0;
int idxSubtitleTextAppearance = 1;
a = context.obtainStyledAttributes(appToolbarStyle, attr);
int titleTextAppearance = a.getResourceId(idxTitleTextAppearance, 0);
int subtitleTextAppearance = a.getResourceId(idxSubtitleTextAppearance, 0);
a.recycle();
if (titleTextAppearance != 0) {
toolbar.setTitleTextAppearance(context, titleTextAppearance);
}
if (subtitleTextAppearance != 0) {
toolbar.setSubtitleTextAppearance(context, subtitleTextAppearance);
}
toolbar.requestLayout();
}
If you want to keep android:configChanges, you can use this to force 56dp toolbar height, align icons and fix small text issue:
Toolbar XML:
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:minHeight="56dp"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay"
app:titleTextAppearance="#style/titleTextAppearance" />
Styles XML:
<style name="titleTextAppearance" parent="#style/TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">20sp</item>
</style>

FloatingActionButton expand into a new activity

On the android material design principles page, one of the examples shows a FAB expanding into a new full screen. (Under "Full Screen")
http://www.google.com/design/spec/components/buttons-floating-action-button.html#buttons-floating-action-button-transitions
I've tried to implement the same effect in my app, but with little success.
I managed to create a FAB that expands into a view using this code as reference: https://gist.github.com/chris95x8/882b5c5d0aa2096236ba.
It worked, but I was wondering whether I could apply the same effect to an activity transition. I've tried looking it up and playing with it myself but could not find anything that might work.
I know I could make the FAB expand into a Fragment and not a whole new activity, but I'm not sure if that's what being done, and whether that's optimal or not.
And so my question is, is there a way to implement the fab-expanding reveal effect as an activity transition, or is it supposed to just reveal a new fragment?
I am developing an app which expands a FloatingActionButton into a new Activity. I'm not sure that if you like my implementation, but please see pictures at first:
So the first picture shows MainActivity and the last one shows SecondActivity, which is "expanded" from FAB.
Now, I want to mention that I'm not actually expanding a FAB into a new Activity but I can let user feel that the new page is expanded from that FAB, and I think that's enough for both developers and users.
Here's implementation:
Preparation:
A FloatingActionButton of course,
Visit https://github.com/kyze8439690/RevealLayout and import this library to your project. It is used to play reveal animation. It has a custom BakedBezierInterpolator to control reveal animation and make it material-styled.
Steps:
create activity_main.xml like this:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--Your main content here-->
<RevealLayout
android:id="#+id/reveal_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible">
<View
android:id="#+id/reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
</RevealLayout>
</FrameLayout>
find Views:
mRevealLayout = (RevealLayout) findViewById(R.id.reveal_layout);
mRevealView = findViewById(R.id.reveal_view);
expand when user clicks FAB:
mFab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mFab.setClickable(false); // Avoid naughty guys clicking FAB again and again...
int[] location = new int[2];
mFab.getLocationOnScreen(location);
location[0] += mFab.getWidth() / 2;
location[1] += mFab.getHeight() / 2;
final Intent intent = new Intent(MainActivity.this, SecondActivity.class);
mRevealView.setVisibility(View.VISIBLE);
mRevealLayout.setVisibility(View.VISIBLE);
mRevealLayout.show(location[0], location[1]); // Expand from center of FAB. Actually, it just plays reveal animation.
mFab.postDelayed(new Runnable() {
#Override
public void run() {
startActivity(intent);
/**
* Without using R.anim.hold, the screen will flash because of transition
* of Activities.
*/
overridePendingTransition(0, R.anim.hold);
}
}, 600); // 600 is default duration of reveal animation in RevealLayout
mFab.postDelayed(new Runnable() {
#Override
public void run() {
mFab.setClickable(true);
mRevealLayout.setVisibility(View.INVISIBLE);
mViewToReveal.setVisibility(View.INVISIBLE);
}
}, 960); // Or some numbers larger than 600.
}
});
And here is hold.xml in res/anim:
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="960" <!-- Enough-large time is OK -->
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%"/>
</set>
That's all.
Improvements:
RevealLayout has a bug(plays rectangular instead of circular reveal animation) for devices under API 17(Android 4.2), you can add these lines in constructor of it:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
If your SecondActivity contains complicated contents, a simple View used as reveal_view in the layout.xml isn't enough/perfect. You can include the second layout inside the RevealLayout reveal_layout. It seems wasteful and hard to control if the second layout won't appear same at every time. But for me, it will. So you can make other improvements if you should.
If you want to implement totally same animation shown in Material Design Guide, you can set layout_height of the RevealLayout into a specific number instead of match_parent. After expanding animation ends(or some time after the animation plays, which should make the whole process of animation smoothly), then you can animate translationY. The important point is, just cheat users visually by controlling animation duration.
Finally, this is my own experience/attempt and I'm a beginner in developing Android apps. If there are any mistakes/further improvements, please leave comments/edit my answer. Thank you.
I made a custom activity, based on this question Circular reveal transition for new activity , that handle the CircularRevealAnimation and his reverse effect when the activity finish:
public class RevealActivity extends AppCompatActivity {
private View revealView;
public static final String REVEAL_X="REVEAL_X";
public static final String REVEAL_Y="REVEAL_Y";
public void showRevealEffect(Bundle savedInstanceState, final View rootView) {
revealView=rootView;
if (savedInstanceState == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
rootView.setVisibility(View.INVISIBLE);
ViewTreeObserver viewTreeObserver = rootView.getViewTreeObserver();
if(viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
circularRevealActivity(rootView);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
}
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void circularRevealActivity(View rootView) {
int cx = getIntent().getIntExtra(REVEAL_X, 0);
int cy = getIntent().getIntExtra(REVEAL_Y, 0);
float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());
// create the animator for this view (the start radius is zero)
Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, 0, finalRadius);
circularReveal.setDuration(400);
// make the view visible and start the animation
rootView.setVisibility(View.VISIBLE);
circularReveal.start();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: onBackPressed();break;
return super.onOptionsItemSelected(item);
}
}
#Override
public void onBackPressed() {
destroyActivity(revealView);
}
private void destroyActivity(View rootView) {
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
destroyCircularRevealActivity(rootView);
else
finish();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void destroyCircularRevealActivity(final View rootView) {
int cx = getIntent().getIntExtra(REVEAL_X, 0);
int cy = getIntent().getIntExtra(REVEAL_Y, 0);
float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());
// create the animator for this view (the start radius is zero)
Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, finalRadius, 0);
circularReveal.setDuration(400);
circularReveal.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationEnd(Animator animator) {
rootView.setVisibility(View.INVISIBLE);
finishAfterTransition();
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
// make the view visible and start the animation
rootView.setVisibility(View.VISIBLE);
circularReveal.start();
}
}
You can extend this with your own activity and call in your onCreate the method 'showRevealEffect' like this:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activity_layout);
//your code
View root= findViewById(R.id.your_root_id);
showRevealEffect(savedInstanceState, root);
}
You also have to use a transparent theme like this one:
<style name="Theme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#android:color/transparent</item>
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="colorControlNormal">#android:color/white</item>
</style>
In the end, to launch this activity you should pass via extra the coordinates where the animation should start:
int[] location = new int[2];
fab.getLocationOnScreen(location);
Intent intent = new Intent(this, YourRevealActivity.class);
intent.putExtra(SearchActivity.REVEAL_X, location[0]);
intent.putExtra(SearchActivity.REVEAL_Y, location[1]);
startActivity(intent);
you can use this lib [https://github.com/sergiocasero/RevealFAB][1]
[1]: https://github.com/sergiocasero/RevealFAB 3rd party its easy and simple to use
Add to your layout
<RelativeLayout...>
<android.support.design.widget.CoordinatorLayout...>
<!-- YOUR CONTENT -->
</android.support.design.widget.CoordinatorLayout>
<com.sergiocasero.revealfab.RevealFAB
android:id="#+id/reveal_fab"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fab_color="#color/colorAccent"
app:fab_icon="#drawable/ic_add_white_24dp"
app:reveal_color="#color/colorAccent" />
</RelativeLayout>
Important: This component goes above your content. You can use Coordinator, LinearLayout... or another Relative layout if you want :)
As you can see, you have 3 custom attributes for customizing colors and icon
Setting information about intent:
revealFAB = (RevealFAB) findViewById(R.id.reveal_fab);
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
revealFAB.setIntent(intent);
revealFAB.setOnClickListener(new RevealFAB.OnClickListener() {
#Override
public void onClick(RevealFAB button, View v) {
button.startActivityWithAnimation();
}
});
Don't forget call onResume() method!
#Override
protected void onResume() {
super.onResume();
revealFAB.onResume();
}
Someone investigated the implementation of transition between activities from Plaid. Her example were published via https://github.com/hujiaweibujidao/FabDialogMorph.
Briefly speaking, she transits two activities with:
The FAB as the shared element.
The layout in the target activity with the same android:transitionName as the FAB.
To smooth the animation, MorphDrawable (extended from Drawable) and MorphTransition (extended from ChangeBounds) are implemented and applied.

How can I Animate the color change of the statusbar and toolbar (like the new Calendar app does)

The new Google Calendar app has an animation I would like to do in my app. When you create a new event you can choose a color for the event. When you do, the statusbar and toolbar change to that color with a circular effect that covers both of them.
Here's an example of what I'd like to do:
I can change the color of the statusbar and toolbar, but how can I apply the circular animation effect (or similar) to both of them as the color is changed?
I don't know if this is the exact way the Calendar app does it, but it's close enough for me.
Caveats
The method uses the ViewAnimationUtils.createCircularReveal method introduced in Lollipop.
It requires knowing the height of the status bar and your toolbar actionbar. You can still use ?attr/actionBarSize for your actionbar and get both dynamically, but for simplicity here I've assumed 56dp for the actionbar height and 24dp for the status bar height.
General Idea
The general idea is to set your actionbar and status bar to transparent. This will shift your actionbar up under the statusbar so you have to adjust the size and padding of the actionbar to compensate. You then use a view behind it and ViewAnimationUtils.createCircularReveal to reveal the new background color. You need one more view behind that to show the old background color as the middle view is revealing the new one.
The Animation
The animation requires:
The transparent toolbar actionbar that covers the space of the regular actionbar and the statusbar. The hard-coded height, in this case, is 56dp (actionbar) + 24dp (statusbar) = 80dp. You also need to set the top padding to 24dp to keep the actionbar content our from under the statusbar.
A middle view (I'll call it the reveal view) that's the same size (80dp height) but just behind the actionbar. This will be the view the ViewAnimationUtils.createCircularReveal acts on.
A bottom view (I'll call it the reveal background view) that's the same size as the reveal view but behind it. This view is there to show the old background color while the reveal view is revealing the new color on top of it.
Code
Here are the key pieces of code I used. See the example project at 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);
}
}
Notes
Be careful of the android:elevation property on the toolbar, reveal, and reveal background views. If the elevation is lower on the toolbar, the others will cover the buttons and text.
I don't know how they achieved the ripple effect, but you can have a smooth color transition of both bars simultaneously with the following code.
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);
}
After A great amount of research ,
I've found an answer that you'd want.
This animation is called a reveal animation introduced in the 21.0 Android API - lollipop . Unfortunately , it is not backwards compatible.
I've found a library which does the same reveal animation but not exactly a backport , but the effect you want can be achieved API 14 onwards with this library
https://github.com/markushi/android-ui
Thank You,
If you are keen on using this animation only with lollipop then just google "Android Reveal Colour Animation implementation".
Try this, it works great for me and it gets the same effect Google Calendar app does.
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);
}

How to implement dynamic values on menu item in Android [duplicate]

This question already has answers here:
How to get text on an ActionBar Icon?
(4 answers)
Closed 4 years ago.
I have a menu item in the action bar. Along with the menu item image, I need to show some number associated with it which will change often. I am not using Action bar sherlock. I don't want to use that. Other than this everything else just works fine. In the shown image, the white icon color icon is mine. I need to generate the number with the red color background dynamically. How can I do that in Android?
Here is the sample image:
Update:
I have this menu item in my menu.xml. This should work like a notification menu item which shows the number of notification count. I set the menu icon like,
menuItem.setIcon(image);
Now, on top of the menu item I need to place one text view which has the total count of notifications.
is it possible to implement this functionality with viewbadger?
Github url
I discovered how to add an actionView to a menu item and retrieve as set values to the view in code.
See here: https://stackoverflow.com/a/16648170/857681
After a lot of trying of nearly all resources on SO I turned to blogs; succesfully. I want to share what worked for me (Api >= 13); source.
Let's start with the sweet code, the way it's used:
public boolean onCreateOptionsMenu(Menu menu) {
//inflate menu
getMenuInflater().inflate(R.menu.menu_my, menu);
// Get the notifications MenuItem and LayerDrawable (layer-list)
MenuItem item = menu.findItem(R.id.action_notifications);
LayerDrawable icon = (LayerDrawable) item.getIcon();
// Update LayerDrawable's BadgeDrawable
Utils2.setBadgeCount(this, icon, 2);
return true;
}
The menu_my.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item
android:id="#+id/action_notifications"
android:icon="#drawable/ic_menu_notifications"
android:title="Notifications"
app:showAsAction="always" />
</menu>
This class that conveniently makes a BadgeDrawable; its appearance can be modified as well:
public class BadgeDrawable extends Drawable {
private float mTextSize;
private Paint mBadgePaint;
private Paint mTextPaint;
private Rect mTxtRect = new Rect();
private String mCount = "";
private boolean mWillDraw = false;
public BadgeDrawable(Context context) {
//mTextSize = context.getResources().getDimension(R.dimen.badge_text_size);
mTextSize = 12F;
mBadgePaint = new Paint();
mBadgePaint.setColor(Color.RED);
mBadgePaint.setAntiAlias(true);
mBadgePaint.setStyle(Paint.Style.FILL);
mTextPaint = new Paint();
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
#Override
public void draw(Canvas canvas) {
if (!mWillDraw) {
return;
}
Rect bounds = getBounds();
float width = bounds.right - bounds.left;
float height = bounds.bottom - bounds.top;
// Position the badge in the top-right quadrant of the icon.
float radius = ((Math.min(width, height) / 2) - 1) / 2;
float centerX = width - radius - 1;
float centerY = radius + 1;
// Draw badge circle.
canvas.drawCircle(centerX, centerY, radius, mBadgePaint);
// Draw badge count text inside the circle.
mTextPaint.getTextBounds(mCount, 0, mCount.length(), mTxtRect);
float textHeight = mTxtRect.bottom - mTxtRect.top;
float textY = centerY + (textHeight / 2f);
canvas.drawText(mCount, centerX, textY, mTextPaint);
}
/*
Sets the count (i.e notifications) to display.
*/
public void setCount(int count) {
mCount = Integer.toString(count);
// Only draw a badge if there are notifications.
mWillDraw = count > 0;
invalidateSelf();
}
#Override
public void setAlpha(int alpha) {
// do nothing
}
#Override
public void setColorFilter(ColorFilter cf) {
// do nothing
}
#Override
public int getOpacity() {
return PixelFormat.UNKNOWN;
}
}
This class that helps to set the number. I recommend implementing even more thods to set badge as date, etc:
public class Utils2 {
public static void setBadgeCount(Context context, LayerDrawable icon, int count) {
BadgeDrawable badge;
// Reuse drawable if possible
Drawable reuse = icon.findDrawableByLayerId(R.id.ic_badge);
if (reuse != null && reuse instanceof BadgeDrawable) {
badge = (BadgeDrawable) reuse;
} else {
badge = new BadgeDrawable(context);
}
badge.setCount(count);
icon.mutate();
icon.setDrawableByLayerId(R.id.ic_badge, badge);
}
}
And mui importante a drawable (like a layout) in res/drawable:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/ic_notification"
android:drawable="#drawable/ice_skate"
android:gravity="center" />
<!-- set a place holder Drawable so android:drawable isn't null -->
<item
android:id="#+id/ic_badge"
android:drawable="#drawable/ice_skate" />
</layer-list>
Good lucks!
Here is one thing you can try:
Create a custom Drawable that paint you image in the background and text on top of the image. Check out this post for sample.
Then set this Drawable as the MenuItem background dynamically...
Use action view. It works with both: default ActionBar and ActionBarSherlock.
Here is an example
With this approach you can just create your own View (by inflating some layout for example) and then do whatever you want (change background, change content, add another views dynamically if your action view is subclass of ViewGroup etc.).

Categories

Resources