「构建UI模板库-HarmonyOS组件再包装」»» 详细总结来了~ 原创 精华
一、公开课内容总结
如果你在互联网企业或者即将进入互联网企业,或多或少你都接触过以下组件库。
既然HarmonyOS已经有了7大类400多种组件,我们为什么还要构建组件库呢?
使用“WWH”来解决问题,最终都可以得到你想要的。本次直播我也使用“WWH”来阐述为何要进行组件化。“WWH”即Why(为什么?)、What(是什么?这里指解决哪些痛点)、How(怎么做?代码干货)。
- 为什么:并不是因为“懒”才构建组件库,如果App中使用多个相同的样式组件,需要修改的时候,所有使用的地方都需要修改,这样无形中增加了负担,同时也会出现纰漏。组件库的构建能够有效的解决这类问题,达到一处修改,多处共同修改的目的。
- 是什么:将具有共用性、可裁剪的HarmonyOS组件进行再次封装,这些封装的组件构成组件库。组件库降低了维护出现问题的几率,能够使产品迭代迅速、体验一致、开发更高效、代码更简洁。
- 怎么做:我将以实际的案例带大家一步一步构建组件库中的IMIcon(图标组件)和UI模板库中的自定义标题栏(IMTitleBar)。
1、构建核心组件库(IMIcon)
上图是我构建的三种类型的组件,一按钮组件、二图标组件、三输入框组件。这里以图标组件为例,其他可==参考代码仓中的代码==。
构建一个HarmonyOS库分为三步:
1、创建Module,指定为HarmonyOS Library(选中项目右键New—>Module…)
2、使用Gradle构建Har包(右侧gradle选项卡中ohos:debug下packageDebugHar)
3、主项目引入Har包(在build.gradle中dependencies添加implementation project(“:coptlib”) )
先不着急的写代码,先完成以上三步曲,然后再进行下一步。
/**
* 在java目录下创建IMIcon类,继承Text基础组件,实现三个构造器
* 将初始化写到第三个构造器中,其他两个构造器调用第三个构造器(默认直接调用第二个构造器)
*/
public class IMIcon extends Text {
public IMIcon(Context context) {
this(context, null);
}
public IMIcon(Context context, AttrSet attrSet) {
this(context, attrSet, null);
}
public IMIcon(Context context, AttrSet attrSet, String styleName) {
super(context, attrSet, styleName);
setTypeFace(context);
initIMIcon(attrSet);
}
private void initIMIcon(AttrSet attrSet) {
//设置文本内容对齐方式
setTextAlignment(TextAlignment.CENTER);
//设置文本内容大小
if (attrSet.getAttr("icon_size").isPresent()) {
setTextSize(attrSet.getAttr("icon_size").get().getIntegerValue(), TextSizeType.FP);
} else {
setTextSize(Constant.DEFAULT_ICON_WIDTH_HEIGHT, TextSizeType.FP);
}
//设置显示文本内容
if (attrSet.getAttr("icon").isPresent()) {
String icon = attrSet.getAttr("icon").get().getStringValue();
setText(IMButtonIconType.valueOf(icon).getStyle());
} else {
//IMButtonIconType.ANGLE_LEFT需要我们根据fontawesome提供的矢量图中的Unicode编码进行转化
//具体参考代码仓中的IMButtonIconType枚举类
setText(IMButtonIconType.ANGLE_LEFT.getStyle());
}
}
/**
* 设置字体显示样式 - font awesome
* @param context
*/
private void setTypeFace(Context context) {
String typeFace = "fontawesome-webfont.ttf";
File file = new File(context.getCodeCacheDir(), typeFace);
OutputStream outputStream = null;
ResourceManager resManager = context.getResourceManager();
//在resources/rawfile目录下创建fonts目录,并拷贝下载的fontawesome字体文件
//这里读取字体文件
RawFileEntry rawFileEntry = resManager.getRawFileEntry("resources/rawfile/fonts/" + typeFace);
Resource resource = null;
try {
//打开字体文件,使用流的方式读取
resource = rawFileEntry.openRawFile();
outputStream = new FileOutputStream(file);
int index;
byte[] bytes = new byte[1024];
if (resource != null) {
while ((index = resource.read(bytes)) != -1) {
outputStream.write(bytes, 0, index);
outputStream.flush();
}
//设置当前组件的文本样式
Font font = new Font.Builder(file).build();
setFont(font);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
resource.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
<!--
在头部加上我们的自定义属性规则
使用自定义规则设置属性值
-->
<com.itming.ui.coptlib.core.IMIcon
ohos:margin="10fp"
ohos:height="64fp"
ohos:width="64fp"
ohos:text_color="#088888"
itming:icon="USER"
itming:icon_size="36"/>
然后运行程序,查看效果。
2、自定义标题栏(IMCustomTitleBar)
上图是自定义标题栏实例,可以看到图标按钮是设置了可见性的。UI模板库与组件库不同之处在于,UI模板库是以核心组件为基础,各个元素整合后的产物。
构建一个HarmonyOS库分为三步:
1、创建Module,指定为HarmonyOS Library(选中项目右键New—>Module…)
2、使用Gradle构建Har包(右侧gradle选项卡中ohos:debug下packageDebugHar)
3、主项目引入Har包(在build.gradle中dependencies添加implementation project(“:uilib”) )
先不着急的写代码,先完成以上三步曲,然后再进行下一步。
/**
* <p>自定义标题栏</p>
* <ol>
* <li>左侧返回按钮</li>
*
* <li>中间标题内容</li>
*
* <li>
* <p>右侧图标按钮组</p>
* <ol>
* <li>右侧搜索按钮</li>
* <li>右侧多选按钮组</li>
* </ol>
* </li>
* </ol>
* @author itming
* @date 2021-06-26
*/
public class IMCustomTitleBar extends DependentLayout {
public IMCustomTitleBar(Context context) {
this(context, null);
}
public IMCustomTitleBar(Context context, AttrSet attrSet) {
this(context, attrSet, null);
}
public IMCustomTitleBar(Context context, AttrSet attrSet, String styleName) {
super(context, attrSet, styleName);
initImCustomTitleBar(context, attrSet);
}
/**
* 初始化标题栏
* @param attrSet
*/
private void initImCustomTitleBar(Context context, AttrSet attrSet) {
//动态加载Layout
Component component = LayoutScatter.getInstance(context)
.parse(ResourceTable.Layout_layout_title, null, false);
//子组件
IMIconButton backBtn = (IMIconButton) component.findComponentById(ResourceTable.Id_back_btn);
Text titleBarContent = (Text) component.findComponentById(ResourceTable.Id_page_title);
DirectionalLayout groupBtn = (DirectionalLayout) component.findComponentById(ResourceTable.Id_group_btn);
IMIconButton searchBtn = (IMIconButton) groupBtn.findComponentById(ResourceTable.Id_search_btn);
IMIconButton selectBtn = (IMIconButton) groupBtn.findComponentById(ResourceTable.Id_plus_btn);
//添加layout到父组件
addComponent(component);
//设置标题栏背景色
if (attrSet.getAttr("bar_bg").isPresent()) {
component.setBackground(attrSet.getAttr("bar_bg").get().getElement());
} else {
component.setBackground(getBackgroundElement());
}
/**
* 设置标题栏内容
*/
if (attrSet.getAttr("bar_content").isPresent()) {
titleBarContent.setText(attrSet.getAttr("bar_content").get().getStringValue());
} else {
titleBarContent.setText("");
}
//设置标题栏内容字体大小
if (attrSet.getAttr("bar_content_size").isPresent()) {
titleBarContent.setTextSize(attrSet.getAttr("bar_content_size").get().getIntegerValue(), Text.TextSizeType.FP);
} else {
titleBarContent.setTextSize(Constant.DEFAULT_BAR_CONTENT_SIZE, Text.TextSizeType.FP);
}
//设置标题内容字体颜色
if (attrSet.getAttr("bar_content_color").isPresent()) {
titleBarContent.setTextColor(attrSet.getAttr("bar_content_color")
.get().getColorValue());
} else {
if (attrSet.getAttr("bar_bg").isPresent()) {
titleBarContent.setTextColor(Color.WHITE);
} else {
titleBarContent.setTextColor(Color.BLACK);
}
}
//设置是否显示返回按钮
if (attrSet.getAttr("bar_back_btn").isPresent()) {
if (attrSet.getAttr("bar_back_btn").get().getBoolValue()) {
backBtn.setVisibility(VISIBLE);
} else {
backBtn.setVisibility(INVISIBLE);
}
} else {
backBtn.setVisibility(INVISIBLE);
}
//设置返回按钮颜色
if (attrSet.getAttr("bar_back_btn_color").isPresent()) {
backBtn.setTextColor(attrSet.getAttr("bar_back_btn_color")
.get().getColorValue());
} else {
if (attrSet.getAttr("bar_bg").isPresent()) {
backBtn.setTextColor(Color.WHITE);
} else {
backBtn.setTextColor(Color.BLACK);
}
}
//设置右边按钮组可见值(其中之一可见,即容器可见)
if (attrSet.getAttr("bar_search_btn").isPresent()
|| attrSet.getAttr("bar_select_btn").isPresent()) {
if (attrSet.getAttr("bar_search_btn").isPresent()) {
if (attrSet.getAttr("bar_search_btn").get().getBoolValue()) {
searchBtn.setVisibility(VISIBLE);
} else {
searchBtn.setVisibility(HIDE);
}
} else {
selectBtn.setWidth(MATCH_PARENT);
searchBtn.setVisibility(HIDE);
}
if (attrSet.getAttr("bar_select_btn").isPresent()) {
if (attrSet.getAttr("bar_select_btn").get().getBoolValue()) {
selectBtn.setVisibility(VISIBLE);
} else {
selectBtn.setVisibility(HIDE);
}
} else {
searchBtn.setWidth(MATCH_PARENT);
selectBtn.setVisibility(HIDE);
}
groupBtn.setVisibility(VISIBLE);
} else {
groupBtn.setVisibility(HIDE);
}
//设置右侧按钮颜色
if (attrSet.getAttr("bar_search_btn_color").isPresent()) {
searchBtn.setTextColor(attrSet.getAttr("bar_search_btn_color")
.get().getColorValue());
} else {
if (attrSet.getAttr("bar_bg").isPresent()) {
searchBtn.setTextColor(Color.WHITE);
} else {
searchBtn.setTextColor(Color.BLACK);
}
}
if (attrSet.getAttr("bar_select_btn_color").isPresent()) {
selectBtn.setTextColor(attrSet.getAttr("bar_select_btn_color")
.get().getColorValue());
} else {
if (attrSet.getAttr("bar_bg").isPresent()) {
selectBtn.setTextColor(Color.WHITE);
} else {
selectBtn.setTextColor(Color.BLACK);
}
}
}
}
<!--布局文件-->
<DependentLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
xmlns:itming="http://schemas.huawei.com/res/ohos-auto"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:background_element="#088888"
ohos:alignment="vertical_center">
<com.itming.ui.coptlib.core.IMIconButton
ohos:id="$+id:back_btn"
ohos:height="match_parent"
ohos:width="48vp"
ohos:text_color="#F2F2F2"
itming:icon="ANGLE_LEFT"
itming:icon_size="36"
ohos:align_parent_left="true"/>
<Text
ohos:id="$+id:page_title"
ohos:height="match_parent"
ohos:width="match_content"
ohos:text="Page title"
ohos:text_size="24fp"
ohos:text_color="#FFFFFF"
ohos:center_in_parent="true"/>
<DirectionalLayout
ohos:id="$+id:group_btn"
ohos:height="match_parent"
ohos:width="96vp"
ohos:orientation="horizontal"
ohos:align_parent_right="true">
<com.itming.ui.coptlib.core.IMIconButton
ohos:id="$+id:search_btn"
ohos:height="match_parent"
ohos:width="48vp"
ohos:text_color="#F2F2F2"
itming:icon="DEFAULT_SEARCH"
itming:icon_size="24"/>
<com.itming.ui.coptlib.core.IMIconButton
ohos:id="$+id:plus_btn"
ohos:height="match_parent"
ohos:width="48vp"
ohos:text_color="#F2F2F2"
itming:icon="PLUS_SQUARE"
itming:icon_size="24"/>
</DirectionalLayout>
</DependentLayout>
<!--
在头部加上我们的自定义属性规则
使用自定义规则设置属性值
-->
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
xmlns:itming="http://schemas.huawei.com/res/ohos-auto"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
<com.itming.ui.uilib.bar.IMCustomTitleBar
ohos:height="54vp"
ohos:width="match_parent"
itming:bar_bg="#FFE95757"
itming:bar_content="仅内容标题栏"
itming:bar_content_size="18"/>
<com.itming.ui.uilib.bar.IMCustomTitleBar
ohos:id="$+id:custom_title_bar"
ohos:top_margin="20vp"
ohos:height="54vp"
ohos:width="match_parent"
itming:bar_bg="#FFE95757"
itming:bar_content="带返回图标标题栏"
itming:bar_content_size="18"
itming:bar_back_btn="true"/>
<com.itming.ui.uilib.bar.IMCustomTitleBar
ohos:top_margin="20vp"
ohos:height="54vp"
ohos:width="match_parent"
itming:bar_bg="#FFE95757"
itming:bar_content="带返回+搜索标题栏"
itming:bar_content_size="18"
itming:bar_back_btn="true"
itming:bar_search_btn="true"/>
<com.itming.ui.uilib.bar.IMCustomTitleBar
ohos:top_margin="20vp"
ohos:height="54vp"
ohos:width="match_parent"
itming:bar_bg="#FFE95757"
itming:bar_content="带返回+多选标题栏"
itming:bar_content_size="18"
itming:bar_back_btn="true"
itming:bar_select_btn="true"/>
<com.itming.ui.uilib.bar.IMCustomTitleBar
ohos:top_margin="20vp"
ohos:height="54vp"
ohos:width="match_parent"
itming:bar_bg="#FFE95757"
itming:bar_content="带返回+搜索+多选标题栏"
itming:bar_content_size="16"
itming:bar_back_btn="true"
itming:bar_search_btn="true"
itming:bar_select_btn="true"/>
<Text
ohos:height="match_parent"
ohos:width="match_parent"
ohos:background_element="#EFEFEF"
ohos:text_alignment="center"
ohos:text="这是一个自定义标题"
ohos:text_size="14fp"/>
</DirectionalLayout>
运行程序查看效果。
3、沉浸式登录(IMCustomLogin)
登录功能是最常见的功能,社交类的先登录后使用,咨询类的先使用后登录。两者区别在于是不是需要关闭按钮。有一个应用两种都有,可以不登录就查看部分功能,使用特定功能需要登录,那么启动程序先到登录界面,登录界面具有跳过能力。点击特定功能判断是否登录,若没有登录,则弹出登录页面,不想登录,点击关闭即可。本实例代码抽取两种情况,通过关闭按钮的可见性控制登录页面的使用情况。==代码参见代码仓。==
4、在线教育中服务卡片思考
服务卡片是HarmonyOS的一大亮点,在线教育中我所关注的是所学课程是否更新(目前以短信或者通知形式呈现)、所学课程到第几节次、关注领域是否上架新课程、我的学习记录等。只有课程在更新后以短信或者通知形式推送给我,其他的我只能进入App中查看。如果使用服务卡片,我将把这些卡片放置在桌面上,能够使我一目了然的知道自己想要的信息。这部分的思考,后续会以代码的形式提交到代码仓中。
二、代码仓
»»»» 公开课用到的代码都在这里!!! ««««
PS:如果你有好的建议,也可以和我交流,在持续的提交中也许就会有你的成果。
三、公开课直播回放地址
»»»» 公开课直播回放地址 ««««
给白老师点赞!
点赞!