[文章]拥有只属于你的音乐app,是一种什么体验?

阅读量0
0
1
不知道有没有同学遇到过这种场景,和好朋友一起出去游玩时,坐在地铁或公交车上,每当听到一首好听的歌曲,总是忍不住和身边的伙伴一起分享,于是和同学每人戴着一只耳机共用一只手机听,这种方式难免会影响听歌的质感。在这种情况下,想到了使用服务流转开发一款“一起听吧”app,这样就可以和身边的朋友一起听好听的音乐了,可以同时间体会到的高低起伏优美动听的音乐,还能进行歌曲切换,是不是还挺不错。

言归正传,接下来就开始介绍“一起听吧”应用开发的功能。 该应用使用服务流转方式实现,流转:在HarmonyOS中泛指多设备分布式操作。流转能力打破设备界限,多设备联动,使用户应用程序可分可合、可流转。流转按照体验可分为跨端迁移(指在A端运行的FA迁移到B端上,完成迁移后, B端FA继续任务,而A端应用退出)和多端协同(指多端上的不同FA/PA同时运行、或者交替运行实现完整的业务)。可以说开发者站在了巨人的肩膀上创造更多可能,在此对HarmonyOS系统工程师们表示最真挚的崇拜。

开发准备
  • 万丈高楼平地起, 开始前请参考下载与安装软件、配置开发环境,完成DevEco Studio的安装和开发环境配置。
  • 开发环境配置完成后,请参考创建和运行Hello World创建工程
接下来就从创建工程项目开始介绍

创建一个新工程
  • 打开DevEco Studio,在欢迎页点击Create Project,创建一个新工程。
  • 根据工程创建向导,选择需要的Ability工程模板,然后点击Next。关于工程模板的介绍和支持的设备类型,请参考工程模板和开发语言介绍。



填写工程相关信息,Device Type选择Phone,Language选择Java ,其他保持默认值即可,点击Finish。关于各个参数的详细介绍,请参考创建一个新的工程。



工程创建完成后,DevEco Studio会自动进行工程的同步

使用模拟器运行HelloWorld
DevEco Studio提供远程模拟器和本地模拟器,本示例以远程模拟器为例进行说明。关于本地模拟器的使用请参考1.6.1-使用Local Emulator运行应用。

DevEco Studio提供模拟器供开发者运行和调试HarmonyOS应用。

  • 在DevEco Studio菜单栏,点击Tools > Device Manager。
  • 在Remote Emulator页签中点击Login,在浏览器中弹出华为开发者联盟帐号登录界面,请输入已实名认证的华为开发者联盟帐号的用户名和密码进行登录(查看远程模拟器登录常见问题)。
  • 登录后,请点击界面的允许按钮进行授权。


  • 在设备列表中,选择Phone设备P40,并点击▶按钮,运行模拟器(此处选择Super Device)


  • 点击DevEco Studio工具栏中的▶按钮运行工程,或使用默认快捷键Shift+F10(Mac为Control+R)运行工程


  • DevEco Studio会启动应用的编译构建,完成后应用即可运行在模拟器上


