Android自定义状态栏颜色与应用标题栏颜色一致
每次看ios上的应用,应用中状态栏的颜色总能与应用标题栏颜色保持一致,用户体验很不错,对于这种效果,像我这种好奇心强的人就会去看看那安卓是否可以呢?若是在安卓4.4之前,答案是否定的,但在4.4之后,谷歌允许开发者自定义状态栏背景颜色啦,这是个不错的体验!若你手机上安装有最新版的qq,并且你的安卓sdk版本是4.4及以上,你可以看下它的效果:
实现此功能有两种方法:
1.在xml中设置主题或自定义style;
theme.holo.light.noactionbar.translucentdecor
theme.holo.noactionbar.translucentdecor
<style name="apptheme" parent="appbasetheme"> <!-- status bar --> <item name="android:windowtranslucentstatus">true</item> <!-- navigation bar --> <item name="android:windowtranslucentnavigation">true</item> </style>
鉴于市面上各种手机的sdk的各种版本,不建议采用这种方法;
2.在代码中控制;
首先创建一个activity,在oncreate方法中进行处理:
@override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); if (build.version.sdk_int >= build.version_codes.kitkat) { settranslucentstatus(true); systembartintmanager tintmanager = new systembartintmanager(this); tintmanager.setstatusbartintenabled(true); tintmanager.setstatusbartintresource(r.color.barcolor);//通知栏所需颜色 } setcontentview(r.layout.activity_main); } @targetapi(19) private void settranslucentstatus(boolean on) { window win = getwindow(); windowmanager.layoutparams winparams = win.getattributes(); final int bits = windowmanager.layoutparams.flag_translucent_status; if (on) { winparams.flags |= bits; } else { winparams.flags &= ~bits; } win.setattributes(winparams); }
需注意的是, tintmanager.setstatusbartintresource(r.color.top_bg_color);这一步的颜色值(即把你的状态栏颜色与你的标题栏颜色保持一致)要写在color.xml中去,如果用color.prasecolor则会报错。
systembartintmanager.java文件
package com.test.statusbarcolor; import android.annotation.suppresslint; import android.annotation.targetapi; import android.app.activity; import android.content.context; import android.content.res.configuration; import android.content.res.resources; import android.content.res.typedarray; import android.graphics.drawable.drawable; import android.os.build; import android.util.displaymetrics; import android.util.typedvalue; import android.view.gravity; import android.view.view; import android.view.viewconfiguration; import android.view.viewgroup; import android.view.window; import android.view.windowmanager; import android.widget.framelayout.layoutparams; import java.lang.reflect.method; /** * created by lplj on 2016/9/6. * class to manage status and navigation bar tint effects when using kitkat * translucent system ui modes. * */ @suppresswarnings({ "rawtypes", "unchecked" }) public class systembartintmanager { static { // android allows a system property to override the presence of the navigation bar. // used by the emulator. // see https://github.com/android/platform_frameworks_base/blob/master/policy/src/com/android/internal/policy/impl/phonewindowmanager.java#l1076 if (build.version.sdk_int >= build.version_codes.kitkat) { try { class c = class.forname("android.os.systemproperties"); method m = c.getdeclaredmethod("get", string.class); m.setaccessible(true); snavbaroverride = (string) m.invoke(null, "qemu.hw.mainkeys"); } catch (throwable e) { snavbaroverride = null; } } } /** * the default system bar tint color value. */ public static final int default_tint_color = 0x99000000; private static string snavbaroverride; private final systembarconfig mconfig; private boolean mstatusbaravailable; private boolean mnavbaravailable; private boolean mstatusbartintenabled; private boolean mnavbartintenabled; private view mstatusbartintview; private view mnavbartintview; /** * constructor. call this in the host activity oncreate method after its * content view has been set. you should always create new instances when * the host activity is recreated. * * @param activity the host activity. */ @targetapi(19) public systembartintmanager(activity activity) { window win = activity.getwindow(); viewgroup decorviewgroup = (viewgroup) win.getdecorview(); if (build.version.sdk_int >= build.version_codes.kitkat) { // check theme attrs int[] attrs = {android.r.attr.windowtranslucentstatus, android.r.attr.windowtranslucentnavigation}; typedarray a = activity.obtainstyledattributes(attrs); try { mstatusbaravailable = a.getboolean(0, false); mnavbaravailable = a.getboolean(1, false); } finally { a.recycle(); } // check window flags windowmanager.layoutparams winparams = win.getattributes(); int bits = windowmanager.layoutparams.flag_translucent_status; if ((winparams.flags & bits) != 0) { mstatusbaravailable = true; } bits = windowmanager.layoutparams.flag_translucent_navigation; if ((winparams.flags & bits) != 0) { mnavbaravailable = true; } } mconfig = new systembarconfig(activity, mstatusbaravailable, mnavbaravailable); // device might not have virtual navigation keys if (!mconfig.hasnavigtionbar()) { mnavbaravailable = false; } if (mstatusbaravailable) { setupstatusbarview(activity, decorviewgroup); } if (mnavbaravailable) { setupnavbarview(activity, decorviewgroup); } } /** * enable tinting of the system status bar. * * if the platform is running jelly bean or earlier, or translucent system * ui modes have not been enabled in either the theme or via window flags, * then this method does nothing. * * @param enabled true to enable tinting, false to disable it (default). */ public void setstatusbartintenabled(boolean enabled) { mstatusbartintenabled = enabled; if (mstatusbaravailable) { mstatusbartintview.setvisibility(enabled ? view.visible : view.gone); } } /** * enable tinting of the system navigation bar. * * if the platform does not have soft navigation keys, is running jelly bean * or earlier, or translucent system ui modes have not been enabled in either * the theme or via window flags, then this method does nothing. * * @param enabled true to enable tinting, false to disable it (default). */ public void setnavigationbartintenabled(boolean enabled) { mnavbartintenabled = enabled; if (mnavbaravailable) { mnavbartintview.setvisibility(enabled ? view.visible : view.gone); } } /** * apply the specified color tint to all system ui bars. * * @param color the color of the background tint. */ public void settintcolor(int color) { setstatusbartintcolor(color); setnavigationbartintcolor(color); } /** * apply the specified drawable or color resource to all system ui bars. * * @param res the identifier of the resource. */ public void settintresource(int res) { setstatusbartintresource(res); setnavigationbartintresource(res); } /** * apply the specified drawable to all system ui bars. * * @param drawable the drawable to use as the background, or null to remove it. */ public void settintdrawable(drawable drawable) { setstatusbartintdrawable(drawable); setnavigationbartintdrawable(drawable); } /** * apply the specified alpha to all system ui bars. * * @param alpha the alpha to use */ public void settintalpha(float alpha) { setstatusbaralpha(alpha); setnavigationbaralpha(alpha); } /** * apply the specified color tint to the system status bar. * * @param color the color of the background tint. */ public void setstatusbartintcolor(int color) { if (mstatusbaravailable) { mstatusbartintview.setbackgroundcolor(color); } } /** * apply the specified drawable or color resource to the system status bar. * * @param res the identifier of the resource. */ public void setstatusbartintresource(int res) { if (mstatusbaravailable) { mstatusbartintview.setbackgroundresource(res); } } /** * apply the specified drawable to the system status bar. * * @param drawable the drawable to use as the background, or null to remove it. */ @suppresswarnings("deprecation") public void setstatusbartintdrawable(drawable drawable) { if (mstatusbaravailable) { mstatusbartintview.setbackgrounddrawable(drawable); } } /** * apply the specified alpha to the system status bar. * * @param alpha the alpha to use */ @targetapi(11) public void setstatusbaralpha(float alpha) { if (mstatusbaravailable && build.version.sdk_int >= build.version_codes.honeycomb) { mstatusbartintview.setalpha(alpha); } } /** * apply the specified color tint to the system navigation bar. * * @param color the color of the background tint. */ public void setnavigationbartintcolor(int color) { if (mnavbaravailable) { mnavbartintview.setbackgroundcolor(color); } } /** * apply the specified drawable or color resource to the system navigation bar. * * @param res the identifier of the resource. */ public void setnavigationbartintresource(int res) { if (mnavbaravailable) { mnavbartintview.setbackgroundresource(res); } } /** * apply the specified drawable to the system navigation bar. * * @param drawable the drawable to use as the background, or null to remove it. */ @suppresswarnings("deprecation") public void setnavigationbartintdrawable(drawable drawable) { if (mnavbaravailable) { mnavbartintview.setbackgrounddrawable(drawable); } } /** * apply the specified alpha to the system navigation bar. * * @param alpha the alpha to use */ @targetapi(11) public void setnavigationbaralpha(float alpha) { if (mnavbaravailable && build.version.sdk_int >= build.version_codes.honeycomb) { mnavbartintview.setalpha(alpha); } } /** * get the system bar configuration. * * @return the system bar configuration for the current device configuration. */ public systembarconfig getconfig() { return mconfig; } /** * is tinting enabled for the system status bar? * * @return true if enabled, false otherwise. */ public boolean isstatusbartintenabled() { return mstatusbartintenabled; } /** * is tinting enabled for the system navigation bar? * * @return true if enabled, false otherwise. */ public boolean isnavbartintenabled() { return mnavbartintenabled; } private void setupstatusbarview(context context, viewgroup decorviewgroup) { mstatusbartintview = new view(context); layoutparams params = new layoutparams(layoutparams.match_parent, mconfig.getstatusbarheight()); params.gravity = gravity.top; if (mnavbaravailable && !mconfig.isnavigationatbottom()) { params.rightmargin = mconfig.getnavigationbarwidth(); } mstatusbartintview.setlayoutparams(params); mstatusbartintview.setbackgroundcolor(default_tint_color); mstatusbartintview.setvisibility(view.gone); decorviewgroup.addview(mstatusbartintview); } private void setupnavbarview(context context, viewgroup decorviewgroup) { mnavbartintview = new view(context); layoutparams params; if (mconfig.isnavigationatbottom()) { params = new layoutparams(layoutparams.match_parent, mconfig.getnavigationbarheight()); params.gravity = gravity.bottom; } else { params = new layoutparams(mconfig.getnavigationbarwidth(), layoutparams.match_parent); params.gravity = gravity.right; } mnavbartintview.setlayoutparams(params); mnavbartintview.setbackgroundcolor(default_tint_color); mnavbartintview.setvisibility(view.gone); decorviewgroup.addview(mnavbartintview); } /** * class which describes system bar sizing and other characteristics for the current * device configuration. * */ public static class systembarconfig { private static final string status_bar_height_res_name = "status_bar_height"; private static final string nav_bar_height_res_name = "navigation_bar_height"; private static final string nav_bar_height_landscape_res_name = "navigation_bar_height_landscape"; private static final string nav_bar_width_res_name = "navigation_bar_width"; private static final string show_nav_bar_res_name = "config_shownavigationbar"; private final boolean mtranslucentstatusbar; private final boolean mtranslucentnavbar; private final int mstatusbarheight; private final int mactionbarheight; private final boolean mhasnavigationbar; private final int mnavigationbarheight; private final int mnavigationbarwidth; private final boolean minportrait; private final float msmallestwidthdp; private systembarconfig(activity activity, boolean translucentstatusbar, boolean traslucentnavbar) { resources res = activity.getresources(); minportrait = (res.getconfiguration().orientation == configuration.orientation_portrait); msmallestwidthdp = getsmallestwidthdp(activity); mstatusbarheight = getinternaldimensionsize(res, status_bar_height_res_name); mactionbarheight = getactionbarheight(activity); mnavigationbarheight = getnavigationbarheight(activity); mnavigationbarwidth = getnavigationbarwidth(activity); mhasnavigationbar = (mnavigationbarheight > 0); mtranslucentstatusbar = translucentstatusbar; mtranslucentnavbar = traslucentnavbar; } @targetapi(14) private int getactionbarheight(context context) { int result = 0; if (build.version.sdk_int >= build.version_codes.ice_cream_sandwich) { typedvalue tv = new typedvalue(); context.gettheme().resolveattribute(android.r.attr.actionbarsize, tv, true); result = typedvalue.complextodimensionpixelsize(tv.data, context.getresources().getdisplaymetrics()); } return result; } @targetapi(14) private int getnavigationbarheight(context context) { resources res = context.getresources(); int result = 0; if (build.version.sdk_int >= build.version_codes.ice_cream_sandwich) { if (hasnavbar(context)) { string key; if (minportrait) { key = nav_bar_height_res_name; } else { key = nav_bar_height_landscape_res_name; } return getinternaldimensionsize(res, key); } } return result; } @targetapi(14) private int getnavigationbarwidth(context context) { resources res = context.getresources(); int result = 0; if (build.version.sdk_int >= build.version_codes.ice_cream_sandwich) { if (hasnavbar(context)) { return getinternaldimensionsize(res, nav_bar_width_res_name); } } return result; } @targetapi(14) private boolean hasnavbar(context context) { resources res = context.getresources(); int resourceid = res.getidentifier(show_nav_bar_res_name, "bool", "android"); if (resourceid != 0) { boolean hasnav = res.getboolean(resourceid); // check override flag (see static block) if ("1".equals(snavbaroverride)) { hasnav = false; } else if ("0".equals(snavbaroverride)) { hasnav = true; } return hasnav; } else { // fallback return !viewconfiguration.get(context).haspermanentmenukey(); } } private int getinternaldimensionsize(resources res, string key) { int result = 0; int resourceid = res.getidentifier(key, "dimen", "android"); if (resourceid > 0) { result = res.getdimensionpixelsize(resourceid); } return result; } @suppresslint("newapi") private float getsmallestwidthdp(activity activity) { displaymetrics metrics = new displaymetrics(); if (build.version.sdk_int >= build.version_codes.jelly_bean) { activity.getwindowmanager().getdefaultdisplay().getrealmetrics(metrics); } else { // todo this is not correct, but we don't really care pre-kitkat activity.getwindowmanager().getdefaultdisplay().getmetrics(metrics); } float widthdp = metrics.widthpixels / metrics.density; float heightdp = metrics.heightpixels / metrics.density; return math.min(widthdp, heightdp); } /** * should a navigation bar appear at the bottom of the screen in the current * device configuration? a navigation bar may appear on the right side of * the screen in certain configurations. * * @return true if navigation should appear at the bottom of the screen, false otherwise. */ public boolean isnavigationatbottom() { return (msmallestwidthdp >= 600 || minportrait); } /** * get the height of the system status bar. * * @return the height of the status bar (in pixels). */ public int getstatusbarheight() { return mstatusbarheight; } /** * get the height of the action bar. * * @return the height of the action bar (in pixels). */ public int getactionbarheight() { return mactionbarheight; } /** * does this device have a system navigation bar? * * @return true if this device uses soft key navigation, false otherwise. */ public boolean hasnavigtionbar() { return mhasnavigationbar; } /** * get the height of the system navigation bar. * * @return the height of the navigation bar (in pixels). if the device does not have * soft navigation keys, this will always return 0. */ public int getnavigationbarheight() { return mnavigationbarheight; } /** * get the width of the system navigation bar when it is placed vertically on the screen. * * @return the width of the navigation bar (in pixels). if the device does not have * soft navigation keys, this will always return 0. */ public int getnavigationbarwidth() { return mnavigationbarwidth; } /** * get the layout inset for any system ui that appears at the top of the screen. * * @param withactionbar true to include the height of the action bar, false otherwise. * @return the layout inset (in pixels). */ public int getpixelinsettop(boolean withactionbar) { return (mtranslucentstatusbar ? mstatusbarheight : 0) + (withactionbar ? mactionbarheight : 0); } /** * get the layout inset for any system ui that appears at the bottom of the screen. * * @return the layout inset (in pixels). */ public int getpixelinsetbottom() { if (mtranslucentnavbar && isnavigationatbottom()) { return mnavigationbarheight; } else { return 0; } } /** * get the layout inset for any system ui that appears at the right of the screen. * * @return the layout inset (in pixels). */ public int getpixelinsetright() { if (mtranslucentnavbar && !isnavigationatbottom()) { return mnavigationbarwidth; } else { return 0; } } } }
接下来我们看看效果图(前一个图是没有设置颜色的效果图,后一个是设置颜色的效果图):
貌似已经达到效果了,但仔细观察,好像标题栏被提上去了,就是说app界面全屏了,状态了盖在了app上,恩,这并非我们想要的效果,那如何将界面从状态栏下部开始呢,只需要在activity的布局文件最外层控件加上一个属性:
android:fitssystemwindows="true"就可以啦!看下效果:
大功告成!
ps:在使用过程中发现了一些问题,使用以上方法对单个activity有效,但是对继承了tabactivity的导航页怎么办呢?假如mainactivity继承了tabactivity,tab1activity、tab2activity、tab3activity是三个子项,那么设置状态栏的代码需写在mainactivity中,而 android:fitssystemwindows="true"需写在三个子activity的xml布局文件中,这样设置后仍然有问题,就是进入应用后首页也就是tab1activity没有问题,而tab2activity、tab3activity却没达到效果,它们的效果相当于未加android:fitssystemwindows="true"时的效果,期初我怀疑是activity不同的原因,因此我把tab1activity和tab3activity调了下位置,结果tab3activity成为首页后正常,而tab1activity又不正常了,百思不得姐,最后实在没办法,就在tab2activity、tab3activity的oncreate方法中加了几句代码:
if (build.version.sdk_int >= build.version_codes.kitkat) { ((linearlayout)findviewbyid(r.id.ll)).setpadding(0, sysutils.getstatusheight(this), 0,0); }
意思是,先求出状态栏高度,然后设置最外层控件的paddingtop值为状态栏高度,结果正好达到效果,至于为什么只有首页activity可以达到效果,而后面的子项无法达到效果,本人也在郁闷中,有知道的朋友可以分享下!
状态栏高度算法:
/** * 状态栏高度算法 * @param activity * @return */ public static int getstatusheight(activity activity){ int statusheight = 0; rect localrect = new rect(); activity.getwindow().getdecorview().getwindowvisibledisplayframe(localrect); statusheight = localrect.top; if (0 == statusheight){ class<?> localclass; try { localclass = class.forname("com.android.internal.r$dimen"); object localobject = localclass.newinstance(); int i5 = integer.parseint(localclass.getfield("status_bar_height").get(localobject).tostring()); statusheight = activity.getresources().getdimensionpixelsize(i5); } catch (classnotfoundexception e) { e.printstacktrace(); } catch (illegalaccessexception e) { e.printstacktrace(); } catch (instantiationexception e) { e.printstacktrace(); } catch (numberformatexception e) { e.printstacktrace(); } catch (illegalargumentexception e) { e.printstacktrace(); } catch (securityexception e) { e.printstacktrace(); } catch (nosuchfieldexception e) { e.printstacktrace(); } } return statusheight; }
伴随着android5.0的发布也更新了support-v7-appcompat 到v21,其中增加了toolbar,recyclerview,cardview等控件。
android5.0对改变app样式下了不小的功夫,写法也和以前不一样了,这里也简单介绍一下
coloraccent,colorprimary,colorprimarydark的作用:
这样是不是清晰很多了,继续加油吧,编程的路还很长,我们慢慢走