HarmonyOS開發(fā),從listContainer談容器類控件的使用
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
1.什么叫容器類控件
容器類控件在生活中還是比較常見的,比如文件列表,圖片輪播等等。
2.容器類控件有什么特點(diǎn)
容器類控件主要就是負(fù)責(zé)容納真正的內(nèi)容,在界面上一般沒有自己真正的"形象",屬于內(nèi)容后面的綠葉,在后面默默的撐起控件的光彩。
以listContainer的示意圖:

假設(shè)我們有一個(gè)下載列表,要做這樣一個(gè)列表,有兩種方法,一種就是硬編碼,把東西一個(gè)一個(gè)寫在layout上面,但那顯然是最低級(jí)最無(wú)腦的做法,咱們程序員都自詡高智商人才,不能做那蠢事是不。
另一種高級(jí)的做法就是搞一個(gè)列表容器,把要顯示的內(nèi)容抽象成一個(gè)小的layout,然后按順序排列出來塞到listContainer就行.
3.怎么學(xué)
3.1 準(zhǔn)備好你的layout內(nèi)容
首先是官方的教程:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-listcontainer-0000001060007847
這篇教程很基礎(chǔ),介紹了一個(gè)最基本的列表容器,但是也僅限基礎(chǔ),如果是app開發(fā)新手比如我這樣的就很容易搞蒙,一眼看去不知所以,所以我覺得還是按我的方式去解釋這個(gè)東西。
4.準(zhǔn)備工作
首先設(shè)計(jì)好你要展示的內(nèi)容,比如下載列表,我們?cè)O(shè)計(jì)成左邊放一張圖片,右邊分兩個(gè)控件,一個(gè)Text,下方放一個(gè)進(jìn)度條,大概就這意思:

先把這個(gè)layout設(shè)計(jì)出來:
- <?xml version="1.0" encoding="utf-8"?>
- <DirectionalLayout
- xmlns:ohos="http://schemas.huawei.com/res/ohos"
- ohos:height="match_parent"
- ohos:width="match_parent"
- ohos:orientation="horizontal">
- <Image
- ohos:id="$+id:movie_img"
- ohos:height="100vp"
- ohos:width="100vp"
- ohos:layout_alignment="left"
- ohos:image_src="$media:setting"
- ohos:scale_mode="clip_center"
- />
- <DirectionalLayout
- ohos:id="$+id:movie_info"
- ohos:height="match_parent"
- ohos:width="match_parent"
- ohos:orientation="vertical"
- >
- <Text
- ohos:id="$+id:movie_name"
- ohos:width="match_parent"
- ohos:height="30vp"
- ohos:left_margin="3vp"
- ohos:text_size="30fp"
- ohos:text="亮劍-18"
- />
- <ProgressBar
- ohos:id="$+id:progressbar"
- ohos:progress_width="10vp"
- ohos:height="60vp"
- ohos:width="600vp"
- ohos:max="100"
- ohos:min="0"
- ohos:progress="60"/>
- </DirectionalLayout>
- </DirectionalLayout>
預(yù)覽窗口看見到是這樣:

3.2 準(zhǔn)備listContainer
有了layout,還要有容器把他裝起來,就跟果樹長(zhǎng)果子一樣,有了果子,還得有樹。
- <?xml version="1.0" encoding="utf-8"?>
- <DirectionalLayout
- xmlns:ohos="http://schemas.huawei.com/res/ohos"
- ohos:height="match_parent"
- ohos:width="match_parent"
- ohos:orientation="vertical">
- <Text
- ohos:width="match_parent"
- ohos:height="30vp"
- ohos:text="下載列表"
- ohos:text_size="30vp"
- ohos:top_margin="10vp"
- ohos:text_alignment="center"
- />
- <ListContainer
- ohos:id="$+id:download_list"
- ohos:height="match_content"
- ohos:width="match_parent"
- ohos:scrollbar_background_color="#d2a456"
- >
- </ListContainer>
- </DirectionalLayout>
但是這東西沒有界面,目前還沒法預(yù)覽,實(shí)在要預(yù)覽的話是這個(gè)界面。