编写第一个页面
在Java UI框架中,提供了两种编写布局的方式:在XML中声明UI布局和在代码中创建布局。这两种方式创建出的布局没有本质差别,为了熟悉两种方式,我们将通过XML的方式编写第一个页面。

  • 在Project窗口,点击“entry > src > main > resources > base > layout”,打开“ability_main.xml”文件。


  • 第一个页面内有一个HarmonyOS图标和一个按钮,使用DependentLayout布局,通过DependentLayout 和Button组件来实现,其中vp和fp分别表示虚拟像素和字体像素。“ability_main.xml”的示例代码如下:
  1. <DirectionalLayout
  2.     xmlns:ohos="http://schemas.huawei.com/res/ohos"
  3.     ohos:height="match_parent"
  4.     ohos:width="match_parent"
  5.     ohos:orientation="vertical"
  6.     ohos:background_element="$media:black">
  7.     <DirectionalLayout
  8.         ohos:height="360vp"
  9.         ohos:width="380vp"
  10.         ohos:alignment="center"
  11.         ohos:top_margin="100vp"
  12.         ohos:background_element="$media:harmonyos_logo">
  13.     </DirectionalLayout>

  14.     <DirectionalLayout
  15.         ohos:id="$+id:music_play_control_container"
  16.         ohos:weight="1"
  17.         ohos:height="0vp"
  18.         ohos:width="match_parent"
  19.         ohos:alignment="center"
  20.         ohos:bottom_margin="80vp"
  21.         ohos:start_padding="10vp"
  22.         ohos:end_padding="10vp"
  23.         ohos:top_margin="100vp">
  24.         <Button
  25.             ohos:id="$+id:btn_music"
  26.             ohos:height="100vp"
  27.             ohos:width="300vp"
  28.             ohos:background_element="$graphic:background_button"
  29.             ohos:text_color="$color:white"
  30.             ohos:layout_alignment="horizontal_center"
  31.             ohos:text_alignment="center"
  32.             ohos:left_padding="15vp"
  33.             ohos:right_padding="15vp"
  34.             ohos:text="$string:btn_into_page"
  35.             ohos:text_size="30vp"
  36.             ohos:top_margin="20vp">
  37.         </Button>

  38.     </DirectionalLayout>

  39. </DirectionalLayout
复制代码

  • 页面预览效果:


  • 在XML文件中添加组件后,需要在Java代码中加载XML布局。在Project窗口,选择“entry > src > main > java > com.luxiaolu.musictogether > slice” ,打开“MainAbilitySlice.java”文件,使用setUIContent方法加载“ability_main.xml”布局,同时在该Ability中检查相关权限,MainAbilitySlice.java源码如下:
  1. public class MainAbilitySlice extends AbilitySlice {
  2.     private static final String TAG = CommonData.TAG + MainAbilitySlice.class.getSimpleName();

  3.     private static final int PERMISSION_CODE = 10000000;

  4.     @Override
  5.     public void onStart(Intent intent) {
  6.         super.onStart(intent);
  7.         super.setUIContent(ResourceTable.Layout_ability_main);
  8.         grantPermission();
  9.         initView();
  10.     }

  11.     void grantPermission() {
  12.         if (verifySelfPermission(DISTRIBUTED_DATASYNC) != IBundleManager.PERMISSION_GRANTED) {
  13.             if (canRequestPermission(DISTRIBUTED_DATASYNC)) {
  14.                 requestPermissionsFromUser(new String[] {DISTRIBUTED_DATASYNC}, PERMISSION_CODE);
  15.             }
  16.         }
  17.     }

  18.     private void initView() {
  19.         findComponentById(ResourceTable.Id_btn_music).setClickedListener(new ButtonClick());
  20.     }

  21.     private void TogetherMusic() {
  22.         LogUtil.info(TAG, "Click ResourceTable Id_togetherMusic");
  23.         Intent musicIntent = new Intent();
  24.         Operation operationMusic = new Intent.OperationBuilder().withBundleName(getBundleName())
  25.                 .withAbilityName(CommonData.ABILITY_MAIN)
  26.                 .withAction(CommonData.MUSIC_PAGE)
  27.                 .build();
  28.         musicIntent.setOperation(operationMusic);
  29.         startAbility(musicIntent);
  30.     }
  31.    
  32.     /**
  33.      * ButtonClick
  34.      */
  35.     private class ButtonClick implements Component.ClickedListener {
  36.         @Override
  37.         public void onClick(Component component) {
  38.             int btnId = component.getId();
  39.             switch (btnId) {
  40.                 case ResourceTable.Id_btn_music:
  41.                     TogetherMusic();
  42.                     break;
  43.                 default:
  44.                     LogUtil.info(TAG, "Click default");
  45.                     break;
  46.             }
  47.         }
  48.     }
  49. }
复制代码

