鴻蒙開(kāi)源第三方組件—頁(yè)面滑動(dòng)組件 ViewPagerIndicator_ohos
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
前言
基于安卓平臺(tái)的頁(yè)面滑動(dòng)組件ViewPagerIndicator(https://github.com/LuckyJayce/ViewPagerIndicator),實(shí)現(xiàn)了鴻蒙化遷移和重構(gòu)。
背景
ViewPagerIndicator_ohos是一個(gè)信息展示組件,適用于展示新聞?lì)悺①?gòu)物類等復(fù)雜信息。使用傳統(tǒng)的頁(yè)面展示內(nèi)容時(shí),同種類的信息在同一個(gè)頁(yè)面上展示。使用ViewPagerIndicator_ohos組件展示內(nèi)容時(shí),一個(gè)種類的信息可以在多個(gè)頁(yè)面展示,用戶通過(guò)滑動(dòng)屏幕的方式來(lái)實(shí)現(xiàn)頁(yè)面切換,二者的效果對(duì)比如圖1所示。

圖1 ViewPagerIndicator_ohos組件與傳統(tǒng)的頁(yè)面在展示內(nèi)容時(shí)的區(qū)別
組件效果展示
組件應(yīng)用的主界面中有3個(gè)按鈕,其中,“主頁(yè)”和“我的”按鈕展示的是組件的局部效果,“社區(qū)”按鈕展示的是組件的整體效果。此處,我們不再贅述局部效果,直接講解組件的整體效果,有對(duì)其余兩個(gè)按鈕的功能感興趣的朋友可以下載源碼了解。
當(dāng)用戶點(diǎn)擊應(yīng)用底部導(dǎo)航欄區(qū)“社區(qū)”標(biāo)簽時(shí),組件向用戶展示“社區(qū)”板塊的內(nèi)容,內(nèi)容分布在多個(gè)頁(yè)面內(nèi)。在應(yīng)用的頂部有頁(yè)面指示器,用戶可以通過(guò)兩種方式實(shí)現(xiàn)頁(yè)面的切換效果:1)滑動(dòng)屏幕時(shí),頁(yè)面隨滑動(dòng)切換,頁(yè)面指示器始終指示當(dāng)前正在展示的頁(yè)面。2)點(diǎn)擊頁(yè)面指示器,頁(yè)面會(huì)切換到被選中的指示器所指示的頁(yè)面,效果如圖2所示。