4.初始化
在下載列表的abilitySlice的onStart里面假如初始化函數(shù):
- @Override
- protected void onStart(Intent intent) {
- super.onStart(intent);
- setUIContent(ResourceTable.Layout_movie_containerlist);
- initDownloadListContainer();
- }
- private void initDownloadContainerList(){
- ListContainer listContainer=(ListContainer) findComponentById(ResourceTable.Id_download_list);
- if(listContainer != null){
- ArrayList<downloadItem> list=getData();
- downloadItemProvider itemPro=new downloadItemProvider(list,this);
- listContainer.setItemProvider(itemPro);
- }
- }
看第10行,首先咱們把listContainer獲取到,為什么要獲取到呢,因?yàn)榻酉聛砦覀兙鸵锶麞|西了。
那怎么塞呢,看第15行,我們往這個(gè)listContainer里面設(shè)置了一叫itemPro的東西,這里引入了一個(gè)Provider,這個(gè)provider負(fù)責(zé)幫我們提供數(shù)據(jù),對(duì)于這個(gè)provider官方的文檔有簡(jiǎn)單的幾個(gè)接口介紹:

其他的都還好懂,感覺最后一個(gè)有些費(fèi)解,什么意思呢:
你那個(gè)listContainer嘛,里面肯定是有很多項(xiàng),像一個(gè)數(shù)組一樣,我們想拿到每一個(gè)列表項(xiàng)里面的圖形控件,就得用這個(gè)接口,不然大家都長(zhǎng)一樣,你怎么知道你的數(shù)據(jù)結(jié)構(gòu)對(duì)應(yīng)哪個(gè)圖形控件。
這里我們看到itemPro的創(chuàng)建需要一個(gè)list做參數(shù),那在java里面就用ArrayList了,我們就來實(shí)現(xiàn)一下這個(gè)獲取列表的接口:
- private ArrayList<downloadItem> getData(){
- ArrayList<downloadItem> list=new ArrayList<>();
- for (int i=0;i<8;i++)
- {
- downloadItem item = new downloadItem("亮劍-"+i+1);
- DirectionalLayout layout = (DirectionalLayout) LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_movie_item,null,false);
- item.setLayout(layout);
- item.setProgress(i*10);
- list.add(item);
- }
- return list;
- }
這個(gè)很好理解了,就是創(chuàng)建一個(gè)list,然后把數(shù)據(jù)填充進(jìn)去,這里的示例,我讓每一個(gè)列表顯示不一樣的文字,然后再把每個(gè)進(jìn)度條顯示不一樣的值,返回過去就行了。
這里面又涉及到一個(gè)東西,就是我們的downloadItem,這個(gè)item其實(shí)就是對(duì)應(yīng)的我們那個(gè)下載信息展示的具體信息,就是java里面的一個(gè)類,先創(chuàng)造出一個(gè)結(jié)構(gòu)類出來:
- public class downloadItem {
- private static final String TAG = imageScannerSlice.class.getSimpleName();
- private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0x00201, TAG);
- String name;
- Image image;
- int progress;//[0-100]%
- DirectionalLayout layout;
- public downloadItem(String name){
- this.name = name;
- }
- public void downloadItem(String name, Image img) {
- this.name = name;
- this.image = image;
- }
- public void setLayout(DirectionalLayout layout){
- if(layout == null){
- HiLog.error(LABEL_LOG,"setLayout is NULL");
- return;
- }
- this.layout =layout;
- Text text =(Text) layout.findComponentById(ResourceTable.Id_movie_name);
- text.setText(name);
- }
- public void setProgress(int progress) {
- if(progress<0) progress=0;
- if (progress>100) progress=100;
- this.progress = progress;
- if(layout == null){
- return;
- }
- ProgressBar progressBar =(ProgressBar) layout.findComponentById(ResourceTable.Id_progressbar);
- progressBar.setProgressValue(progress);
- }
- }
可以看到,我們?cè)谶@個(gè)結(jié)構(gòu)里面做了一個(gè)setLayout的動(dòng)作,這個(gè)就是為了把我們每個(gè)結(jié)構(gòu)對(duì)應(yīng)的圖形控件對(duì)應(yīng)起來,有了這東西,在Provider里面的getComponent接口里面就好填充數(shù)據(jù)了,整個(gè)provider長(zhǎng)這樣:
- public class downloadItemProvider extends BaseItemProvider {
- private List<downloadItem> list;
- private AbilitySlice slice;
- public downloadItemProvider(List<downloadItem> list, AbilitySlice slice){
- this.list = list;
- this.slice = slice;
- }
- @Override
- public int getCount() {
- return this.list!=null ? this.list.size() : 0;
- }
- @Override
- public Object getItem(int i) {
- if(this.list != null && i>0 && i <list.size())
- {
- return this.list.get(i);
- }
- return null;
- }
- @Override
- public long getItemId(int i) {
- return i;
- }
- @Override
- public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
- if(component != null){
- return component;
- }
- downloadItem item = (downloadItem)this.getItem(i);
- return item.layout;
- }
- }
可以看到我們的getComponent就是把我們的downloadItem找到,然后直接返回我們事先用setlayout接口傳進(jìn)去的layout控件。
5.效果展示
把上面的代碼片段進(jìn)行整合,放進(jìn)工程里面,最后的效果就是這樣:

