第四章:UI高级控件
4.1、ListContainer列表控件

ListContainer是用来呈现连续、多行数据的组件,包含一系列相同类型的列表项。有点类似Android中的ListView控件。

支持的XML属性

ListContainer的共有XML属性继承自:Component

综合案例一:加载简单数据

1、在layout目录下,AbilitySlice对应的布局文件ability_main.xml文件中创建ListContainer

<?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">

    <ListContainer
        ohos:id="$+id:list_container"
        ohos:height="match_parent"
        ohos:width="match_parent"
        ohos:layout_alignment="horizontal_center"
        />

</DirectionalLayout>

2、在layout目录下新建xml文件(例:item_city.xml),作为ListContainer的子布局。

<?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:left_margin="16vp"
    ohos:right_margin="16vp"
    ohos:orientation="vertical">
    <Text
        ohos:id="$+id:item_index"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:padding="4vp"
        ohos:text="Item0"
        ohos:text_size="20fp"
        ohos:layout_alignment="center"/>

    <Component
        ohos:height="1vp"
        ohos:width="match_parent"
        ohos:background_element="gray"
        />
</DirectionalLayout>

3、创建City.java类,作为ListContainer的数据包装类

package com.sudojava.listcontainerdemo.bean;

public class City {
    private String name;

    public City(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "City{" +
                "name='" + name + '\'' +
                '}';
    }
}

4、ListContainer每一行可以为不同的数据,因此需要适配不同的数据结构,使其都能添加到ListContainer上。

创建CityProvider.java,继承自BaseItemProvider。必须重写的方法如下:

方法 作用
int getCount() 返回填充的表项个数。
Object getItem(int position) 根据position返回对应的数据。
long getItemId(int position) 返回某一项的id。
Component getComponent(int position, Component covertComponent,ComponentContainer componentContainer) 根据position返回对应的界面组件。
package com.sudojava.listcontainerdemo.provider;

import com.sudojava.listcontainerdemo.ResourceTable;
import com.sudojava.listcontainerdemo.bean.City;
import ohos.aafwk.ability.AbilitySlice;
import ohos.agp.components.*;

import java.util.List;

public class CityProvider extends BaseItemProvider {
    private List<City> list;
    private AbilitySlice slice;

    public CityProvider(List<City> 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 component, ComponentContainer componentContainer) {
        final Component cpt;
        if (component==null){
            cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_city,null,false);
        }else {
            cpt = component;
        }
        City city = list.get(position);
        Text text = (Text)cpt.findComponentById(ResourceTable.Id_item_index);
        text.setText(city.getName());
        return cpt;
    }
}

5、在Java代码中添加ListContainer的数据,并适配其数据结构。

package com.sudojava.listcontainerdemo.slice;

import com.sudojava.listcontainerdemo.ResourceTable;
import com.sudojava.listcontainerdemo.bean.City;
import com.sudojava.listcontainerdemo.provider.CityProvider;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.ToastDialog;

import java.util.ArrayList;
import java.util.List;

public class MainAbilitySlice extends AbilitySlice {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        initListContainer();
    }

    private void initListContainer(){
        ListContainer listContainer = (ListContainer)findComponentById(ResourceTable.Id_list_container);
        List<City> list = getData();
        CityProvider cityProvider = new CityProvider(list,this);
        listContainer.setItemProvider(cityProvider);
        listContainer.setBindStateChangedListener(new Component.BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                cityProvider.notifyDataChanged();
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {

            }
        });
        listContainer.setItemClickedListener(new ListContainer.ItemClickedListener() {
            @Override
            public void onItemClicked(ListContainer listContainer, Component component, int i, long l) {
                City city = (City)listContainer.getItemProvider().getItem(i);
                new ToastDialog(getContext())
                        .setText("you clicked:"+city.getName())
                        .setAlignment(LayoutAlignment.CENTER)
                        .show();
            }
        });
    }
    @Override
    public void onActive() {
        super.onActive();
    }

    public List<City> getData(){
        List<City> list = new ArrayList<>();
        for (int i = 0; i <= 40; i++) {
            list.add(new City("City Name : " + i));
        }
        return list;
    }
    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }
}
4.2、Json-Server服务端搭建