5.第二个页面实现音乐播放页面,创建顺序和第一个页面方式相同,可以使用复制修改的方式添加,依然使用DependentLayout布局,通过Text和Image,RoundProgressBar,Slider来实现, ability_music.xml 代码如下:

  1. <DirectionalLayout
  2.     xmlns:ohos="http://schemas.huawei.com/res/ohos"
  3.     ohos:id="$+id:play_music_root"
  4.     ohos:height="match_parent"
  5.     ohos:width="match_parent"
  6.     ohos:background_element="$media:black2"
  7.     ohos:orientation="vertical">

  8.     <DirectionalLayout
  9.         ohos:height="match_content"
  10.         ohos:width="match_parent"
  11.         ohos:alignment="center"
  12.         ohos:orientation="vertical"
  13.         ohos:top_margin="50vp">

  14.         <Text
  15.             ohos:id="$+id:music_name"
  16.             ohos:height="match_content"
  17.             ohos:width="match_content"
  18.             ohos:bottom_margin="10vp"
  19.             ohos:text="$string:video_name"
  20.             ohos:text_alignment="center"
  21.             ohos:text_color="#FFFFFF"
  22.             ohos:text_size="34fp"
  23.             />
  24.     </DirectionalLayout>

  25.     <DirectionalLayout
  26.         ohos:height="0vp"
  27.         ohos:width="match_parent"
  28.         ohos:alignment="center"
  29.         ohos:weight="1">

  30.         <Image
  31.             ohos:id="$+id:music_posters"
  32.             ohos:height="match_content"
  33.             ohos:image_src="$media:cd_music2"
  34.             ohos:width="match_content"
  35.             ohos:visibility="hide"
  36.             />

  37.         <RoundProgressBar
  38.             ohos:id="$+id:progressCircularImage"
  39.             ohos:height="250vp"
  40.             ohos:width="250vp"
  41.             ohos:progress="1"
  42.             ohos:progress_width="0vp"
  43.             ohos:progress_color="$color:default_transparent"
  44.             ohos:background_element="$media:cd_music1"
  45.             ohos:max="100"
  46.             ohos:min="0"
  47.             />
  48.     </DirectionalLayout>

  49.     <DirectionalLayout
  50.         ohos:id="$+id:progress_container"
  51.         ohos:height="match_content"
  52.         ohos:width="match_parent"
  53.         ohos:start_margin="20vp"
  54.         ohos:orientation="vertical"
  55.         ohos:end_margin="20vp"
  56.         ohos:top_margin="37.25vp">

  57.         <DirectionalLayout
  58.             ohos:height="match_parent"
  59.             ohos:width="match_parent"
  60.             ohos:orientation="horizontal">

  61.             <Text
  62.                 ohos:id="$+id:play_progress_time"
  63.                 ohos:height="match_content"
  64.                 ohos:width="match_content"
  65.                 ohos:layout_alignment="vertical_center"
  66.                 ohos:end_margin="10vp"
  67.                 ohos:start_margin="0vp"
  68.                 ohos:text="$string:play_time"
  69.                 ohos:text_alignment="center"
  70.                 ohos:text_color="#FFCCE7FF"
  71.                 ohos:text_size="13vp"/>

  72.             <Text
  73.                 ohos:height="match_content"
  74.                 ohos:width="0vp"
  75.                 ohos:weight="1"/>

  76.             <Text
  77.                 ohos:id="$+id:play_total_time"
  78.                 ohos:height="match_content"
  79.                 ohos:width="match_content"
  80.                 ohos:layout_alignment="vertical_center"
  81.                 ohos:end_margin="10vp"
  82.                 ohos:start_margin="0vp"
  83.                 ohos:text="$string:play_time"
  84.                 ohos:text_alignment="center"
  85.                 ohos:text_color="#FFCCE7FF"
  86.                 ohos:text_size="13vp"/>
  87.         </DirectionalLayout>

  88.         <Slider
  89.             ohos:id="$+id:play_progress_bar"
  90.             ohos:height="14vp"
  91.             ohos:width="match_parent"
  92.             ohos:background_instruct_element="#64CCE7FF"
  93.             ohos:layout_alignment="vertical_center"
  94.             ohos:progress_color="#FFCCE7FF"
  95.             ohos:weight="1"/>

  96.     </DirectionalLayout>

  97.     <DirectionalLayout
  98.         ohos:id="$+id:music_play_control_container"
  99.         ohos:height="72vp"
  100.         ohos:width="match_parent"
  101.         ohos:bottom_margin="80vp"
  102.         ohos:start_padding="10vp"
  103.         ohos:orientation="horizontal"
  104.         ohos:end_padding="10vp"
  105.         ohos:top_margin="40vp">

  106.         <DirectionalLayout
  107.             ohos:id="$+id:control_box"
  108.             ohos:height="match_content"
  109.             ohos:width="match_content"
  110.             ohos:layout_alignment="center"
  111.             ohos:orientation="vertical"
  112.             ohos:weight="1">

  113.             <Image
  114.                 ohos:id="$+id:remote_play"
  115.                 ohos:height="48vp"
  116.                 ohos:width="48vp"
  117.                 ohos:background_element="$graphic:button_bg"
  118.                 ohos:image_src="$media:remote_play_selected"
  119.                 ohos:layout_alignment="center"
  120.                 ohos:scale_mode="inside"/>
  121.         </DirectionalLayout>

  122.         <DirectionalLayout
  123.             ohos:id="$+id:control_box2"
  124.             ohos:height="match_content"
  125.             ohos:width="match_content"
  126.             ohos:layout_alignment="center"
  127.             ohos:orientation="vertical"
  128.             ohos:weight="1">

  129.             <Image
  130.                 ohos:id="$+id:music_play_prev_btn"
  131.                 ohos:height="48vp"
  132.                 ohos:width="48vp"
  133.                 ohos:background_element="$graphic:button_bg"
  134.                 ohos:image_src="$media:ic_himusic_previous"
  135.                 ohos:layout_alignment="center"
  136.                 ohos:scale_mode="inside"/>
  137.         </DirectionalLayout>

  138.         <DirectionalLayout
  139.             ohos:id="$+id:control_box3"
  140.             ohos:height="match_content"
  141.             ohos:width="match_content"
  142.             ohos:layout_alignment="center"
  143.             ohos:orientation="vertical"
  144.             ohos:weight="1">

  145.             <Image
  146.                 ohos:id="$+id:music_play_btn"
  147.                 ohos:height="48vp"
  148.                 ohos:width="48vp"
  149.                 ohos:background_element="$graphic:button_bg"
  150.                 ohos:image_src="$media:ic_himusic_play"
  151.                 ohos:layout_alignment="center"
  152.                 ohos:scale_mode="inside"/>
  153.         </DirectionalLayout>

  154.         <DirectionalLayout
  155.             ohos:id="$+id:control_box4"
  156.             ohos:height="match_content"
  157.             ohos:width="match_content"
  158.             ohos:layout_alignment="center"
  159.             ohos:orientation="vertical"
  160.             ohos:weight="1">

  161.             <Image
  162.                 ohos:id="$+id:music_play_next_btn"
  163.                 ohos:height="48vp"
  164.                 ohos:width="48vp"
  165.                 ohos:background_element="$graphic:button_bg"
  166.                 ohos:image_src="$media:ic_himusic_next"
  167.                 ohos:layout_alignment="center"
  168.                 ohos:scale_mode="inside"/>
  169.         </DirectionalLayout>

  170.     </DirectionalLayout>
  171. </DirectionalLayout>