圖2 滑動(dòng)頁(yè)面同時(shí)指示器顯示當(dāng)前頁(yè)面功能
Sample解析
Sample部分主要用于構(gòu)建顯示布局,MainAbilitySlice文件負(fù)責(zé)構(gòu)建應(yīng)用主界面布局,CommunityFraction文件負(fù)責(zé)構(gòu)建頁(yè)面和頁(yè)面指示器布局,CommunityFraction形成的UI以Fraction的形式嵌入到主界面布局中。
1、MainAbilitySlice文件
MainAbilitySlice文件負(fù)責(zé)構(gòu)建組件應(yīng)用的主界面布局,下面給出布局構(gòu)建的具體步驟:
第1步:設(shè)置組件應(yīng)用的布局文件
組件的主界面布局定義在ability_main.xml中,界面包含三個(gè)Button和一個(gè)StackLayout,前者表示“主頁(yè)”、“我的”和“社區(qū)”按鈕,后者表示內(nèi)容顯示頁(yè)面。通過(guò)setUIContent()方法將ability_main.xml文件設(shè)置為組件應(yīng)用的主界面布局。
- super.setUIContent(ResourceTable.Layout_ability_main);
第2步:按鈕的定位
在MainAbilitySlice文件的OnStart()方法中,通過(guò)findComponentById()的方法實(shí)現(xiàn)第1步中“社區(qū)”按鈕的定位。
- //“主頁(yè)”、“我的”和“社區(qū)”按鈕
- private Button basebtn,mybtn,communitybtn;
- ......
- //定位“社區(qū)”按鈕
- communitybtn= (Button) findComponentById(ResourceTable.Id_main_community_btn);
第3步:按鈕監(jiān)聽(tīng)
給“社區(qū)”按鈕設(shè)置點(diǎn)擊事件,點(diǎn)擊按鈕時(shí),將表示社區(qū)內(nèi)容的CommunityFraction嵌入到第1步的StackLayout中,實(shí)現(xiàn)點(diǎn)擊“社區(qū)”按鈕后的顯示效果。
- //設(shè)置按鈕監(jiān)聽(tīng)
- communitybtn.setClickedListener(this);
- ......
- @Override
- public void onClick(Component component) { //點(diǎn)擊事件
- switch (component.getId()){
- ......
- case ResourceTable.Id_main_community_btn:
- displayCommunityFraction(); //將CommunityFraction嵌入主界面布局
- break;
- default:
- break;
- }
- }
2、CommunityFraction文件
CommunityFraction類繼承自Fraction類,作為整體顯示布局的一部分嵌入MainAbility中,不能單獨(dú)使用。CommunityFraction文件負(fù)責(zé)構(gòu)建頁(yè)面和頁(yè)面指示器布局,此處使用ViewPager對(duì)象管理頁(yè)面切換,使用TabList創(chuàng)建頁(yè)面指示器,將ViewPager里的頁(yè)面與TabList里的Tab按順序綁定,以實(shí)現(xiàn)組件效果展示中描述的效果,下面給出具體的實(shí)現(xiàn)步驟。
第1步:創(chuàng)建CommunityFraction文件的布局
fraction_community.xml是CommunityFraction文件的布局文件,布局中包含一個(gè)ViewPager和一個(gè)TabList。通過(guò)LayoutScatter類對(duì)象的parse()方法將fraction_community.xml形成一個(gè)Component 對(duì)象,方便后續(xù)的步驟使用。
- Component component = scatter.parse(ResourceTable.Layout_fraction_community, container, false)
第2步:導(dǎo)入相關(guān)類并聲明對(duì)象
在CommunityFraction文件中導(dǎo)入ViewPager、PagerAdapter類,其中ViewPager類繼承自PageSlider類,通過(guò)響應(yīng)屏幕滑動(dòng)完成頁(yè)面之間的切換;PagerAdapter類繼承自PageSliderProvider類,提供了頁(yè)面項(xiàng)管理功能。
導(dǎo)入ohos.agp.components下的所有類,其中包含用于創(chuàng)建頁(yè)面指示器的TabList類,和用于設(shè)置TabList的監(jiān)聽(tīng)和樣式FixedIndicatorView類。
- import com.shizhefei.view.indicator.CommunityTabListener;
- import com.shizhefei.view.indicator.FixedIndicatorView;
- import com.shizhefei.view.viewpager.PagerAdapter;
- import com.shizhefei.view.viewpager.ViewPager;
- public class CommunityFraction extends Fraction {
- ......
- private ViewPager viewPager;
- private PagerAdapter adapter;
- private Component component ;
- private TabList tabList;
- .....
- }
第3步:創(chuàng)建不同的頁(yè)面
因?yàn)镃ommunityFraction類繼承自Fraction類,因此需要重寫onComponentAttached()方法,當(dāng)CommunityFraction被添加到主界面布局時(shí),此方法被調(diào)用。在onComponentAttached()方法中,用xml的方式創(chuàng)建三個(gè)不同的顯示頁(yè)面,分別為“頁(yè)面1”、“頁(yè)面2”、“頁(yè)面3”。
- @Override
- protected Component onComponentAttached(LayoutScatter scatter, ComponentContainer container, Intent intent) {
- ......
- DirectionalLayout directionalLayout1 = (DirectionalLayout) scatter.parse(ResourceTable.Layout_page1, null, false); //頁(yè)面1
- DirectionalLayout directionalLayout2 = (DirectionalLayout) scatter.parse(ResourceTable.Layout_page2, null, false); //頁(yè)面2
- DirectionalLayout directionalLayout3 = (DirectionalLayout) scatter.parse(ResourceTable.Layout_page3, null, false); //頁(yè)面3
- ......
- }
同時(shí)創(chuàng)建一個(gè)元素類型為Component的ArrayList(數(shù)組列表),將上述創(chuàng)建的三個(gè)頁(yè)面依次添加到ArrayList中。
- //創(chuàng)建ArrayList
- pages = new ArrayList<Component>();
- //將頁(yè)面裝入ArrayList
- pages = new ArrayList<Component>();
- pages.add(directionalLayout1);
- pages.add(directionalLayout2);
- pages.add(directionalLayout3);
第4步:創(chuàng)建頁(yè)面指示器
首先定義一個(gè)String類型的數(shù)組,數(shù)組的長(zhǎng)度決定頁(yè)面指示器的個(gè)數(shù),數(shù)組的元素決定頁(yè)面指示器上的內(nèi)容。
然后創(chuàng)建一個(gè)TabList 類對(duì)象,通過(guò)findComponentById()方法定位到fraction_community.xml中的TabList。
最后使用for循環(huán),將數(shù)組里的內(nèi)容逐個(gè)設(shè)置為TabList 里各Tab的文本。
- private String[] str={"主頁(yè)1","主頁(yè)2","主頁(yè)3"};
- ......
- this.tabList = tabList;
- if(this.tabList!=null){
- for(int i=0;i<str.length;i++){ //頁(yè)面數(shù)量小于之前設(shè)定的tab標(biāo)簽數(shù)量
- TabList.Tab tab = this.tabList.new Tab(getContext());
- tab.setText(str[i]);
- tabList.addTab(tab);
- }
- }
- //tabList初始化默認(rèn)選擇第一個(gè)tab
- this.tabList.selectTabAt(0);
第5步:ViewPager載入頁(yè)面
首先實(shí)例化ViewPager類對(duì)象,定位到fraction_community.xml里的ViewPager;并實(shí)例化PagerAdapter類對(duì)象,形成頁(yè)面管理適配器。然后通過(guò)setPages()方法將第3步中包含三個(gè)頁(yè)面的ArrayList傳入適配器中。最后將適配器和上述ViewPager對(duì)象綁定,即可實(shí)現(xiàn)滑動(dòng)屏幕后多個(gè)頁(yè)面來(lái)回切換的效果。
- viewPager = (ViewPager)mcomponent.findComponentById(ResourceTable.Id_pageslider_community); //定位ViewPager
- adapter = new PagerAdapter(); //實(shí)例化PagerAdapter類對(duì)象
- adapter.setPages(pages); //傳入包含三個(gè)頁(yè)面的ArrayList
- viewPager.setProvider(adapter);
第6步:實(shí)現(xiàn)TabList跟隨頁(yè)面切換而變化
為ViewPager添加頁(yè)面滑動(dòng)監(jiān)聽(tīng)事件,當(dāng)頁(yè)面切換時(shí),執(zhí)行相應(yīng)操作來(lái)實(shí)現(xiàn)TabList跟隨頁(yè)面切換而變化的效果。onPageChosen()方法用于設(shè)置當(dāng)頁(yè)面處于被選中狀態(tài)時(shí)執(zhí)行的操作,在重寫方法該方法時(shí),需傳入當(dāng)前被選中的(正在顯示的)頁(yè)面的編號(hào)i,并將tabList中相應(yīng)編號(hào)的Tab設(shè)置為選中狀態(tài)。
- viewPager.addPageChangedListener(new PageSlider.PageChangedListener() {
- // 頁(yè)面滑動(dòng)轉(zhuǎn)換過(guò)程中調(diào)用
- .......
- @Override
- public void onPageChosen(int i) { // i表示頁(yè)面編號(hào)
- tabList.selectTabAt(i); // tabIndicator隨頁(yè)面滑動(dòng)切換而改變
- }
- });
第7步:綁定TabList并使其按固定大小平均排列
通過(guò)CommunityTabListener類的setViewPager()方法能夠?qū)iewPager和TabList綁定;FixedIndicatorView類的setFixedIndicator()方法能夠固定TabList中各Tab的尺寸,實(shí)現(xiàn)等距離平均排列的效果。
- FixedIndicatorView fixedIndicatorView = new FixedIndicatorView(str,this.tabList);
- //CommunityTabListener實(shí)現(xiàn)了TabList.TabSelectedListener
- CommunityTabListener tabListener=new CommunityTabListener();
- tabListener.setViewPager(viewPager);
- //設(shè)置TabList 監(jiān)聽(tīng)
- fixedIndicatorView.setTabListListener(tabListener);
- //設(shè)置TabList UI風(fēng)格
- fixedIndicatorView.setIndicatorStyle(TabList.INDICATOR_BOTTOM_LINE);
- //設(shè)置TabList的各Tab的長(zhǎng)度固定且相等
- fixedIndicatorView.setFixedIndicator(true);
Library解析
ViewPagerIndicator_ohos組件的關(guān)鍵功能包括ViewPager頁(yè)面切換和TabList頁(yè)面指示器切換。Library按照上述兩個(gè)功能劃分為兩個(gè)文件:indicator文件、viewpager文件,如圖3所示。
indicator文件夾包括CommunityTabListener、FixedIndicatorView、和TabListener(在“我的”板塊被引用,因此不作詳細(xì)講解);viewpager文件夾包括PagerAdapter和ViewPager。接下來(lái)將針對(duì)上述文件進(jìn)行具體講解。

