鴻蒙開源第三方組件—頁面滑動組件 ViewPagerIndicator_ohos
前言
基于安卓平臺的頁面滑動組件ViewPagerIndicator(https://github.com/LuckyJayce/ViewPagerIndicator),實現(xiàn)了鴻蒙化遷移和重構。
背景
ViewPagerIndicator_ohos是一個信息展示組件,適用于展示新聞類、購物類等復雜信息。使用傳統(tǒng)的頁面展示內(nèi)容時,同種類的信息在同一個頁面上展示。使用ViewPagerIndicator_ohos組件展示內(nèi)容時,一個種類的信息可以在多個頁面展示,用戶通過滑動屏幕的方式來實現(xiàn)頁面切換,二者的效果對比如圖1所示。

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

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

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