复制代码

  • 预览效果图:


  • MusicAbilitySlice.java是业务逻辑主代码,实现多设备连接,音乐播放/暂停,上下歌曲切换和音乐播放时动态图片旋转效果实现功能1)、 查找在线设备,并选择设备连接
  1. <pre class="public-DraftStyleDefault-pre" data-offset-key="biqkm-0-0"><pre class="Editable-styled" data-block="true" data-editor="2i0lu" data-offset-key="biqkm-0-0"><div data-offset-key="biqkm-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="biqkm-0-0"><span data-text="true">/**
  2. * 播放音乐时的动画效果(定时循环旋转图片)
  3. */
  4. private RoundProgressBar roundProgressBar;
  5. private PixelMapElement element;
  6. private int rotateDegrees =0;
  7. private int progressValue = 0;
  8. private int maxProgressVale = 100;

  9. private void initMyImage(){
  10.     roundProgressBar = (RoundProgressBar) findComponentById(ResourceTable.Id_progressCircularImage);
  11.     roundProgressBar.setMaxValue(maxProgressVale);
  12.     Timer timer = new Timer();
  13.     timer.schedule(new TimerTask(){
  14.         @Override
  15.         public void run() {
  16.             if(isPlaying){
  17.                 // 背景图片旋转角度
  18.                 if(rotateDegrees<360){
  19.                     // 每秒旋转角度
  20.                     rotateDegrees +=10;
  21.                 }else{
  22.                     rotateDegrees =0;
  23.                 }
  24.                 // 进度变化
  25.                 if(maxProgressVale>progressValue){
  26.                     progressValue +=10;
  27.                 }else {
  28.                     progressValue = 0;
  29.                 }
  30.                 element =  new PixelMapElement(transIdToPixelMap(rotateDegrees, resoureId,250,250));
  31.                 element.setFilterPixelMap(true);
  32.                 getUITaskDispatcher().asyncDispatch(new Runnable() {
  33.                     @Override
  34.                     public void run() {
  35.                         // 设置进度
  36.                         roundProgressBar.setProgressValue(progressValue);
  37.                         // 进度条背景图片
  38.                         roundProgressBar.setBackground(element);
  39.                     }
  40.                 });
  41.             }
  42.         }

  43.     },0,200);
  44. }

  45. // 将本地图片resId转换成PixelMap
  46. private PixelMap transIdToPixelMap(int rotateDegrees, int resId, int width, int height) {
  47.     InputStream source = null;
  48.     ImageSource imageSource = null;
  49.     try {
  50.         source = getContext().getResourceManager().getResource(resId);
  51.         imageSource = ImageSource.create(source, null);
  52.         ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();
  53.         decodingOpts.desiredSize = new Size(width, height);
  54.         decodingOpts.rotateDegrees = rotateDegrees;
  55.         return imageSource.createPixelmap(decodingOpts);

  56.     } catch (IOException | NotExistException e) {
  57.         LogUtil.error(TAG, "getPixelMap error");
  58.     } finally {
  59.         try {
  60.             source.close();
  61.         } catch (IOException e) {
  62.             LogUtil.error(TAG, "getPixelMap source close error");
  63.         }
  64.     }
  65.     return PixelMap.create(null);
  66. }</span></span></div></pre></pre><div class="Editable-unstyled" data-block="true" data-editor="2i0lu" data-offset-key="dnofi-0-0"><div data-offset-key="dnofi-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="dnofi-0-0"><span data-text="true">2)、</span></span><span data-offset-key="dnofi-1-0"><span data-text="true">启动远程FA/PA</span></span><span data-offset-key="dnofi-2-0"><span data-text="true"> 设备A连接设备B侧的PA,利用连接关系调用该PA执行特定任务</span></span></div></div><pre class="public-DraftStyleDefault-pre" data-offset-key="cvbo4-0-0"><pre class="Editable-styled" data-block="true" data-editor="2i0lu" data-offset-key="cvbo4-0-0"><div data-offset-key="cvbo4-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="cvbo4-0-0"><span data-text="true">private void connectRemotePa(String deviceId, int requestType) {
  67.     if (!deviceId.isEmpty()) {
  68.         Intent connectPaIntent = new Intent();
  69.         Operation operation = new Intent.OperationBuilder().withDeviceId(deviceId)
  70.                 .withBundleName(getBundleName())
  71.                 .withAbilityName(CommonData.MUSIC_SERVICE_NAME)
  72.                 .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
  73.                 .build();
  74.         connectPaIntent.setOperation(operation);
  75.         conn = new IAbilityConnection() {
  76.             @Override
  77.             public void onAbilityConnectDone(ElementName elementName, IRemoteObject remote, int resultCode) {
  78.                 LogUtil.info(TAG, "onAbilityConnectDone......");
  79.                 connectAbility(elementName, remote, requestType);
  80.             }
  81.             @Override
  82.             public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {
  83.                 disconnectAbility(this);
  84.                 LogUtil.info(TAG, "onAbilityDisconnectDone......");
  85.             }
  86.         };
  87.         isConnected = getContext().connectAbility(connectPaIntent, conn);
  88.     }
  89. }

  90. private void connectAbility(ElementName elementName, IRemoteObject remote, int requestType) {
  91.     proxy = new MusicRemoteProxy(remote);
  92.     try {
  93.         proxy.senDataToRemote(requestType);
  94.     } catch (RemoteException e) {
  95.         LogUtil.error(TAG, "onAbilityConnectDone RemoteException");
  96.     }
  97. }</span></span></div></pre></pre><div class="Editable-unstyled" data-block="true" data-editor="2i0lu" data-offset-key="a141v-0-0"><div data-offset-key="a141v-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="a141v-0-0"><span data-text="true">3)、在本地发起连接侧和对端被连接侧分别实现代理</span></span></div></div><pre class="public-DraftStyleDefault-pre" data-offset-key="16s8a-0-0"><pre class="Editable-styled" data-block="true" data-editor="2i0lu" data-offset-key="16s8a-0-0"><div data-offset-key="16s8a-0-0" class="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"><span data-offset-key="16s8a-0-0"><span data-text="true">class MusicRemoteProxy implements IRemoteBroker {
  98.     private static final int ERR_OK = 0;
  99.     private static final int REQUEST_START_ABILITY = 1;
  100.     private static final int REQUEST_SEND_DATA = 2;
  101.     private final IRemoteObject remote;

  102.     MusicRemoteProxy(IRemoteObject remote) {
  103.         this.remote = remote;
  104.     }

  105.     @Override
  106.     public IRemoteObject asObject() {
  107.         return remote;
  108.     }

  109.     private void senDataToRemote(int requestType) throws RemoteException {
  110.         MessageParcel data = MessageParcel.obtain();
  111.         MessageParcel reply = MessageParcel.obtain();
  112.         try {
  113.             isLocal = false;
  114.             data.writeInt(actionFlag);
  115.             data.writeString(localDeviceId);
  116.             data.writeBoolean(isLocal);
  117.             MessageOption option = new MessageOption(MessageOption.TF_SYNC);
  118.             remote.sendRequest(requestType, data, reply, option);
  119.             LogUtil.info(TAG, "send action: "+actionFlag+"; isLocal: "+isLocal);
  120.             int ec = reply.readInt();
  121.             if (ec != ERR_OK) {
  122.                 LogUtil.error(TAG, "ec != ERR_OK RemoteException");
  123.             }
  124.         } catch (RemoteException e) {
  125.             LogUtil.error(TAG, "RemoteException");
  126.         } finally {
  127.             data.reclaim();
  128.             reply.reclaim();
  129.         }
  130.     }
  131. }</span></span></div></pre></pre>
