HarmonyOS开发入门笔记 在线词典

创建默认工程

创建工程时注意这里

service是不会在本地安装APP的

所以在选择service 时你在调试设备时时桌面时不会有图标的 HarmonyOS开发入门笔记 在线词典

通过下面这个位置我们可以启动自己的鸿蒙环境

HarmonyOS开发入门笔记 在线词典

然后看下我们的配置文件夹

HarmonyOS开发入门笔记 在线词典

找到main文件夹,其中config.json是我们的配置文件

HarmonyOS开发入门笔记 在线词典

具体可以查看

https://harmonyos.51cto.com/posts/7621

界面开发

<?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="match_parent"
        ohos:width="match_parent"
        ohos:left_margin="20vp"
        ohos:right_margin="20vp"
        ohos:top_margin="40vp"
        ohos:background_element="#FFFAFA"
        ohos:orientation="vertical">
        <!--上半部分-->
        <DirectionalLayout
            ohos:height="50vp"
            ohos:width="match_parent"
            ohos:orientation="horizontal">
            <!--左侧文本框-->
            <!--        weight代表权重 设置此值之后 会线让其他元素填充区域后占满剩余区域 -->
            <!--        padding 内边距-->
            <!--        ohos:background_element="$graphic:select_border" 设置背景 这里使用了xml配置 通过xml可以设置边框等  也可以不使用xml直接赋给颜色值-->

            <TextField
                ohos:id="$+id:textField_ID"
                ohos:height="match_content"
                ohos:width="match_parent"
                ohos:hint="请输入要查找的单词"
                ohos:weight="1"
                ohos:background_element="$graphic:select_border"
                ohos:padding="2vp"
                ohos:text_size="20vp"
                ohos:hint_color="#FF4500"/>
            <!--右侧搜索按钮-->
            <Image
                ohos:id="$+id:sou_word"
                ohos:height="23vp"
                ohos:width="23vp"
                ohos:image_src="$media:sou"
                ohos:scale_mode="zoom_center"/>
        </DirectionalLayout>
        <!--搜索结果-->
<!--        layout_alignment="left|top"  文本对齐方式:左上角-->
<!--            visibility="hide"  设置隐藏-->

        <Text
            ohos:id="$+id:text_select_return"
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:multiple_lines="true"
            ohos:background_element="$graphic:background_ability_main"
            ohos:visibility="hide"
            ohos:top_margin="40"
            ohos:layout_alignment="left|top"
            ohos:text_size="20vp"
            />
        <Image
            ohos:id="$+id:image"
            ohos:height="match_parent"
            ohos:width="match_parent"
            ohos:top_margin="60vp"
            ohos:image_src="$media:OIP_C"
            ohos:scale_mode="zoom_center"/>
    </DirectionalLayout>

</DirectionalLayout>

HarmonyOS开发入门笔记 在线词典

我们看到这里引用了select_border.xml这个文件

HarmonyOS开发入门笔记 在线词典

<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">
    <solid
        ohos:color="#FFFFFF"/>
    <stroke
        ohos:color="#00FFFF"
        ohos:width="1vp"
        />
</shape>

主要配置了边框和背景色

底层部分

这里先贴出鸿蒙官方的api开发文档

https://developer.harmonyos.com/cn/docs/documentation/doc-references/component-0000001054678683

爬取单词,封装sql

我们先从网站上爬出词库,并封装成sqlite

这里使用py来爬取

import os.path
import sqlite3

import requests
import re

words = {}
def add_word(word_line):
    global words
    result = re.split('[a-z]+\.',word_line)
    pattern = re.compile('[a-z]+\.')
    result2 = pattern.findall(word_line)
    word = result[0].strip()
    meanings = {}
    for i in range(0,len(result2)):
        key = result2[i].strip()
        value = result[i+1].strip()
        meanings[key] = value
    words[word] = meanings

r = requests.get('https://www.eol.cn/html/en/cetwords/cet4.shtml')
html = r.content
html_doc = str(html, 'utf-8')
print(html_doc)
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc,'lxml')
tags = soup.find_all(attrs={'class': 'wordL fl'})
for tag in tags:
    p_list = tag.select('p')
    for p in p_list:
        add_word(p.text)
