HarmonyOS 分布式之仿抖音應(yīng)用
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
項(xiàng)目介紹
使用Java UI開(kāi)發(fā)分布式仿抖音應(yīng)用,上下滑動(dòng)切換視頻,評(píng)論功能,設(shè)備遷移功能:記錄播放的視頻頁(yè)和進(jìn)度、評(píng)論數(shù)據(jù)。
效果演示
1.上下滑動(dòng)切換視頻、點(diǎn)擊遷移圖標(biāo),彈框選擇在線的設(shè)備,完成視頻數(shù)據(jù)的遷移。
2.點(diǎn)擊評(píng)論圖標(biāo)查看評(píng)論,編輯評(píng)論內(nèi)容并發(fā)送。點(diǎn)擊遷移圖標(biāo),彈框選擇在線的設(shè)備,完成評(píng)論數(shù)據(jù)的遷移。
項(xiàng)目結(jié)構(gòu)
主要代碼
1、上下滑動(dòng)頁(yè)面
頁(yè)面切換用到系統(tǒng)組件PageSlider,默認(rèn)左右切換,設(shè)置為上下方向:setOrientation(Component.VERTICAL);
- import ohos.aafwk.ability.AbilitySlice;
- import ohos.aafwk.content.Intent;
- import ohos.agp.components.*;
- import java.util.ArrayList;
- import java.util.List;
- public class MainAbilitySlice extends AbilitySlice {
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(ResourceTable.Layout_ability_main);
- // 查找滑動(dòng)頁(yè)面組件
- PageSlider pageSlider = (PageSlider) findComponentById(ResourceTable.Id_pageSlider);
- // 設(shè)置滑動(dòng)方向?yàn)樯舷禄瑒?dòng)
- pageSlider.setOrientation(Component.VERTICAL);
- // 集合測(cè)試數(shù)據(jù)
- List<String> listData=new ArrayList<>();
- listData.add("第一頁(yè)");
- listData.add("第二頁(yè)");
- listData.add("第三頁(yè)");
- // 設(shè)置頁(yè)面適配器
- pageSlider.setProvider(new PageSliderProvider() {
- /**
- * 獲取當(dāng)前適配器中可用視圖的數(shù)量
- */
- @Override
- public int getCount() {
- return listData.size();
- }
- /**
- * 創(chuàng)建頁(yè)面
- */
- @Override
- public Object createPageInContainer(ComponentContainer container, int position) {
- // 查找布局
- Component component = LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_item_page, null, false);
- Text textContent = (Text) component.findComponentById(ResourceTable.Id_text_item_page_content);
- // 設(shè)置數(shù)據(jù)
- textContent.setText(listData.get(position));
- // 添加到容器中
- container.addComponent(component);
- return component;
- }
- /**
- * 銷(xiāo)毀頁(yè)面
- */
- @Override
- public void destroyPageFromContainer(ComponentContainer container, int position, Object object) {
- // 從容器中移除
- container.removeComponent((Component) object);
- }
- /**
- * 檢查頁(yè)面是否與對(duì)象匹配
- */
- @Override
- public boolean isPageMatchToObject(Component page, Object object) {
- return true;
- }
- });
- // 添加頁(yè)面改變監(jiān)聽(tīng)器
- pageSlider.addPageChangedListener(new PageSlider.PageChangedListener() {
- /**
- * 頁(yè)面滑動(dòng)時(shí)調(diào)用
- */
- @Override
- public void onPageSliding(int itemPos, float itemPosOffset, int itemPosOffsetPixels) {}
- /**
- * 當(dāng)頁(yè)面滑動(dòng)狀態(tài)改變時(shí)調(diào)用
- */
- @Override
- public void onPageSlideStateChanged(int state) {}
- /**
- * 選擇新頁(yè)面時(shí)回調(diào)
- */
- @Override
- public void onPageChosen(int itemPos) {
- // 在此方法下,切換頁(yè)面獲取當(dāng)前頁(yè)面的視頻源,進(jìn)行播放
- String data = listData.get(itemPos);
- }
- });
- }
- }
2、播放視頻
視頻播放使用Player,視頻畫(huà)面窗口顯示使用SurfaceProvider。
- import ohos.aafwk.ability.AbilitySlice;
- import ohos.aafwk.content.Intent;
- import ohos.agp.components.surfaceprovider.SurfaceProvider;
- import ohos.agp.graphics.SurfaceOps;
- import ohos.global.resource.RawFileDescriptor;
- import ohos.media.common.Source;
- import ohos.media.player.Player;
- import java.io.IOException;
- public class MainAbilitySlice extends AbilitySlice {
- // 視頻路徑
- private final String videoPath = "resources/rawfile/HarmonyOS.mp4";
- // 播放器
- private Player mPlayer;
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(ResourceTable.Layout_ability_main);
- // 初始化播放器
- mPlayer = new Player(getContext());
- // 查找視頻窗口組件
- SurfaceProvider surfaceProvider = (SurfaceProvider) findComponentById(ResourceTable.Id_surfaceProvider);
- // 設(shè)置視頻窗口在頂層
- surfaceProvider.pinToZTop(true);
- // 設(shè)置視頻窗口操作監(jiān)聽(tīng)
- if (surfaceProvider.getSurfaceOps().isPresent()) {
- surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceOps.Callback() {
- /**
- * 創(chuàng)建視頻窗口
- */
- @Override
- public void surfaceCreated(SurfaceOps holder) {
- try {
- RawFileDescriptor fileDescriptor = getResourceManager().getRawFileEntry(videoPath).openRawFileDescriptor();
- Source source = new Source(fileDescriptor.getFileDescriptor(),
- fileDescriptor.getStartPosition(),
- fileDescriptor.getFileSize()
- );
- // 設(shè)置媒體文件
- mPlayer.setSource(source);
- // 設(shè)置播放窗口
- mPlayer.setVideoSurface(holder.getSurface());
- // 循環(huán)播放
- mPlayer.enableSingleLooping(true);
- // 準(zhǔn)備播放環(huán)境并緩沖媒體數(shù)據(jù)
- mPlayer.prepare();
- // 開(kāi)始播放
- mPlayer.play();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- /**
- * 視頻窗口改變
- */
- @Override
- public void surfaceChanged(SurfaceOps holder, int format, int width, int height) {}
- /**
- * 視頻窗口銷(xiāo)毀
- */
- @Override
- public void surfaceDestroyed(SurfaceOps holder) {}
- });
- }
- }
- @Override
- protected void onStop() {
- super.onStop();
- // 頁(yè)面銷(xiāo)毀,釋放播放器
- if (mPlayer != null) {
- mPlayer.stop();
- mPlayer.release();
- }
- }
- }
3、跨設(shè)備遷移示例
跨設(shè)備遷移使用IAbilityContinuation接口。
1、在entry下的config.json配置權(quán)限
- "reqPermissions": [
- {
- "name": "ohos.permission.DISTRIBUTED_DATASYNC"
- },
- {
- "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
- },
- {
- "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
- }
- ]
2、實(shí)現(xiàn)IAbilityContinuation接口,說(shuō)明:一個(gè)應(yīng)用可能包含多個(gè)Page,僅需要在支持遷移的Page中通過(guò)以下方法實(shí)現(xiàn)IAbilityContinuation接口。同時(shí),此Page所包含的所有AbilitySlice也需要實(shí)現(xiàn)此接口。
- import ohos.aafwk.ability.AbilitySlice;
- import ohos.aafwk.ability.IAbilityContinuation;
- import ohos.aafwk.content.Intent;
- import ohos.aafwk.content.IntentParams;
- import ohos.agp.components.Button;
- import ohos.agp.components.Text;
- import ohos.bundle.IBundleManager;
- import ohos.distributedschedule.interwork.DeviceInfo;
- import ohos.distributedschedule.interwork.DeviceManager;
- import java.util.List;
- public class MainAbilitySlice extends AbilitySlice implements IAbilityContinuation {
- private String data = "";
- String PERMISSION = "ohos.permission.DISTRIBUTED_DATASYNC";
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(ResourceTable.Layout_ability_main);
- // 申請(qǐng)權(quán)限
- if (verifySelfPermission(PERMISSION) != IBundleManager.PERMISSION_GRANTED) {
- requestPermissionsFromUser(new String[]{PERMISSION}, 0);
- }
- Button button = (Button)findComponentById(ResourceTable.Id_button);
- Text text = (Text)findComponentById(ResourceTable.Id_text);
- // 點(diǎn)擊遷移
- button.setClickedListener(component -> {
- // 查詢分布式網(wǎng)絡(luò)中所有在線設(shè)備(不包括本地設(shè)備)的信息。
- List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
- if (deviceList.size()>0) {
- // 啟動(dòng)遷移,指定的設(shè)備ID
- continueAbility(deviceList.get(0).getDeviceId());
- }
- });
- // 顯示遷移的數(shù)據(jù)
- text.setText("遷移的數(shù)據(jù):"+data);
- }
- /**
- * 啟動(dòng)遷移時(shí)首次調(diào)用此方法
- * @return 是否進(jìn)行遷移
- */
- @Override
- public boolean onStartContinuation() {
- return true;
- }
- /**
- * 遷移時(shí)存入數(shù)據(jù)
- */
- @Override
- public boolean onSaveData(IntentParams intentParams) {
- intentParams.setParam("data","測(cè)試數(shù)據(jù)");
- return true;
- }
- /**
- * 獲取遷移存入的數(shù)據(jù),在生命周期的onStart之前執(zhí)行
- */
- @Override
- public boolean onRestoreData(IntentParams intentParams) {
- data= (String) intentParams.getParam("data");
- return true;
- }
- /**
- * 遷移完成
- */
- @Override
- public void onCompleteContinuation(int i) {}
- }
根據(jù)上面的核心代碼示例,了解實(shí)現(xiàn)原理,接下來(lái)便可以結(jié)合實(shí)際需求完善功能了。
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)