作者:韩茹
公司:程序咖(北京)科技有限公司
鸿蒙巴士专栏作家
本案例用到了ListContainer,BaseItemProvider,网络下载,线程之间的通信等等。。
一、项目展示
首先我们先新建一个HarmonyOS的项目:
运行效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTaac4gJ-1628642823154)(https://img.chengxuka.com/qiushibaikeyunxing1.gif)]
二、布局文件
现在ability_main.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:padding="10vp"
ohos:orientation="vertical">
<Button
ohos:id="$+id:btn_download"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="点击下载糗百数据"
ohos:text_size="25fp"
ohos:background_element="#EEEEEE"
ohos:padding="5vp"
/>
<RoundProgressBar
ohos:id="$+id:round_progress_bar1"
ohos:height="200vp"
ohos:width="200vp"
ohos:progress_width="10vp"
ohos:top_margin="150vp"
ohos:progress_hint_text="加载ing。。"
ohos:progress_hint_text_color="#C71585"
ohos:layout_alignment="center"
ohos:visibility="hide"
ohos:progress_color="#C71585"/>
<ListContainer
ohos:id="$+id:list_container"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:rebound_effect="true"
/>
</DirectionalLayout>
新建一个xml文件,表示list中的每一个item,list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_content"
ohos:width="match_parent"
ohos:background_element="$graphic:list_item_bg"
ohos:bottom_margin="10vp"
ohos:orientation="vertical">
<Text
ohos:id="$+id:list_item_id"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="ID"
ohos:text_size="20fp"
ohos:text_color="#0000AA"
ohos:padding="5vp"
/>
<Text
ohos:id="$+id:list_item_user"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="User"
ohos:text_size="20fp"
ohos:text_color="#AA0000"
ohos:padding="5vp"
ohos:multiple_lines="true"
/>
<Text
ohos:id="$+id:list_item_content"
ohos:height="match_content"
ohos:width="match_parent"
ohos:multiple_lines="true"
ohos:text_alignment="left|top"
ohos:text="Content"
ohos:text_size="20fp"
ohos:text_color="#000000"
ohos:padding="5vp"
/>
</DirectionalLayout>
三、网络请求
首先新建一个包constant,用于存储程序中使用到的网址常量:
package com.example.hanruqiushibaike.constant;
/**
* 常量
*/
public class Constant {
public static final String QIUSHIBAIKE_URL="https://m2.qiushibaike.com/article/list/suggest?page=1";
}
再创建utils,提供一个网络下载的工具类:
package com.example.hanruqiushibaike.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class HttpUtils {
/**
* 根据网络接口,获取指定的InputStream
* @param baseUrl
* @return
*/
public static InputStream getInputStream(String baseUrl){
try {
URL url = new URL(baseUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko");
if(connection.getResponseCode()==HttpURLConnection.HTTP_OK){
return connection.getInputStream();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据网络接口,获取数据,byte数组
* @param baseUrl
* @return
*/
public static byte[] getDataFromNet(String baseUrl){
InputStream inputStream = getInputStream(baseUrl);
if(inputStream!=null){
byte[]bs = new byte[1024];
int len=0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
while((len = inputStream.read(bs))!=-1){
baos.write(bs, 0, len);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 根据网络接口,获取json数据
* @param baseUrl
* @return
*/
public static String getJsonFromNet(String baseUrl){
byte[] data = getDataFromNet(baseUrl);
if(data!=null){
return new String(data);
}
return null;
}
}
然后我们可以测试一下,点击开下面的test包,在ExampleTest类中再添加一个单元测试方法:
@Test
public void testHttpUtils(){
String json = HttpUtils.getJsonFromNet(Constant.QIUSHIBAIKE_URL);
System.out.println("json--"+json);
}
然后点击方法前面的小绿揪揪,选择Run这个方法:
我们就可以看到下面的运行结果了:
到此网络下载json数据是没有问题的。
四、json解析
这里下载到的糗百数据是json格式的,我们需要将它进行解析,存储到List集合里。浏览器上打开糗百的网址,然后将json数据复制,随便搜索一个在线格式化的工具进行格式化后,这里我们只要3个字段:id,login,和content:
我们在utils包下新建一个json解析的工具类:JsonParseUtils.java
package com.example.hanruqiushibaike.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* 解析糗百的json数据
*/
public class JsonParseUtils {
public static List<Map<String, String>> parseJsonToList(String json){
List<Map<String, String>> list = new ArrayList<Map<String,String>>();
try {
JSONArray jsonArray = new JSONObject(json).getJSONArray("items");
for(int i=0;i<jsonArray.length();i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
Map<String, String> map = new HashMap<String, String>();
int id = jsonObject.optInt("id");
map.put("id", id+"");
map.put("content", jsonObject.optString("content"));
JSONObject jsonObject2 = jsonObject.optJSONObject("user");
if(jsonObject2!=null){
map.put("login", jsonObject2.optString("login"));
}
list.add(map);
}
} catch (JSONException e) {
e.printStackTrace();
}
return list;
}
}
我们测试一下,同样还是在ExampleTest下添加一个单元测试方法:
@Test
public void testJson(){
List<Map<String, String>> list = JsonParseUtils.parseJsonToList(HttpUtils.getJsonFromNet(Constant.QIUSHIBAIKE_URL));
System.out.println(list.size());
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
然后运行后观察结果:
到此我们已经解析了json数据。
五、ListItemProvider
然后我们就可以创建ListItemProvider类来进行填充ListContainer了。我们先创建一个包provider,然后新建一个provider文件:ListItemProvider.java
package com.example.hanruqiushibaike.provider;
import com.example.hanruqiushibaike.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.agp.components.*;
import java.util.List;
import java.util.Map;
public class ListItemProvider extends BaseItemProvider{
private List<Map<String, String>> list;
private AbilitySlice slice;
public ListItemProvider(List<Map<String, String>> list, AbilitySlice slice) {
this.list = list;
this.slice = slice;
}
@Override
public int getCount() {
return list == null?0: list.size();//一般返回数据源的长度
}
@Override
public Object getItem(int position) {
if(list!= null && position >= 0 && position < list.size()){
return list.get(position);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) {
final Component cpt;
// 如果还没有convertComponent对象,那么将xml布局文件转为一个Component对象。
if(convertComponent == null){
//从当前的AbilitySlice对应的xml布局中,
cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_list_item,null,false);
}else{
cpt = convertComponent;
}
Map<String,String> map =list.get(position);//获取数据
Text textId = (Text) cpt.findComponentById(ResourceTable.Id_list_item_id);
Text textUser = (Text) cpt.findComponentById(ResourceTable.Id_list_item_user);
Text textContent = (Text) cpt.findComponentById(ResourceTable.Id_list_item_content);
textId.setText("用户ID:"+map.get("id"));
textUser.setText("用户名:"+map.get("login"));
textContent.setText(map.get("content"));
String content = map.get("content");
return cpt;
}
}
六、投递InnerEvent
我们现在MainAbilitySlice中,初始化组件,首先声明Button,RoundProgressBar和ListContainer对象,并在onStart()方法中调用该初始化方法。
// 获取UI组件
private void initComponent(){
btnDownload= (Button) findComponentById(ResourceTable.Id_btn_download);
roundProgressBar = (RoundProgressBar)findComponentById(ResourceTable.Id_round_progress_bar1);
listContainer = (ListContainer) findComponentById(ResourceTable.Id_list_container);
}
然后声明EventHandler和EventRunner对象,再实例化:
// 实例化EventHandler和EventRunner对象,并在onStart()方法中调用该初始化方法。
private void initHandler() {
eventRunner = EventRunner.create("TestRunner");
handler = new DownLoadImageEventHandler(eventRunner,this);
}
接下来给按钮添加点击事件,当点击下载按钮的时候,我们要进行任务投递,完整的MainAbilitySlice代码如下:
package com.example.hanruqiushibaike.slice;
import com.example.hanruqiushibaike.ResourceTable;
import com.example.hanruqiushibaike.constant.Constant;
import com.example.hanruqiushibaike.handler.DownLoadImageEventHandler;
import com.example.hanruqiushibaike.utils.HttpUtils;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
public class MainAbilitySlice extends AbilitySlice {
private Button btnDownload;
private RoundProgressBar roundProgressBar;
private ListContainer listContainer;
private EventRunner eventRunner;
private EventHandler handler;
// 先定义全局变量
public static final int EVENT_MESSAGE_NORMAL = 1;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
initComponent();
initHandler();
// 按钮的点击事件
btnDownload.setClickedListener(component -> {
// 点击按钮,下载糗事百科的json数据
long param = 0;
InnerEvent innerEvent = InnerEvent.get(EVENT_MESSAGE_NORMAL, param, EventRunner.current());
handler.sendEvent(innerEvent, EventHandler.Priority.IMMEDIATE);
System.out.println("UI线程-->InnerEvent已发送。。");
roundProgressBar.setVisibility(Component.VISIBLE);
});
}
public ListContainer getListContainer(){
return listContainer;
}
public RoundProgressBar getRoundProgressBar(){
return roundProgressBar;
}
// 获取UI组件
private void initComponent(){
btnDownload= (Button) findComponentById(ResourceTable.Id_btn_download);
roundProgressBar = (RoundProgressBar)findComponentById(ResourceTable.Id_round_progress_bar1);
listContainer = (ListContainer) findComponentById(ResourceTable.Id_list_container);
}
//step2:实例化EventHandler和EventRunner对象,并在onStart()方法中调用该初始化方法。
private void initHandler() {
eventRunner = EventRunner.create("TestRunner");
handler = new DownLoadImageEventHandler(eventRunner,this);
}
}
七、EventHandler处理
新建一个包handler,然后新建一个java文件,DownLoadImageEventHandler.java:
这里主要的思路是,
1.先根据网络请求获取到json数据。
2.然后解析json数据得到数据源list。
3.将list数据传回到原来的UI线程,并进行设置listContainer。
示例代码:
package com.example.hanruqiushibaike.handler;
import com.example.hanruqiushibaike.constant.Constant;
import com.example.hanruqiushibaike.provider.ListItemProvider;
import com.example.hanruqiushibaike.slice.MainAbilitySlice;
import com.example.hanruqiushibaike.utils.HttpUtils;
import com.example.hanruqiushibaike.utils.JsonParseUtils;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.components.RoundProgressBar;
import ohos.agp.window.dialog.ToastDialog;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import java.util.List;
import java.util.Map;
//1.创建自定义的EventHandler子类
public class DownLoadImageEventHandler extends EventHandler {
private MainAbilitySlice slice;
//2.添加构造方法
public DownLoadImageEventHandler(EventRunner runner, MainAbilitySlice slice) {
super(runner);
this.slice = slice;
}
//3.重写processEvent()方法
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
if (event == null) {
return;
}
switch (event.eventId) {
case MainAbilitySlice.EVENT_MESSAGE_NORMAL:
System.out.println("----开始网络下载---");
// 下载完毕后,解析json数据
String json = HttpUtils.getJsonFromNet(Constant.QIUSHIBAIKE_URL);
System.out.println("===json:" + json);
if (json != null) {
List<Map<String, String>> listData = JsonParseUtils.parseJsonToList(json);
// 在UI线程上设置ListContainer
EventRunner runner = (EventRunner) event.object;
EventHandler eventHandler = new EventHandler(runner) {
@Override
protected void processEvent(InnerEvent event) {
// 进度条消失
RoundProgressBar roundProgressBar = slice.getRoundProgressBar();
roundProgressBar.setVisibility(Component.HIDE);
// 设置数据到ListContainer上
if (listData.size() == 0) {
new ToastDialog(slice.getContext()).setText("没有数据显示").show();
} else {
// 1.初始化Provider对象
ListItemProvider listItemProvider = new ListItemProvider(listData, slice);
// 2.获取listContainer对象
ListContainer listContainer = slice.getListContainer();
// 3.适配要展示的内容数据
listContainer.setItemProvider(listItemProvider);
}
}
};
int testEventId = 1;
long testParam = 0;
Object testObject = listData;
InnerEvent innerEvent = InnerEvent.get(testEventId, testParam, testObject);
eventHandler.sendEvent(innerEvent);
}
}
}
}
到此结束。
更多内容:
1、社区:鸿蒙巴士https://www.harmonybus.net/
2、公众号:HarmonyBus
3、技术交流QQ群:714518656
4、视频课:https://www.chengxuka.com