1 简介

通讯录demo主要分为联系人界面、设置紧急联系人、服务卡片3个模块,分为Java和JS两个版本,本篇主要讲解用尽可能的用Java去实现。

1.1 原型

感兴趣的小伙伴,可以自己根据原型效果自己尝试着去实现【通讯录demo简易原型】

1.2 场景示例

通过学习与练习本demo,可以延伸至以下场景

1.3 项目实战

《HarmonyOS 项目实战之通讯录Demo(JS)》

《HarmonyOS 项目实战之通讯录(Java)》

《HarmonyOS 项目实战之新闻头条(ArkUI-TS》

2 功能开发

2.1 联系人列表

2.1.1 实现效果

2.1.2 核心代码

参考:ListContainer-常用组件开发指导-Java UI框架-UI-开发-HarmonyOS应用开发

  • ListContainer设置StickyContactProvider适配器
  • HeaderDecor头部联动效果设置
  • ContactData数据处理相关类,sortContactData方法用于排序等数据处理
ContactData categoryData = ContactData.get(); categoryData.sortContactData(); contactList = (ListContainer) findComponentById(ResourceTable.Id_contactList); Text headerText = (Text) findComponentById(ResourceTable.Id_sticky_text); List<ContactBean> dataList = categoryData.getResultList(); mStickyContactProvider = new StickyContactProvider(this, dataList); contactList.setItemProvider(mStickyContactProvider); HeaderDecor headerDecor = new HeaderDecor(contactList, headerText);

sortContactData方法数据处理,排序,字母索引:

public void sortContactData() { List<ContactBean> mContactList = new ArrayList<>(); Map<String, String> map = new HashMap<>(); for (ContactBean contactBean : mContactBeans) { String pinyin = Utils.getPingYin(contactBean.getName()); map.put(pinyin, contactBean.getName()); contactBean.setNamepy(pinyin); mContactList.add(contactBean); } mContactList.sort(new ContactComparator()); characterList = new ArrayList<>(); resultList = new ArrayList<>(); for (ContactBean contactBean : mContactList) { String namepy = contactBean.getNamepy(); String character = (namepy.charAt(0) + "").toUpperCase(Locale.ENGLISH); if (!characterList.contains(character)) { if (character.hashCode() >= "A".hashCode() && character.hashCode() <= "Z".hashCode()) { // 是字母 characterList.add(character); resultList.add(new ContactBean(character, ContactBean.ITEM_TYPE.ITEM_TYPE_CHARACTER.ordinal())); } else { if (!characterList.contains("#")) { characterList.add("#"); resultList.add(new ContactBean("#", ContactBean.ITEM_TYPE.ITEM_TYPE_CHARACTER.ordinal())); } } } resultList.add(new ContactBean(contactBean.getName(), contactBean.getTelephone(), map.get(namepy), ContactBean.ITEM_TYPE.ITEM_TYPE_CONTACT.ordinal())); }
}

2.2 数据的增删改查

2.2.1 实现效果

2.2.2 增删改查实现

ListContainer删除实现

categoryData.deleteContactBeans(item);
categoryData.sortContactData();
mStickyContactProvider.setDataListChanged(categoryData.getResultList());

随机添加一个联系人

categoryData.addContactBean("胡六一", "15269856587");
categoryData.sortContactData();
mStickyContactProvider.setDataListChanged(categoryData.getResultList());

ContactData数据处理效果类,实现数据增删改查

// Generate the javaBean of ContactData
public static ContactData get() { return new ContactData();
} public List<ContactBean> getDefaultContactBeans() { return mDefaultContactBeans;
} public List<ContactBean> getContactBeans() { return mContactBeans;
} public void addContactBean(String name, String phone) { mContactBeans.add(new ContactBean(name, phone));
} public List<ContactBean> deleteContactBeans(ContactBean item) { mContactBeans.removeIf(contactBean -> contactBean.getName().equals(item.getName())); return mContactBeans;
} public List<ContactBean> getResultList() { return resultList;
} public List<String> getCharacterList() { return characterList;
} public int getScrollPosition(String character) { if (characterList.contains(character)) { for (int i = 0; i < resultList.size(); i++) { if (resultList.get(i).getCharacter().equals(character)) { return i; } } } return -1; // -1不会滑动
}

2.2.3 紧急联系人数据存储

轻量级数据存储:轻量级数据存储概述-轻量级数据存储-数据管理-开发-HarmonyOS应用开发

Key-Value数据结构

一种键值结构数据类型。Key是不重复的关键字,Value是数据值。

运作机制

  • 本模块提供轻量级数据存储的操作类,应用通过这些操作类完成数据库操作。
  • 借助DatabaseHelper API,应用可以将指定文件的内容加载到Preferences实例,每个文件最多有一个Preferences实例,系统会通过静态容器将该实例存储在内存中,直到应用主动从内存中移除该实例或者删除该文件。
  • 获取到文件对应的Preferences实例后,应用可以借助Preferences API,从Preferences实例中读取数据或者将数据写入Preferences实例,通过flush或者flushSync将Preferences实例持久化。

核心代码实现

添加紧急联系人,并通知java卡片更新

ZSONObject zsonObject = new ZSONObject();
zsonObject.put("urgent1", nameTf1.getText());
zsonObject.put("urgentPhone1", phoneTf1.getText());
zsonObject.put("urgent2", nameTf2.getText());
zsonObject.put("urgentPhone2", phoneTf2.getText());
PreferenceUtils.putString(getContext(),"urgentPerson", ZSONObject.toZSONString(zsonObject));
FormBindingData formBindingData = new FormBindingData(zsonObject);
((ContactPersonAbility) getAbility()).confirmUpdateForm(formBindingData);

PreferenceUtils封装工具类,实现数据存储

public class PreferenceUtils { private static String PREFERENCE_FILE_NAME = "prefrence_file"; private static Preferences preferences; private static DatabaseHelper databaseHelper; private static Preferences.PreferencesObserver mPreferencesObserver; private static void initPreference(Context context) { if (databaseHelper == null) { databaseHelper = new DatabaseHelper(context); } if (preferences == null) { preferences = databaseHelper.getPreferences(PREFERENCE_FILE_NAME); } } //存放、获取时传入的context必须是同一个context,否则存入的数据无法获取 public static void putString(Context context, String key, String value) { initPreference(context); preferences.putString(key, value); preferences.flush(); } /** * @param context 上下文 * @param key     键 * @return 获取的String 默认值为:null */ public static String getString(Context context, String key) { initPreference(context); return preferences.getString(key, null); } public static String getString(Context context, String key, String d) { initPreference(context); return preferences.getString(key, d); } public static void putInt(Context context, String key, int value) { initPreference(context); preferences.putInt(key, value); preferences.flush(); } /** * @param context 上下文 * @param key     键 * @return 获取int的默认值为:-1 */ public static int getInt(Context context, String key) { initPreference(context); return preferences.getInt(key, -1); } public static void putLong(Context context, String key, long value) { initPreference(context); preferences.putLong(key, value); preferences.flush(); } /** * @param context 上下文 * @param key     键 * @return 获取long的默认值为:-1 */ public static long getLong(Context context, String key) { initPreference(context); return preferences.getLong(key, -1L); } public static void putBoolean(Context context, String key, boolean value) { initPreference(context); preferences.putBoolean(key, value); preferences.flush(); } /** * @param context 上下文 * @param key     键 * @return 获取boolean的默认值为:false */ public static boolean getBoolean(Context context, String key) { initPreference(context); return preferences.getBoolean(key, false); } public static void putFloat(Context context, String key, float value) { initPreference(context); preferences.putFloat(key, value); preferences.flush(); } /** * @param context 上下文 * @param key     键 * @return 获取float的默认值为:0.0 */ public static float getFloat(Context context, String key) { initPreference(context); return preferences.getFloat(key, 0.0F); } public static void putStringSet(Context context, String key, Set<String> set) { initPreference(context); preferences.putStringSet(key, set); preferences.flush(); } /** * @param context 上下文 * @param key     键 * @return 获取set集合的默认值为:null */ public static Set<String> getStringSet(Context context, String key) { initPreference(context); return preferences.getStringSet(key, null); } public static boolean deletePreferences(Context context) { initPreference(context); boolean isDelete = databaseHelper.deletePreferences(PREFERENCE_FILE_NAME); return isDelete; } public static void registerObserver(Context context, Preferences.PreferencesObserver preferencesObserver) { initPreference(context); mPreferencesObserver = preferencesObserver; preferences.registerObserver(mPreferencesObserver); } public static void unregisterObserver() { if (mPreferencesObserver != null) { // 向preferences实例注销观察者 preferences.unregisterObserver(mPreferencesObserver); } }

2.3 第三方跳转

2.3.1 实现效果

2.3.2 拨打电话与发送短信

/** * 跳转系统短信 */ private void doMessage(String telephone) { Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder()
//                .withAction("android.intent.action.SENDTO") // Android写法 android.intent.action.SENDTO .withAction(IntentConstants.ACTION_SEND_SMS) .withUri(Uri.parse("smsto:" + telephone)) // 设置号码 .withFlags(Intent.FLAG_NOT_OHOS_COMPONENT) .build(); intent.setOperation(operation); context.startAbility(intent, 11); } /** * 申请拨打电话权限 */ private boolean requestPermissions() { if (context.verifySelfPermission("android.permission.CALL_PHONE") != IBundleManager.PERMISSION_GRANTED) { // 应用未被授予权限 if (context.canRequestPermission("android.permission.CALL_PHONE")) { // 是否可以申请弹框授权(首次申请或者用户未选择禁止且不再提示) context.requestPermissionsFromUser(new String[]{"android.permission.CALL_PHONE"}, 100); } return false; } else { // 权限已被授予 return true; } } /** * 直接拨打电话 * 需要申请权限 */ private void doCall(String destinationNum) { if (!requestPermissions()) { return; } Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withAction("android.intent.action.CALL") // 系统应用拨号盘 .withUri(Uri.parse("tel:" + destinationNum)) // 设置号码 .withFlags(2) .build(); intent.setOperation(operation); // 启动Ability context.startAbility(intent, 10); } /** * 跳转系统拨打电话界面 */ private void doDial(String destinationNum) { Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withAction(IntentConstants.ACTION_DIAL) // 系统应用拨号盘
//                .withBundleName(context.getCallingBundle()) // 应用拨号选择器 .withUri(Uri.parse("tel:" + destinationNum)) // 设置号码 .withFlags(2) .build(); intent.setOperation(operation); // 启动Ability context.startAbility(intent, 10); }

2.4 JS服务卡片

2.4.1 实现效果

2.4.2 创建卡片模板

使用DevEco Studio创建卡片工程

创建成功后,在config.json的module中会生成js模块,用于对应卡片的js相关资源,配置示例如下:

"js": [ { "pages": [ "pages/index/index" ], "name": "widget", "window": { "designWidth": 720, "autoDesignWidth": true }, "type": "form" }
]

config.json文件“abilities”配置forms模块细节如下:

"name": "com.huhu.contact.ContactPersonAbility",
"icon": "$media:icon",
"description": "$string:contactpersonability_description",
"formsEnabled": true,
"label": "$string:contact_ContactPersonAbility",
"type": "page",
"forms": [ { "jsComponentName": "widget", "isDefault": true, "scheduledUpdateTime": "10:30", "defaultDimension": "2*2", "name": "widget", "description": "This is a service widget", "colorMode": "auto", "type": "JS", "supportDimensions": [ "2*2" ], "updateEnabled": true, "updateDuration": 1 }
]

创建一个ContactPersonAbility,覆写卡片相关回调函数。

  • onCreateForm(Intent intent)
  • onUpdateForm(long formId)
  • onDeleteForm(long formId)
  • onCastTempForm(long formId)
  • onEventNotify(Map
  • onTriggerFormEvent(long formId, String message)
  • onAcquireFormState(Intent intent)

当卡片使用方请求获取卡片时,卡片提供方会被拉起并调用onCreateForm(Intent intent)回调,intent中会带有卡片ID、卡片名称和卡片外观规格信息,可按需获取使用。

开发JS卡片时,FormAbility可以继承AceAbility或Ability,继承Ability时,需在onStart()方法中额外设置路由信息。

2.4.3 卡片数据绑定

@Override public ProviderFormInfo bindFormData() { HiLog.info(TAG, "bind form data"); ProviderFormInfo providerFormInfo = new ProviderFormInfo(); String urgentPersonStr = PreferenceUtils.getString(context, "urgentPerson", ""); ZSONObject zsonObject = ZSONObject.stringToZSON(urgentPersonStr); if (dimension == DEFAULT_DIMENSION_2X2) { if (zsonObject != null) { providerFormInfo.setJsBindingData(new FormBindingData(zsonObject)); } } return providerFormInfo; }

2.4.4 卡片数据更新

public void confirmUpdateForm(FormBindingData formBindingData) { FormControllerManager formControllerManager = FormControllerManager.getInstance(this); List<Long> allFormIdFromSharePreference = formControllerManager.getAllFormIdFromSharePreference(); if (allFormIdFromSharePreference == null || allFormIdFromSharePreference.isEmpty()) return; Long formId = allFormIdFromSharePreference.get(0); try { updateForm(formId,formBindingData); } catch (FormException e) { e.printStackTrace(); }

2.4.5 卡片事件处理

{ "data": { "text_content": "Name", "cardPrimaryText": "Contacts", "cardSecondaryText": "+8612345678912", "urgent1": "无", "urgent2": "无", "urgentPhone1": "+8612345678912", "urgentPhone2": "+8612345678915" }, "actions": { "urgentCall1": { "action": "message", "params": { "action": "urgentCall1", "phoneNumber": "10086" } }, "urgentCall2": { "action": "message", "params": { "action": "urgentCall2", "phoneNumber": "15565339857" } }, "startMainRouter": { "action": "router", "abilityName": "com.huhu.contact.ContactPersonAbility" } }
}

卡片支持触发事件,覆写onTriggerFormEvent方法实现对事件的触发,

doCall就是前面的播打电话的方法

@Override
protected void onTriggerFormEvent(long formId, String message) { super.onTriggerFormEvent(formId, message); HiLog.info(loglabel, "onTriggerFormEvent: " + message); FormControllerManager formControllerManager = FormControllerManager.getInstance(this); FormController formController = formControllerManager.getController(formId); formController.onTriggerFormEvent(formId, message); ZSONObject params = ZSONObject.stringToZSON(message); String action = params.getString("action"); String phoneNumber = params.getString("phoneNumber"); HiLog.info(loglabel, "onTriggerFormEvent: action:" + action); String urgentPersonStr = PreferenceUtils.getString(this, "urgentPerson", ""); ZSONObject zsonObject = ZSONObject.stringToZSON(urgentPersonStr); switch (action) { case "urgentCall1": String urgentPhone1 = zsonObject.getString("urgentPhone1"); doCall(urgentPhone1); break; case "urgentCall2": String urgentPhone2 = zsonObject.getString("urgentPhone2"); doCall(urgentPhone2); break; default: break; }
}

3 注意事项

Demo还有很多需要完善的地方

滑动时,上滑头部联动效果,索引有时会错乱;

紧急联系人没有和列表数据联动。

搜索功能未实现;

4 总结

有不对或者更优的处理技术方案请多多指教,共同学习,共同进步。

代码地址: https://gitee.com/hu-lingqing/contact-person.git

HarmonyOS 项目实战之通讯录(Java)相关推荐

  1. 【项目实战】用 Java 写了一个类QQ界面聊天小项目,可在线聊天!

    1.功能实现 1.修改功能(密码.昵称.个性签名) 2.添加好友.删除好友 3.单聊功能 4.判断好友是否在线 2.模块划分 == 3.使用的知识 netty swing 集合等同步阻塞队列synch ...

  2. 企业级项目实战讲解!java的war包能直接改名么

    Java核心架构笔记大致如下 0~1年: Java基础(基本语法.面向对象.继承.多态.接口和抽象类.异常.内部类.集合.泛型.反射.I/O等) Web编程(Servlet+MySQL数据库+商品管理 ...

  3. Redis高级项目实战,西安java程序员工资

    一面问题:MySQL+Redis+Kafka+线程+算法 mysql知道哪些存储引擎,它们的区别 mysql索引在什么情况下会失效 mysql在项目中的优化场景,慢查询解决等 mysql有什么索引,索 ...

  4. Redis高级项目实战,适合java开发的笔记本电脑

    面试前的准备 老实说,我自己平常没事就会看一些面试题,所以我都是直接去面的.不过我还是要建议大家如果准备面试的话,需要做以下准备 背题:看一看最近的面经文,了解现在公司都在面什么类型的题,准备一些常见 ...

  5. Redis高级项目实战,华为java开发工资

    个人基本情况: 首先介绍一下自己的个人基本情况,某专科学校毕业,计算机技术与应用专业,有过2年的工作经验,毕业以后一直想要进入一线互联网大厂工作,但无奈学历受限,屡屡被挡在门外.后来接触到一个朋友,了 ...

  6. Redis高级项目实战!北京java编程入门培训

    Dubbo面试专题 JVM面试专题 Java并发面试专题 Kafka面试专题 MongDB面试专题 MyBatis面试专题 MySQL面试专题 Netty面试专题 RabbitMQ面试专题 Redis ...

  7. 企业级项目实战讲解!java类内部定义枚举

    蚂蚁一面 ⼀⾯就做了⼀道算法题,要求两⼩时内完成,给了⻓度为N的有重复元素的数组,要求输出第10⼤的数.典型的TopK问题,快排算法搞定.算法题要注意的是合法性校验.边界条件以及异常的处理.另外,如果 ...

  8. 新一代微服务全家桶AlibabaCloud+Docker+JDK11阿里云容器部署零基础到项目实战

    新一代微服务全家桶AlibabaCloud+Docker+JDK11阿里云容器部署零基础到项目实战 近年来,微服务架构已经成为企业标配,它以更加灵活的部署方式和高度解耦的架构设计,为企业带来了极大的业 ...

  9. java 搜索业务怎么写_Java项目实战第11天:搜索功能的实现

    原标题:Java项目实战第11天:搜索功能的实现 今天是刘小爱自学Java的第110天. 感谢你的观看,谢谢你. 话不多说,开始今天的学习: 事先说明:关于今天的搜索功能实现. 并没有使用到倒排索引这 ...

  10. Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...

    JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过! 1.JPG (37.82 KB, 下载次数: 0) 2018-12-3 09:40 上传 2.JPG (28 ...

最新文章

  1. C++的STL 堆 实现获取数组堆第K大的数
  2. 阿里云边缘云全新架构升级,助力CDN操控新体验
  3. 【java学习笔记】浅析JavaWeb开发中Model1模式和Model2模式
  4. ubuntu下sudo命令不能使用问题
  5. echarts 柱状图不显示y坐标轴_Python+matplotlib自定义坐标轴位置、颜色、箭头
  6. tensorflow的基础
  7. Spark:windows下配置spark开发环境
  8. SQL正则表达式的妙用
  9. 马哥python培训班靠谱吗
  10. 渗透之——Metasploit自定义FTP扫描模块
  11. [大学物理实验-0]修约规则和常见的实验数据的处理
  12. 国家信息安全水平考试NISP考试经验分享
  13. 二维条码防伪技术原理及应用简介
  14. win11恢复win10的右击菜单,自测可用
  15. 阿里云物联网Iot设备上下线状态数据流转的设置
  16. 使用EasyExcel进行百万数据文件导出思路
  17. 战地3皓月服务器win10系统,战地3配置
  18. 110配线架打法图解_配线架打线的方法以及110配线架的按照流程
  19. 自然语言处理 - 二元语法与中文分词
  20. 智能化软件开发微访谈·第十九期暨2022新年特辑:软件智能化开发:进展与挑战...

热门文章

  1. 精锐万能票据打印专家
  2. rzsz工具 源码交叉编译
  3. 涂鸦蓝牙SDK开发系列教程——4.烧录授权
  4. 《统计自然语言处理》(宗成庆)学习笔记(一)
  5. 将EBS设为首页worklist删除误报
  6. Java简单输出,输出Hello World,将代码上传到Github
  7. html个人学籍模板,学籍模板老师评语
  8. 在visio里面插入带圆圈的数字字符
  9. java ipa签名_iOS苹果应用IPA一键签名工具及重签教程
  10. 简单的POST sql注入