print(words)
print('单词抓取完毕')

db_path = 'dict.sqlite'
if os.path.exists(db_path):
    os.remove(db_path)
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute('''create table words
    (id int primary key not null,
    word varchar(30) not null,
    type  varchar(10) not null,
    meanings text not null);
    ''')
c.execute('create index word_index on words(word)')
conn.commit()
conn.close()
print("数据库创建完毕")

conn = sqlite3.connect(db_path)
c = conn.cursor()
i = 1
for word in words:
    value = words[word]
    for type in value:
        meanmings = value[type]
        sql = f'insert into words(id,word,type,meanings) values ( {i},"{word}","{type}","{meanmings}" )'
        c.execute(sql)
        print(sql)
        i += 1
    conn.commit()
conn.close()
print("生成完毕")

这时我们就得到了sqlite

sqlite文件操作类

我们在鸿蒙项目里创建sqlite类

package com.example.onlinedict.common;

import ohos.app.AbilityContext;
import ohos.data.DatabaseHelper;
import ohos.data.rdb.RdbOpenCallback;
import ohos.data.rdb.RdbStore;
import ohos.data.rdb.StoreConfig;
import ohos.global.resource.Resource;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;

public class MyDict {
    //AbilityContext 是一个 Context 类,其功能是与系统环境进行交互,可以获取和改变一些与应用有关的属性值。
    private AbilityContext context;
    //词典数据库文件路径
    private File dictPath;
    //词典数据库文件
    private File dbPath;

    //鸿蒙数据库https://blog.csdn.net/dsbdsbdsb/article/details/112603421  及  https://harmonyos.51cto.com/posts/8013
    //数据
    private RdbStore rdbStore;
    private StoreConfig storeConfig=StoreConfig.newDefaultConfig("dict.sqlite");
    //创建数据库需要的回调  虽然这回调啥都不用写 但是需要有
    private static final RdbOpenCallback callback = new RdbOpenCallback() {
        @Override
        public void onCreate(RdbStore rdbStore) {

        }

        @Override
        public void onUpgrade(RdbStore rdbStore, int i, int i1) {

        }
    };



    //创建HAP私有路径
    public MyDict(AbilityContext context) {
        this.context = context;
        dictPath = new File(context.getDataDir().toString()+"/MainAbility/databases/db");
        if (!dictPath.exists()){//如果路径不存在就创建路径
            dictPath.mkdirs();
        }
        //获取文件
        dbPath = new File(Paths.get(dictPath.toString(),"dict.sqlite").toString());
    }

    //读取数据库文件拷贝到私有路径
    private void extractDB() throws IOException{
        //加载资源
        Resource resource = context.getResourceManager().getRawFileEntry("resources/rawfile/dict.sqlite").openRawFile();
        //如果路径为空 则删除  我也不知道为啥要写这个
        if (dbPath.exists()){
            dbPath.delete();
        }
        //输出流创建sqlite文件
        FileOutputStream fileOutputStream = new FileOutputStream(dbPath);
        //用于读取
        byte[] buffer = new byte[4096];
        int count = 0;
        while ((count=resource.read(buffer))>0){
            //将数组内容输出到文件中
            fileOutputStream.write(buffer,0,count);
        }
        resource.close();
        fileOutputStream.close();
    }

    //初始化
    public void init() throws IOException{
        //创建数据库
        extractDB();
        //打开数据库
        //数据库操作的辅助类
        DatabaseHelper helper = new DatabaseHelper(context);
        rdbStore = helper.getRdbStore(storeConfig,1,callback);

    }
}

调用初始化,添加前端监听事件

更改MainAbilitySlice文件

package com.example.onlinedict.slice;

import com.example.onlinedict.ResourceTable;
import com.example.onlinedict.common.MyDict;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.components.Text;
import ohos.agp.components.TextField;

import java.io.IOException;