JSON-Server 是一个 Node 模块,运行 Express 服务器,你可以指定一个 json 文件作为 api 的数据源。

下面我们就用基于NodeJs的Json-Server,关于NodeJs在windows下的配置,大家可以百度一下。这里我就不在详细描述了。我们安装好NodeJs之后,直接可以在命令行输入以下命令来安装JSON Server:

npm install -g json-server

安装成功之后,可以在命令行终端输入:json-server来检测是否安装成功。

C:\Users\Administrator>json-server
bin.js [options] <source>

Options:
  -c, --config                   Path to config file
                                                   [default: "json-server.json"]
  -p, --port                     Set port                        [default: 3000]
  -H, --host                     Set host                 [default: "localhost"]
  -w, --watch                    Watch file(s)                         [boolean]
  -r, --routes                   Path to routes file
  -m, --middlewares              Paths to middleware files               [array]
  -s, --static                   Set static files directory
      --read-only, --ro          Allow only GET requests               [boolean]
      --no-cors, --nc            Disable Cross-Origin Resource Sharing [boolean]
      --no-gzip, --ng            Disable GZIP Content-Encoding         [boolean]
  -S, --snapshots                Set snapshots directory          [default: "."]
  -d, --delay                    Add delay to responses (ms)
  -i, --id                       Set database id property (e.g. _id)
                                                                 [default: "id"]
      --foreignKeySuffix, --fks  Set foreign key suffix (e.g. _id as in post_id)
                                                                 [default: "Id"]
  -q, --quiet                    Suppress log messages from output     [boolean]
  -h, --help                     Show help                             [boolean]
  -v, --version                  Show version number                   [boolean]

Examples:
  bin.js db.json
  bin.js file.js
  bin.js http://example.com/db.json

https://github.com/typicode/json-server

Missing <source> argument

json-server服务的启动

json-server可以直接把一个json文件托管成一个具备全RESTful风格的API,并支持跨域、jsonp、路由订制、数据快照保存等功能的 web 服务器。
db.json文件的内容格式如下:

{
    "students":[
        {
            "sno":108,
            "sname":"曾华",
            "ssex":"男",
            "sbirthday":"九月 1, 1996",
            "classid":"95033"
        },
        {
            "sno":105,
            "sname":"匡明",
            "ssex":"男",
            "sbirthday":"十月 2, 1995",
            "classid":"95031"
        },
        {
            "sno":107,
            "sname":"王丽",
            "ssex":"女",
            "sbirthday":"一月 23, 1996",
            "classid":"95033"
        },
        {
            "sno":101,
            "sname":"李军",
            "ssex":"男",
            "sbirthday":"二月 20, 1996",
            "classid":"95033"
        },
        {
            "sno":109,
            "sname":"王芳",
            "ssex":"女",
            "sbirthday":"二月 10, 1995",
            "classid":"95031"
        },
        {
            "sno":103,
            "sname":"陆君",
            "ssex":"男",
            "sbirthday":"六月 3, 1994",
            "classid":"95031"
        }
    ]
}

启动命令如下:

json-server --watch --port 8080 student.json

执行结果如下:

jack@luoliwendeMacBook-Pro Documents % json-server --watch --port 8080 student.json

  \{^_^}/ hi!

  Loading student.json
  Done

  Resources
  http://localhost:8080/students

  Home
  http://localhost:8080

  Type s + enter at any time to create a snapshot of the database
  Watching...

json-server的相关启动参数

  • 语法:json-server [options]
参数 简写 默认值 说明
–config -c 指定配置文件 [默认值: “json-server.json”]
–port -p 设置端口 [默认值: 3000] Number
–host -H 设置域 [默认值: “0.0.0.0”] String
–watch -w Watch file(s) 是否监听
4.3、Ngrok 内网穿透技术

ngrok 我们可以理解为一个内网穿透的工具,是国内用的比较多的工具,什么是内网穿透呢?简言之就是将内网IP映射成对外可以访问的网址和域名。

