项目介绍
使用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);
}
});
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
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();
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
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"
}
]
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
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) {}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
根据上面的核心代码示例,了解实现原理,接下来便可以结合实际需求完善功能了。