-
先看iphone效果圖
image.png 如何在android上實現(xiàn),需求分析:
其實是希望我們和系統(tǒng)之間有個接口,如果我的某個app正在運行中,給系統(tǒng)發(fā)個消息。一但被切到了后臺時,系統(tǒng)就把其它app的顯示區(qū)域整小點兒,并在上面顯示個條兒。高級一點的話,希望可以通過不同的接口參數(shù)來控制顯示條上的文字和背景顏色。點擊藍條得回到app,這樣.可以動態(tài)調(diào)整狀態(tài)欄的高度、顏色狀態(tài)欄下面預留一個區(qū)域用于顯示后臺消息這個問題難點就是動態(tài)改變狀態(tài)欄高度
private void setStatusBarHight(int mHight){//增加這個方法可以設置狀態(tài)欄的高度
mStatusBarWindowManager.setBarHeight(mHight);
}
public void resetStatusBarHight(){//增加這個方法可以恢復狀態(tài)欄的高度
mNaturalBarHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
if (mStatusBarWindowManager != null){
mStatusBarWindowManager.setBarHeight(mNaturalBarHeight);
}
}
增加一個view,動態(tài)隱藏的思路:
super_status_bar.xml 這個是root布局想辦法在底部增加一個布局用來顯示其他信息
protected StatusBarWindowView mStatusBarWindow; 包括狀態(tài)欄跟下拉 對應的xml super_status_bar.xml
protected PhoneStatusBarView mStatusBarView; 指的是狀態(tài)欄,初始化步驟如下 對應的xml status_bar.xml
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
最后發(fā)現(xiàn)
status_bar.xml中對狀態(tài)欄的高度進行了定義是寫死的
android:layout_height="@dimen/status_bar_height"
CollapsedStatusBarFragment.java 包含一個view private PhoneStatusBarView mStatusBar; 解析status_bar return inflater.inflate(R.layout.status_bar, container, false);
在系統(tǒng)的整體布局文件中updateSystemBarsLw這個方法用于布局狀態(tài)欄在系統(tǒng)的位置 、大小
在phonewindowmanager中可以看出來statusbar的高度是固定的,系統(tǒng)應用整體布局也是要根據(jù)這個值來計算空間大小
@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);//這里獲取的是系統(tǒng)里面的一個固定的值24dp,是否可以動態(tài)控制
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;
想到之前做單手模式的時候,下面這個方法系統(tǒng)會根據(jù)當前狀態(tài)重新布局下
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);//在這里調(diào)用了狀態(tài)欄的布局,是否會應用系統(tǒng)的整體布局
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
}
下面來看看還有哪些地方用到了這個狀態(tài)欄高度的值,可以看出這個值影響的主要在systemui跟PhoneWindowManager中
要想動態(tài)控制這個值的話,需要使用一個全局的變量比如系統(tǒng)屬性
persist.statusbar.height = 1、2 (代表倍數(shù))
比如外界要求狀態(tài)欄高度翻倍,就用這個值乘以原來的大小
- 于是有了一個思路
設計一個后臺全局服務,或者用系統(tǒng)已經(jīng)有的服務
功能:
1、對狀態(tài)欄的高度的控制 -------------persist
2、對狀態(tài)欄的顏色控制 -------------BackMessageImp 的成員變量 mStatusBarColor
3、對需要顯示的消息的控制-------------BackMessageImp 的成員變量 mMessage
4、對外界提供一個aidl接口,重寫里面的方法,具體再跟客戶討論(需要傳入 顏色、包名類名 消息等參數(shù))
5、還需要記錄當前的后臺消息,因為客戶有個需求是點擊的時候需要返回當前的應用,所以還需要傳入當前的包名類名
6、包名類名的控制---------------------BackMessageImp 的成員變量 mComponent
首先需要實現(xiàn)第一點:對狀態(tài)欄的高度的控制 -------------persist
systemui有一個系統(tǒng)服務
StatusBarManagerService是運行于SystemServer的一個系統(tǒng)服務。并由ServiceManager管理,它比StatusBar創(chuàng)建的早,繼承IStatusBarService.Stub
它接受用戶操作狀態(tài)欄的請求并將其轉(zhuǎn)發(fā)給BaseStatusBar
如下是一個動態(tài)設置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);//創(chuàng)建幀布局對象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);
把這個值設過去就行了
修改后的顯現(xiàn)是狀態(tài)欄整體下移了,apk并沒有向下移動,狀態(tài)欄飄在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: <dimen name="status_bar_height">48dp</dimen> 后顯示是想要的效果
//實現(xiàn)動態(tài)控制
@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);
}
}
- 狀態(tài)欄-藍色---參考沉浸式狀態(tài)欄
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的設計,后臺運行程序時候,狀態(tài)欄的顏色是綠色,閃爍的是后臺運行的提示
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中添加調(diào)用方法
public void setBackRunStatusBarColor(boolean isForce,int color) {
mForcedStatusBarColor = true;
if (mDecor != null) {
mDecor.ForceStatusBarColor(isForce,color);
}
}
- 關(guān)于觸摸返回的功能
public class SystemGesturesPointerEventListener 用于處理是不是觸摸到狀態(tài)欄 以及是不是需要下拉
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();
于是
狀態(tài)欄的觸摸事件;
/**
* 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) { 創(chuàng)建了一個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();
}
}
實現(xiàn)點擊返回到指定的應用:
PanelView.java
狀態(tài)欄肯定要獲取當前通知的狀態(tài),如果是有后臺運行的statusbar,那么他的點擊事件下拉就要改成返回到指定的應用
總結(jié)下就是:
1、如何動態(tài)調(diào)整狀態(tài)欄高度,重繪
2、如何動態(tài)調(diào)整狀態(tài)欄亞瑟
3、狀態(tài)欄觸摸事件處理
