实现一个仿iphone来电状态栏功能

  • 先看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: 48dp 后显示是想要的效果

//实现动态控制
@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、状态栏触摸事件处理

你可能感兴趣的:(实现一个仿iphone来电状态栏功能)