圖 3 Library部分的工程結(jié)構(gòu)
1、頁(yè)面指示器功能實(shí)現(xiàn)
(1) CommunityTabListener功能實(shí)現(xiàn)
頁(yè)面指示器中的各Tab標(biāo)簽中設(shè)有監(jiān)聽(tīng),點(diǎn)擊不同的Tab可以切換至不同的頁(yè)面,具體效果可參考圖2。
上述功能具體由CommunityTabListener類來(lái)完成,該類實(shí)現(xiàn)了TabList類的TabSelectedListener接口。在此接口中,主要重寫三個(gè)函數(shù):onSelected()、onUnSelected()、和onReselected(),分別負(fù)責(zé)設(shè)置當(dāng)頁(yè)面上tab被選中、未被選中、以及被釋放時(shí)的行為。此處設(shè)置:當(dāng)Tab被選中時(shí),頁(yè)面切換到Tab指示的頁(yè)面。
- public class CommunityTabListener implements TabList.TabSelectedListener {
- private ViewPager mviewPager;
- @Override
- //頁(yè)面指示器的某個(gè)Tab被選中時(shí)調(diào)用該方法
- public void onSelected(TabList.Tab tab) {
- int i = tab.getPosition();//獲取當(dāng)前Tab的位置
- if(i>=0){//當(dāng)前tab位置大于0
- mviewPager.setCurrentPage(i);// 頁(yè)面切換到Tab指示的頁(yè)面
- }
- }
- @Override
- //頁(yè)面沒(méi)有被選中時(shí)
- public void onUnselected(TabList.Tab tab) {
- }
- @Override
- //頁(yè)面重新被選中時(shí)
- public void onReselected(TabList.Tab tab) {
- }
- //ViewPager 傳入
- public void setViewPager(ViewPager viewPager) {
- this.mviewPager = viewPager;
- }
- }
(2) FixdIndicatorView功能實(shí)現(xiàn)
FixedIndicatorView類用于設(shè)置TabList監(jiān)聽(tīng)事件和UI樣式。其中,setTabListListener()方法用來(lái)設(shè)置指示器的監(jiān)聽(tīng);setFixedIndicator()方法用來(lái)固定TabList中各Tab的尺寸和位置,實(shí)現(xiàn)個(gè)Tab大小相等且平均排列分布的效果。
- //設(shè)置頁(yè)面指示器的監(jiān)聽(tīng)
- public void setTabListListener(TabList.TabSelectedListener tabSelectedListener){
- tabList.addTabSelectedListener(tabSelectedListener);
- }
- //設(shè)置頁(yè)面指示器的UI風(fēng)格
- public void setIndicatorStyle(int style){
- this.tabList.setIndicatorType(style);
- }
- //設(shè)置頁(yè)面指示器的Tab尺寸固定且相等
- public void setFixedIndicator(boolean b){
- tabList.setFixedMode(b);
- }
2、頁(yè)面管理功能實(shí)現(xiàn)
(1)PagerAdapter 功能實(shí)現(xiàn)
頁(yè)面管理適配器由PagerAdapter類來(lái)完成,其主要用于和上述ViewPager類對(duì)象綁定,可實(shí)現(xiàn)滑動(dòng)屏幕時(shí)多個(gè)頁(yè)面切換,提供了頁(yè)面項(xiàng)管理功能。
setPages()方法將已經(jīng)創(chuàng)建好的頁(yè)面?zhèn)魅脒m配器。
createPageInContainer()方法用于在特定的位置添加Page。在剛載入ViewPager的時(shí)候,默認(rèn)顯示第一個(gè)頁(yè)面,頁(yè)面加載需要調(diào)用createPageInContainer()方法。第一個(gè)頁(yè)面顯示后,用戶可能會(huì)立刻滑動(dòng)屏幕,切換到相鄰的頁(yè)面,為了頁(yè)面的順滑切換,在第一個(gè)頁(yè)面顯示的同時(shí),相鄰頁(yè)面也需要調(diào)用createPageInContainer()方法加載出來(lái),因此在載入ViewPager的時(shí)候,createPageInContainer()方法被調(diào)用了兩遍。
destroyPageFromContainer()方法用于銷毀某個(gè)特定的界面。ViewPager會(huì)同時(shí)緩存3個(gè)頁(yè)面,當(dāng)我們創(chuàng)建的顯示頁(yè)面多于3個(gè)時(shí),需要在ViewPager中銷毀多余頁(yè)面,防止程序崩潰。
- //頁(yè)面管理適配器
- public class PagerAdapter extends PageSliderProvider {
- private ArrayList<Component> pages;
- //創(chuàng)建頁(yè)面所需元件
- public void setPages(ArrayList<Component> pages) {
- this.pages = pages;
- }
- @Override
- //獲取頁(yè)面數(shù)量及大小
- public int getCount() {
- return pages.size();
- }
- @Override
- //特定位置創(chuàng)建頁(yè)面
- public Object createPageInContainer(ComponentContainer componentContainer, int i) {
- componentContainer.addComponent(pages.get(i));
- return pages.get(i);
- }
- @Override
- //刪除特定頁(yè)面
- public void destroyPageFromContainer(ComponentContainer componentContainer, int i, Object o) {
- componentContainer.removeComponent(pages.get(i));
- }
- //判定是否為同一張Page
- @Override
- public boolean isPageMatchToObject(Component component, Object o) {
- return component==o;
- }
- }
(2)ViewPager 功能實(shí)現(xiàn)
滑動(dòng)頁(yè)面功能由ViewPager類來(lái)完成,該類繼承自PageSlider,其主要用于通過(guò)響應(yīng)屏幕滑動(dòng)完成頁(yè)面之間的切換,類中預(yù)留了三個(gè)接口:slideLock()、setCanSlide()、isCanScroll(),用戶可以在開(kāi)發(fā)其他功能時(shí)調(diào)用此接口。isCanScroll()方法判斷頁(yè)面是否可以滑動(dòng),slideLock()方法設(shè)置頁(yè)面不可滑動(dòng),setCanSlide()方法設(shè)置頁(yè)面可以滑動(dòng)。
- //接口預(yù)留
- public class ViewPager extends PageSlider {
- ......
- //設(shè)置頁(yè)面不可以滑動(dòng)
- public void slideLock() {
- this.setSlidingPossible(false);
- }
- //設(shè)置頁(yè)面可以滑動(dòng)
- public void setCanSlide() {
- this.setSlidingPossible(true);
- }
- //判斷頁(yè)面是否可以滑動(dòng)
- public boolean isCanScroll() {
- return getSlidingPossible();
- }
- }
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)