【木棉花】UI学习(二)对Java布局模板News_Ability的解析 原创 精华
第二个页面的UI解析
xml代码的分析
不多逼逼,直接开始第二个页面的UI解析。
看图说话:页面的顶部是文章的题目,阅读量和点赞数,显然都是用Text组件实现,并且猜测是用一个水平方向得Directionnal组件装起来的。然后是文章的封面图片,文章的正文内容。为了达到当我们向下滑动时文章的内容也随之拓展的效果,应该用ScrollView组件把上面的内容装起来。最底部就是评论输入栏,以及点赞,转发,收藏(疯狂暗示)等操作,我猜测分别是使用TextField,button组件来实现的。最底部的工具栏部分与文章内容呈现部分要做一个分界的效果,根据上一文的内容我们知道可以使用Component来达到这种效果。那么现在我们来查看一下xml代码。
<DependentLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:id="$+id:parent_layout"
ohos:height="match_parent"
ohos:width="match_parent">
<ScrollView
ohos:height="match_parent"
ohos:width="match_parent">
<!--ScrollView的方向跟随其子组件的布局方向,由下面DirectionalLayout知ScrollView也是纵向的-->
<DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:bottom_padding="70vp"
ohos:left_margin="20vp"
ohos:orientation="vertical"
ohos:right_margin="20vp">
<!--一个水平朝向的方向子布局,即文章的最上面部分-->
<DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:alignment="vertical_center"
ohos:orientation="horizontal">
<!--文章标题-->
<Text
ohos:id="$+id:title_icon"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="$string:title"
ohos:text_size="20fp"
ohos:weight="1"/>
<!--右上角的阅读量-->
<Text
ohos:id="$+id:read_num"
ohos:height="match_content"
ohos:width="match_content"
ohos:right_margin="10vp"
ohos:text_size="10fp"/>
<!--右上角的喜爱量-->
<Text
ohos:id="$+id:like_num"
ohos:height="match_content"
ohos:width="match_content"
ohos:text_size="10fp"/>
</DirectionalLayout>
<!--下半部分-->
<!--文章标题-->
<Text
ohos:id="$+id:title_text"
ohos:height="match_content"
ohos:width="match_parent"
ohos:max_text_lines="4"
ohos:multiple_lines="true"
ohos:text_color="#000000"
ohos:text_size="18fp"/>
<!--文章图片-->
<Image
ohos:id="$+id:image_content"
ohos:height="300vp"
ohos:width="match_parent"
ohos:scale_mode="stretch"
ohos:top_margin="10vp"/>
<!--文章内容-->
<Text
ohos:id="$+id:title_content"
ohos:height="match_content"
ohos:width="match_parent"
ohos:multiple_lines="true"
ohos:text_alignment="horizontal_center"
ohos:text_color="#708090"
ohos:text_size="16vp"
ohos:top_margin="5vp"/>
</DirectionalLayout>
</ScrollView>
<Component
ohos:height="0.5vp"
ohos:width="match_parent"
ohos:above="$+id:bottom_layout"
ohos:background_element="#EAEAEC"
/>
<DirectionalLayout
ohos:id="$+id:bottom_layout"
ohos:height="50vp"
ohos:width="match_parent"
ohos:align_parent_bottom="true"
ohos:alignment="vertical_center"
ohos:background_element="#ffffff"
ohos:left_padding="20vp"
ohos:orientation="horizontal"
ohos:right_padding="20vp"
>
<!--评论输入框-->
<TextField
ohos:id="$+id:text_file"
ohos:height="30vp"
ohos:width="160vp"
ohos:background_element="$graphic:corner_bg_comment"
ohos:hint="$string:comment"
ohos:left_padding="5vp"
ohos:right_padding="10vp"
ohos:text_alignment="vertical_center"
ohos:text_size="15vp"/>
<!--查看他人评论的按钮-->
<Image
ohos:id="$+id:button1"
ohos:height="20vp"
ohos:width="20vp"
ohos:image_src="$media:message_icon"
ohos:left_margin="20vp"
ohos:scale_mode="stretch"/>
<!--收藏图标-->
<Image
ohos:id="$+id:button2"
ohos:height="20vp"
ohos:width="20vp"
ohos:image_src="$media:collect_icon"
ohos:left_margin="20vp"
ohos:scale_mode="stretch"/>
<!--点赞按钮-->
<Image
ohos:id="$+id:button3"
ohos:height="20vp"
ohos:width="20vp"
ohos:image_src="$media:like_icon"
ohos:left_margin="20vp"
ohos:scale_mode="stretch"/>
<!--分享按钮-->
<Image
ohos:id="$+id:button4"
ohos:height="20vp"
ohos:width="20vp"
ohos:image_src="$media:share_icon"
ohos:left_margin="20vp"
ohos:scale_mode="stretch"/>
</DirectionalLayout>
与我的猜想的不同,点赞,收藏,分享居然是用Image组件来呈现。其中Image组件的scale_mode=stretch表示将原图缩放到与Image大小一致。
Java代码的分析
第二个页面除了显示新闻内容和新闻相关信息之外,其实还在分享键里面添加了分布式功能,点击分享键,会弹出一个显示在网设备的对话框供分享者选择分享的目标设备。
java代码的第一部分是对前面的xml文件中组件的一些实例化和简单的设置,需要注意的一点是,新闻图片是放在ScrollView组件中的,因此需要设置其在组件上可显示。
接下来看看dialog界面,dialog装载了一个ListContainer,模板的代码以及我添加的注释如下:
//实例化对话框,对话框是允许客制化的,也就是我们可以决定里面放什么,但是同时要注意对话框的生命周期的设置
dialog = new CommonDialog(MainAbilityDetailSlice.this);
//对话框的自动关闭选项,如果这个值设置为真,那么点击对话框以外的部分的时候,对话框就会自动关闭
dialog.setAutoClosable(true);
//设置对话框的标题
dialog.setTitleText("Harmony devices");
//设置对话框的大小
dialog.setSize(DIALOG_SIZE_WIDTH, DIALOG_SIZE_HEIGHT);
//创建一个ListContainer
ListContainer devicesListContainer = new ListContainer(getContext());
//熟悉的配方,ListContainer组件基本操作
DevicesListProvider devicesListProvider = new DevicesListProvider(devices, this);
devicesListContainer.setItemProvider(devicesListProvider);
devicesListContainer.setItemClickedListener(//设置ListContainer里面的项被点击时候的动作
(listContainer, component, position, id) -> {
dialog.destroy();//对话框生命周期管理的典型例子,先摧毁这个对话框
//然后使用分布式拉起
startAbilityFA(devices.get(position).getDeviceId());
});
devicesListProvider.notifyDataChanged();
//把上面创建的ListContainer类丢进dialog中,作为客制化的内容
dialog.setContentCustomComponent(devicesListContainer);
//显示dialog
dialog.show();
首先,先实例了一个dialog并对其进行大小,标题,是否在点击dialog以外的部分时让其自动关闭的设置。然后就是熟悉的环节,创建一个ListContainer组件,创建步骤在上一文已经有很详细的解析了,这里不再重复。需要提醒的是,dialog组件支持客制化,但是同时也提出了我们需要管理好其生命周期的要求,回看上面的代码,在设置ListContainer的项的点击监听器的时候首先调用了 diaog.destroy(),再进行分布式拉起,是很好的生命周期管理的实例。dialog.setContentCustomComponent(devicesListContainer)把创建好的ListContainer组件放入dialog中,并使用dialog.show()把对话框显示出来。
IDL
解析之前我认为这是个比较简单的环节,毕竟之前就用分布式能力做过一个小游戏了。但是解析代码的时候又学习到了很多东西,下面的内容我还不算弄得特别清楚,只能算是有了初步的认识,如果有错误的地方,欢迎大家指出。
什么是IDL,使用IDL的好处是什么?
在解析模板的时候会看见下面这些文件
第一张图的所有文件我都没有见过,第二张图则是比我们平时创建项目自动生成的Ability多出了一个ShareService。之前我使用分布式拉起都是简单地使用几行语句来实现,这一下多出来这么多个类实属把我看懵了。于是我就了解了一下IDL究竟是什么。一切都以文档为准,先上文档链接IDL文档链接1
IDL文档链接2
当客户端和服务器通信时,需要定义双方都认可的接口,以保障双方可以成功通信,HarmonyOS IDL(HarmonyOS Interface Definition Language)则是一种定义此类接口的工具。HarmonyOS IDL先把需要传递的对象分解成操作系统能够理解的基本类型,并根据开发者的需要封装跨边界的对象。在HarmonyOS中,HarmonyOS IDL接口包含面向应用程序的北向接口和面向硬件设备的南向接口。
HarmonyOS IDL接口描述语言主要用于:
1.声明系统服务对外提供的服务接口,根据接口声明在编译时生成跨进程调用(IPC)或跨设备调用(RPC)的代理(Proxy)和桩(Stub)的C/C++代码或Java代码。
2.声明Ability对外提供的服务接口,根据接口声明在编译时生成跨进程调用(IPC)或跨设备调用(RPC)的代理(Proxy)和桩(Stub)的C/C++代码或Java代码。
使用HarmonyOS IDL接口描述语言声明接口具有以下优点:
1.HarmonyOS IDL中是以接口的形式定义服务,可以专注于定义而隐藏实现细节。
2.HarmonyOS IDL中定义的接口可以支持跨进程调用或跨设备调用。根据HarmonyOS IDL中的定义生成的信息或代码可以简化跨进程或跨设备调用接口的实现。
IDL的开发步骤
1.创建.idl文件
HarmonyOS SDK工具支持生成Java、C++语言的接口类、桩类和代理类。以Java语言开发为例,开发者只需将.idl文件保存至DevEco Studio项目的src/目录内,工具则会在构建应用时,在项目的generated/目录中生成IRemoteObject接口文件、Stub文件、Proxy文件。生成的接口类文件名称和.idl文件名称保持一致,区别在于其使用.java 扩展名。例如,IRemoteAbility.idl 生成的文件名是 IRemoteAbility.java、RemoteAbilityProxy.java、RemoteAbilityStub.java。在这个模板中,打开main文件中的idl文件夹就可以看到.idl文件。可以看到.idl所定义几个变量正是用来描述新闻的几个变量。
那么,下面几个文件就是在构建应用时自动生成的IRemoteObject接口文件、Stub文件、Proxy文件。
2.服务端公开接口
HarmonyOS IDL工具生成的Stub类是接口类的抽象实现,并且会声明.idl文件中的所有方法。
public abstract class NewsDemoIDLStub extends RemoteObject implements INewsDemoIDL {
//用于描述的字符串
private static final String DESCRIPTOR = "com.example.ui_template_news_ability.INewsDemoIDL";
private static final int COMMAND_TRAN_SHARE = IRemoteObject.MIN_TRANSACTION_ID;
//构造方法
public NewsDemoIDLStub(String descriptor) {
super(descriptor);
}
public static INewsDemoIDL asInterface(IRemoteObject object) {
if (object == null) {
return null;
}
INewsDemoIDL result = null;
//如果这是一个服务提供者,那么返回一个有效的IRemoteBroker对象
//如果这是一个服务使用者,那么返回null,代表这不是本地的。
IRemoteBroker broker = object.queryLocalInterface(DESCRIPTOR);
//如果broker是服务提供者
if (broker != null) {
//如果broker实现了INewsDemoIDL接口,那么把broker作为接口返回
if (broker instanceof INewsDemoIDL) {
result = (INewsDemoIDL) broker;
}
}
//如果broker不是服务的提供者,那么就返回一个新的代理对象
else {
result = new NewsDemoIDLProxy(object);
}
return result;
}
@Override
public IRemoteObject asObject() {
return this;
}
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) throws RemoteException
{
String token = data.readInterfaceToken();
if (!DESCRIPTOR.equals(token)) {
return false;
}
if (code == COMMAND_TRAN_SHARE) {
String title = data.readString();
String reads = data.readString();
String likes = data.readString();
String content = data.readString();
String image = data.readString();
tranShare(title, reads, likes, content, image);
reply.writeNoException();
return true;
}
return super.onRemoteRequest(code, data, reply, option);
}
在服务实现接口后,需要向客户端公开该接口,以便客户端进程绑定。如果开发者的服务要公开该接口,请扩展Ability并实现onConnect()从而返回IRemoteObject,以便客户端能与服务进程交互。
public class SharedService extends Ability {
private static final String DESCRIPTOR = "com.example.ui_template_news_ability.idl.INewsDemoIDL";
private static final String ACTION = "action.detail";
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
}
/*
* 在服务实现接口后,需要向客户端公开该接口,以便客户端进程绑定。如果开发者的服务要公开该接口,
* 请扩展Ability并实现onConnect()从而返回IRemoteObject,
* 以便客户端能与服务进程交互。服务端向客户端公开IRemoteAbility接口的代码示例如下:*/
@Override
protected IRemoteObject onConnect(Intent intent) {
return new MyBinder(DESCRIPTOR);
}
/**
* MyBinder
*/
/*
* 要实现.idl生成的接口,请扩展生成的RemoteObject接口,并实现继承自.idl文件的方法。
* MyRemote定义了服务的远程过程调用接口,示例代码如下:*/
private class MyBinder extends NewsDemoIDLStub {
//构造方法
MyBinder(String descriptor) {
super(descriptor);
}
//
@Override
public void tranShare(String title, String reads, String likes, String content, String image) {
Intent intent = new Intent();
//设置跳转操作
Operation operation =
new Intent.OperationBuilder()
.withBundleName(getBundleName())
.withAbilityName(MainAbility.class.getName())
.withAction(ACTION)
.build();
//设置跳转时候的所携带的参数
intent.setOperation(operation);
intent.setParam(MainAbilityDetailSlice.INTENT_TITLE, title);
intent.setParam(MainAbilityDetailSlice.INTENT_READ, reads);
intent.setParam(MainAbilityDetailSlice.INTENT_LIKE, likes);
intent.setParam(MainAbilityDetailSlice.INTENT_CONTENT, content);
intent.setParam(MainAbilityDetailSlice.INTENT_IMAGE, image);
startAbility(intent);
}
}
}
总结
第二个页面的解析我所学习到的东西有如下:
1.dialog的客制化使用
2.Image来呈现点赞、收藏、转发的功能,Image组件在其他组件上的显示
3.分布式能力:IDL接口的使用(初步理解)
IDL的使用我感觉会非常重要,而这篇帖子里面的IDL内容显然质量不够高,之后我会再进行更加深入的学习,希望到时能够出一篇关于IDL使用的高质量帖子
写在最后
更多资料请关注我们的项目 :Awesome-HarmonyOS_木棉花
本项目会长期更新 ,希望随着鸿蒙一同成长变强的既有我们,也有正在看着这个项目的你。明年3月,深大校园内的木棉花会盛开,那时,鸿蒙也会变的更好,愿这花开,有你我的一份。
支持一下
学习了IDL的相关知识,支持!
小弟谢谢大佬的支持!!!
IDL写的有点水,大佬可以出一篇干货一点的😄