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

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

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

1 简介

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

1.1 原型

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

#星光计划2.0# HarmonyOS 项目实战之通讯录(Java)-鸿蒙HarmonyOS技术社区#星光计划2.0# HarmonyOS 项目实战之通讯录(Java)-鸿蒙HarmonyOS技术社区#星光计划2.0# HarmonyOS 项目实战之通讯录(Java)-鸿蒙HarmonyOS技术社区

1.2 场景示例

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

#星光计划2.0# HarmonyOS 项目实战之通讯录(Java)-鸿蒙HarmonyOS技术社区#星光计划2.0# HarmonyOS 项目实战之通讯录(Java)-鸿蒙HarmonyOS技术社区#星光计划2.0# HarmonyOS 项目实战之通讯录(Java)-鸿蒙HarmonyOS技术社区

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方法用于排序等数据处理
  1. ContactData categoryData = ContactData.get(); 
  2.     categoryData.sortContactData(); 
  3.  
  4.     contactList = (ListContainer) findComponentById(ResourceTable.Id_contactList); 
  5.     Text headerText = (Text) findComponentById(ResourceTable.Id_sticky_text); 
  6.     List<ContactBean> dataList = categoryData.getResultList(); 
  7.  
  8.     mStickyContactProvider = new StickyContactProvider(this, dataList); 
  9.     contactList.setItemProvider(mStickyContactProvider); 
  10.     HeaderDecor headerDecor = new HeaderDecor(contactList, headerText); 

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

  1. public void sortContactData() { 
  2.     List<ContactBean> mContactList = new ArrayList<>(); 
  3.     Map<String, String> map = new HashMap<>(); 
  4.  
  5.     for (ContactBean contactBean : mContactBeans) { 
  6.         String pinyin = Utils.getPingYin(contactBean.getName()); 
  7.         map.put(pinyin, contactBean.getName()); 
  8.         contactBean.setNamepy(pinyin); 
  9.         mContactList.add(contactBean); 
  10.     } 
  11.     mContactList.sort(new ContactComparator()); 
  12.     characterList = new ArrayList<>(); 
  13.     resultList = new ArrayList<>(); 
  14.     for (ContactBean contactBean : mContactList) { 
  15.         String namepy = contactBean.getNamepy(); 
  16.         String character = (namepy.charAt(0) + "").toUpperCase(Locale.ENGLISH); 
  17.         if (!characterList.contains(character)) { 
  18.             if (character.hashCode() >= "A".hashCode() && character.hashCode() <= "Z".hashCode()) { // 是字母 
  19.                 characterList.add(character); 
  20.                 resultList.add(new ContactBean(character, ContactBean.ITEM_TYPE.ITEM_TYPE_CHARACTER.ordinal())); 
  21.             } else { 
  22.                 if (!characterList.contains("#")) { 
  23.                     characterList.add("#"); 
  24.                     resultList.add(new ContactBean("#", ContactBean.ITEM_TYPE.ITEM_TYPE_CHARACTER.ordinal())); 
  25.                 } 
  26.             } 
  27.         } 
  28.  
  29.         resultList.add(new ContactBean(contactBean.getName(), contactBean.getTelephone(), map.get(namepy), ContactBean.ITEM_TYPE.ITEM_TYPE_CONTACT.ordinal())); 
  30.     } 

2.2 数据的增删改查

2.2.1 实现效果

#星光计划2.0# HarmonyOS 项目实战之通讯录(Java)-鸿蒙HarmonyOS技术社区

2.2.2 增删改查实现

ListContainer删除实现

  1. categoryData.deleteContactBeans(item); 
  2. categoryData.sortContactData(); 
  3. mStickyContactProvider.setDataListChanged(categoryData.getResultList()); 

 随机添加一个联系人

  1. categoryData.addContactBean("胡六一""15269856587"); 
  2. categoryData.sortContactData(); 
  3. mStickyContactProvider.setDataListChanged(categoryData.getResultList()); 

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

  1. // Generate the javaBean of ContactData 
  2. public static ContactData get() { 
  3.     return new ContactData(); 
  4.  
  5. public List<ContactBean> getDefaultContactBeans() { 
  6.     return mDefaultContactBeans; 
  7.  
  8. public List<ContactBean> getContactBeans() { 
  9.     return mContactBeans; 
  10.  
  11. public void addContactBean(String name, String phone) { 
  12.     mContactBeans.add(new ContactBean(name, phone)); 
  13.  
  14. public List<ContactBean> deleteContactBeans(ContactBean item) { 
  15.     mContactBeans.removeIf(contactBean -> contactBean.getName().equals(item.getName())); 
  16.  
  17.     return mContactBeans; 
  18.  
  19. public List<ContactBean> getResultList() { 
  20.     return resultList; 
  21.  
  22. public List<String> getCharacterList() { 
  23.     return characterList; 
  24.  
  25. public int getScrollPosition(String character) { 
  26.     if (characterList.contains(character)) { 
  27.         for (int i = 0; i < resultList.size(); i++) { 
  28.             if (resultList.get(i).getCharacter().equals(character)) { 
  29.                 return i; 
  30.             } 
  31.         } 
  32.     } 
  33.  
  34.     return -1; // -1不会滑动 

2.2.3 紧急联系人数据存储

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

Key-Value数据结构

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

运作机制

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

核心代码实现

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

  1. ZSONObject zsonObject = new ZSONObject(); 
  2. zsonObject.put("urgent1", nameTf1.getText()); 
  3. zsonObject.put("urgentPhone1", phoneTf1.getText()); 
  4. zsonObject.put("urgent2", nameTf2.getText()); 
  5. zsonObject.put("urgentPhone2", phoneTf2.getText()); 
  6. PreferenceUtils.putString(getContext(),"urgentPerson", ZSONObject.toZSONString(zsonObject)); 
  7. FormBindingData formBindingData = new FormBindingData(zsonObject); 
  8. ((ContactPersonAbility) getAbility()).confirmUpdateForm(formBindingData); 

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

  1. public class PreferenceUtils { 
  2.     private static String PREFERENCE_FILE_NAME = "prefrence_file"
  3.     private static Preferences preferences; 
  4.     private static DatabaseHelper databaseHelper; 
  5.     private static Preferences.PreferencesObserver mPreferencesObserver; 
  6.  
  7.     private static void initPreference(Context context) { 
  8.         if (databaseHelper == null) { 
  9.             databaseHelper = new DatabaseHelper(context); 
  10.         } 
  11.         if (preferences == null) { 
  12.             preferences = databaseHelper.getPreferences(PREFERENCE_FILE_NAME); 
  13.         } 
  14.     } 
  15.  
  16.     //存放、获取时传入的context必须是同一个context,否则存入的数据无法获取 
  17.     public static void putString(Context context, String key, String value) { 
  18.         initPreference(context); 
  19.         preferences.putString(key, value); 
  20.         preferences.flush(); 
  21.     } 
  22.  
  23.     /** 
  24.      * @param context 上下文 
  25.      * @param key     键 
  26.      * @return 获取的String 默认值为:null 
  27.      */ 
  28.     public static String getString(Context context, String key) { 
  29.         initPreference(context); 
  30.         return preferences.getString(keynull); 
  31.     } 
  32.  
  33.     public static String getString(Context context, String key, String d) { 
  34.         initPreference(context); 
  35.         return preferences.getString(key, d); 
  36.     } 
  37.  
  38.     public static void putInt(Context context, String keyint value) { 
  39.         initPreference(context); 
  40.         preferences.putInt(key, value); 
  41.         preferences.flush(); 
  42.     } 
  43.  
  44.     /** 
  45.      * @param context 上下文 
  46.      * @param key     键 
  47.      * @return 获取int的默认值为:-1 
  48.      */ 
  49.     public static int getInt(Context context, String key) { 
  50.         initPreference(context); 
  51.         return preferences.getInt(key, -1); 
  52.     } 
  53.     public static void putLong(Context context, String key, long value) { 
  54.         initPreference(context); 
  55.         preferences.putLong(key, value); 
  56.         preferences.flush(); 
  57.     } 
  58.  
  59.     /** 
  60.      * @param context 上下文 
  61.      * @param key     键 
  62.      * @return 获取long的默认值为:-1 
  63.      */ 
  64.     public static long getLong(Context context, String key) { 
  65.         initPreference(context); 
  66.         return preferences.getLong(key, -1L); 
  67.     } 
  68.  
  69.     public static void putBoolean(Context context, String key, boolean value) { 
  70.         initPreference(context); 
  71.         preferences.putBoolean(key, value); 
  72.         preferences.flush(); 
  73.     } 
  74.  
  75.     /** 
  76.      * @param context 上下文 
  77.      * @param key     键 
  78.      * @return 获取boolean的默认值为:false 
  79.      */ 
  80.     public static boolean getBoolean(Context context, String key) { 
  81.         initPreference(context); 
  82.         return preferences.getBoolean(keyfalse); 
  83.     } 
  84.  
  85.     public static void putFloat(Context context, String keyfloat value) { 
  86.         initPreference(context); 
  87.         preferences.putFloat(key, value); 
  88.         preferences.flush(); 
  89.     } 
  90.  
  91.     /** 
  92.      * @param context 上下文 
  93.      * @param key     键 
  94.      * @return 获取float的默认值为:0.0 
  95.      */ 
  96.     public static float getFloat(Context context, String key) { 
  97.         initPreference(context); 
  98.         return preferences.getFloat(key, 0.0F); 
  99.     } 
  100.  
  101.     public static void putStringSet(Context context, String keySet<String> set) { 
  102.         initPreference(context); 
  103.         preferences.putStringSet(keyset); 
  104.         preferences.flush(); 
  105.     } 
  106.  
  107.     /** 
  108.      * @param context 上下文 
  109.      * @param key     键 
  110.      * @return 获取set集合的默认值为:null 
  111.      */ 
  112.     public static Set<String> getStringSet(Context context, String key) { 
  113.         initPreference(context); 
  114.         return preferences.getStringSet(keynull); 
  115.     } 
  116.  
  117.     public static boolean deletePreferences(Context context) { 
  118.         initPreference(context); 
  119.         boolean isDelete = databaseHelper.deletePreferences(PREFERENCE_FILE_NAME); 
  120.         return isDelete; 
  121.     } 
  122.  
  123.     public static void registerObserver(Context context, Preferences.PreferencesObserver preferencesObserver) { 
  124.         initPreference(context); 
  125.         mPreferencesObserver = preferencesObserver; 
  126.         preferences.registerObserver(mPreferencesObserver); 
  127.     } 
  128.  
  129.     public static void unregisterObserver() { 
  130.         if (mPreferencesObserver != null) { 
  131.             // 向preferences实例注销观察者 
  132.             preferences.unregisterObserver(mPreferencesObserver); 
  133.         } 
  134.     } 

2.3 第三方跳转

2.3.1 实现效果

#星光计划2.0# HarmonyOS 项目实战之通讯录(Java)-鸿蒙HarmonyOS技术社区

2.3.2 拨打电话与发送短信

  1. /** 
  2.      * 跳转系统短信 
  3.      */ 
  4.     private void doMessage(String telephone) { 
  5.         Intent intent = new Intent(); 
  6.         Operation operation = new Intent.OperationBuilder() 
  7. //                .withAction("android.intent.action.SENDTO") // Android写法 android.intent.action.SENDTO 
  8.                 .withAction(IntentConstants.ACTION_SEND_SMS) 
  9.                 .withUri(Uri.parse("smsto:" + telephone)) // 设置号码 
  10.                 .withFlags(Intent.FLAG_NOT_OHOS_COMPONENT) 
  11.                 .build(); 
  12.         intent.setOperation(operation); 
  13.         context.startAbility(intent, 11); 
  14.     } 
  15.  
  16.     /** 
  17.      * 申请拨打电话权限 
  18.      */ 
  19.     private boolean requestPermissions() { 
  20.         if (context.verifySelfPermission("android.permission.CALL_PHONE") != IBundleManager.PERMISSION_GRANTED) { 
  21.             // 应用未被授予权限 
  22.             if (context.canRequestPermission("android.permission.CALL_PHONE")) { 
  23.                 // 是否可以申请弹框授权(首次申请或者用户未选择禁止且不再提示) 
  24.                 context.requestPermissionsFromUser(new String[]{"android.permission.CALL_PHONE"}, 100); 
  25.             } 
  26.             return false
  27.         } else { 
  28.             // 权限已被授予 
  29.             return true
  30.         } 
  31.     } 
  32.  
  33.     /** 
  34.      * 直接拨打电话 
  35.      * 需要申请权限 
  36.      */ 
  37.     private void doCall(String destinationNum) { 
  38.         if (!requestPermissions()) { 
  39.             return
  40.         } 
  41.         Intent intent = new Intent(); 
  42.         Operation operation = new Intent.OperationBuilder() 
  43.                 .withAction("android.intent.action.CALL") // 系统应用拨号盘 
  44.                 .withUri(Uri.parse("tel:" + destinationNum)) // 设置号码 
  45.                 .withFlags(2) 
  46.                 .build(); 
  47.         intent.setOperation(operation); 
  48.         // 启动Ability 
  49.         context.startAbility(intent, 10); 
  50.  
  51.     } 
  52.  
  53.     /** 
  54.      * 跳转系统拨打电话界面 
  55.      */ 
  56.     private void doDial(String destinationNum) { 
  57.         Intent intent = new Intent(); 
  58.         Operation operation = new Intent.OperationBuilder() 
  59.                 .withAction(IntentConstants.ACTION_DIAL) // 系统应用拨号盘 
  60. //                .withBundleName(context.getCallingBundle()) // 应用拨号选择器 
  61.                 .withUri(Uri.parse("tel:" + destinationNum)) // 设置号码 
  62.                 .withFlags(2) 
  63.                 .build(); 
  64.         intent.setOperation(operation); 
  65.         // 启动Ability 
  66.         context.startAbility(intent, 10); 
  67.  
  68.     } 

2.4 JS服务卡片

2.4.1 实现效果

#星光计划2.0# HarmonyOS 项目实战之通讯录(Java)-鸿蒙HarmonyOS技术社区

2.4.2 创建卡片模板

使用DevEco Studio创建卡片工程

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

  1. "js": [ 
  2.   { 
  3.     "pages": [ 
  4.       "pages/index/index" 
  5.     ], 
  6.     "name""widget"
  7.     "window": { 
  8.       "designWidth": 720, 
  9.       "autoDesignWidth"true 
  10.     }, 
  11.     "type""form" 
  12.   } 

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

  1. "name""com.huhu.contact.ContactPersonAbility"
  2. "icon""$media:icon"
  3. "description""$string:contactpersonability_description"
  4. "formsEnabled"true
  5. "label""$string:contact_ContactPersonAbility"
  6. "type""page"
  7. "forms": [ 
  8.   { 
  9.     "jsComponentName""widget"
  10.     "isDefault"true
  11.     "scheduledUpdateTime""10:30"
  12.     "defaultDimension""2*2"
  13.     "name""widget"
  14.     "description""This is a service widget"
  15.     "colorMode""auto"
  16.     "type""JS"
  17.     "supportDimensions": [ 
  18.       "2*2" 
  19.     ], 
  20.     "updateEnabled"true
  21.     "updateDuration": 1 
  22.   } 

 创建一个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 卡片数据绑定

  1. @Override 
  2.  public ProviderFormInfo bindFormData() { 
  3.      HiLog.info(TAG, "bind form data"); 
  4.      ProviderFormInfo providerFormInfo = new ProviderFormInfo(); 
  5.      String urgentPersonStr = PreferenceUtils.getString(context, "urgentPerson"""); 
  6.      ZSONObject zsonObject = ZSONObject.stringToZSON(urgentPersonStr); 
  7.      if (dimension == DEFAULT_DIMENSION_2X2) { 
  8.          if (zsonObject != null) { 
  9.              providerFormInfo.setJsBindingData(new FormBindingData(zsonObject)); 
  10.          } 
  11.      } 
  12.      return providerFormInfo; 
  13.  } 

 2.4.4 卡片数据更新

  1. public void confirmUpdateForm(FormBindingData formBindingData) { 
  2.     FormControllerManager formControllerManager = FormControllerManager.getInstance(this); 
  3.     List<Long> allFormIdFromSharePreference = formControllerManager.getAllFormIdFromSharePreference(); 
  4.     if (allFormIdFromSharePreference == null || allFormIdFromSharePreference.isEmpty()) return
  5.     Long formId = allFormIdFromSharePreference.get(0); 
  6.     try { 
  7.         updateForm(formId,formBindingData); 
  8.     } catch (FormException e) { 
  9.         e.printStackTrace(); 
  10.     } 

 2.4.5 卡片事件处理

  1.   "data": { 
  2.     "text_content""Name"
  3.     "cardPrimaryText""Contacts"
  4.     "cardSecondaryText""+8612345678912"
  5.     "urgent1""无"
  6.     "urgent2""无"
  7.     "urgentPhone1""+8612345678912"
  8.     "urgentPhone2""+8612345678915" 
  9.   }, 
  10.   "actions": { 
  11.     "urgentCall1": { 
  12.       "action""message"
  13.       "params": { 
  14.         "action""urgentCall1"
  15.         "phoneNumber""10086" 
  16.       } 
  17.     }, 
  18.     "urgentCall2": { 
  19.       "action""message"
  20.       "params": { 
  21.         "action""urgentCall2"
  22.         "phoneNumber""15565339857" 
  23.       } 
  24.     }, 
  25.     "startMainRouter": { 
  26.       "action""router"
  27.       "abilityName""com.huhu.contact.ContactPersonAbility" 
  28.     } 
  29.   } 

卡片支持触发事件,覆写onTriggerFormEvent方法实现对事件的触发,doCall就是前面的播打电话的方法

  1. @Override 
  2. protected void onTriggerFormEvent(long formId, String message) { 
  3.     super.onTriggerFormEvent(formId, message); 
  4.     HiLog.info(loglabel, "onTriggerFormEvent: " + message); 
  5.     FormControllerManager formControllerManager = FormControllerManager.getInstance(this); 
  6.     FormController formController = formControllerManager.getController(formId); 
  7.     formController.onTriggerFormEvent(formId, message); 
  8.     ZSONObject params = ZSONObject.stringToZSON(message); 
  9.     String action = params.getString("action"); 
  10.     String phoneNumber = params.getString("phoneNumber"); 
  11.     HiLog.info(loglabel, "onTriggerFormEvent: action:" + action); 
  12.  
  13.     String urgentPersonStr = PreferenceUtils.getString(this, "urgentPerson"""); 
  14.     ZSONObject zsonObject = ZSONObject.stringToZSON(urgentPersonStr); 
  15.     switch (action) { 
  16.         case "urgentCall1"
  17.             String urgentPhone1 = zsonObject.getString("urgentPhone1"); 
  18.             doCall(urgentPhone1); 
  19.             break; 
  20.         case "urgentCall2"
  21.             String urgentPhone2 = zsonObject.getString("urgentPhone2"); 
  22.             doCall(urgentPhone2); 
  23.             break; 
  24.         default
  25.             break; 
  26.     } 

3 注意事项

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

  • 滑动时,上滑头部联动效果,索引有时会错乱;
  • 搜索功能未实现;
  • 紧急联系人没有和列表数据联动。

4 总结

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

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-12-10 10:12:44

鸿蒙HarmonyOS应用

2011-09-05 14:08:21

微信Andriod安卓

2014-09-24 10:29:14

微信企业号开发

2011-09-16 14:05:42

Andrioid应用iPhone应用Symbian应用

2014-09-28 22:30:13

微信企业号

2012-02-09 09:10:44

Path通讯录隐私

2014-09-28 22:26:11

微信企业号

2010-06-18 22:42:42

智能手机平台Android网秦

2013-03-18 10:19:27

安卓软件手机通讯录隐私信息

2012-06-05 13:53:03

天天联系华为

2010-09-08 23:11:01

2012-12-21 14:51:52

手机中国

2010-09-26 08:35:01

火种通讯录

2011-07-20 08:49:24

jQuery MobiAndroid

2010-05-12 14:42:20

2015-07-30 15:58:15

EC企信企业即时通讯

2011-08-12 10:16:10

iPhone通讯录联系人

2012-01-09 16:43:13

点心通讯录

2011-11-28 14:37:32

点心通讯录

2017-09-06 14:51:21

Swift
点赞
收藏

51CTO技术栈公众号