HarmonyOS 分布式之仿抖音應用
項目介紹
使用Java UI開發分布式仿抖音應用,上下滑動切換視頻,評論功能,設備遷移功能:記錄播放的視頻頁和進度、評論數據。
效果演示
1.上下滑動切換視頻、點擊遷移圖標,彈框選擇在線的設備,完成視頻數據的遷移。
2.點擊評論圖標查看評論,編輯評論內容并發送。點擊遷移圖標,彈框選擇在線的設備,完成評論數據的遷移。
項目結構
主要代碼
1、上下滑動頁面
頁面切換用到系統組件PageSlider,默認左右切換,設置為上下方向: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);
- // 查找滑動頁面組件
- PageSlider pageSlider = (PageSlider) findComponentById(ResourceTable.Id_pageSlider);
- // 設置滑動方向為上下滑動
- pageSlider.setOrientation(Component.VERTICAL);
- // 集合測試數據
- List<String> listData=new ArrayList<>();
- listData.add("第一頁");
- listData.add("第二頁");
- listData.add("第三頁");
- // 設置頁面適配器
- pageSlider.setProvider(new PageSliderProvider() {
- /**
- * 獲取當前適配器中可用視圖的數量
- */
- @Override
- public int getCount() {
- return listData.size();
- }
- /**
- * 創建頁面
- */
- @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);
- // 設置數據
- textContent.setText(listData.get(position));
- // 添加到容器中
- container.addComponent(component);
- return component;
- }
- /**
- * 銷毀頁面
- */
- @Override
- public void destroyPageFromContainer(ComponentContainer container, int position, Object object) {
- // 從容器中移除
- container.removeComponent((Component) object);
- }
- /**
- * 檢查頁面是否與對象匹配
- */
- @Override
- public boolean isPageMatchToObject(Component page, Object object) {
- return true;
- }
- });
- // 添加頁面改變監聽器
- pageSlider.addPageChangedListener(new PageSlider.PageChangedListener() {
- /**
- * 頁面滑動時調用
- */
- @Override
- public void onPageSliding(int itemPos, float itemPosOffset, int itemPosOffsetPixels) {}
- /**
- * 當頁面滑動狀態改變時調用
- */
- @Override
- public void onPageSlideStateChanged(int state) {}
- /**
- * 選擇新頁面時回調
- */
- @Override
- public void onPageChosen(int itemPos) {
- // 在此方法下,切換頁面獲取當前頁面的視頻源,進行播放
- String data = listData.get(itemPos);
- }
- });
- }
- }
2、播放視頻
視頻播放使用Player,視頻畫面窗口顯示使用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);
- // 設置視頻窗口在頂層
- surfaceProvider.pinToZTop(true);
- // 設置視頻窗口操作監聽
- if (surfaceProvider.getSurfaceOps().isPresent()) {
- surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceOps.Callback() {
- /**
- * 創建視頻窗口
- */
- @Override
- public void surfaceCreated(SurfaceOps holder) {
- try {
- RawFileDescriptor fileDescriptor = getResourceManager().getRawFileEntry(videoPath).openRawFileDescriptor();
- Source source = new Source(fileDescriptor.getFileDescriptor(),
- fileDescriptor.getStartPosition(),
- fileDescriptor.getFileSize()
- );
- // 設置媒體文件
- mPlayer.setSource(source);
- // 設置播放窗口
- mPlayer.setVideoSurface(holder.getSurface());
- // 循環播放
- mPlayer.enableSingleLooping(true);
- // 準備播放環境并緩沖媒體數據
- mPlayer.prepare();
- // 開始播放
- mPlayer.play();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- /**
- * 視頻窗口改變
- */
- @Override
- public void surfaceChanged(SurfaceOps holder, int format, int width, int height) {}
- /**
- * 視頻窗口銷毀
- */
- @Override
- public void surfaceDestroyed(SurfaceOps holder) {}
- });
- }
- }
- @Override
- protected void onStop() {
- super.onStop();
- // 頁面銷毀,釋放播放器
- if (mPlayer != null) {
- mPlayer.stop();
- mPlayer.release();
- }
- }
- }
3、跨設備遷移示例
跨設備遷移使用IAbilityContinuation接口。
1、在entry下的config.json配置權限
- "reqPermissions": [
- {
- "name": "ohos.permission.DISTRIBUTED_DATASYNC"
- },
- {
- "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
- },
- {
- "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
- }
- ]
2、實現IAbilityContinuation接口,說明:一個應用可能包含多個Page,僅需要在支持遷移的Page中通過以下方法實現IAbilityContinuation接口。同時,此Page所包含的所有AbilitySlice也需要實現此接口。
- 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);
- // 申請權限
- 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);
- // 點擊遷移
- button.setClickedListener(component -> {
- // 查詢分布式網絡中所有在線設備(不包括本地設備)的信息。
- List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
- if (deviceList.size()>0) {
- // 啟動遷移,指定的設備ID
- continueAbility(deviceList.get(0).getDeviceId());
- }
- });
- // 顯示遷移的數據
- text.setText("遷移的數據:"+data);
- }
- /**
- * 啟動遷移時首次調用此方法
- * @return 是否進行遷移
- */
- @Override
- public boolean onStartContinuation() {
- return true;
- }
- /**
- * 遷移時存入數據
- */
- @Override
- public boolean onSaveData(IntentParams intentParams) {
- intentParams.setParam("data","測試數據");
- return true;
- }
- /**
- * 獲取遷移存入的數據,在生命周期的onStart之前執行
- */
- @Override
- public boolean onRestoreData(IntentParams intentParams) {
- data= (String) intentParams.getParam("data");
- return true;
- }
- /**
- * 遷移完成
- */
- @Override
- public void onCompleteContinuation(int i) {}
- }
根據上面的核心代碼示例,了解實現原理,接下來便可以結合實際需求完善功能了。