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

来源:互联网 发布:虚拟网络映射 编辑:程序博客网 时间:2024/06/10 01:36

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

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一边网上查资料。感谢roserose0002,Cynthia&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
原创粉丝点击