-
先看iphone效果图
image.png -
如何在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: <dimen name="status_bar_height">48dp</dimen> 后显示是想要的效果
//实现动态控制
@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、状态栏触摸事件处理
网友评论