复制代码

4)、 订阅事件(每个应用都可以订阅自己感兴趣的公共事件,订阅成功后且公共事件发布后,系统会把其发送给应用。这些公共事件可能来自系统、其他应用和应用自身)

公共事件相关基础类包含CommonEventData、CommonEventPublishInfo、CommonEventSubscribeInfo、CommonEventSubscriber和CommonEventManager

  1. /**
  2. * 订阅 接收事件
  3. */
  4. private void subscribe() {
  5.     MatchingSkills matchingSkills = new MatchingSkills();
  6.     matchingSkills.addEvent(CommonData.MUSIC_PALY_EVENT);
  7.     matchingSkills.addEvent(CommonEventSupport.COMMON_EVENT_SCREEN_ON);
  8.     CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills);
  9.     subscriber = new MyCommonEventSubscriber(subscribeInfo);
  10.     try {
  11.         LogUtil.info(TAG, "subscribeCommonEvent");
  12.         CommonEventManager.subscribeCommonEvent(subscriber);
  13.     } catch (RemoteException e) {
  14.         LogUtil.error(TAG, "subscribeCommonEvent occur exception.");
  15.     }
  16. }

  17. /**
  18. * 订阅公共事件
  19. */
  20. class MyCommonEventSubscriber extends CommonEventSubscriber {
  21.     MyCommonEventSubscriber(CommonEventSubscribeInfo info) {
  22.         super(info);
  23.     }

  24.     @Override
  25.     public void onReceiveEvent(CommonEventData commonEventData) {
  26.         reciveTime = System.currentTimeMillis();
  27.         LogUtil.info(TAG, "onReceiveEvent, sendTime - reciveTime is " + (sendTime - reciveTime));
  28.         if (Math.abs(sendTime - reciveTime) <= MAX_RECIVE_TIME) {
  29.             LogUtil.info(TAG, "almost at the same time, do not handle recive msg");
  30.             isShare = false;
  31.             new ToastDialog(getContext()).setText("操作冲突,后续独立").setAlignment(LayoutAlignment.CENTER).show();
  32.             return;
  33.         }
  34.         // 接收远程数据
  35.         Intent intent = commonEventData.getIntent();
  36.         updateDataInfo(intent);
  37.     }
  38. }

  39. private void updateDataInfo(Intent intent) {
  40.     if(null != intent){
  41.         actionFlag = intent.getIntParam(CommonData.KEY_MUSIC_ACTION_ID, 0);
  42.         getUITaskDispatcher().delayDispatch(this::actionFun, DELAY_TIME);
  43.     }
  44. }

  45. private void actionFun(){
  46.     if(100 == actionFlag){
  47.         playOrPause();
  48.     }else if(200 == actionFlag){
  49.         nextMusic();
  50.     }else if(300 == actionFlag){
  51.         prevMusic();
  52.     }
  53. }
