项目介绍
使用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) {}
- }
根据上面的核心代码示例,了解实现原理,接下来便可以结合实际需求完善功能了。