关闭

SystemUI下的快速设置面板显示异常

263人阅读 评论(0) 收藏 举报
分类:

前言:坚持自己能坚持的,成为自己想成为的,相信时间的累积是有效果的。

Step1复现

这个bug困扰了快一周了,今天终于突然来了灵感解决掉了,记录之。
测试:

General description: 
The notification bar display incorrect when edit quick settings. 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Reproducibility: 
10/10 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Precondition: 
Turn on auto-rotate 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Step: 
1.Homescreen->Settings->Display->Rotate dut to landscape mode->View->Display size->Change size then change to default size->Back to home screen->Rotate dut to portrait mode->Pull down notification bar->EDIT 
2.Check the screen display. 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Actual result: 
The notification bar display incorrect when edit quick settings. 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Expect result: 
The notification bar display correct when edit quick settings.

效果图如下:
这里写图片描述

Step2知识回顾

**A、横竖屏:**

通过这个bug的复现流程,横屏模式下设置 displaySize ,嗯,涉及到android横竖屏切换方面的知识,这方面的知识很薄弱,于是一边解bug一边网上查资料。感谢roserose0002Cynthia&Sky二位的精彩分析,下面我结合他们的博客做个自己的理解和学习。

当我们在清单文件中不添加关于横竖屏的配置信息时,横竖屏切换时会有一个Activity的销毁和重新创建的过程,一般的项目都会通过在清单文件中配置相关的信息通过执行onConfigureChanged方法来做一些处理。
即:

<uses-permission
 Android:name="android.permission.CHANGE_CONFIGURATION"></uses-permission>

允许改变配置信息,系统允许我们通过重写activity中的onConfigurationChanged方法来捕获和修改某些配置信息。

另外在我们想配置的相关的Activity需要添加一个节点

 <activity android:name=".recents.RecentsActivity"
                  android:label="@string/accessibility_desc_recent_apps"
                  android:exported="false"
                  android:launchMode="singleInstance"
                  android:excludeFromRecents="true"
                  android:stateNotNeeded="true"
                  android:resumeWhilePausing="true"
                  android:screenOrientation="behind"
                  android:resizeableActivity="true"
                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
                  android:theme="@style/RecentsTheme.Wallpaper">
            <intent-filter>
                <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
            </intent-filter>
        </activity>

它规定了可以再程序中捕获的事件类型。实际上它有这些字段
这里写图片描述

修改字体时会调用fontScale,修改display size会调用densityDpi。

B、页面和源代码的对应(MTK)
这个不是什么干货,相当于自己加深印象了,
a、在SystemUI下的qs文件夹下,QSPanel.java(View that represents the quick settings tile panel)

/** View that represents the quick settings tile panel. **/
public class QSPanel extends LinearLayout implements Tunable, Callback {
......
public QSPanel(Context context) {
        this(context, null);
    }

    public QSPanel(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;

        setOrientation(VERTICAL);

        mBrightnessView = LayoutInflater.from(context).inflate(
                R.layout.quick_settings_brightness_dialog, this, false);
        addView(mBrightnessView);

        mQuickSettingsPlugin = PluginManager.getQuickSettingsPlugin(mContext);
        mQuickSettingsPlugin.addOpViews(this);

        setupTileLayout();

        mFooter = new QSFooter(this, context);
        addView(mFooter.getView());

        updateResources();

        mBrightnessController = new BrightnessController(getContext(),
                (ImageView) findViewById(R.id.brightness_icon),
                (ToggleSlider) findViewById(R.id.brightness_slider));

    }
......
}

再看看哪里调用了这个控件,在Qs_panel.xml

<com.android.systemui.qs.QSContainer
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/quick_settings_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/qs_background_primary"
        android:clipToPadding="false"
        android:clipChildren="false"
        android:elevation="4dp">

    <com.android.systemui.qs.QSPanel
            android:id="@+id/quick_settings_panel"
            android:background="#0000"
            android:layout_marginTop="@dimen/status_bar_header_height"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="8dp" />

    <include layout="@layout/quick_status_bar_expanded_header" />

    <include android:id="@+id/qs_detail" layout="@layout/qs_detail" />

    <include android:id="@+id/qs_customize" layout="@layout/qs_customize_panel"
        android:visibility="gone" />

</com.android.systemui.qs.QSContainer>

QSPanel属于QSContainer的子控件。效果图是这样的:
这里写图片描述
QSPanel,点击edit

  mEditText = (TextView) findViewById(android.R.id.edit);
        mEditText.setOnClickListener(view ->
                mHost.startRunnableDismissingKeyguard(() -> showEdit(view)));
private void showEdit(final View v) {
        v.post(new Runnable() {
            @Override
            public void run() {
                if (mCustomizePanel != null) {
                    if (!mCustomizePanel.isCustomizing()) {
                        int[] loc = new int[2];
                        v.getLocationInWindow(loc);
                        int x = loc[0];
                        int y = loc[1];
                        mCustomizePanel.show(x, y);
                    }
                }

            }
        });
    }

执行mCustomizePanel.show(x, y);方法,这个类的描述是

/**
 * Allows full-screen customization of QS, through show() and hide().
 *
 * This adds itself to the status bar window, so it can appear on top of quick settings and
 * *someday* do fancy animations to get into/out of it.

show 或者 hide详情,是不是这个类的configuration变化,导致quicksetting显示异常

 protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        View navBackdrop = findViewById(R.id.nav_bar_background);
        if (navBackdrop != null) {
            boolean shouldShow = newConfig.smallestScreenWidthDp >= 600
                    || newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE;
            navBackdrop.setVisibility(shouldShow ? View.VISIBLE : View.GONE);
        }
    }

事实上QSCustomizer.java的这个方法比较简单,就是在实现父类的同时加上了自己navBackdrop 视图。
在这里里面我尝试reloadwitdth是没用的

private void reloadWidth(View view) {
        LayoutParams params = (LayoutParams) view.getLayoutParams();
        params.width = getContext().getResources().getDimensionPixelSize(
                R.dimen.notification_panel_width);
        view.setLayoutParams(params);
    }

Step3 找源头

偶然发现了这个类

/**
 * Custom {@link FrameLayout} that re-inflates when changes to {@link Configuration} happen.
 * Currently supports changes to density and locale.
 */
public class AutoReinflateContainer extends FrameLayout {
......
  @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        boolean shouldInflateLayout = false;
        final int density = newConfig.densityDpi;
        if (density != mDensity) {
            mDensity = density;
            shouldInflateLayout = true;
        }
        final LocaleList localeList = newConfig.getLocales();
        if (localeList != mLocaleList) {
            mLocaleList = localeList;
            shouldInflateLayout = true;
        }

        if (shouldInflateLayout) {
            inflateLayout();
        }
    }

    private void inflateLayout() {
        removeAllViews();
        LayoutInflater.from(getContext()).inflate(mLayout, this);
        final int N = mInflateListeners.size();
        for (int i = 0; i < N; i++) {
            mInflateListeners.get(i).onInflated(getChildAt(0));
        }
    }

......
}

当density 和locale变化时会重新加载view,所以我强行的去掉if,只要配置信息变化就加在view,这个是治标不治本的。

// if (shouldInflateLayout) {
            inflateLayout();
      //  }

至于为什么横屏切换时导致计算错误还要进一步找原因。
最终的图是这样的这里写图片描述

谢谢。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:5938次
    • 积分:219
    • 等级:
    • 排名:千里之外
    • 原创:16篇
    • 转载:1篇
    • 译文:0篇
    • 评论:0条
    文章分类