本文使用 Java UI 开发分布式仿抖音应用,上下滑动切换视频,评论功能,设备迁移功能:记录播放的视频页和进度、评论数据。
效果演示
①上下滑动切换视频、点击迁移图标,弹框选择在线的设备,完成视频数据的迁移。
②点击评论图标查看评论,编辑评论内容并发送。点击迁移图标,弹框选择在线的设备,完成评论数据的迁移。
项目结构
如下图:

主要代码
①上下滑动页面
页面切换用到系统组件 PageSlider:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-pageslider-0000001091933258
默认左右切换,设置为上下方向: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 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);
}
});
}
}
②播放视频
视频播放使用 Player:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/media-video-player-0000000000044178
视频画面窗口显示使用 SurfaceProvider:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/faq-media-0000001124842486#section0235506211
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();
}
}
}
③跨设备迁移示例
跨设备迁移使用 IAbilityContinuation 接口:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ability-page-cross-device-0000001051072880
在 entry 下的 config.json 配置权限:
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
},
{
"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
},
{
"name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
}
]
实现 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 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) {}
}
根据上面的核心代码示例,了解实现原理,接下来便可以结合实际需求完善功能了。
责任编辑:haq
全部0条评论
快来发表一下你的评论吧 !