赞
踩
目前,CSDN官方App并没有适配鸿蒙系统,但是我们是程序员,完全可以自己开发,何须等待CSDN呢?
自己动手丰衣足食,今天,我们来实现一个有趣的鸿蒙卡片。也就是将自己的最新的博文前10篇搬上鸿蒙卡片,并展示给大家。
除了将10篇最新的博文搬上鸿蒙卡片之外,我们还需要给鸿蒙的卡片提供可编辑的功能,让用户替换博主,自动替换对应博主的前10篇博文。
下面,我们来一一实现这些功能。
我们首先需要观察一下CSDN博文的标题长度,可以发现有些CSDN标题还是很长的,如果用小卡片肯定连标题都显示不下。所以,我们需要提供4*4的长卡片。
创建步骤如下,这里我们首先通过DevEco Studio创建一个纯JS项目,如下图所示:
项目创建完成之后,我们会进入项目开发页面。这里选择entry-src右键创建JS的4*4的卡片内容,具体创建步骤如下所示:
这样,我们就完成了卡片的创建。但是这里一般来说,因为我们刚创建项目的时候,没有默认的2*2卡片,所以这里会创建2*2和4*4两个卡片。
不过,2*2太小,并不能完整显示博文列表。下面,我们来实现博文浏览的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=new ZSONArray(); ZSONObject zsonObject=new ZSONObject(); LYJUtils.getData(zsonObject,zsonArray,url); getUITaskDispatcher().asyncDispatch(new Runnable() { @Override public void run() { zsonObject.put("blogList",zsonArray); FormBindingData bindingData = new FormBindingData(zsonObject); try { getAbility().updateForm(formId,bindingData); terminateAbility(); } catch (FormException e) { e.printStackTrace(); } } }); } catch (Exception e) { e.printStackTrace(); } } }); } catch (Exception e) { HiLog.info(TAG, "12w"); e.printStackTrace(); } } }); } public void init(){ this.csdnBlog.put("老猿Python","https://blog.csdn.net/LaoYuanPython"); this.csdnBlog.put("李元静","https://blog.csdn.net/liyuanjinglyj"); this.csdnBlog.put("大家一起学编程","https://blog.csdn.net/qq_39046854"); this.csdnBlog.put("主打Python","https://blog.csdn.net/weixin_54733110"); this.csdnBlog.put("CHQIUU","https://blog.csdn.net/QIU176161650"); } }
这里,博主只定义了5位博主的信息添加到Picker组件,如果你刚学习鸿蒙开发,对Picker组件陌生,可以参考博主的鸿蒙开发专栏,有专门一篇博文讲解该组件。
运行之后,长按卡片点击编辑,就会弹出如下界面。
当然,弹出来的界面有点不好看,主要是因为博主没有系统学过设计与色彩搭配,这里就随便找了一个背景。
读者如果点击上面的“替换博主信息”的按钮,就会自动替换卡片上的内容,与博文最开头实现的效果一致。
编辑界面的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"> <DirectionalLayout ohos:height="800px" ohos:width="800px" ohos:background_element="$media:background"> <DirectionalLayout ohos:height="match_parent" ohos:width="match_parent" ohos:alignment="center"> <Picker ohos:id="$+id:ability_csdnchoice_blog_picker" ohos:width="match_parent" ohos:height="match_content" ohos:normal_text_size="18fp" ohos:selected_text_size="25fp"/> <Button ohos:id="$+id:ability_csdnchoice_blog_button" ohos:height="match_content" ohos:width="match_content" ohos:top_margin="20px" ohos:layout_alignment="horizontal_center" ohos:background_element="$graphic:button_background_ability" ohos:padding="5px" ohos:text_size="25fp" ohos:text="替换博主信息"/> </DirectionalLayout> </DirectionalLayout> </DirectionalLayout>
到这里,我们的CSDN博文卡片就移值到鸿蒙系统的卡片上了。当然,你可以添加更多的火爆博主供大家选择。
不过,这里还有几个地方需要注意:
第一,上面的HTML解析需要用到jsoup.jar文件,你在项目中使用的使用,需要将jsoup包复制到entry-libs文件夹中,并右键点击add Libraries。
第二,上面的网页请求涉及到网络权限,需要在项目的配置文件config.json中配置网络权限,代码如下:
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
}
],
}
第三,默认创建的2*2与4*4的卡片,但我们只需要4*4的卡片,你只需要在配置文件中,删除supportDimensions属性里面的2*2即可。
本文源代码地址:https://gitee.com/liyuanjinglyj/CSDNCard
本文正在参与“有奖征文 | HarmonyOS征文大赛”活动:
活动链接:https://marketing.csdn.net/p/ad3879b53f4b8b31db27382b5fc65bbc
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。