Ngrok的官网是:https://www.ngrok.cc/ 打开官网直接注册账号,可以使用QQ号码来注册。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分别下载Windows和Mac客户端,这里ngrok提供了对Windows和Mac的支持,下载网址如:

https://www.ngrok.cc/download.html

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下载好客户端了,使用终端控制台进入到执行下载的文件夹中,执行如下命令:

./sunny clientid a27ec75a06e6be46--隧道ID编号
Sunny-Ngrok   官 网 www.ngrok.cc                                 (Ctrl+C 退 出 )

Tunnel Status                 online
Version                       2.1/2.1
Forwarding                    http://sudojava.free.idcfengye.com -> 127.0.0.1:80
Web Interface                 127.0.0.1:4040
# Conn                        0
Avg Conn Time                 0.00ms

使用浏览器访问网址:http://sudojava.free.idcfengye.com/students 结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.4、ListContainer访问网络数据

ListContainer大部分都是请求的网络数据,比如在线的Json数据。生成在线的数据一般都是采用Web服务器来生成,比如SpringBoot、PHP或者NodeJS等服务端。

1、准备一段产品的Json数据

{
    "products":[
        {
            "pid":1,
            "pname":"联想电脑",
            "price":5000,
            "category_name":"电脑办公"
        },
        {
            "pid":2,
            "pname":"海尔电脑",
            "price":3000,
            "category_name":"电脑办公"
        },
        {
            "pid":3,
            "pname":"雷神电脑",
            "price":5000,
            "category_name":"电脑办公"
        },
        {
            "pid":4,
            "pname":"JACK JONES",
            "price":800,
            "category_name":"服装"
        },
        {
            "pid":5,
            "pname":"真维斯",
            "price":200,
            "category_name":"服装"
        },
        {
            "pid":6,
            "pname":"花花公子",
            "price":440,
            "category_name":"服装"
        },
        {
            "pid":7,
            "pname":"劲霸",
            "price":2000,
            "category_name":"服装"
        },
        {
            "pid":8,
            "pname":"香奈儿",
            "price":800,
            "category_name":"女士用品"
        },
        {
            "pid":9,
            "pname":"相宜本草",
            "price":200,
            "category_name":"女士用品"
        },
        {
            "pid":10,
            "pname":"面霸",
            "price":5,
            "category_name":"女士用品"
        },
        {
            "pid":11,
            "pname":"雪碧",
            "price":56,
            "category_name":"饮料饮品"
        },
        {
            "pid":12,
            "pname":"香飘飘奶茶",
            "price":1,
            "category_name":"饮料饮品"
        }
    ]
}

2、生成Restful风格的json数据

在Windows系统E盘目录下,创建一个json文件夹,把上面的数据复制到一个product.json文件中。使用命令行终端进入到E盘的json目录中执行以下命令。

备注:由于HarmonyOS的远程模拟器访问本地的json数据是不支持localhost的地址,所以要将localhost地址映射到外网去,这样就会使用到上面我们介绍的ngrok,内网穿透技术。

E:\json>json-server --watch --host 127.0.0.1 --port 8080 product.json

  \{^_^}/ hi!

  Loading product.json
  Done

  Resources
  http://192.168.20.23:3001/products

  Home
  http://192.168.20.23:3001

  Type s + enter at any time to create a snapshot of the database
  Watching...

GET /products 200 21.495 ms - -

3、添加布局文件

添加一个主布局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:alignment="center"
    ohos:orientation="vertical">

    <ListContainer
        ohos:id="$+id:product_list"
        ohos:height="match_parent"
        ohos:width="match_parent"/>

</DirectionalLayout>

添加一个用于ListContainer显示Item的布局文件