public class MainAbilitySlice extends AbilitySlice {
    private MyDict myDict;
    private Image imageSearch;
    private Text textSelectReturn;
    private TextField textField_ID;
    private Image image;
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);

        myDict = new MyDict(this);
        try {
            myDict.init();
        } catch (IOException e) {
            e.printStackTrace();
        }
        imageSearch = (Image) findComponentById(ResourceTable.Id_sou_word);
        textSelectReturn = (Text) findComponentById(ResourceTable.Id_text_select_return);
        textField_ID = (TextField) findComponentById(ResourceTable.Id_textField_ID);
        image = (Image) findComponentById(ResourceTable.Id_image);
        if (imageSearch!=null){
            imageSearch.setClickable(true);
            imageSearch.setClickedListener(new Component.ClickedListener() {
                @Override
                public void onClick(Component component) {
                    image.setVisibility(Component.HIDE);//下面图像不可见
                    textSelectReturn.setVisibility(Component.VISIBLE);
                    String s = textField_ID.getText();//获取文字
                }
            });
        }
    }

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

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

此时运行就可以实现点击搜索按钮下方图片隐藏

添加查询本地单词功能

为MyDict类添加方法

    /**
     * 搜索单词
     * @param word
     * @return
     */
    public List<DictBean> searchLocalWord(String word){
        //将其全部转换为小写
        word = word.toLowerCase();
        String[] args = new String[]{word};
        ResultSet resultSet = rdbStore.querySql("select * from words where word = ?",args);
        ArrayList<DictBean> dictBeans = new ArrayList<DictBean>();
        while (resultSet.goToNextRow()){
            DictBean dictBean = new DictBean();
            dictBean.setType(resultSet.getString(2));
            dictBean.setChineseWord(resultSet.getString(3));
            dictBeans.add(dictBean);
        }
        resultSet.close();
        return dictBeans;
    }

之后将监听器内的代码更改一下

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);

        //初始化本地词典
        myDict = new MyDict(this);
        try {
            myDict.init();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取前端控件的对象
        imageSearch = (Image) findComponentById(ResourceTable.Id_sou_word);
        textSelectReturn = (Text) findComponentById(ResourceTable.Id_text_select_return);
        textField_ID = (TextField) findComponentById(ResourceTable.Id_textField_ID);
        image = (Image) findComponentById(ResourceTable.Id_image);

        //添加搜索按钮监听
        if (imageSearch!=null){
            imageSearch.setClickable(true);
            imageSearch.setClickedListener(new Component.ClickedListener() {
                @Override
                public void onClick(Component component) {
                    selectWord();
                }
            });
        }
//        //添加文本框变更的监听器
//        textField_ID.addTextObserver(new Text.TextObserver() {
//            @Override
//            public void onTextUpdated(String s, int i, int i1, int i2) {
//                selectWord();
//            }
//        });
    }
        
    public void selectWord(){
        image.setVisibility(Component.HIDE);//下面图像不可见
        textSelectReturn.setVisibility(Component.VISIBLE);
        String s = textField_ID.getText();//获取文字
        List<DictBean> dictBeans = myDict.searchLocalWord(s);//获取list
        textSelectReturn.setText("");
        if (!dictBeans.isEmpty()){
            for (int i = 0; i < dictBeans.size(); i++) {
                textSelectReturn.append(dictBeans.get(i).getType()+" "+dictBeans.get(i).getChineseWord()+"\r\n");//添加文本内容
            }
        }
        else{
            textSelectReturn.setText("本地词库不存在此单词");
            myDict.searchWebWord(s,new SearchWordCallBackimpl());
        }
    }

添加网络爬虫搜索单词的功能

首先创建一个回调的接口ISearchWordCallBack

package com.example.onlinedict.common;

import com.example.onlinedict.common.bean.DictBean;

import java.util.List;

public interface ISearchWordCallBack {
    void onResult(List<DictBean> result);
}

之后创建线程类AsyncSearchWord

这里创建线程类的原因:主线程是ui线程 如果ui线程堵塞则程序会卡死,所以要再重新创建线程来在网络上查询

注意:这里用到了Jsoup这个jar包

package com.example.onlinedict.common;

