HarmonyOS实战—将CSDN博文搬上鸿蒙卡片
Posted 李元静
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HarmonyOS实战—将CSDN博文搬上鸿蒙卡片相关的知识,希望对你有一定的参考价值。
HarmonyOS实战
目前,CSDN官方App并没有适配鸿蒙系统,但是我们是程序员,完全可以自己开发,何须等待CSDN呢?
自己动手丰衣足食,今天,我们来实现一个有趣的鸿蒙卡片。也就是将自己的最新的博文前10篇搬上鸿蒙卡片,并展示给大家。
除了将10篇最新的博文搬上鸿蒙卡片之外,我们还需要给鸿蒙的卡片提供可编辑的功能,让用户替换博主,自动替换对应博主的前10篇博文。
下面,我们来一一实现这些功能。
创建4*4的卡片
我们首先需要观察一下CSDN博文的标题长度,可以发现有些CSDN标题还是很长的,如果用小卡片肯定连标题都显示不下。所以,我们需要提供4*4的长卡片。
创建步骤如下,这里我们首先通过DevEco Studio创建一个纯JS项目,如下图所示:
项目创建完成之后,我们会进入项目开发页面。这里选择entry-src右键创建JS的4*4的卡片内容,具体创建步骤如下所示:
这样,我们就完成了卡片的创建。但是这里一般来说,因为我们刚创建项目的时候,没有默认的2*2卡片,所以这里会创建2*2和4*4两个卡片。
不过,2*2太小,并不能完整显示博文列表。下面,我们来实现博文浏览的4*4功能卡片。
实现4*4博文列表
首先,我们可以回博文的最上面看看最终的实现效果。可以发现,我们的4*4卡片有头像、姓名、简介以及一个最新的博文列表。
博文卡片的定义
所以,我们需要创建这样一个布局,来完美搭建这些信息,并完成博文点击的交互。首先,是我们需要实现的界面布局代码(index.hml):
<div class="div_layout" >
<div style="flex-direction: row;height: 80px;margin: 12px;" >
<image class="head_img" src="随便填一个图片链接"></image>
<div style="flex-direction: column;margin-left: 12px;">
<text class="head_name" >{{name}}</text>
<text class="head_content" >{{content}}</text>
</div>
</div>
<list>
<list-item for="{{ blogList }}" style="flex-direction: column;">
<div class="list_item_container" onclick="sendRouteEvent" >
<text class="item_title">{{ $item.title }}</text>
</div>
<divider class="divider"></divider>
</list-item>
</list>
</div>
这里,我们的image组件头像用的是固定的image图片。因为获取到的CSDN头像图片,image组件不更新,只有Java卡片目前能完美实现该功能。(js卡片好像只有展示第一次能显示图片,后面更新图片都不显示)
这也是我期望反馈给鸿蒙官方的问题。所以,这里我们用固定的头像替代,除了头像图片无法替换之外,其他信息可以完美替换。
接着,我们需要实现样式(index.css)代码:
.div_layout{
flex-direction: column;
width: 100%;
height: 100%;
background-image: url("common/background.png");
}
.list_item_container{
margin: 12px;
flex-direction: column;
}
.item_title{
font-size: 15px;
font-weight: bold;
}
.divider {
min-height: 0.75px;
background-color: #11000000;
margin-left: 12px;
margin-right: 12px;
}
.head_img{
border-radius: 30px;
width: 60px;
height: 60px;
}
.head_name{
margin-top: 13px;
font-size: 12px;
font-weight: bold;
}
.head_content{
font-size: 12px;
}
最后,就是完成交互信息的反馈。卡片的交互变量以及交互跳转界面都是通过index.json文件进行定义的,代码如下:
{
"data": {
"blogList": "",
"head": "https://avatar.csdnimg.cn/6/8/2/3_liyuanjinglyj_1623337572.jpg",
"name": "李元静",
"content": "一个能让你轻松学习鸿蒙与Python的博主"
},
"actions": {
"sendRouteEvent": {
"action": "router",
"bundleName": "com.liyuanjinglyj.csdncard",
"abilityName": "com.liyuanjinglyj.csdncard.WebViewPage",
"params": {
"url": "{{$item.url}}"
}
}
}
}
其中,blogList是博文列表信息,head是头像,但因为image不更新,这里忽略。name是博文的归属者昵称,content是博主的简介。
actions这里只定义了一个跳转界面的交互,也就是用户点击博文信息,然后就跳转到博文的详细页面。参数为博文的链接,在博文详细页面通过WebView进行加载。
博文信息的获取
既然要获取到博文首页的博文信息以及用户的资料,这就涉及到爬虫解析。而Java比较好用的爬虫解析包是jsoup。
而后面我们选择替换博主博文信息,也是同一个方法。所以,需要将解析的代码独立出来,减少代码的冗余程度。具体的帮助类为LYJUtils.java:
public class LYJUtils {
public static void getData(ZSONObject zsonObject,ZSONArray zsonArray,String url) {
try {
Connection connect = Jsoup.connect(url);
Connection conheader = connect.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
Document doc = conheader.get();
//获取头像
Element head_img=doc.selectFirst(".avatar_pic");
String head_img_url=head_img.attr("src");
//获取博主名称
Element name_ele=doc.selectFirst(".title-box");
String name=name_ele.selectFirst("a").text();
//获取博主简介
String content=name_ele.selectFirst("p").text();
//zsonObject.put("head",head_img_url);
zsonObject.put("name",name);
if(content.length()>20){
zsonObject.put("content",content.substring(0,20));
}else{
zsonObject.put("content",content);
}
Elements list_blog = doc.select(".article-item-box");
int i=0;
for (Element div : list_blog) {
if(i==10){
break;
}
String titleStr = div.select("h4 a").text();
String contentStr = div.select(".content").text();
ZSONObject zsonObject1=new ZSONObject();
zsonObject1.put("title",titleStr.replace("原创 ",""));
zsonObject1.put("content",contentStr.substring(0,20));
zsonObject1.put("url",div.select("h4 a").attr("href"));
zsonArray.add(zsonObject1);
i++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
而卡片的数据初始化方法在WidgetImpl.java类之中,它的初始化代码如下所示:
public class WidgetImpl extends FormController {
//其他代码省略,基本都是创建卡片的默认代码
private ZSONObject zsonObject = new ZSONObject();
private ZSONArray zsonArray=new ZSONArray();
@Override
public ProviderFormInfo bindFormData() {
HiLog.info(TAG, "bind form data");
LYJUtils.getData(zsonObject,zsonArray,"https://blog.csdn.net/liyuanjinglyj");
zsonObject.put("blogList",zsonArray);
ProviderFormInfo providerFormInfo = new ProviderFormInfo();
providerFormInfo.setJsBindingData(new FormBindingData(zsonObject));
return providerFormInfo;
}
}
这样运行之后,我们的初始化卡片博文样式就完美实现了。
卡片交互
到这里,我们仅仅实现了卡片的数据展示。但我们看一个博主的博文并不是只看标题的,而是要看自己感兴趣的内容。所以,点击博文标题应该实现跳转到博文详情界面。
跳转到博文
首先,我们实现点击博文标题跳转到博文。读者可以往博文前面看一下,是不是有index.json文件中有一个actions定义,这里的类就是跳转的界面。
WebViewPageSlice.java代码如下所示:
public class WebViewPageSlice extends AbilitySlice {
private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, WebViewPageSlice.class.getName());
private WebView webView;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_web_view_page);
this.webView=(WebView)findComponentById(ResourceTable.Id_ability_web_view_page_webview);
WindowManager.getInstance().getTopWindow().get().setStatusBarColor(Color.BLUE.getValue()); // 设置状态栏颜色
getWindow().addFlags(WindowManager.LayoutConfig.MARK_TRANSLUCENT_STATUS);//沉浸式状态栏
if(intent != null) {
HiLog.info(TAG, String.valueOf(1111));
String param = intent.getStringParam("params");//从intent中获取 跳转事件定义的params字段的值
String url = "";
if(param !=null){
ZSONObject data = ZSONObject.stringToZSON(param);
url = data.getString("url");
}
HiLog.info(TAG, url);
this.webView.getWebConfig().setjavascriptPermit(true);
this.webView.load(url);
}
}
}
这个界面的内容很简单,就是获取跳转传递过来的网址参数信息。然后WebView组件,根据网址的内容即可,当然需要支持JavaScript,不然加载出来的界面非常难看。
博文的XML布局文件代码如下所示(ability_web_view_page.xml):
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
<ohos.agp.components.webengine.WebView
ohos:id="$+id:ability_web_view_page_webview"
ohos:height="match_parent"
ohos:width="match_parent"/>
</DirectionalLayout>
编辑替换卡片内容
不过,这些内容都是博主自己的。肯定有读者也对本博主的内容不感兴趣,想要看其他博主的内容怎么办?
我们这里,可以提供一个滑动可选组件,让用户选择自己感兴趣的博主。这样卡片就能完成更新,达到真实意义上的交互。
首先,我们需要定义卡片的编辑跳转界面,需要在config.json文件中定义,代码如下:
{
"name": "com.liyuanjinglyj.csdncard.widget.WidgetAbility",
"icon": "$media:icon",
"description": "$string:widget_widgetability_description",
"formsEnabled": true,
"label": "$string:entry_WidgetAbility",
"type": "page",
"forms": [
{
"jsComponentName": "widget",
"isDefault": true,
"formConfigAbility": "ability://com.liyuanjinglyj.csdncard.CSDNChoiceBlogAbility",
"scheduledUpdateTime": "10:30",
"defaultDimension": "4*4",
"name": "widget",
"description": "This is a service widget",
"colorMode": "auto",
"type": "JS",
"supportDimensions": [
"4*4"
],
"updateEnabled": true,
"updateDuration": 1
}
],
"launchType": "singleton"
},
这里,主要的定义代码是formConfigAbility,它负责提供卡片的编辑交互功能的跳转界面,与之前跳转界面一样,就是一个普通的Java界面类。
下面,我们实现这里编辑界面,并提供完成交互的能力。
public class CSDNChoiceBlogAbilitySlice extends AbilitySlice {
private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, CSDNChoiceBlogAbilitySlice.class.getName());
private Picker picker;
private Button button;
private Long formId;
private Map<String,String> csdnBlog=new HashMap<>();
private String url="https://blog.csdn.net/qq_39046854";
private static List<String> headImgUrl=new ArrayList<>();
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_csdnchoice_blog);
WindowManager.getInstance().getTopWindow().get().setTransparent(true);
formId=intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, -1);
init();
getWindow().addFlags(WindowManager.LayoutConfig.MARK_TRANSLUCENT_STATUS);
this.button=(Button)findComponentById(ResourceTable.Id_ability_csdnchoice_blog_button);
this.picker=(Picker) findComponentById(ResourceTable.Id_ability_csdnchoice_blog_picker);
String[] keyStr= Arrays.stream(csdnBlog.keySet().toArray()).toArray(String[]::new);
this.picker.setDisplayedData(keyStr);
this.picker.setWheelModeEnabled(true);
this.picker.setMaxValue(csdnBlog.size()-1);
this.picker.setValueChangedListener(new Picker.ValueChangedListener() {
@Override
public void onValueChanged(Picker picker, int i, int i1) {
if(i1>=0 && i1<csdnBlog.size()){
url=csdnBlog.get(keyStr[i1]);
}
}
});
this.button.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
try {
HiLog.info(TAG, url);
TaskDispatcher globalTaskDispatcher = getGlobalTaskDispatcher(TaskPriority.DEFAULT);
Revocable revocable = globalTaskDispatcher.asyncDispatch(new Runnable() {
@Override
public void run() {
try {
ZSONArray zsonArray=HarmonyOS实战:基于鸿蒙服务卡片的分布式游戏