<?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:orientation="vertical">

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:orientation="horizontal">

        <Text
            ohos:text="产品名称:"
            ohos:text_size="15vp"
            ohos:height="match_content"
            ohos:left_margin="20vp"
            ohos:top_margin="20vp"
            ohos:text_color="black"
            ohos:width="match_content"/>

        <Text
            ohos:id="$+id:product_name"
            ohos:height="match_content"
            ohos:hint="香蕉"
            ohos:top_margin="20vp"
            ohos:hint_color="red"
            ohos:text_size="15vp"
            ohos:width="match_content"/>

    </DirectionalLayout>


    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:orientation="horizontal">

        <Text
            ohos:text="产品价格:"
            ohos:text_size="15vp"
            ohos:height="match_content"
            ohos:left_margin="20vp"
            ohos:top_margin="20vp"
            ohos:text_color="black"
            ohos:width="match_content"/>

        <Text
            ohos:id="$+id:product_price"
            ohos:height="match_content"
            ohos:top_margin="20vp"
            ohos:text_size="15vp"
            ohos:width="match_content"/>

    </DirectionalLayout>



    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:orientation="horizontal">

        <Text
            ohos:text="产品类型:"
            ohos:text_size="15vp"
            ohos:height="match_content"
            ohos:left_margin="20vp"
            ohos:top_margin="20vp"
            ohos:text_color="black"
            ohos:width="match_content"/>

        <Text
            ohos:id="$+id:product_type"
            ohos:height="match_content"
            ohos:hint="电脑"
            ohos:top_margin="20vp"
            ohos:hint_color="red"
            ohos:text_size="15vp"
            ohos:width="match_content"/>

    </DirectionalLayout>

    <Component
        ohos:height="1vp"
        ohos:width="match_parent"
        ohos:background_element="gray"
        />
</DirectionalLayout>

4、配置config.json

工程需要访问外网的环境,所以需要配置以下两项:

1、配置deviceConfig,访问的url是以http开始

 "deviceConfig": {
    "default": {
      "network": {
        "cleartextTraffic": true
      }
    }
  },

2、配置reqPermissions以用来访问外网

"reqPermissions": [
      {
        "reason": "",
        "name": "ohos.permission.INTERNET"
      }
    ],

5、配置项目级别的build.gradle文件

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
    testImplementation 'junit:junit:4.13'
    compile 'com.google.code.gson:gson:2.6.2'
    implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.1.0'
    ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.200'
}

6、创建一个Product实体类

注意的是该类的属性一定要和解析的Json格式要对应,主要是使用GsonFormatPlus这个插件来完成

package com.qf.listcontainerjson.bean;

import java.util.List;

public class Product {

    private List<ProductsBean> products;

    public List<ProductsBean> getProducts() {
        return products;
    }

    public void setProducts(List<ProductsBean> products) {
        this.products = products;
    }

    public static class ProductsBean {
        private int pid;
        private String pname;
        private int price;
        private String category_name;

        public int getPid() {
            return pid;
        }

        public void setPid(int pid) {
            this.pid = pid;
        }

        public String getPname() {
            return pname;
        }

        public void setPname(String pname) {
            this.pname = pname;
        }

        public int getPrice() {
            return price;
        }

        public void setPrice(int price) {
            this.price = price;
        }

        public String getCategory_name() {
            return category_name;
        }

        public void setCategory_name(String category_name) {
            this.category_name = category_name;
        }

        @Override
        public String toString() {
            return "ProductsBean{" +
                    "pid=" + pid +
                    ", pname='" + pname + '\'' +
                    ", price=" + price +
                    ", category_name='" + category_name + '\'' +
                    '}';
        }
    }

}

7、封装一个OKHttp工具类

封装一个基于Okhttp的工具类,主要使用单例模式,异步的Get请求来获取Json数据

package com.qf.listcontainerjson.http;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;

public class HTTPManager {

    private static HTTPManager instance;
    private OkHttpClient okHttpClient;


    private HTTPManager(){
         okHttpClient = new OkHttpClient().newBuilder().build();
    }

    public static HTTPManager getInstance(){
        if (instance==null){
            synchronized (HTTPManager.class){
                if (instance==null){
                    instance = new HTTPManager();
                }
            }
        }
        return instance;
    }
    public void _getasync(String url, Callback callback){
        Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(callback);
    }
}

8、声明一个产品适配器ProductAdapter

package com.qf.listcontainerjson.adapter;

