欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义状态栏颜色与应用标题栏颜色一致

程序员文章站 2024-03-05 23:40:01
每次看ios上的应用,应用中状态栏的颜色总能与应用标题栏颜色保持一致,用户体验很不错,对于这种效果,像我这种好奇心强的人就会去看看那安卓是否可以呢?若是在安卓4.4之前,答...

每次看ios上的应用,应用中状态栏的颜色总能与应用标题栏颜色保持一致,用户体验很不错,对于这种效果,像我这种好奇心强的人就会去看看那安卓是否可以呢?若是在安卓4.4之前,答案是否定的,但在4.4之后,谷歌允许开发者自定义状态栏背景颜色啦,这是个不错的体验!若你手机上安装有最新版的qq,并且你的安卓sdk版本是4.4及以上,你可以看下它的效果:

Android自定义状态栏颜色与应用标题栏颜色一致

实现此功能有两种方法:

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; 
} 
} 
} 
}

接下来我们看看效果图(前一个图是没有设置颜色的效果图,后一个是设置颜色的效果图):

Android自定义状态栏颜色与应用标题栏颜色一致Android自定义状态栏颜色与应用标题栏颜色一致

貌似已经达到效果了,但仔细观察,好像标题栏被提上去了,就是说app界面全屏了,状态了盖在了app上,恩,这并非我们想要的效果,那如何将界面从状态栏下部开始呢,只需要在activity的布局文件最外层控件加上一个属性:

android:fitssystemwindows="true"就可以啦!看下效果:

Android自定义状态栏颜色与应用标题栏颜色一致

大功告成!

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的作用:

Android自定义状态栏颜色与应用标题栏颜色一致

这样是不是清晰很多了,继续加油吧,编程的路还很长,我们慢慢走