-
先看iphone效果图
如何在android上实现,需求分析:
其实是希望我们和系统之间有个接口,如果我的某个app正在运行中,给系统发个消息。一但被切到了后台时,系统就把其它app的显示区域整小点儿,并在上面显示个条儿。高级一点的话,希望可以通过不同的接口参数来控制显示条上的文字和背景颜色。点击蓝条得回到app,这样.可以动态调整状态栏的高度、颜色状态栏下面预留一个区域用于显示后台消息这个问题难点就是动态改变状态栏高度
private void setStatusBarHight(int mHight){//增加这个方法可以设置状态栏的高度
mStatusBarWindowManager.setBarHeight(mHight);
}
public void resetStatusBarHight(){//增加这个方法可以恢复状态栏的高度
mNaturalBarHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
if (mStatusBarWindowManager != null){
mStatusBarWindowManager.setBarHeight(mNaturalBarHeight);
}
}
增加一个view,动态隐藏的思路:
super_status_bar.xml 这个是root布局想办法在底部增加一个布局用来显示其他信息
protected StatusBarWindowView mStatusBarWindow; 包括状态栏跟下拉 对应的xml super_status_bar.xml
protected PhoneStatusBarView mStatusBarView; 指的是状态栏,初始化步骤如下 对应的xml status_bar.xml
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
最后发现
status_bar.xml中对状态栏的高度进行了定义是写死的
android:layout_height="@dimen/status_bar_height"
CollapsedStatusBarFragment.java 包含一个view private PhoneStatusBarView mStatusBar; 解析status_bar return inflater.inflate(R.layout.status_bar, container, false);
在系统的整体布局文件中updateSystemBarsLw这个方法用于布局状态栏在系统的位置 、大小
在phonewindowmanager中可以看出来statusbar的高度是固定的,系统应用整体布局也是要根据这个值来计算空间大小
@Override
public void onConfigurationChanged() {
// TODO(multi-display): Define policy for secondary displays.
Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
final Resources res = uiContext.getResources();
mStatusBarHeight =
res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);//这里获取的是系统里面的一个固定的值24dp,是否可以动态控制
private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui,
boolean isKeyguardShowing) {
mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
想到之前做单手模式的时候,下面这个方法系统会根据当前状态重新布局下
public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
int displayRotation, int uiMode) {
navVisible |= !canHideNavigationBar();
boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
navAllowedHidden, statusBarExpandedNotKeyguard);
if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
mDockLeft, mDockTop, mDockRight, mDockBottom));
updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing);//在这里调用了状态栏的布局,是否会应用系统的整体布局
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
}
下面来看看还有哪些地方用到了这个状态栏高度的值,可以看出这个值影响的主要在systemui跟PhoneWindowManager中
要想动态控制这个值的话,需要使用一个全局的变量比如系统属性
persist.statusbar.height = 1、2 (代表倍数)
比如外界要求状态栏高度翻倍,就用这个值乘以原来的大小
- 于是有了一个思路
设计一个后台全局服务,或者用系统已经有的服务
功能:
1、对状态栏的高度的控制 -------------persist
2、对状态栏的颜色控制 -------------BackMessageImp 的成员变量 mStatusBarColor
3、对需要显示的消息的控制-------------BackMessageImp 的成员变量 mMessage
4、对外界提供一个aidl接口,重写里面的方法,具体再跟客户讨论(需要传入 颜色、包名类名 消息等参数)
5、还需要记录当前的后台消息,因为客户有个需求是点击的时候需要返回当前的应用,所以还需要传入当前的包名类名
6、包名类名的控制---------------------BackMessageImp 的成员变量 mComponent
首先需要实现第一点:对状态栏的高度的控制 -------------persist
systemui有一个系统服务
StatusBarManagerService是运行于SystemServer的一个系统服务。并由ServiceManager管理,它比StatusBar创建的早,继承IStatusBarService.Stub
它接受用户操作状态栏的请求并将其转发给BaseStatusBar
如下是一个动态设置view高度的方
View child = new View(this);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) child.getLayoutParams();
layoutParams.width = 120;
layoutParams.height = 120;
child.setLayoutParams(layoutParams);
FrameLayout layout= new FrameLayout(this);//创建帧布局对象layout
FrameLayout.LayoutParams frameLayout =new FrameLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);//设置帧布局的高宽属性
FrameLayout.LayoutParams viewPream =new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTEN);
指定高度:
float height = getResources().getDimension(R.dimens.frmelayout_height);
把这个值设过去就行了
修改后的显现是状态栏整体下移了,apk并没有向下移动,状态栏飘在apk上面
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
//return inflater.inflate(R.layout.status_bar, container, false);
View mView = inflater.inflate(R.layout.status_bar, container, false);
FrameLayout.LayoutParams frameLayout =new FrameLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
frameLayout.height = 96;
mView.setLayoutParams(frameLayout);
return mView;
}
直接修改frameworks/base/core/res/res/values/dimens.xml:
//实现动态控制
@Override
public void onConfigurationChanged() {
// TODO(multi-display): Define policy for secondary displays.
Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
final Resources res = uiContext.getResources();
if(mForcedStatusBarHight){
mStatusBarHeight =res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height)+res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}else{
mStatusBarHeight =
res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}
private boolean mForcedStatusBarHight = false;
public void setBackRunStatusBarHight(boolean isForceHight){
mForcedStatusBarHight = isForceHight;
if(mForcedStatusBarHight){
mStatusBarHeight =res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height)+res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}else{
mStatusBarHeight =
res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}
}
- 状态栏-蓝色---参考沉浸式状态栏
PhoneWindow.java DecoderView.java
@Override
public void setStatusBarColor(int color) {
mStatusBarColor = color;
mForcedStatusBarColor = true;
if (mDecor != null) {
mDecor.updateColorViews(null, false /* animate */);
}
}
private int calculateStatusBarColor() {
return calculateStatusBarColor(mWindow.getAttributes().flags,
mSemiTransparentStatusBarColor, mWindow.mStatusBarColor);
}
public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor,
int statusBarColor) {
return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor
: (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor
: Color.BLACK;
}
于是
DecorView.java
参考iphone的设计,后台运行程序时候,状态栏的颜色是绿色,闪烁的是后台运行的提示
public void ForceStatusBarColor(boolean isForce,int color){
mForceColor = isForce;
mColor = color;
updateColorViews(null /* insets */, true /* animate */);
}
public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor,
int statusBarColor) {
if(mForceColor){
return mColor;//Color.RED
}else{
return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor
: (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor
: Color.BLACK;
}
}
PhoneWindow.java中添加调用方法
public void setBackRunStatusBarColor(boolean isForce,int color) {
mForcedStatusBarColor = true;
if (mDecor != null) {
mDecor.ForceStatusBarColor(isForce,color);
}
}
- 关于触摸返回的功能
public class SystemGesturesPointerEventListener 用于处理是不是触摸到状态栏 以及是不是需要下拉
phonewindowmanager.java中处理
private void requestTransientBars(WindowState swipeTarget) {
if (sb) mStatusBarController.showTransient();
mStatusBarWindow:
protected void inflateStatusBarWindow(Context context) {
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
}
也就是mStatusBarWindowManager 将 mStatusBarWindow 加进来了
private void addStatusBarWindow() {
makeStatusBarView();
mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
mRemoteInputController = new RemoteInputController(mHeadsUpManager);
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
mStatusBarView:
mStatusBarView = (PhoneStatusBarView) fragment.getView();
于是
状态栏的触摸事件;
/**
* Returns the {@link android.view.View.OnTouchListener} that will be invoked when the
* background window of the status bar is clicked.
*/
protected View.OnTouchListener getStatusBarWindowTouchListener() {
return (v, event) -> {
checkUserAutohide(v, event);
checkRemoteInputOutside(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mExpandedVisible) {
animateCollapsePanels();
}
}
return mStatusBarWindow.onTouchEvent(event);
};
点击返回到对应的应用的功能分析:
参考电话通知代码:StatusBarNotifier.java
private PendingIntent createLaunchPendingIntent(boolean isFullScreen) { 创建了一个pending intent
Intent intent =
InCallActivity.getIntent(
mContext, false /* showDialpad */, false /* newOutgoingCall */, isFullScreen);
int requestCode = PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN;
if (isFullScreen) {
// Use a unique request code so that the pending intent isn't clobbered by the
// non-full screen pending intent.
requestCode = PENDING_INTENT_REQUEST_CODE_FULL_SCREEN;
}
// PendingIntent that can be used to launch the InCallActivity. The
// system fires off this intent if the user pulls down the windowshade
// and clicks the notification's expanded view. It's also used to
// launch the InCallActivity immediately when when there's an incoming
// call (see the "fullScreenIntent" field below).
return PendingIntent.getActivity(mContext, requestCode, intent, 0);
}
显示与取消显示都是app的行为
private void updateInCallNotification(CallList callList) {
LogUtil.d("StatusBarNotifier.updateInCallNotification", "");
final DialerCall call = getCallToShow(callList);
if (call != null) {
showNotification(callList, call);
} else {
cancelNotification();
}
}
实现点击返回到指定的应用:
PanelView.java
状态栏肯定要获取当前通知的状态,如果是有后台运行的statusbar,那么他的点击事件下拉就要改成返回到指定的应用
总结下就是:
1、如何动态调整状态栏高度,重绘
2、如何动态调整状态栏亚瑟
3、状态栏触摸事件处理