• Android 禁止状态栏下拉status bar


    如果你有这样的需求:用户进入你的app以后,所有的操作都是你的app中设定的,用户不可以拥有系统设置等行为的能力。然而,Android系统,可以通过从顶部往下拉,从而得到一个通知和快速系统设置的页面: 
    这里写图片描述 
    因此,现在你想禁止它弹出,怎么办呢? 
    我不知道在app中怎么做,但是如果你们的处境像我一样:android系统是一个针对特殊平台定制的,它一旦启动就进入特定的功能页面,并且不允许用户有进入系统设置的能力,那么您可以像下面这样,直接在系统代码中进行修改。

    分析如何解决问题

    使用Android device monitor工具,我们可以看到Android 状态栏的布局,我们会发现,平时我们看到的状态栏(如下图所示)是由PhoneStatusBarView负责绘制个管理的: 
    这里写图片描述 
    结合我们的操作,当我们点击状态栏或者下拉的时候,都会出现通知界面。而点击和下拉都是触摸事件,因此,理所当然的,我们会想到在PhoneStatusBarView的onTouchEvent中处理相应的逻辑。onTouchEvent定义在frameworksasepackagesSystemUIsrccomandroidsystemuistatusbarphonePhoneStatusBarView.Java中:

        @Override     public boolean onTouchEvent(MotionEvent event) {         boolean barConsumedEvent = mBar.interceptTouchEvent(event);          if (DEBUG_GESTURES) {             if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {                 EventLog.writeEvent(EventLogTags.SYSUI_PANELBAR_TOUCH,                         event.getActionMasked(), (int) event.getX(), (int) event.getY(),                         barConsumedEvent ? 1 : 0);             }         }          return barConsumedEvent || super.onTouchEvent(event);     }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    它似乎什么都没有做…然而不要忽视了,它调用了super.onTouchEvent(event)方法。PhoneStatusBarView继承了PanelBar类,这个类继承自PanelBar类。因此,super.onTouchEvent就是调用PanelBar中的onTouchEvent方法,PanelBar也在frameworksasepackagesSystemUIsrccomandroidsystemuistatusbarphone目录下:

       @Override     public boolean onTouchEvent(MotionEvent event) {         // Allow subclasses to implement enable/disable semantics         if (!panelsEnabled()) {             if (event.getAction() == MotionEvent.ACTION_DOWN) {                 Log.v(TAG, String.format("onTouch: all panels disabled, ignoring touch at (%d,%d)",                         (int) event.getX(), (int) event.getY()));             }             return false;         }          // figure out which panel needs to be talked to here         if (event.getAction() == MotionEvent.ACTION_DOWN) {             final PanelView panel = selectPanelForTouch(event);             if (panel == null) {                 // panel is not there, so we'll eat the gesture                 Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",                         (int) event.getX(), (int) event.getY()));                 mTouchingPanel = null;                 return true;             }             boolean enabled = panel.isEnabled();             if (DEBUG) LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s %s", mState, panel,                     (enabled ? "" : " (disabled)"));             if (!enabled) {                 // panel is disabled, so we'll eat the gesture                 Log.v(TAG, String.format(                         "onTouch: panel (%s) is disabled, ignoring touch at (%d,%d)",                         panel, (int) event.getX(), (int) event.getY()));                 mTouchingPanel = null;                 return true;             }             startOpeningPanel(panel);         }         final boolean result = mTouchingPanel != null                 ? mTouchingPanel.onTouchEvent(event)                 : true;         return result;     }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    从函数的名字猜测,startOpeningPanel方法似乎就是弹出下拉菜单的入口,把它注释掉,重新编译SystemUI模块,然后替换/system/priv-app/SystemUI/SystemUI.apk,重启系统,就发现无论你是点击还是下拉屏幕顶部,都不会出现下拉也面了。 
    我们不妨简单分析下这里: 
    startOpeningPanel接收一个panel作为参数,而这个panel则是selectPanelForTouch(event);方法返回的。 
    PhoneStatusBarView中覆写了该方法:

         @Override     public PanelView selectPanelForTouch(MotionEvent touch) {         // No double swiping. If either panel is open, nothing else can be pulled down.         return mNotificationPanel.getExpandedHeight() > 0                 ? null                 : mNotificationPanel;     }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以看到,这里返回的是mNotificationPanel。是的它就是下面的样子: 
    这里写图片描述 
    既然我们在这里得到了这个页面,startOpeningPanel应该就是将这个页面呈现出来吧。 
    startOpeningPanel如下:

        // called from PanelView when self-expanding, too     public void startOpeningPanel(PanelView panel) {         if (DEBUG) LOG("startOpeningPanel: " + panel);         mTouchingPanel = panel;         mPanelHolder.setSelectedPanel(mTouchingPanel);         for (PanelView pv : mPanels) {             if (pv != panel) {                 pv.collapse(false /* delayed */);             }         }     }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    对所有的PanelView ,调用它的collapse方法,改方法如下:

        public void collapse(boolean delayed) {         if (DEBUG) logf("collapse: " + this);         if (mPeekPending || mPeekAnimator != null) {             mCollapseAfterPeek = true;             if (mPeekPending) {                  // We know that the whole gesture is just a peek triggered by a simple click, so                 // better start it now.                 removeCallbacks(mPeekRunnable);                 mPeekRunnable.run();             }         } else if (!isFullyCollapsed() && !mTracking && !mClosing) {             cancelHeightAnimator();             mClosing = true;             notifyExpandingStarted();             if (delayed) {                 postDelayed(mFlingCollapseRunnable, 120);             } else {                 fling(0, false /* expand */);             }         }     }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    如果我们正在下拉,同时下拉的动画不为空,那么会调用mPeekRunnable.run();

        private Runnable mPeekRunnable = new Runnable() {         @Override         public void run() {             mPeekPending = false;             runPeekAnimation();         }     };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    调用runPeekAnimation:

       private void runPeekAnimation() {         mPeekHeight = getPeekHeight();         if (DEBUG) logf("peek to height=%.1f", mPeekHeight);         if (mHeightAnimator != null) {             return;         }         mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight)                 .setDuration(250);         mPeekAnimator.setInterpolator(mLinearOutSlowInInterpolator);         mPeekAnimator.addListener(new AnimatorListenerAdapter() {             private boolean mCancelled;              @Override             public void onAnimationCancel(Animator animation) {                 mCancelled = true;             }              @Override             public void onAnimationEnd(Animator animation) {                 mPeekAnimator = null;                 if (mCollapseAfterPeek && !mCancelled) {                     postOnAnimation(new Runnable() {                         @Override                         public void run() {                             collapse(false /* delayed */);                         }                     });                 }                 mCollapseAfterPeek = false;             }         });         notifyExpandingStarted();         mPeekAnimator.start();         mJustPeeked = true;     } 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    这里使用了属性动画将它移动到指定的高度上。

  • 相关阅读:
    [转]VS2010几款超赞的扩展辅助工具总结
    从客户端中检测到有潜在危险的Request.Form 值
    面试系列28 分布式服务接口的幂等性如何设计
    面试系列26 如何基于dubbo进行服务治理、服务降级、失败重试以及超时重试
    面试系列25 dubbo的spi思想是什么
    面试系列24 dubbo负载均衡策略和集群容错策略
    面试系列23
    面试系列22 dubbo的工作原理
    面试系列21 为什么要进行系统拆分
    面试系列20 生产环境中的redis是怎么部署的
  • 原文地址:https://www.cnblogs.com/muhuacat/p/7448656.html
Copyright © 2020-2023  润新知