复制代码

5)、 播放音乐时显示图片动画效果(暂时使用定时器,循环旋转图片)

  1. /**
  2. * 播放音乐时的动画效果(定时循环旋转图片)
  3. */
  4. private RoundProgressBar roundProgressBar;
  5. private PixelMapElement element;
  6. private int rotateDegrees =0;
  7. private int progressValue = 0;
  8. private int maxProgressVale = 100;

  9. private void initMyImage(){
  10.     roundProgressBar = (RoundProgressBar) findComponentById(ResourceTable.Id_progressCircularImage);
  11.     roundProgressBar.setMaxValue(maxProgressVale);
  12.     Timer timer = new Timer();
  13.     timer.schedule(new TimerTask(){
  14.         @Override
  15.         public void run() {
  16.             if(isPlaying){
  17.                 // 背景图片旋转角度
  18.                 if(rotateDegrees<360){
  19.                     // 每秒旋转角度
  20.                     rotateDegrees +=10;
  21.                 }else{
  22.                     rotateDegrees =0;
  23.                 }
  24.                 // 进度变化
  25.                 if(maxProgressVale>progressValue){
  26.                     progressValue +=10;
  27.                 }else {
  28.                     progressValue = 0;
  29.                 }
  30.                 element =  new PixelMapElement(transIdToPixelMap(rotateDegrees, resoureId,250,250));
  31.                 element.setFilterPixelMap(true);
  32.                 getUITaskDispatcher().asyncDispatch(new Runnable() {
  33.                     @Override
  34.                     public void run() {
  35.                         // 设置进度
  36.                         roundProgressBar.setProgressValue(progressValue);
  37.                         // 进度条背景图片
  38.                         roundProgressBar.setBackground(element);
  39.                     }
  40.                 });
  41.             }
  42.         }

  43.     },0,200);
  44. }

  45. // 将本地图片resId转换成PixelMap
  46. private PixelMap transIdToPixelMap(int rotateDegrees, int resId, int width, int height) {
  47.     InputStream source = null;
  48.     ImageSource imageSource = null;
  49.     try {
  50.         source = getContext().getResourceManager().getResource(resId);
  51.         imageSource = ImageSource.create(source, null);
  52.         ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();
  53.         decodingOpts.desiredSize = new Size(width, height);
  54.         decodingOpts.rotateDegrees = rotateDegrees;
  55.         return imageSource.createPixelmap(decodingOpts);

  56.     } catch (IOException | NotExistException e) {
  57.         LogUtil.error(TAG, "getPixelMap error");
  58.     } finally {
  59.         try {
  60.             source.close();
  61.         } catch (IOException e) {
  62.             LogUtil.error(TAG, "getPixelMap source close error");
  63.         }
  64.     }
  65.     return PixelMap.create(null);
  66. }