import com.example.onlinedict.common.bean.DictBean;
import ohos.data.rdb.RdbStore;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

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

class AsyncSearchWord extends Thread{
    private String word;
    private RdbStore rdbStore;
    private ISearchWordCallBack callBack;
    public AsyncSearchWord(String word,RdbStore rdbStore,ISearchWordCallBack callBack){
        this.word = word;
        this.callBack = callBack;
        this.rdbStore = rdbStore;
    }

    @Override
    public void run() {
        try{
            HiLogLabel label = new HiLogLabel(HiLog.LOG_APP ,223, "MY_RUN");
            //获取html
            String url="https://www.iciba.com/word?w=" + word;
            HiLog.warn(label, url+"/");
            Document document = Jsoup.connect(url).get();
            Elements ul = document.getElementsByClass("Mean_part__1Xi6p");
//            String sql = "insert into word(word ,type ,meanings ) values(?,?,?);";
            List<DictBean> dictBeans = new ArrayList<DictBean>();

            for (Element element : ul){
                //获取词义和词性
                Elements lis = element.getElementsByTag("li");
                HiLog.warn(label, lis.text()+"/listext");
                for (Element li:lis){
                    DictBean dictBean = new DictBean();
                    //词性
                    Elements itags = li.getElementsByTag("i");
                    for (Element itag:itags){
                        dictBean.setType(itag.text());
                        break;
                    }
                    //词义
                    Elements divs = li.getElementsByTag("div");
                    for (Element div :divs){
                        dictBean.setChineseWord(div.text());
                        break;
                    }
                    dictBeans.add(dictBean);
                    HiLog.warn(label, dictBean.toString()+"/");
//                    rdbStore.executeSql(sql,new String[]{word,dictBean.getType(),dictBean.getChineseWord()});
                }
                break;
            }
            if (callBack!=null){
                callBack.onResult(dictBeans);
            }
        }
        catch (Exception e){
            HiLogLabel label = new HiLogLabel(HiLog.LOG_APP ,223, "MY_errwo");
            HiLog.error(label, e.toString());
            e.printStackTrace();
        }
    }
}

在MyDict类中添加网络查询的方法,用于开启线程

    public void searchWebWord(String word , ISearchWordCallBack callBack){
        word = word.toLowerCase();
        HiLogLabel label = new HiLogLabel(HiLog.LOG_APP ,223, "MY_TAG");
        HiLog.error(label, word+"//");
        //异步搜索
        new AsyncSearchWord(word,rdbStore,callBack).start();
    }

添加MyEventHandler类负责主线程与网络线程的通信

 private class  MyEventHandler extends EventHandler {
        private List<DictBean> dictBeans;
        public MyEventHandler(EventRunner runner,List<DictBean> dictBeans) throws IllegalArgumentException {
            super(runner);
            this.dictBeans=dictBeans;
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            if (event==null){
                return;
            }
            int eventid = event.eventId;
            switch (eventid){
                case SEARCH_RESULT:{
                    if (dictBeans.size()==0){
                        textSelectReturn.setText("单词不存在");
                    }
                    else {
                        textSelectReturn.setText("");
                        for (int i = 0; i < dictBeans.size(); i++) {
                            textSelectReturn.append(dictBeans.get(i).getType()+" "+dictBeans.get(i).getChineseWord()+"\r\n");//添加文本内容
                        }
                    }
                    break;
                }
            }
        }
    }

创建回调的实现类

    private class SearchWordCallBackimpl implements ISearchWordCallBack{
        @Override
        public void onResult(List<DictBean> result) {
            //得到主线程
            EventRunner runner = EventRunner.getMainEventRunner();
            MyEventHandler eventHandler = new MyEventHandler(runner,result);
            eventHandler.sendEvent(SEARCH_RESULT);
            runner = null;
        }
    }

HarmonyOS开发入门笔记 在线词典

此时运行发现还是会无法访问,这是因为网络权限没有打开

HarmonyOS开发入门笔记 在线词典


上一篇:前端小练习1:交叉观察器 页面元素入场动画


下一篇:TCP wrapper的访问控制