Dialog组件(对话框组件),是UI设计中经常会用到的一种页面布局元素。Dialog可以与许多交互事件进行关联,当用户由于手误而造成错误的交互操作时,Dialog能及时起到缓冲的作用,为用户提供再次交互的机会。Dialog有三种类型,分别是ToastDialog,PopupDialog和CommonDialog。
本次的知识分享以一个简单的Demo为例,向大家展示如何在页面跳转中恰当地穿插CommonDialog组件。话不多说,发车吧!
首先,我们不妨创建一个名为“Dialog”的新工程,相关选项的勾选如下图所示;
在实现页面跳转的逻辑之前,我们需要先创建新的AbilitySlice和xml文件来构建一个页面;
(1)打开entry>src>main>java>slice,右击sIice并新建一个Java类,将其命名为SecondAbilitySlice;
(2)打开entry>src>main>resources>base>layout,右击layout并新建一个Layout Resource File,将其命名为second;
3月28日 14:54问题比较复杂,请听我细细道来。
我把更新文件通过网口,下载到了STM32f427的download分区后
,重启发现程序并未更新,不知道是怎么回事。
首先,BootLoader的制作,flash分区应该是没有问题的,因为我用Xshell中的Ymodem传输固件,发现能够升级成功。
我的步骤如下:
1、擦除download分区
2、将rtthread.rbl文件,通过网口传输到stm32f427中,每包1kb,边传输边更新download分区flash。
3、软件reboot。
网口接收部分代码如下:
接收文件部分:
if(cmd == 0x14)
{
uint8_t buf[paramlen-4];
memcpy(&buf, &data_buf[10], paramlen-4);
uint16_t packnumber = 0;
packnumber = data_buf[6] + (data_buf[7] << 8);
uint16_t packsum = 0;
packsum = data_buf[8] + (data_buf[9] << 8);
fal_partition_t fal_dev = RT_NULL;
int fal_addr = (packnumber-1) * 1024;
fal_dev = fal_partition_find("download");
if(fal_dev == RT_NULL)
{
rt_kprintf("can not find download partition
");
return ;
}
if(fal_partition_write(fal_dev, fal_addr, buf, paramlen-4) < 0)
{
rt_kprintf("fal write download partition addr:%x size:%d fail
");
return ;
}
rt_kprintf("receive length: %d packnumber: %d packsum: %d fal_addr: %d buf0: %x
", paramlen-4,packnumber,packsum,fal_addr,buf[0]);
//传输完成
if(packnumber == packsum)
{
rt_thread_mdelay(1000);
rt_hw_cpu_reset();
}
}
擦除flash部分:
fal_partition_t fal_dev = RT_NULL;
fal_dev = fal_partition_find("download");
if(fal_dev == RT_NULL)
{
rt_kprintf("can not find download partition
");
return 0;
}
if(fal_partition_erase_all(fal_dev) < 0)
{
rt_kprintf("erase download partition all fail
");
return 0;
}
else {
rt_kprintf("erase download partition all success
");
}
上位机是用Qt做的部分代码如下:
void MainWindow::slot_update()
{
qDebug() << "update";
if(!filepath.isNull())
{
//初始
QFile* file = new QFile(filepath, this);
qDebug() << "filename = " << file->fileName();
qDebug() << "size = " << file->size();
//打开
if (!file->open(QIODevice::ReadOnly | QIODevice::Text))
return;
//读取
QByteArray data = file->readAll();
qDebug() << "qbytesize = " << data.size();
//拆包发送 每包1024
const int packsize = 1024;
int num = data.size()/packsize + 1;
for(int i=0 ; i
{
//要发送的数据
QByteArray part = data.left(packsize);
data.remove(0,packsize);
QByteArray SendData;
uint8_t head1 = 0xB5;
uint8_t head2 = 0x5B;
SendData.append(head1);
SendData.append(head2);
//cmd命令 在前是低位,在后是高位。
uint8_t temp0 = 0x14;
uint8_t temp1 = 0x00;
SendData.append(temp0);
SendData.append(temp1);
//参数长度
uint16_t paramlen = part.size() + 4;
temp0 = BYTE0(paramlen);
temp1 = BYTE1(paramlen);
SendData.append(temp0);
SendData.append(temp1);
//当前包编号
uint16_t packnumber = i+1;
temp0 = BYTE0(packnumber);
temp1 = BYTE1(packnumber);
SendData.append(temp0);
SendData.append(temp1);
//包总数编号
uint16_t packsum = num;
temp0 = BYTE0(packsum);
temp1 = BYTE1(packsum);
SendData.append(temp0);
SendData.append(temp1);
//压入数据
SendData += part;
//校验位
uint8_t temp_a[SendData.size()];
for(int j=0; j
{
temp_a[j] = SendData.at(j);
}
uint16_t crc = crc16(temp_a, SendData.size(), crc_16_MODBUS);
SendData.append(BYTE0(crc));
SendData.append(BYTE1(crc));
qDebug() << "part" << i << ":" << part.size();
qDebug("%x", (uint16_t)part[0]);
socket->write(SendData);
ui->bar_update->setValue( (i+1)*100/num );
//下面这个函数很重要
socket->waitForBytesWritten();
QThread::msleep(200);
}
}
else
{
QMessageBox::critical(this,"critical","没有选择升级文件");
}
}
打印信息:3月28日 14:56问题已经解决了,解决方法:
1、用自己的ota升级过后,先用jlink连接板子,打开jflash并选择对应芯片型号,file->new Project;
2、连接板子并读出flash,target->connect, target->manual programing->read back->entire chip;此时可读到板子对应寄存器的值
3、flash,target->connect, target->manual programing->read back->range,读取对应地址的值,我的是download分区0x08080000 - 0x080e0000,并保存为bin文件file->save date file as选择文件类型为bin。
4、比对文件,先用beyond compare比对,发现是有文件不同,但是无法直观查看,于是我用winhex打开对比,发现是有些byte缺失。
5、后来发现,传输与写入并无问题,最后检查出,原来是qt读取文件的问题,在读取文件时使能了QIODevice::Text,这个参数会导致读取二进制文件时,造成一些转义字符无法读取。修改后就成功了。
// if (!file->open(QIODevice::ReadOnly | QIODevice::Text))
if (!file->open(QIODevice::ReadOnly))今天 09:2009:20使用步骤如下:
(1) 配置驱动匹配表,完成主机端驱动总体信息的配置,具体如下:
struct U***PnpMatchIdTable {
//驱动模块名,该字段的值必须和驱动入口结构的moduleName一致
const char *moduleName;
//驱动对外发布服务的名称,必须唯一
const char *serviceName;
//驱动私有数据匹配关键字
const char *deviceMatchAttr;
//从该字段开始(包含该字段)之后数据长度,以byte为单位
uint8_t length;
//USB驱动匹配规则
uint16_t matchFlag;
//厂商编号
uint16_t vendorId;
//产品编号
uint16_t productId;
//设备出厂编号,低16位
uint16_t bcdDeviceLow;
//设备出厂编号,高16位
uint16_t bcdDeviceHigh;
//USB分配的设备类代码
uint8_t deviceClass;
//USB分配的子类代码
uint8_t deviceSubClass;
//USB分配的设备协议代码
uint8_t deviceProtocol;
//接口类型,根据实际需要可填写多个
uint8_t interfaceClass[USB_PNP_INFO_MAX_INTERFACES];
//接口子类型,根据实际需要可填写多个
uint8_t interfaceSubClass[USB_PNP_INFO_MAX_INTERFACES];
//接口所遵循的协议,根据实际需要可填写多个
uint8_t interfaceProtocol[USB_PNP_INFO_MAX_INTERFACES];
//接口的编号,根据实际需要可填写多个
uint8_t interfaceNumber[USB_PNP_INFO_MAX_INTERFACES];
};
其中matchFlag表示驱动匹配规则,每个bit表示一种匹配方式,其取值如下:
enum {
USB_PNP_NOTIFY_MATCH_VENDOR = 0x0001,
USB_PNP_NOTIFY_MATCH_PRODUCT = 0x0002,
USB_PNP_NOTIFY_MATCH_DEV_LOW = 0x0004,
USB_PNP_NOTIFY_MATCH_DEV_HIGH = 0x0008,
USB_PNP_NOTIFY_MATCH_DEV_CLASS = 0x0010,
USB_PNP_NOTIFY_MATCH_DEV_SUBCLASS = 0x0020,
USB_PNP_NOTIFY_MATCH_DEV_PROTOCOL = 0x0040,
USB_PNP_NOTIFY_MATCH_INT_CLASS = 0x0080,
USB_PNP_NOTIFY_MATCH_INT_SUBCLASS = 0x0100,
USB_PNP_NOTIFY_MATCH_INT_PROTOCOL = 0x0200,
USB_PNP_NOTIFY_MATCH_INT_NUMBER = 0x0400,
};
(2) USB主机端驱动开发工具包初始化,使用如下接口:
int32_t U***InitHostSdk(struct U***Session **session)
(3) 待步骤2初始化完后获取U***Interface对象,使用如下接口:
const struct U***Interface *U***ClaimInterface(const struct U***Session *session, uint8_t busNum, uint8_t u***Addr, uint8_t interfaceIndex);
(4) 打开步骤3获取到的U***Interface接口对象,获取对应接口的U***InterfaceHandle对象,使用如下接口:
U***InterfaceHandle *U***OpenInterface(const struct U***Interface *interfaceObj);
(5) 根据步骤4获取到的U***InterfaceHandle对象,获取指定索引为pinpeIndex的pipeInfo信息,使用如下接口:
int32_t U***GetPipeInfo(const U***InterfaceHandle *interfaceHandle, uint8_t settingIndex, uint8_t pipeId, struct U***PipeInfo *pipeInfo);
(6) 为步骤4获取到的U***InterfaceHandle预先分配待发送的IO Request对象,使用如下接口:
struct U***Request *U***AllocRequest(const U***InterfaceHandle *interfaceHandle, int isoPackets, int length);
(7) 根据输入参数params填充步骤6预先分配的IO Request,使用如下接口:
int32_t U***FillRequest(const struct U***Request *request, const U***InterfaceHandle *interfaceHandle, const struct U***RequestParams *params);
(8) 提交IO Request对象,可以选择同步或异步两种模式,使用如下接口:
int32_t U***SubmitRequestSync(const struct U***Request *request);//发送同步IO请求
int32_t U***SubmitRequestAsync(const struct U***Request *request);//发送异步IO请求
2. USB RAW API 的使用
USB RAW API主要实现USB更加复杂的功能,如获取描述符信息、获取设备指针、复位设备、提交传输请求等,如图4所示,是USB RAW API提供的部分接口。 09:21使用步骤如下:
(1) 同USB DDK API的步骤1一样,需先进行驱动匹配表配置。
(2) 初始化Host RAW,使用如下接口:
int32_t U***RawInit(struct U***Session **session);
(3) 待步骤2完成后打开USB设备,使用如下接口:
U***RawHandle *U***RawOpenDevice(const struct U***Session *session, uint8_t busNum, uint8_t u***Addr);
(4) 待步骤3完成后获取描述符,通过描述符获取接口、端点信息,使用如下接口:
int32_t U***RawGetConfigDescriptor(const U***RawDevice *rawDev, uint8_t configIndex, struct U***RawConfigDescriptor **config);
(5) 分配Request,并根据不同的传输类型使用相应的接口对Request进行填充:
int32_t U***RawFillBulkRequest(const struct U***RawRequest *request, const U***RawHandle *devHandle, const struct U***RawFillRequestData *fillData);// 填充用于批量传输的请求
int32_t U***RawFillControlSetup(const unsigned char *setup, const struct U***ControlRequestData *requestData);
int32_t U***RawFillControlRequest(const struct U***RawRequest *request, const U***RawHandle *devHandle, const struct U***RawFillRequestData *fillData);// 填充用于控制传输的请求
int32_t U***RawFillInterruptRequest(const struct U***RawRequest *request, const U***RawHandle *devHandle, const struct U***RawFillRequestData *fillData);// 填充用于中断传输的请求
int32_t U***RawFillIsoRequest(const struct U***RawRequest *request, const U***RawHandle *devHandle, const struct U***RawFillRequestData *fillData);// 填充用于同步传输的请求
(6) 提交IO Request对象,可以选择同步或异步两种模式,分别使用如下接口:
int32_t U***RawSendControlRequest(const struct U***RawRequest *request, const U***RawHandle *devHandle, const struct U***ControlRequestData *requestData);//发送同步USB控制传输请求
int32_t U***RawSendBulkRequest(const struct U***RawRequest *request, const U***RawHandle *devHandle, const struct U***RequestData *requestData);//发送同步USB批量传输请求
int32_t U***RawSendInterruptRequest(const struct U***RawRequest *request, const U***RawHandle *devHandle, const struct U***RequestData *requestData);//发送同步执行USB中断传输请求
int32_t U***RawSubmitRequest(const struct U***RawRequest *request);//提交异步IO请求
2)USB Device的开发
USB Device(设备端驱动)主要实现设备管理、配置管理、IO管理、数据通信等。USB Deivce DDK给开发者提供了设备创建、获取接口、接收Event事件、收发数据等驱动能力接口,如图5所示: 今天 09:2309:23101E20A7-83BA-440d-9493-74F74F253BB3.txt12.2 KB在线预览在线编辑打开存入云盘09:2778BB5C35-EA99-4622-8FF3-DBE396F5CCEE.txt12.3 KB在线预览在线编辑打开存入云盘09:39
@Override
protected void onBackground() {
LogUtil.info("onBackground","+++++");
webView.onInactive();
}
我的在手机回到桌面的时候,使用webView.onInactive(); 但是不起作用,游戏的声音依然在播放,参考了android的解决方案但是好像不适用与鸿蒙这块 09:46是否在config.json中添加了文件的读写权限,
"reqPermissions": [
{
"name": "ohos.permission.READ_USER_STORAGE"
},
{
"name": "ohos.permission.WRITE_USER_STORAGE"
}
]今天 10:2810:28使用WebView的public void load(String data, String mimeType, String encodingName, String baseUrl, String virtualUrl)这个重载方法,
WebConfig webConfig = webview.getWebConfig();
// 是否支持Javascript,默认值false
webConfig.setJavaScriptPermit(true);
webview.setWebAgent(new WebAgent() {
public boolean isNeedLoadUrl(WebView webView, ResourceRequest request) {
if (request == null || request.getRequestUrl() == null) {
return false;
}
String url = request.getRequestUrl().toString();
if (url.startsWith("http:") || url.startsWith("https:")) {
webView.load(url);
return false;
} else {
return super.isNeedLoadUrl(webView, request);
}
}
});
这个request里可以保存token信息10:31//单个文件的复制
duplicate1(path) {
let dir = fileio.openSync(path)
let index = path.lastIndexOf(".")
let ind = path.lastIndexOf("/")
let item = {
size: fileio.statSync(path).size,
path: path,
fileName: path.slice(ind+1, index),
fileType: path.slice(index + 1)
}
let currentPath = path.slice(0, ind)
let duplicationSize = item.size
let duplicatedSize = 0
let fd = fileio.openSync(item.path, 0o100 | 0o2, 0o666)
//经过实验,在使用fileio.readSync这个api之前,一定要先写入..算是一个小bug了
let num = fileio.writeSync(fd, "");
fileio.closeSync(fd);
let i = 0
let processor = 0
let timer = setInterval(() => {
//我们需要对边界值进行一定的处理,以防我们复制文件的大小与我们设定的读取的长度成整数倍
if (i > (Math.floor((item.size - 1) / READ_LENGTH) + 1)) {
clearInterval(timer)
} else {
//打开被复制的文件
let fd2 = fileio.openSync(item.path, 0o100 | 0o2, 0o666)
//创建缓存区
let buf = new ArrayBuffer(READ_LENGTH)
//设定读取的长度,读取的位置
let num1 = fileio.readSync(fd2, buf, { position: READ_LENGTH * i, length: READ_LENGTH })
//关闭被复制的文件
fileio.closeSync(fd2);
//创建与文件格式相同的空文件
let copypath = currentPath + '/' + item.fileName + '*.' + item.fileType;
//打开新文件
let copyfd = fileio.openSync(copypath, 0o100 | 0o2, 0o666)
//写入,position与读取被复制文件的时的位置相同,length的长度应该与读取被复制文件时的长度相同
fileio.writeSync(copyfd, buf, { position: i * READ_LENGTH, length: num1 });
//关闭新文件
fileio.closeSync(copyfd)
//获取当前新文件的大小
duplicatedSize = fileio.statSync(copypath).size
let processValue = duplicatedSize
//新文件的大小除以被复制文件的大小即为进度
processor = (processValue / duplicationSize) * 100
log("this.duplicatedSize:", duplicatedSize)
this.getProcessor(processor)
i += 1
}
}, 1)
}今天 10:4910:49[plat_pm]wifi need always on,do not close!!今天 11:0511:0593207608-3B74-41ca-8A6A-0A337C69A913.txt16.7 KB在线预览在线编辑打开存入云盘今天 11:2011:20有两块c8T6的板子,一块开发板,一块最小系统板,刚开始编程时用的是开发板,程序测试好好的,finsh也可以使用,但是换了最小系统板之后,msh就打印不出来了,使用的是nano版本,启动文件是startup_stm32f103xb.s
11:23我用下面这个代码,不停的创建线程,线程执行完自己的任务就会退出,但是我每次都在运行到第64次的时候,执行create返回RT_NULL,这是什么原因?
rt_thread_create()创建的线程,运行完自己退出,那么操作系统会回收该线程的栈资源吗。
rt_thread_t skb_recv_thd = rt_thread_create("skb_recv",
skb_recv_task,
&skb_recv_task_args,
1024 + 256,
21, //优先级低于MQTT线程
5);
if(skb_recv_thd == RT_NULL)
rt_kprintf("+NSONMI RT_NULl!
");
11:34用Deepin,那编译应该是GCC了,检查下检查脚本的text中,是否有
/* section information for initialization */
. = ALIGN(4);
__rt_init_start = .;
KEEP(*(SORT(.rti_fn*)))
__rt_init_end = .;
没有的话,就有可能被链接器自动丢弃了,你可以通过map文件确认这点。11:38使用RTstudio创建的基于STM32L496VET6程序,没做任何修改烧录板子,程序debug直接进入rt_hw_hard_fault_exception,jlink直接下载提示:
Downloading file [D:RT-ThreadStudioworkspace estDebug
tthread.bin]...
J-Link: Flash download: Bank 0 @ 0x08000000: Skipped. Contents already match
O.K.
Reset delay: 0 ms
Reset type NORMAL: Resets core & peripherals via SYSRESETREQ & VECTRESET bit.
Reset: Halt core after reset via DEMCR.VC_CORERESET.
Reset: Reset device via AIRCR.SYSRESETREQ.
Script processing completed.
使用stm32f103系列,直接相同方式生成代码,就可以正常跑31分钟前13:49下面逐步分析基于Rdb创建的一个Data_Ability的数据操作过程
//=====================导入模块部分=========================
// 导入DataAbility 谓词相关模块
import dataAbility from '@ohos.data.dataability'
//导入rdb关系数据库 模块
import dataRdb from '@ohos.data.rdb'
//=====================定义变量部分=========================
//定义数据库表名 book
const TABLE_NAME = 'book'
// 与此RDB存储相关的数据库配置。. encryptKey: 为数据库设置的加密密钥
const STORE_CONFIG = { name: 'book.db', encryptKey: new Uint8Array([]) }
//定义 SQL_CREATE_TABLE 关键字参数,下面执行参数会用到
const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS book(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, introduction TEXT NOT NULL)'
//定义一个变量 进行储存数据库返回内容,避免多次调用执行,节省性能
let rdbStore: any = undefined
//测试用打log用的
const TAG = 'DataAbility.data'
//=====================创建数据库=========================
export default {
// onInitialized 在Ability初始化调用,通过此回调方法执行rdb等初始化操作。
onInitialized(abilityInfo) {
console.info('DataAbility onInitialized,abilityInfo=' + abilityInfo.bundleName)
//getRdbStore() 获得一个相关的RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,然后通过RdbStore调用相关接口可以执行相关的数据操作,结果以callback形式返回。
dataRdb.getRdbStore(STORE_CONFIG, 1, (err, store) => {
console.info('[data]getRdbStoreThen')
//执行包含指定参数但不返回值的SQL语句,结果以Promise形式返回。
store.executeSql(SQL_CREATE_TABLE, [])
//把获得的数据库存入本地变量,防止后面多次操作读取创建
rdbStore = store
});
},
//=====================数据库操作方法=========================
//url:通信使用的URI
//callback:自定义回调函数名 如果操作成功,则返回ResultSet对象。
//valueBucket:指示数据库中要更新的数据行。键值对与数据库表的列名相关联
//TABLE_NAME:指定的目标表名。
//insert:向数据中插入一条数据。 callbacke方式调用:
insert(uri, valueBucket, callback) {
console.info(TAG + ' insert start')
rdbStore.insert(TABLE_NAME, valueBucket, callback)
},
//url:通信使用的URI
//valueBucket:指示数据库中要更新的数据行。键值对与数据库表的列名相关联
//callback:指定callback回调函数。如果操作成功,则返回ResultSet对象。
// batchInsert: 向数据库中插入多条数据。
batchInsert(uri, valueBuckets, callback) {
console.info(TAG + ' batch insert start')
//循环遍历
for (let i = 0;i < valueBuckets.length; i++) {
console.info(TAG + ' batch insert i=' + i)
if (i < valueBuckets.length - 1) {
//最终还是用到的insert方法
rdbStore.insert(TABLE_NAME, valueBuckets, (num: number) => {
console.info(TAG + ' batch insert ret=' + num)
})
} else {
rdbStore.insert(TABLE_NAME, valueBuckets, callback)
}
}
},
//url:通信使用的URI
//predicates:表示rdbPredicates的实例对象指定的查询条件。
//columns:表示要查询的列。如果值为空,则查询应用于所有列。
//callback:指定callback回调函数。如果操作成功,则返回ResultSet对象。
//query:查询数据库中的数据。
query(uri, columns, predicates, callback) {
console.info(TAG + ' query start')
let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates)
rdbStore.query(rdbPredicates, columns, callback)
},
//url:通信使用的URI
//valueBucket:指示数据库中要更新的数据行。键值对与数据库表的列名相关联
//predicates:表示要插入到表中的数据行。
//callback:指定callback回调函数。如果操作成功,则返回ResultSet对象。
// update:更新数据库中的数据。
update(uri, valueBucket, predicates, callback) {
console.info(TAG + 'update start')
let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates)
rdbStore.update(valueBucket, rdbPredicates, callback)
},
//url:通信使用的URI
//delete: 删除一条或多条数据。
//predicates:表示要删除表中的数据行。
//callback:指定callback回调函数。如果操作成功,则返回ResultSet对象。
delete(uri, predicates, callback) {
console.info(TAG + 'delete start')
let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates)
rdbStore.delete(rdbPredicates, callback)
}
};
14:01C9D20247-AC57-459c-A47C-5C575FEE9352.txt14.9 KB在线预览在线编辑打开存入云盘14:08前言
项目需要用到数据持久化存储,没有使用过HarmonyOS数据库时,我们就需要去官方文档或其他渠道去学习怎么使用,但是官方文档等长长的文字教程通常需要自己花很长时间去学习和理解才能掌握那本可以很容易就上手的知识。本篇速成教程直接使用最精准和简短的文字,再配上讲解代码,让我们能在10分钟左右就能掌握最基本的数据库使用方法。数据库的三大要素:数据库、表、字段,接下来为大家介绍关系型数据库和对象关系数据库的使用方法。
关系型数据库
在关系型数据库中,负责对应的类或创建的方式分别为:
1.数据库:RdbStore
2.表:通过RdbStore的executeSql方法,传入对应的sql语句创建表
3.字段:通过RdbStore的executeSql方法,传入对应的sql语句添加字段
建表和字段
// 如果不存在表student,则创建student表,并创建“id”(自增主键)、“name”(不为空)、“age”(integer类型)、“salary”(real类型)这4个字段
rdbStore.executeSql("create table if not exists student(id integer primary key autoincrement,name text not null, age integer, salary real)");
使用RdbStore类创建数据库
根据数据库操作的辅助类DatabaseHelper创建,并传入对应的数据库配置对象、数据库版本号、数据库创建或升降级等操作的回调对象,创建RdbStore数据库对象,并在数据库创建的回调里新增对应的表和字段,如下:
// 表名称
private static String StudentTable = "student";
// 数据库操作类
private RdbStore rdbStore;
// 数据库辅助类
private DatabaseHelper helper;
// 根据slice创建数据库辅助类
helper = new DatabaseHelper(context);
// 初始化数据库,包括数据库的创建。
private void initDB() {
// 创建数据库配置对象
StoreConfig config = StoreConfig.newDefaultConfig(StudentTable + ".db");
RdbOpenCallback callback = new RdbOpenCallback() {
@Override
public void onCreate(RdbStore rdbStore) {
rdbStore.executeSql("create table if not exists student(id integer primary key autoincrement,name text not null, age integer, salary real)");
}
@Override
public void onUpgrade(RdbStore rdbStore, int i, int i1) {
}
};
// 1为数据库的版本号
rdbStore = helper.getRdbStore(config,1,callback);
}
既然拿到了数据库操作类RdbStore,我们就可以对数据库进行增删改查的操作了:
新增数据操作:
// 添加数据
public boolean insertStudent(Student student){
ValuesBucket bucket = new ValuesBucket();
bucket.putInteger("age",student.getAge());
bucket.putString("name", student.getName());
bucket.putDouble("salary", student.getSalary());
long id = rdbStore.insert(StudentTable, bucket);
return id > 0 ? true : false;
}
删除、修改、查询等操作,都需要配合谓词AbsRdbPredicates的子类RdbPredicates进行:
删除数据操作:
// 删除数据:删除StudentTable表中对应的id那一条数据
public boolean deleteStudent(Integer id){
RdbPredicates predicates = new RdbPredicates(StudentTable);
predicates.equalTo("id", id);
int res = rdbStore.delete(predicates);
return res > 0 ? true : false;
}
修改数据操作:
/*
* 修改数据:修改StudentTable表中对应id的那一条数据
* 以下更新语句相当于执行了:
* update student set name=?, age=?, salary=? where id = ?
*/
public boolean updateStudent(Student student){
RdbPredicates predicates = new RdbPredicates(StudentTable);
// 以下代码相当于执行了 where id = ?
predicates.equalTo("id", student.getId());
ValuesBucket bucket = new ValuesBucket();
bucket.putInteger("age",student.getAge());
bucket.putString("name", student.getName());
bucket.putDouble("salary", student.getSalary());
int id = rdbStore.update(bucket, predicates);
return id > 0 ? true : false;
}
查询数据操作:
// 查询数据:查询StudentTable表中所有包含指定name的数据
public List queryStudents(String name){
// String[]为想要查询的字段
String[] strings = new String[]{
"id", "age", "name", "salary"
};
RdbPredicates predicates = new RdbPredicates(StudentTable);
predicates.like("name", name);
// ResultSet:查询的结果都包含在这个对象里边
ResultSet resultSet = rdbStore.query(predicates, strings);
List students = new ArrayList<>();
// resultSet.goToNextRow()为true时,表示还有下一条数据,并指定到下一条数据
while (resultSet.goToNextRow()){
Student student = new Student();
student.setId(resultSet.getInt(resultSet.getColumnIndexForName("id")));
student.setAge(resultSet.getInt(resultSet.getColumnIndexForName("age")));
student.setName(resultSet.getString(resultSet.getColumnIndexForName("name")));
student.setSalary(resultSet.getDouble(resultSet.getColumnIndexForName("salary")));
students.add(student);
}
return students;
}
对象型数据库
配置“build.gradle”文件:
1.如果使用注解处理器的模块为“com.huawei.ohos.hap”模块,则需要在模块的“build.gradle”文件的ohos节点中添加以下配置:
compileOptions{
annotationEnabled true
}
2.如果使用注解处理器的模块为“com.huawei.ohos.library”模块,则需要在模块的“build.gradle”文件的“dependencies”节点中配置注解处理器。
查看“orm_annotations_java.jar”、“orm_annotations_processor_java.jar” 、“javapoet_java.jar”这3个jar包在HUAWEI SDK中的Sdk/java/x.x.x.xx/build-tools/lib/目录,并将目录的这三个jar包导进来。
dependencies {
compile files("orm_annotations_java.jar的路径", "orm_annotations_processor_java.jar的路径", "javapoet_java.jar的路径")
annotationProcessor files("orm_annotations_java.jar的路径", "orm_annotations_processor_java.jar的路径", "javapoet_java.jar的路径")
}
如下图所示配置:14:09在对象型数据库中,负责操作对应三大要素的类分别为:
1.数据库:被开发者用@Database注解,且继承了OrmDatabase的类,对应关系型数据库。
// 定义了一个数据库类UserStore.java,数据库包含了“User”,"Book","AllDataType"三个表,版本号为“1”。数据库类的getVersion方法和getHelper方法不需要实现,直接将数据库类设为虚类即可。
@Database(entities = {User.class, Book.class, AllDataType.class}, version = 1)
public abstract class UserStore extends OrmDatabase {
}
2.表:被开发者用@Entity注解的实体类,且继承了OrmObject的类,对应关系型数据库中的表。
// 定义了一个实体类User.java,对应数据库内的表名为“user”;indices 为“firstName”和“lastName”两个字段建立了复合索引“name_index”,并且索引值是唯一的;“ignoredColumns”表示该字段不需要添加到“user”表的属性中。
@Entity(tableName = "user", ignoredColumns = {"ignoredColumn1", "ignoredColumn2"},
indices = {@Index(value = {"firstName", "lastName"}, name = "name_index", unique = true)})
public class User extends OrmObject {
// 此处将userId设为了自增的主键。注意只有在数据类型为包装类型时,自增主键才能生效。
// 注意:实体类至少要声明一个主键并实现对应的getter和setter方法,不然会编译报错!
@PrimaryKey(autoGenerate = true)
private Integer userId;
private String firstName;
private String lastName;
private int age;
private double balance;
private int ignoredColumn1;
private int ignoredColumn2;
// 需添加各字段的getter和setter方法。
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
...
}
3.字段:对应实体类的属性
在HarmonyOS的对象型数据库中,有一个最重要的负责对象数据操作接口的类:OrmContext。
对象数据操作接口类OrmContext配合谓词接口OrmPredicate等,就可以实现对数据库的增删改查功能!如下为数据库的增删改查操作:
// 插入数据
public boolean insertUser(User user){
boolean flag = ormContext.insert(user);
return ormContext.flush();
}
// 删除数据:删除User表中,对应userId的那一条数据。
public boolean deleteUser(Integer userId){
OrmPredicates predicates = ormContext.where(User.class).equalTo("userId", userId);
int flag = ormContext.delete(predicates);
return flag > 0 ? true : false;
}
// 修改数据:修改User表中对应userId的那一条数据。
public boolean updateUser(Integer userId, User user){
ValuesBucket bucket = new ValuesBucket();
bucket.putInteger("age",user.getAge());
bucket.putString("firstName", user.getFirstName());
bucket.putString("lastName",user.getLastName());
bucket.putDouble("balance",user.getBalance());
OrmPredicates predicates = ormContext.where(user.getClass()).equalTo("userId", userId);
int row_id = ormContext.update(predicates, bucket);
return row_id > 0 ? true : false;
}
// 查询数据:查询User表中包含对应firstName的所有数据。
public List queryUsersWithFirstName(String firstName){
OrmPredicates predicates = ormContext.where(User.class).like("firstName", firstName);
List users = ormContext.query(predicates);
return users;
}
那ormContext是怎么创建出来的呢?
// 数据库名称
private String database_name = "mydb.db";
// 数据库别名
private String database_name_alias = "mydb";
DatabaseHelper helper = new DatabaseHelper(context);
// UserStore.class:数据库类
ormContext = helper.getOrmContext(database_name_alias, database_name, UserStore.class);
DatabaseHelper是数据库操作的辅助类,当数据库创建成功后,数据库文件将存储在由上下文指定的目录里。注意:context入参类型为ohos.app.Context,注意不要使用slice.getContext()来获取context,请直接传入slice,否则会出现找不到类的报错。
总结
此文章用较小的篇幅讲解了最基本的HarmonyOS中关系型数据库和对象型数据库的使用,使读者能够快速理解和上手相关的知识和操作,当读者上手了这篇文章时,再去看其他更全更深层次的知识,相信会更加容易读懂和上手。14:20(3)打开新创建的SecondAbilitySlice,在"public class SecondAbilitySlice{ }"的基础上加入如下代码(用于完成页面的基础布置,并将SecondAbilitySlice与second.xml文件进行绑定);
public class SecondAbilitySlice extends AbilitySlice {
@Override
protected void onStart(Intent intent) { //设置onStart()回调函数
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_second); //将SecondAbilitySlice与second.xml文件绑定
}
接着,我们通过xml布局的方式,为MainAbilitySlice和SecondAbilitySlice分别添加一个文本组件;
(4)打开entry>src>main>resources>base>layout>ability_main_xml,加入一个Text组件,示例代码如下;
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:background_element="white"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
ohos:id="$+id:text"
ohos:height="match_content"
ohos:width="match_content"
ohos:center_in_parent="true" //将Text组件摆放在页面最中心的位置
ohos:text="AbilitySlice1"
ohos:text_size="30vp"
ohos:text_color="blue"/>
(5)打开新创建的second.xml,加入一个Text组件,并将页面背景色改为黑色,示例代码如下:
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:background_element="black" //将背景色调为黑色
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
ohos:id="$+id:Text"
ohos:height="match_content"
ohos:width="match_content"
ohos:center_in_parent="true" //将Text组件摆放在页面最中心的位置
ohos:text="AbilitySlice2"
ohos:text_size="30vp"
ohos:text_color="white"/>
最后,我们在MainAbilitySlice与SecondAbilitySlice中分别新创建CommonDialog组件,并写入相关事件逻辑;
(6)打开MainAbilitySlice,在onStart()函数的"{ }"内加入如下代码;
CommonDialog dialog=new CommonDialog(getContext()); // 新创建一个CommonDialog组件,命名为dialog
dialog.setSize(800,400); //设置CommonDialog的宽度和高度
dialog.setTitleText(" 提示"); //设置CommonDialog的标题文本的内容
dialog.setContentText(" 确认切换页面?"); //设置CommonDialog的内容文本的内容
dialog.setButton(IDialog.BUTTON1,"确认",(iDialog, i) -> dialog.destroy()); //设置CommonDialog的第一个按钮,用户触发此按钮后CommonDialog将销毁
dialog.setButton(IDialog.BUTTON2,"取消",(iDialog, i) -> dialog.hide()); //设置CommonDialog的第二个按钮,用户触发此按钮后CommonDialog将隐藏
dialog.setDestroyedListener(new CommonDialog.DestroyedListener() { //设置CommonDialog的销毁监听器
@Override
public void onDestroy() {
present(new SecondAbilitySlice(),new Intent()); //设置present(),当CommonDialog被销毁后执行此方法,用户界面将从原来的MainAbilitySlice导航至系统新创建的SecondAbilitySlice实例中。
}
});
dialog.hide(); //将CommonDialog的初始状态设置为隐藏状态(初始状态默认为隐藏状态)
Text text1=(Text) findComponentById(ResourceTable.Id_text); //定义已在ability_main_xml中创建的Text组件,命名为text1
text1.setClickedListener(new Component.ClickedListener() { //设置Text的点击监听器
@Override
public void onClick(Component component) {
dialog.show(); //设置show(),当用户点击Text组件时执行此方法,CommonDialog将进入前台与用户交互
}
});
(7)打开SecondAbilitySlice,在onStart()函数的"{ }”内加入如下代码;
CommonDialog Dialog=new CommonDialog(getContext());
Dialog.setSize(800,400);
Dialog.setTitleText(" 提示");
Dialog.setContentText(" 确认切换页面?");
Dialog.setButton(IDialog.BUTTON1,"确认",(iDialog, i) -> Dialog.destroy());
Dialog.setButton(IDialog.BUTTON2,"取消",(iDialog, i) -> Dialog.hide());
Dialog.setDestroyedListener(new CommonDialog.DestroyedListener() {
@Override
public void onDestroy() {
terminate(); //设置terminate(),当CommonDialog被销毁时执行此方法,以结束SecondAbilitySlice的生命周期,用户界面返回原来的MainAbilitySlice实例中
}
});
Text text1=(Text) findComponentById(ResourceTable.Id_Text);
text1.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Dialog.show();
}
});
|