import com.qf.listcontainerjson.ResourceTable;
import com.qf.listcontainerjson.bean.Product;
import ohos.aafwk.ability.AbilitySlice;
import ohos.agp.components.*;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import java.util.List;

public class ProductAdapter extends BaseItemProvider {
    private static final HiLogLabel LOG_LABEL = new HiLogLabel(HiLog.LOG_APP,0x00201,"ProductAdapter");
    private List<Product.ProductsBean> list;
    private AbilitySlice slice;

    public ProductAdapter(List<Product.ProductsBean> 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 i, Component component, ComponentContainer componentContainer) {
        ViewHolder viewHolder = null;
        if (component==null){
            component = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_product_item,null,false);
            viewHolder = new ViewHolder();
            viewHolder.p_name = (Text)component.findComponentById(ResourceTable.Id_product_name);
            viewHolder.p_price=(Text)component.findComponentById(ResourceTable.Id_product_price);
            viewHolder.p_type = (Text)component.findComponentById(ResourceTable.Id_product_type);
            component.setTag(viewHolder);
        }else {
            viewHolder = (ViewHolder)component.getTag();
        }
        if (viewHolder!=null){
            viewHolder.p_name.setText(list.get(i).getPname());
            viewHolder.p_price.setText(String.valueOf(list.get(i).getPrice()));
            viewHolder.p_type.setText(list.get(i).getCategory_name());
        }
        return component;
    }
    static class ViewHolder{
        Text p_name;
        Text p_type;
        Text p_price;
    }
}

9、在MainAbilitySlice中加载适配器

package com.qf.listcontainerjson.slice;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.qf.listcontainerjson.ResourceTable;
import com.qf.listcontainerjson.adapter.ProductAdapter;
import com.qf.listcontainerjson.bean.Product;
import com.qf.listcontainerjson.http.HTTPManager;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.List;

public class MainAbilitySlice extends AbilitySlice {

    private static final HiLogLabel LOG_LABEL = new HiLogLabel(HiLog.LOG_APP,0x00201,"MainAbilitySlice");
    private ListContainer listContainer;
    private String url = "http://sudojava.free.idcfengye.com/products";
    private ProductAdapter adapter;
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        listContainer = (ListContainer)findComponentById(ResourceTable.Id_product_list);
        initView();

    }

    private void initView(){
            HTTPManager.getInstance()._getasync(url, new Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull IOException e) {

                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                    List<Product.ProductsBean> list = new Gson().fromJson(response.body().string(),new TypeToken<List<Product.ProductsBean>>(){}.getType());
                    HiLog.info(LOG_LABEL,"--->>"+list);
                    adapter = new ProductAdapter(list,MainAbilitySlice.this);
                    getUITaskDispatcher().asyncDispatch(new Runnable() {
                        @Override
                        public void run() {
                            listContainer.setItemProvider(adapter);
                            adapter.notifyDataChanged();
                        }
                    });
                }
           });
    }
    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }
}

4.5、ListContainer加载图文混排

准备一段图文混排的Json数据,数据格式如下:

{
"vlist":[
{
"publishTime":1439567432000,
"vpic":"http://pic9.iqiyipic.com/image/20190905/96/f9/v_109343020_m_601_m3.jpg",
"shortTitle":"航海王 第1集",
"vt":"我是路飞! 将要成为海贼王的男人"
},
{
"publishTime":1439567432000,
"vpic":"http://pic8.iqiyipic.com/image/20190905/5f/27/v_109343021_m_601_m4.jpg",
"shortTitle":"航海王 第2集",
"vt":"大剑客现身!海贼猎人罗罗诺亚·卓洛"
},
{
"publishTime":1439567432000,
"vpic":"http://pic3.iqiyipic.com/image/20150803/d9/3b/v_109343022_m_601.jpg",
"shortTitle":"航海王 第3集",
"vt":"蒙卡 vs 路飞!神秘的美少女是谁?"
},
{
"publishTime":1439567435000,
"vpic":"http://pic4.iqiyipic.com/image/20150803/91/03/v_109343023_m_601.jpg",
"shortTitle":"航海王 第4集",
"vt":"路飞的过去!红发香克斯登场"
},
{
"publishTime":1439567433000,
"vpic":"http://pic0.iqiyipic.com/image/20150803/b7/aa/v_109343025_m_601.jpg",
"shortTitle":"航海王 第5集",
"vt":"恐怖!神秘的力量 海贼小丑巴基船长"
},
{
"publishTime":1439567432000,
"vpic":"http://pic2.iqiyipic.com/image/20150803/fa/a1/v_109343027_m_601.jpg",
"shortTitle":"航海王 第6集",
"vt":"绝体绝命!驯兽师摩奇VS路飞"
},
{
"publishTime":1439567432000,
"vpic":"http://pic3.iqiyipic.com/image/20150803/a0/99/v_109343028_m_601.jpg",
"shortTitle":"航海王 第7集",
"vt":"壮烈决斗!剑豪卓洛VS杂技师卡巴吉"
},
{
"publishTime":1439567432000,
"vpic":"http://pic2.iqiyipic.com/image/20150804/05/a7/v_109343029_m_601_m1.jpg",
"shortTitle":"航海王 第8集",
"vt":"胜利者是谁?恶魔果实的能力对决!"
},
{
"publishTime":1439567433000,
"vpic":"http://pic6.iqiyipic.com/image/20150803/b7/ee/v_109343030_m_601.jpg",
"shortTitle":"航海王 第9集",
"vt":"正义的骗子?船长乌索普"
},
{
"publishTime":1439567432000,
"vpic":"http://pic8.iqiyipic.com/image/20150804/e6/e9/v_109343032_m_601_m1.jpg",
"shortTitle":"航海王 第10集",
"vt":"史上最强的怪人!催眠师赞高"
},
{
"publishTime":1439567435000,
"vpic":"http://pic5.iqiyipic.com/image/20150803/e9/d9/v_109343035_m_601.jpg",
"shortTitle":"航海王 第11集",
"vt":"阴谋暴露! 海贼管家克洛船长"
},
{
"publishTime":1439567435000,
"vpic":"http://pic6.iqiyipic.com/image/20150803/7d/f1/v_109343036_m_601.jpg",
"shortTitle":"航海王 第12集",
"vt":"激战!与黑猫海贼团坡道的大攻防!"
},
{
"publishTime":1439567436000,
"vpic":"http://pic8.iqiyipic.com/image/20150803/2e/f6/v_109343038_m_601.jpg",
"shortTitle":"航海王 第13集",
"vt":"恐怖的二人组!猫人兄弟VS卓洛"
},
{
"publishTime":1439567435000,
"vpic":"http://pic7.iqiyipic.com/image/20150804/91/6b/v_109343039_m_601_m1.jpg",
"shortTitle":"航海王 第14集",
"vt":"路飞复活!可雅小姐的拼死抵抗"
},
{
"publishTime":1439567435000,
"vpic":"http://pic4.iqiyipic.com/image/20150803/6b/38/v_109343041_m_601.jpg",
"shortTitle":"航海王 第15集",
"vt":"把克洛打倒!男子汉乌索普悲痛的决定!"
}]
}

将该json数据保存到一个movie.json文件中,放到指定的目录下,这里我是放到E盘的json文件夹中。

使用json-server启动,生成restful风格的数据。启动命令如下:

E:\json>json-server --watch --host 127.0.0.1 --port 8080 movie.json

  \{^_^}/ hi!

  Loading movie.json
  Done

  Resources
  http://127.0.0.1:8080/vlist

  Home
  http://127.0.0.1:8080

  Type s + enter at any time to create a snapshot of the database
  Watching...

启动ngrok,将内网的地址映射到外网,启动信息如下:

nnel Status                 online                                                                      
Version                       2.1/2.1                                                                        http://sudojava.free.idcfengye.com -> 127.0.0.1:8080                       

配置config.json文件

工程需要访问外网的环境,所以需要配置以下两项:

  • 配置deviceConfig,访问的url是以http开始
 "deviceConfig": {
    "default": {
      "network": {
        "cleartextTraffic": true
      }
    }
  },
  • 配置module 模块下的reqPermissions以用来访问外网
