6、糗事百科案例

  • Post author:
  • Post category:其他


作者:韩茹

公司:程序咖(北京)科技有限公司

鸿蒙巴士专栏作家

本案例用到了ListContainer,BaseItemProvider,网络下载,线程之间的通信等等。。



一、项目展示

首先我们先新建一个HarmonyOS的项目:

WX20210630-142734@2x

运行效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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这个方法:

WX20210630-152024@2x

我们就可以看到下面的运行结果了:

WX20210630-152223@2x

到此网络下载json数据是没有问题的。



四、json解析

这里下载到的糗百数据是json格式的,我们需要将它进行解析,存储到List集合里。浏览器上打开糗百的网址,然后将json数据复制,随便搜索一个在线格式化的工具进行格式化后,这里我们只要3个字段:id,login,和content:

WX20210630-153015@2x

我们在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));
        }
    }

然后运行后观察结果:

WX20210630-153451@2x

到此我们已经解析了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



版权声明:本文为hanru723原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。