HarmonyOS开发详解(五)——鸿蒙高级组件数据动态绑定案例实践
本文将专门花一篇来讲述ListContainer和PageSlider,ListContainer主要是参考官方例子,PageSlider在原有官方例子上进行了一些升级改造。原有例子添加固定的文字,通过改造动态读取本地json内容和本地图片资源。
本文将专门花一篇来讲述ListContainer和PageSlider,ListContainer主要是参考官方例子,PageSlider在原有官方例子上进行了一些升级改造。原有例子添加固定的文字,通过改造动态读取本地json内容和本地图片资源。
Java UI组件中有些需要动态绑定数据的组件,因为系统不了解你的样式(布局)和行为(方法),这就必须要自己按着规则定义,这就导致组件使用为略显复杂,接下来就只讲一下这些稍微复杂的组件。
1、 ListContainer(列表容器)
1、ListContainer实现思路分析
ListContainer列表容器,用来呈现多行连续的同类数据;导致ListContainer稍嫌复杂有两个原因:
1)列表中每项布局不确定,需要自定义布局(比如新闻每个列表项可能只有标题,也可能有标题、简介、图片,这会导致每行布局不一样)
2)内容不确定,动态绑定(新闻条数不确定、内容动态加载)
以上的不确定导致了每个布局需要自己定义,每个数据类型需要自己定义;
2、ListContainer实现步骤
1)定义ListContainer:layout下创建ListContainer组件
2)定义每项布局:layout下创建ListContainer的内容,即子布局,如果是带标题、简介、图片的新闻列表,就定义两个text和一个image。
3)定义数据实体类:每一条列表的具体组成java类
4)定义每条列表项的属性:通过实现BaseItemProvider类;因为每项列表内容不确定,通过重写getComponent方法,返回每项布局内容(第二步中的layerout是自定义且空的,这里定义布局和数据的组合方法)
5)组装ListContainer:通过BaseItemProvider的实现类将数据和ListContainer组合起来;
6)注册其他行为:比如定义点击事件、定义更新数据;
完整代码太长,结合上述步骤贴出核心代码,上部分布局类代码,下部分java类代码:
//第一步创建layerout下ListContainer布局页面
<ListContainer
ohos:id="$+id:list_container"
ohos:height="200vp"
ohos:width="350vp"
ohos:layout_alignment="horizontal_center"/>
//第二步创建子布局,这里用两个字段name和address来展示
<Text
ohos:id="$+id:item_index"
ohos:height="match_content"
ohos:width="match_content"
ohos:padding="4vp"
ohos:text="Item0"
ohos:text_size="20fp"
ohos:layout_alignment="center"/>
<Text
ohos:id="$+id:item_index1"
ohos:height="match_content"
ohos:width="match_content"
ohos:padding="4vp"
ohos:text="Item0"
ohos:text_size="20fp"
ohos:layout_alignment="left"/>
//第三步,创建ItemBean实体类
public class ItemBean {
private String name;
private String address;
public ItemBean(String name,String address) {
this.name = name;
this.address=address;
}
//属性get、set方法省略...
}
//第四步,实现BaseItemProvider类,必须实现以下override方法,这样系统就可以根据这些方法对listContainer内容进行组装,并提供一些行为(比如查询长度、返回ID)
public class ItemProvider extends BaseItemProvider {
private List<ItemBean> list;
private AbilitySlice abilitySlice;
public ItemProvider(List<ItemBean> list, AbilitySlice abilitySlice) {
this.list = list;
this.abilitySlice = abilitySlice;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
if(list!=null&&list.size()>0){
return list.get(i);
}
return null;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
Component temp;
if(component==null){
temp=LayoutScatter.getInstance(abilitySlice).parse(ResourceTable.Layout_list_container_item,null,false);
}else{
temp=component;
}
ItemBean itemBean= list.get(i);
Text text1=temp.findComponentById(ResourceTable.Id_item_index);
text1.setText(itemBean.getName());
Text text2 =temp.findComponentById(ResourceTable.Id_item_index1);
text2.setText(itemBean.getAddress());
return temp;
}
}
//第五步,在PageSlice里面根据ItemProvider组装ListContainer,构造一个长度20的列表
private void initListContainer() {
ListContainer listCon=findComponentById(ResourceTable.Id_list_container);
List<ItemBean> list=getData();
ItemProvider itemProvider=new ItemProvider(list,this);
listCon.setItemProvider(itemProvider);
}
private List<ItemBean> getData(){
List<ItemBean> list=new ArrayList<>();
for(int i=0;i<20;i++){
ItemBean itembean=new ItemBean("item"+i,"address"+i);
list.add(itembean);
}
return list;
}
//第六步,定义点击事件
listCon.setItemClickedListener((container, component, position, id)->{
ItemBean itemBean=(ItemBean)container.getItemProvider().getItem(position);
new ToastDialog(this)
.setText("你点击了"+itemBean.getName()+"__"+itemBean.getAddress())
.setAlignment(LayoutAlignment.HORIZONTAL_CENTER).show();
});
以上代码实现效果:
2、PageSlider(页面切换组件)
PageSlider可以通过滚动实现页面间内容切换,每个页面样式内容当然不确定;实现思路和ListContainer一模一样,这里就直接贴出核心代码。
为了练习,里面添加通过代码创建Text(内容)、image、Text(通过事件获取页码);
//第一步创建PageSlider
<PageSlider
ohos:id="$+id:page_slider"
ohos:height="300vp"
ohos:width="300vp"
ohos:layout_alignment="horizontal_center"/>
<!--页面导航,前期先不用
<PageSliderIndicator
ohos:id="$+id:indicator"
ohos:height="match_content"
ohos:width="match_content"
ohos:padding="8vp"
ohos:layout_alignment="horizontal_center"
ohos:top_margin="16vp"
ohos:background_element="#55FFC0CB"/>
-->
//第二步创建页面的子布局
<Text
ohos:id="$+id:item_title"
ohos:height="match_content"
ohos:width="match_content"
ohos:layout_alignment="top|center"
ohos:padding="4vp"
ohos:text="Item0"
ohos:text_size="20fp"/>
<Image
ohos:id="$+id:imageComponent"
ohos:height="200vp"
ohos:width="match_parent"
/>
<Text
ohos:id="$+id:item_page_num"
ohos:height="40vp"
ohos:width="80vp"
ohos:layout_alignment="bottom|center"
ohos:padding="4vp"
ohos:text="Item0"
ohos:text_size="20fp"
/>
//第三步,布局数据内容的实体类
public class ItemPageSlider {
private String title;
private String imageUrl;
private String pageNum;
public ItemPageSlider(String title, String imageUrl, String pageNum) {
this.title = title;
this.imageUrl = imageUrl;
this.pageNum = pageNum;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPageNum() {
return pageNum;
}
public void setPageNum(String pageNum) {
this.pageNum = pageNum;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}
//第四步,构建数据提供者
public class ItemPageSliderProvider extends PageSliderProvider {
private List<ItemPageSlider> list;
private Context context;
public ItemPageSliderProvider(List<ItemPageSlider> list, Context context) {
this.list = list;
this.context = context;
}
@Override
public int getCount() {
return list.size();
}
//组装也面内容
@Override
public Object createPageInContainer(ComponentContainer componentContainer, int i) {
Component component = LayoutScatter.getInstance(this.context).parse(ResourceTable.Layout_page_slider_item, null, false);
Text title = component.findComponentById(ResourceTable.Id_item_title);
title.setText(list.get(i).getTitle());
Text pageNum = component.findComponentById(ResourceTable.Id_item_page_num);
pageNum.setText(list.get(i).getPageNum());
Image image = component.findComponentById(ResourceTable.Id_imageComponent);
image.setPixelMap(CommonUtils.getPixelMapFromPath(context, list.get(i).getImageUrl()));
componentContainer.addComponent(component);
return component;
}
//注意必须要实现销毁类,不然内容不断添加,并不会移除
@Override
public void destroyPageFromContainer(ComponentContainer componentContainer, int i, Object o) {
componentContainer.removeComponent((Component)o);
}
@Override
public boolean isPageMatchToObject(Component component, Object o) {
return false;
}
}
//第五步,构建数据提供者
private void initPageSlider() {
PageSlider pageSlider = findComponentById(ResourceTable.Id_page_slider);
ItemPageSliderProvider itemPageSliderProvider=new ItemPageSliderProvider(getData(),this);
pageSlider.setProvider(itemPageSliderProvider);
//监听页面变化
pageSlider.addPageChangedListener(new PageSlider.PageChangedListener() {
@Override
public void onPageSliding(int i, float v, int i1) {
//....
}
@Override
public void onPageSlideStateChanged(int i) {
//....
}
@Override
public void onPageChosen(int i) {
new ToastDialog(getContext()).setText("当前页面索引:"+i++).show();
}
});
//添加页面导航,启用前面的xml布局中PageSliderIndicator,配合此段代码即可实现页面导航
PageSliderIndicator indicator = (PageSliderIndicator)findComponentById(ResourceTable.Id_indicator);
ShapeElement normalElement = new ShapeElement();
normalElement.setRgbColor(RgbColor.fromArgbInt(0xADD8E6));
normalElement.setAlpha(168);
normalElement.setShape(ShapeElement.OVAL);
normalElement.setBounds(0, 0, 32, 32);
ShapeElement selectedElement = new ShapeElement();
selectedElement.setRgbColor(RgbColor.fromArgbInt(0x00BFFF));
selectedElement.setAlpha(168);
selectedElement.setShape(ShapeElement.OVAL);
selectedElement.setBounds(0, 0, 48, 48);
indicator.setItemElement(normalElement, selectedElement);
indicator.setItemOffset(60);
indicator.setPageSlider(pageSlider);
}
辅助类,用于读取页面内容(用本地文件json定义)、图片 (显示图片资源)
public class CommonUtils {
private static final String TAG = "CommonUtils";
//读取资源图片
public static PixelMap getPixelMapFromPath(Context context, String path) {
InputStream inputStream = null;
try {
inputStream = context.getResourceManager().getRawFileEntry(path).openRawFile();
ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
sourceOptions.formatHint = "image/jpg";
ImageSource imageSource = ImageSource.create(inputStream, sourceOptions);
ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
decodingOptions.desiredSize = new Size(0, 0);
decodingOptions.desiredRegion = new Rect(0, 0, 0, 0);
decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;
return imageSource.createPixelmap(decodingOptions);
} catch (IOException e) {
LogUtil.error(TAG, e.getMessage());
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException ex) {
LogUtil.error(TAG, ex.getMessage());
}
}
return null;
}
//读取资源文件json内容
public static String getStringFromPath(Context context, String path) {
try {
Resource dataResource = context.getResourceManager().getRawFileEntry(path).openRawFile();
byte[] buffers = new byte[dataResource.available()];
if (dataResource.read(buffers) != -1) {
return new String(buffers, StandardCharsets.UTF_8);
}
} catch (IOException ex) {
LogUtil.error(TAG, ex.getMessage());
}
return Optional.of(path).toString();
}
}
[
{
"title": "向日葵",
"imageUrl": "entry/resources/base/media/xiangrikui.jpg",
"pageNum": "1"
},
{
"title": "海滩",
"imageUrl": "entry/resources/base/media/haitan.jpg",
"pageNum": "2"
},
{
"title": "萧山城市",
"imageUrl": "entry/resources/base/media/xiaoshan.jpg",
"pageNum": "3"
},
{
"title": "郁金香",
"imageUrl": "entry/resources/base/media/yujinxiang.jpg",
"pageNum": ""
},
{
"title": "鸽子",
"imageUrl": "entry/resources/base/media/gezi.jpg",
"pageNum": ""
}
]
预期效果:
本文因为涉及到动态读取图片和内容稍微嫌的复杂,虽然例子并不复杂,因为是改造实话说开发过程中也遇到不少问题,而且本文原计划要实现能够在PageSlider的子页面中动态添加当前页码(如右图的1位置),但是因为实在不知道怎么获取当前页面的内容,最后用ToastDialog来实现了(即左侧图片下面页面所以提示),留个尾巴吧未来来实现,先继续往下走!
改造例子的时候深深的感受到什么叫“眼高手低”,看代码、抄代码感觉一看都懂,一动手要不不知如何下手,要不就是改错,但别灰心,谁让是新手!
只要不停下来,未来的路会更顺!
更多推荐
所有评论(0)