ActionBar
beautifully and uniquely. Although I mentioned some of the possibilities of applying this effect, I never had the time to add this kind of ActionBar
animation to any of my own applications. But I saw an application in the Play Store using it.ActionBar
. Let's be honest, it literally blew my mind when I first saw it. I fell in love with a nice, subtle, and extremely useful animated effect, and probably even more than in the app itself! I am sure you know the application I am talking about, since it was presented during Google I / O. This application is Play Music!ActionBar
initially invisible and is layered on a large image describing the artist or album. But as soon as you start scrolling down the page (if it is possible), the ActionBar
gradually appears. ActionBar
turns out to be completely opaque when the image becomes scrolled behind the screen.ActionBar
animation:ActionBar
must overlap the screen. This is done using the android:windowActionBarOverlay
XML attribute. The code below describes the definition of a theme that we will use: <?xml version="1.0" encoding="utf-8"?> <resources> <style name="Theme.TranslucentActionBar" parent="@android:style/Theme.Holo.Light.DarkActionBar"> <item name="android:actionBarStyle">@style/Widget.ActionBar</item> </style> <style name="Theme.TranslucentActionBar.ActionBar" /> <style name="Theme.TranslucentActionBar.ActionBar.Overlay"> <item name="android:actionBarStyle">@style/Widget.ActionBar.Transparent</item> <item name="android:windowActionBarOverlay">true</item> </style> </resources>
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="Widget.ActionBar" parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse"> <item name="android:background">@drawable/ab_background</item> </style> <style name="Widget.ActionBar.Transparent"> <item name="android:background">@android:color/transparent</item> </style> </resources>
Activity
: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cyrilmottier.android.translucentactionbar" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.TranslucentActionBar"> <activity android:name=".HomeActivity" android:theme="@style/Theme.TranslucentActionBar.ActionBar.Overlay"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
ActionBar
synchronized on the pixel state of the scroll container. In this example, we'll just use ScrollView
as a scroll container. One of the major drawbacks of this container is that you cannot register a listener in order to receive notifications when the scrolling state has changed. But this can be easily circumvented by creating the NotifyingScrollView
inherited from the original ScrollView
: package com.cyrilmottier.android.translucentactionbar; import android.content.Context; import android.util.AttributeSet; import android.widget.ScrollView; /** * @author Cyril Mottier */ public class NotifyingScrollView extends ScrollView { /** * @author Cyril Mottier */ public interface OnScrollChangedListener { void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt); } private OnScrollChangedListener mOnScrollChangedListener; public NotifyingScrollView(Context context) { super(context); } public NotifyingScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public NotifyingScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (mOnScrollChangedListener != null) { mOnScrollChangedListener.onScrollChanged(this, l, t, oldl, oldt); } } public void setOnScrollChangedListener(OnScrollChangedListener listener) { mOnScrollChangedListener = listener; } }
<?xml version="1.0" encoding="utf-8"?> <com.cyrilmottier.android.translucentactionbar.NotifyingScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/image_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="centerCrop" android:src="@drawable/daft_punk"/> <! -- Some long content --> </LinearLayout> </com.cyrilmottier.android.translucentactionbar.NotifyingScrollView>
ActionBar
quite simple and consists only of calculating the alpha channel depending on the current pixel state of our scrolling container NotifyingScrollView
. It should be noted that the effective scroll distance must be attached to [0, image_height - actionbar_height]
to avoid erroneous values ββthat may arise due to the behavior of scroll container bars in Android: package com.cyrilmottier.android.translucentactionbar; import android.app.Activity; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.util.Log; import android.view.Menu; import android.widget.ScrollView; public class HomeActivity extends Activity { private Drawable mActionBarBackgroundDrawable; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); mActionBarBackgroundDrawable = getResources().getDrawable(R.drawable.ab_background); mActionBarBackgroundDrawable.setAlpha(0); getActionBar().setBackgroundDrawable(mActionBarBackgroundDrawable); ((NotifyingScrollView) findViewById(R.id.scroll_view)).setOnScrollChangedListener(mOnScrollChangedListener); } private NotifyingScrollView.OnScrollChangedListener mOnScrollChangedListener = new NotifyingScrollView.OnScrollChangedListener() { public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) { final int headerHeight = findViewById(R.id.image_header).getHeight() - getActionBar().getHeight(); final float ratio = (float) Math.min(Math.max(t, 0), headerHeight) / headerHeight; final int newAlpha = (int) (ratio * 255); mActionBarBackgroundDrawable.setAlpha(newAlpha); } }; }
ActionBar
does not denote itself invalid when required, since it does not register itself as a callback for Drawable
. You can work around this problem by simply attaching the following onCreate(Bundle)
in the onCreate(Bundle)
method: private Drawable.Callback mDrawableCallback = new Drawable.Callback() { @Override public void invalidateDrawable(Drawable who) { getActionBar().setBackgroundDrawable(who); } @Override public void scheduleDrawable(Drawable who, Runnable what, long when) { } @Override public void unscheduleDrawable(Drawable who, Runnable what) { } };
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { mActionBarBackgroundDrawable.setCallback(mDrawableCallback); }
ActionBar
can lead to design issues, because you usually donβt know about the background color that you will display at the top. For example, you can end up displaying a transparent ActionBar
with a white header and a white image in the description. Needless to say, this makes ActionBar
invisible and useless.ActionBar
making its content (name, icons, buttons, etc.) visible.Drawable
: <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <size android:height="100dp"/> <gradient android:angle="270" android:startColor="#8000" android:endColor="#0000"/> </shape>
FrameLayout
: <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/image_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="centerCrop" android:src="@drawable/daft_punk"/> <View android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/gradient"/> </FrameLayout>
EdgeEffect
was introduced for the first time (available in API starting at API 14), including over-scroll. Although this is not a problem, but in general, it can be very annoying when one of the edges of the scrollable content differs from the background color.ScrollView
up. And you will notice that something white (background color) appears at the top of the screen, because the image is scrolled beyond its limits. Personally, I consider this an interface glitch and usually prefer to disable this behavior.View#setOverScrollMode(int)
to change the View#OVER_SCROLL_NEVER
mode View#OVER_SCROLL_NEVER
. Although it works, this option also removes the EdgeEffect
, which may not be visually acceptable. Therefore, it is easier to change the NotifyingScrollView
to limit the over-scroll values ββto zero when necessary: private boolean mIsOverScrollEnabled = true; public void setOverScrollEnabled(boolean enabled) { mIsOverScrollEnabled = enabled; } public boolean isOverScrollEnabled() { return mIsOverScrollEnabled; } @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { return super.overScrollBy( deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, mIsOverScrollEnabled ? maxOverScrollX : 0, mIsOverScrollEnabled ? maxOverScrollY : 0, isTouchEvent); }
Source: https://habr.com/ru/post/190102/
All Articles