IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 做一个帮你快速调试UI参数的Android插件,这原因我服了 -> 正文阅读

[移动开发]做一个帮你快速调试UI参数的Android插件,这原因我服了


从 入口代码中可以看出,我们要先选一个Process,也就是下面这个界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N10nofbZ-1631089219217)(https://user-gold-cdn.xitu.io/2018/4/14/162c4b744805ad02?imageView2/0/w/1280/h/960/ignore-error/1)]

#### Window选择

之后会在Background执行`LayoutInspectorAction.GetClientWindowsTask`,这个Task会获取当前活跃的ClientWindow(也就是Android中的Window),如果超过一个的话,会出现对话框让我们选择,这里就不贴图了,反正大家都用过。

#### Capture View

选择了Window之后就会在Background执行`LayoutInspectorCaptureTask`,这个Task会获取到需要显示的View Hierarchy,View Properties以及一张BufferedImage(选择Window的截图),这些信息全部以二进制的信息储存在.li文件中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SD0W7UqN-1631089219219)(https://user-gold-cdn.xitu.io/2018/4/14/162c4b7468522e27?imageView2/0/w/1280/h/960/ignore-error/1)]

#### 显示

然后Layout Inspector自定义了一个FileEditor以支持.li文件的显示,也就是我们能看到View Tree和Properties Table的主界面。具体显示细节可参考`LayoutInspectorContext`类

#### Android SDK中的响应

上面介绍了Layout Inspector在插件端的简单流程,它想Android端要了Window信息,View的信息,相关代码都在`HandleViewDebug`类,下面是这个类的一些结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nHzr6L3S-1631089219220)(https://user-gold-cdn.xitu.io/2018/4/14/162c4b7469b2a9b1?imageView2/0/w/1280/h/960/ignore-error/1)]

也就是说服务端发出了一些命令的包,那作为客户端的Android是在哪里作出响应的呢?经过我的代码查找,我在Android SDK中发现了一个`DdmHandleViewDebug`类和`ViewDebug`类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N2CziDZx-1631089219222)(https://user-gold-cdn.xitu.io/2018/4/14/162c4b746cb98937?imageView2/0/w/1280/h/960/ignore-error/1)]

从两个类的structure中就可以看出,Android端是在`ViewDebug`这个类获取各种信息的,具体代码就不分析了,大家感兴趣可以研究研究。

同时,这个类中有一个注解,叫`ExportedProperty`

/**

  • This annotation can be used to mark fields and methods to be dumped by
  • the view server. Only non-void methods with no arguments can be annotated
  • by this annotation.
    */
    @Target({ ElementType.FIELD, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExportedProperty {
    ……
    }

查看这个注解用的地方,可以发现,所有Layout Inspector中显示的Properties都被标注了这个注解。

![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b7472b79633?imageView2/0/w/1280/h/960/ignore-error/1)

通过这个注解我们可以给一些自定义的View暴露出想要显示的属性。

### 扩展Layout Inspector

经过上面的对Layout Inspector的分析,我们已经足够了解它并可以对其做扩展了。Layout Inspector只能查看View Hierarchy和Properties,它完全可以做更多的事情。

在`HandleViewDebug`类中有一个方法`invokeMethod`,这个方法可以做到调用View的相关方法,目前只支持primitive arguments的方法,很可惜,意味着我们不能改变TextView的text。

![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b7489d1d0db?imageView2/0/w/1280/h/960/ignore-error/1)

触发的方法在Android SDK的`ViewDebug`的`invokeViewMethod`方法中,可以看到是通过反射实现的,view post出去的

/**
* Invoke a particular method on given view.
* The given method is always invoked on the UI thread. The caller thread will stall until the
* method invocation is complete. Returns an object equal to the result of the method
* invocation, null if the method is declared to return void
* @throws Exception if the method invocation caused any exception
* @hide
*/
public static Object invokeViewMethod(final View view, final Method method,
final Object[] args) {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference result = new AtomicReference();
final AtomicReference exception = new AtomicReference();

    view.post(new Runnable() {
        @Override
        public void run() {
            try {
                result.set(method.invoke(view, args));
            } catch (InvocationTargetException e) {
                exception.set(e.getCause());
            } catch (Exception e) {
                exception.set(e);
            }

            latch.countDown();
        }
    });

    try {
        latch.await();
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    if (exception.get() != null) {
        throw new RuntimeException(exception.get());
    }

    return result.get();
} 

接下来就好办了,核心方法Idea和Android SDK都为我们提供好了,我们只需要构建我们的插件UI,写入View的相关方法即可。

由于我们需要对View的Property进行操作,由于负责显示View Properties的控件是私有的,所以这里我通过反射获取了实例,并为其添加了一个双击鼠标事件。

private var propertyTable: PTable

init {
val editorReflect = Reflect.on(layoutInspectorEditor)

val context = editorReflect.get(“myContext”)

propertyTable = context.propertiesTable

}


fun hook() {

propertyTable.addMouseListener(object : MouseAdapter() {

}
}


双击过后就是显示一个Popup,不同的类型显示不同的Popup。

##### 不支持动画的普通属性

![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b748d2c0e81?imageView2/0/w/1280/h/960/ignore-error/1)

##### 支持动画的属性

![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b748ebfdc23?imageView2/0/w/1280/h/960/ignore-error/1)

##### 颜色属性

![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b749581a798?imageView2/0/w/1280/h/960/ignore-error/1)

##### Enum类型的属性

![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b7497e1b1d7?imageView2/0/w/1280/h/960/ignore-error/1)

##### Bitwise类型的属性

![image](https://user-gold-cdn.xitu.io/2018/4/14/162c4b74a2507959?imageView2/0/w/1280/h/960/ignore-error/1)

##### 自定义的属性

可以支持自定义View的自定义的属性无疑是最棒的,实现起来也很简单,在介绍`ViewDebug`类时,介绍了`ExportedProperty`注解,我们只需要在自定义的View中运用这个注解就可以了,并设置好setXXX()方法,一个简单例子,注意这个category一定要为custom,插件才会做出响应,属性名中带有color会被认为是颜色。

public class ColorView extends TextView {

@ViewDebug.ExportedProperty(category = “custom”, formatToHexString = true)
private int color = Color.BLACK;

@ViewDebug.ExportedProperty(category = “custom”)
private int number = 0;

@ViewDebug.ExportedProperty(category = “custom”)
private boolean needShowText = true;

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

public ColorView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

public ColorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

public void setColor(int color) {
this.color = color;
setBackgroundColor(color);
}

public void setNeedShowText(boolean needShowText) {
this.needShowText = needShowText;
if (!needShowText) {
setText("");
} else {

最后

为了方便有学习需要的朋友,我把资料都整理成了视频教程(实际上比预期多花了不少精力)

CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。

的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。

加油,共勉。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-09 11:53:22  更:2021-09-09 11:55:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:18:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码