"reqPermissions": [
      {
        "reason": "",
        "name": "ohos.permission.INTERNET"
      }
    ],
  • 配置项目级别的build.gradle文件
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
    testImplementation 'junit:junit:4.13'
    ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.200'
    compile 'com.google.code.gson:gson:2.6.2'
    implementation 'io.openharmony.tpc.thirdlib:picasso:1.0.4'
    implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.1.0'
}

1、创建两个工程对应的布局文件,ability_main.xml和movie_item.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">

    <ListContainer
        ohos:id="$+id:movie_list"
        ohos:height="match_parent"
        ohos:width="match_parent"/>
</DirectionalLayout>
<?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:orientation="vertical">

    <Image
        ohos:id="$+id:vpic"
        ohos:top_margin="10vp"
        ohos:left_margin="20vp"
        ohos:height="match_content"
        ohos:image_src="$media:icon"
        ohos:width="match_content"/>

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:orientation="horizontal">

        <Text
            ohos:text="电影标题:"
            ohos:text_size="15vp"
            ohos:height="match_content"
            ohos:left_margin="20vp"
            ohos:top_margin="20vp"
            ohos:text_color="black"
            ohos:width="match_content"/>

        <Text
            ohos:id="$+id:shortTitle"
            ohos:height="match_content"
            ohos:hint="香蕉"
            ohos:top_margin="20vp"
            ohos:hint_color="red"
            ohos:text_size="15vp"
            ohos:width="match_content"/>

    </DirectionalLayout>


    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:orientation="horizontal">

        <Text
            ohos:text="发布时间:"
            ohos:text_size="15vp"
            ohos:height="match_content"
            ohos:left_margin="20vp"
            ohos:top_margin="20vp"
            ohos:text_color="black"
            ohos:width="match_content"/>

        <Text
            ohos:id="$+id:publishTime"
            ohos:height="match_content"
            ohos:top_margin="20vp"
            ohos:text_size="15vp"
            ohos:width="match_content"/>

    </DirectionalLayout>



    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:orientation="horizontal">

        <Text
            ohos:text="电影简介:"
            ohos:text_size="15vp"
            ohos:height="match_content"
            ohos:left_margin="20vp"
            ohos:top_margin="20vp"
            ohos:text_color="black"
            ohos:width="match_content"/>

        <Text
            ohos:id="$+id:vt"
            ohos:height="match_content"
            ohos:hint="简介"
            ohos:top_margin="20vp"
            ohos:hint_color="red"
            ohos:text_size="15vp"
            ohos:width="match_content"/>

    </DirectionalLayout>

    <Component
        ohos:height="1vp"
        ohos:width="match_parent"
        ohos:background_element="gray"
        />
</DirectionalLayout>

2、创建一个json对应的实体类

package com.qf.listmovie.bean;

import java.util.List;

public class Movie {

    private List<VlistBean> vlist;

    public List<VlistBean> getVlist() {
        return vlist;
    }

    public void setVlist(List<VlistBean> vlist) {
        this.vlist = vlist;
    }

    public static class VlistBean {
        private long publishTime;
        private String vpic;
        private String shortTitle;
        private String vt;

        public long getPublishTime() {
            return publishTime;
        }

        public void setPublishTime(long publishTime) {
            this.publishTime = publishTime;
        }

        public String getVpic() {
            return vpic;
        }

        public void setVpic(String vpic) {
            this.vpic = vpic;
        }

        public String getShortTitle() {
            return shortTitle;
        }

        public void setShortTitle(String shortTitle) {
            this.shortTitle = shortTitle;
        }

        public String getVt() {
            return vt;
        }

        public void setVt(String vt) {
            this.vt = vt;
        }
    }
}

3、编写一个HTTP访问工具类和日期转换类

package com.qf.listmovie.http;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;

public class HTTPManager {

    private static HTTPManager instance;
    private OkHttpClient okHttpClient;


    private HTTPManager(){
        okHttpClient = new OkHttpClient().newBuilder().build();
    }