复制代码

  • 图片旋转效果:


  • MusicServiceAbility.java 基于Service模板的Ability主要用于后台运行任务(建立远程连接,发送事件数据),但不提供用户交互界面。Service可由其他应用或Ability启动,即使用户切换到其他应用,Service仍将在后台继续运行。了解更多创建、启动、连接Service
  1. public class MusicServiceAbility extends Ability {
  2.     private static final String TAG = "####service";

  3.     private TogetherMusicRemote remote = new TogetherMusicRemote();

  4.     @Override
  5.     public void onStart(Intent intent) {
  6.         super.onStart(intent);
  7.         LogUtil.info(TAG, "MusicServiceAbility::onStart");
  8.     }

  9.     @Override
  10.     public void onBackground() {
  11.         super.onBackground();
  12.         LogUtil.info(TAG, "MusicServiceAbility::onBackground");
  13.     }

  14.     @Override
  15.     public void onStop() {
  16.         super.onStop();
  17.         LogUtil.info(TAG, "MusicServiceAbility::onStop");
  18.     }

  19.     @Override
  20.     protected IRemoteObject onConnect(Intent intent) {
  21.         super.onConnect(intent);
  22.         return remote.asObject();
  23.     }

  24.     @Override
  25.     public void onDisconnect(Intent intent) {
  26.         LogUtil.info(TAG, "MusicServiceAbility::onDisconnect");
  27.     }


  28.     /**
  29.      * 发送 点击播放、暂停、下一首音乐切换动作指令
  30.      * [url=home.php?mod=space&uid=3142012]@param[/url] playAction 播放动作指令
  31.      */
  32.     private void sendEvent(int playAction) {
  33.         try {
  34.             Intent intent = new Intent();
  35.             Operation operation = new Intent.OperationBuilder().withAction(CommonData.MUSIC_PALY_EVENT).build();
  36.             intent.setOperation(operation);
  37.             intent.setParam(CommonData.KEY_MUSIC_ACTION_ID, playAction);
  38.             CommonEventData eventData = new CommonEventData(intent);
  39.             CommonEventManager.publishCommonEvent(eventData);
  40.         } catch (RemoteException e) {
  41.             LogUtil.error(TAG, "publishCommonEvent occur exception.");
  42.         }
  43.     }

  44.     /**
  45.      * 建立远程连接
  46.      */
  47.     public class TogetherMusicRemote extends RemoteObject implements IRemoteBroker {
  48.         private static final int ERR_OK = 0;

  49.         private static final int REQUEST_START_ABILITY = 1;

  50.         private TogetherMusicRemote() {
  51.             super("TogetherMusicRemote");
  52.         }

  53.         @Override
  54.         public IRemoteObject asObject() {
  55.             return this;
  56.         }

  57.         @Override
  58.         public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
  59.             String remoteDeviceId = data.readString();
  60.             int playAction = data.readInt();
  61.             boolean isLocal = data.readBoolean();
  62.             reply.writeInt(ERR_OK);
  63.             if (code == REQUEST_START_ABILITY) {
  64.                 Intent secondIntent = new Intent();
  65.                 Operation operation = new Intent.OperationBuilder().withDeviceId("")
  66.                         .withBundleName(getBundleName())
  67.                         .withAbilityName(CommonData.ABILITY_MAIN)
  68.                         .withAction(CommonData.MUSIC_PAGE)
  69.                         .build();
  70.                 secondIntent.setParam(CommonData.KEY_REMOTE_DEVICEID, remoteDeviceId);
  71.                 secondIntent.setParam(CommonData.KEY_MUSIC_ACTION_ID, playAction);
  72.                 secondIntent.setParam(CommonData.KEY_IS_LOCAL, isLocal);
  73.                 secondIntent.setOperation(operation);
  74.                 startAbility(secondIntent);
  75.             } else {
  76.                 sendEvent(playAction);
  77.             }
  78.             return true;
  79.         }
  80.     }
  81. }
复制代码

整体效果演示



希望有更多同学或朋友加入HarmonyOS, 提供更多新鲜的、新奇的idea。

作者简介:我是老王,一个从事鸿蒙开发的中年老吃货。关注我,每天和你聊点关于华为、鸿蒙认证的一些事儿。

回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友