到這里,我們的listContainer,列表容器控件就完成了,如果感覺沒看懂的,可以先去看一下官方的教程:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-listcontainer-0000001060007847
然后再來看本教程,應(yīng)該就能理清了。
6.pageSlider控件
所謂的pageSlider能夠?qū)崿F(xiàn)一個(gè)大圖輪播的效果,官方文檔在這:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-pageslider-0000001091933258
效果在官方手冊(cè)上也有
從編程邏輯上來說,跟listContainer的邏輯是一樣的,首先準(zhǔn)備好你的展示頁(yè)面,然后創(chuàng)建一個(gè)容器:pageSlide,再給pageSlider一個(gè)Provider用來提供數(shù)據(jù),再初始化一下就好了。
如果讀懂了上面listContainer的邏輯,這個(gè)pagesiler簡(jiǎn)直就太小兒科了,基于這樣的邏輯,我們能很快把我們的下載列表改造成pagesilde形式:
相關(guān)的xml文件我就不貼了,參照官方代碼就可以做到
- public class downloadItemPageProvider extends PageSliderProvider {
- //數(shù)據(jù)實(shí)體類
- // public static class DataItem{
- // String mText;
- // public DataItem(String txt) {
- // mText = txt;
- // }
- // }
- // 數(shù)據(jù)源,每個(gè)頁(yè)面對(duì)應(yīng)list中的一項(xiàng)
- private List<downloadItem> list;
- private Context mContext;
- public downloadItemPageProvider(List<downloadItem> list, Context context) {
- this.list = list;
- this.mContext = context;
- }
- @Override
- public int getCount() {
- return list.size();
- }
- @Override
- public Object createPageInContainer(ComponentContainer componentContainer, int i) {
- final downloadItem item = list.get(i);
- // DirectionalLayout layout =new DirectionalLayout(mContext);//(DirectionalLayout) componentContainer.findComponentById(ResourceTable.Id_home_info);
- DirectionalLayout layout =(DirectionalLayout) LayoutScatter.getInstance(mContext).parse(ResourceTable.Layout_home_info,null,false);
- componentContainer.addComponent(layout);
- item.setLayout(layout);
- return layout;
- }
- @Override
- public void destroyPageFromContainer(ComponentContainer componentContainer, int i, Object o) {
- componentContainer.removeComponent((Component) o);
- }
- @Override
- public boolean isPageMatchToObject(Component component, Object o) {
- //可添加具體處理邏輯
- //...
- return true;
- }
- }
這個(gè)pageSlid的Provider跟List的Provider大體邏輯差不多的,只是稍稍有些不同,就是這個(gè)里面需要實(shí)現(xiàn)createPageInContainer,而不是list的Provider里面的getComponent接口,但是仔細(xì)觀察我里面的代碼,跟之前那個(gè)如出一轍,然后改造一下初始化那里:
- private void initPageSlide(String account) {
- pageSlider = (PageSlider) findComponentById(ResourceTable.Id_page_slider);
- pageSlider.setProvider(new downloadItemPageProvider(getData(), this));
- }
這樣就能輕松的做出一個(gè)左右滑屏的頁(yè)面滑動(dòng)器。

7.總結(jié)
個(gè)人感覺比較自豪的一處是那個(gè)數(shù)據(jù)結(jié)構(gòu)跟圖形控件綁定的邏輯,比官方的好用。
其實(shí)總結(jié)下來,做容器類的空間需要的流程是:

理解了這個(gè)套路.你就能做出想要的效果
其實(shí)還可以把pageSilde和listContainer結(jié)合起來使用做出更復(fù)雜好玩的界面效果,期待你的反饋。