    public static HTTPManager getInstance(){
        if (instance==null){
            synchronized (HTTPManager.class){
                if (instance==null){
                    instance = new HTTPManager();
                }
            }
        }
        return instance;
    }
    public void _getasync(String url, Callback callback){
        Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(callback);
    }
}

日期转换工具类

package com.qf.listmovie.utils;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtils {

    public static String dateFormat(Long l){
        Date date = new Date();
        date.setTime(l);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        return format.format(date);
    }
}

4、编写ListContainer对应的适配器类

package com.qf.listmovie.adapter;

import com.qf.listmovie.ResourceTable;
import com.qf.listmovie.bean.Movie;
import com.qf.listmovie.utils.DateUtils;
import com.squareup.picasso.Picasso;
import ohos.aafwk.ability.AbilitySlice;
import ohos.agp.components.*;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import java.util.ArrayList;
import java.util.List;

public class MoiveAdapter extends BaseItemProvider {
    private static final HiLogLabel LOG_LABEL = new HiLogLabel(HiLog.LOG_APP,0x00201,"MoiveAdapter");

    private List<Movie.VlistBean> list;

    private AbilitySlice slice;

    public MoiveAdapter(List<Movie.VlistBean> list,AbilitySlice slice){
     this.list = list;
     this.slice = slice;
    }

    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 i, Component component, ComponentContainer componentContainer) {
        ViewHolder viewHolder = null;
        if (component==null){
            component = LayoutScatter.getInstance(slice)
                    .parse(ResourceTable.Layout_movie_item,null,false);
            viewHolder = new ViewHolder();
            viewHolder.m_publishTime = (Text)component.findComponentById(ResourceTable.Id_publishTime);
            viewHolder.m_shortTitle=(Text)component.findComponentById(ResourceTable.Id_shortTitle);
            viewHolder.m_vt = (Text)component.findComponentById(ResourceTable.Id_vt);
            viewHolder.m_vpic = (Image)component.findComponentById(ResourceTable.Id_vpic);
            component.setTag(viewHolder);
        }else {
            viewHolder = (ViewHolder)component.getTag();
        }
        if (viewHolder!=null){
            viewHolder.m_publishTime.setText(DateUtils.dateFormat(list.get(i).getPublishTime()));
            viewHolder.m_shortTitle.setText(list.get(i).getShortTitle());
            viewHolder.m_vt.setText(list.get(i).getVt());
            Picasso.get().load(list.get(i).getVpic())
                    .placeholder(ResourceTable.Media_icon)
                    .resize(150,150)
                    .into(viewHolder.m_vpic);
        }
        return component;
    }
    static class ViewHolder{
        Text m_publishTime;
        Text m_shortTitle;
        Text m_vt;
        Image m_vpic;
    }
}

5、在MainAbilitySlice中加载数据

package com.qf.listmovie.slice;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.qf.listmovie.ResourceTable;
import com.qf.listmovie.adapter.MoiveAdapter;
import com.qf.listmovie.bean.Movie;
import com.qf.listmovie.http.HTTPManager;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.ListContainer;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.List;


public class MainAbilitySlice extends AbilitySlice {
    private static final HiLogLabel LOG_LABEL = new HiLogLabel(HiLog.LOG_APP,0x00201,"MainAbilitySlice");
    private String url = "http://sudojava.free.idcfengye.com/vlist";
    private ListContainer listContainer;
    private MoiveAdapter adapter;
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        listContainer = (ListContainer)findComponentById(ResourceTable.Id_movie_list);
        initView();
    }

    private void initView() {
        HTTPManager.getInstance()._getasync(url, new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {

            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                List<Movie.VlistBean> list = new Gson()
                        .fromJson(response.body().string(),new TypeToken<List<Movie.VlistBean>>(){}.getType());
                HiLog.info(LOG_LABEL,"--->>"+list);
                adapter = new MoiveAdapter(list,MainAbilitySlice.this);
                getUITaskDispatcher().asyncDispatch(new Runnable() {
                    @Override
                    public void run() {
                      listContainer.setItemProvider(adapter);
                      adapter.notifyDataChanged();
                    }
                });
            }
        });
    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