“显示布局边界”的原理


设置页的处理逻辑

packages/apps/Settings/src/com/android/settings/development/ShowLayoutBoundsPreferenceController.java中有:

public class ShowLayoutBoundsPreferenceController extends DeveloperOptionsPreferenceController
        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {

    private static final String DEBUG_LAYOUT_KEY = "debug_layout";

    public ShowLayoutBoundsPreferenceController(Context context) {
        super(context);
    }

    @Override
    public String getPreferenceKey() {
        return DEBUG_LAYOUT_KEY;
    }

    // 当用户切换选项的时候,通过IBinder通知到各种服务
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        final boolean isEnabled = (Boolean) newValue;
        DisplayProperties.debug_layout(isEnabled);
        SystemPropPoker.getInstance().poke();
        return true;
    }

    @Override
    public void updateState(Preference preference) {
        final boolean isEnabled = DisplayProperties.debug_layout().orElse(false);
        ((SwitchPreference) mPreference).setChecked(isEnabled);
    }

    @Override
    protected void onDeveloperOptionsSwitchDisabled() {
        super.onDeveloperOptionsSwitchDisabled();
        DisplayProperties.debug_layout(false);
        ((SwitchPreference) mPreference).setChecked(false);
    }
}

DisplayProperties是生成的类,看一下其中的debug_layout属性:

    public static void debug_layout(Boolean value) {
        SystemProperties.set("debug.layout", value == null ? "" : value.toString());
    }

Android系统属性定义在.sysprop文件中,其本质上是一个protobuf文件.

看一下debug_layout属性(system/libsysprop/srcs/android/sysprop/DisplayProperties.sysprop中):

prop {
    api_name: "debug_layout"
    type: Boolean
    scope: Internal
    access: ReadWrite
    prop_name: "debug.layout"
}

然后系统会自动生成一些中间类.比如上面的DisplayProperties.java.

总结一下,其实就是:

  1. 系统属性定义在.sysprop文件中,系统会自动生成一些中间类用于读写属性
  2. 设置页中用户打开/关闭开关,会改变属性值,然后通过Binder机制通知给各个服务;

debug.layout运行机制

在ViewRootImpl初始化的时候会执行下面的方法:

    public void loadSystemProperties() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                // ...
                // Layout debugging
                boolean layout = DisplayProperties.debug_layout().orElse(false);
                if (layout != mAttachInfo.mDebugLayout) {
                    mAttachInfo.mDebugLayout = layout;
                    if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {
                        mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
                    }
                }
            }
        });
    }

    // 自身以及其子View全部都重新绘制
    void invalidateWorld(View view) {
        view.invalidate();
        if (view instanceof ViewGroup) {
            ViewGroup parent = (ViewGroup) view;
            for (int i = 0; i < parent.getChildCount(); i++) {
                invalidateWorld(parent.getChildAt(i));
            }
        }
    }    

在View的draw中有下面这一句:

if (isShowingLayoutBounds()) {
    debugDrawFocus(canvas);
}

    final private void debugDrawFocus(Canvas canvas) {
        if (isFocused()) {
            final int cornerSquareSize = dipsToPixels(DEBUG_CORNERS_SIZE_DIP);
            final int l = mScrollX;
            final int r = l + mRight - mLeft;
            final int t = mScrollY;
            final int b = t + mBottom - mTop;

            final Paint paint = getDebugPaint();
            paint.setColor(DEBUG_CORNERS_COLOR);

            // Draw squares in corners.
            paint.setStyle(Paint.Style.FILL);
            // 左
            canvas.drawRect(l, t, l + cornerSquareSize, t + cornerSquareSize, paint);
            // 右
            canvas.drawRect(r - cornerSquareSize, t, r, t + cornerSquareSize, paint);
            // 下
            canvas.drawRect(l, b - cornerSquareSize, l + cornerSquareSize, b, paint);
            // 上
            canvas.drawRect(r - cornerSquareSize, b - cornerSquareSize, r, b, paint);

            // 绘制一个View上的大X
            paint.setStyle(Paint.Style.STROKE);
            canvas.drawLine(l, t, r, b, paint);
            canvas.drawLine(l, b, r, t, paint);
        }
    }

上面这些就是我们看到的那些红色框框的绘制逻辑了.

adb获取系统属性

  • 获取全部属性

    adb shell getprop
  • 获取指定属性

    adb shell getprop debug.layout

adb打开关闭“显示布局边界”

adb shell setprop debug.layout true


adb shell setprop debug.layout false

执行完命令之后要重启APP才会看到效果.


文章作者: 姜康
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 姜康 !
评论
 上一篇
CameraX的简单使用 CameraX的简单使用
基本说明目前CameraX已经处于RC(Release Candidate)阶段了,意味着API不会有大的改动了,官方提示可以应用在实际产品中了. CameraX对之前的Camera2进行了包装,重新设计了API,让开发者可以更简单,更快速
2021-01-24
下一篇 
LayoutInflater原理分析 LayoutInflater原理分析
两种资源 预编译的layout资源(dex文件) xml layout文件 预编译的布局目前的版本(Android 11)并没有开放这个功能. 基本的处理流程如下: 使用view_compiler将xml布局直接编译成CompiledV
2021-01-04
  目录