天天看點

android開發 視圖關聯_Android雙清單關聯和固定頭部ScrollView效果實作

引文:

之前在寫一個stickScrollView的時候對不少人有一定的啟示作用,這次針對stickScrollView再實作雙清單的關聯效果,希望對後續的開發者要實作同樣的效果能有一定的啟示,在實作的思路上比較簡單,但是期間碰到了性能的問題,也會針對我優化的過程中提出自己優化的思路,讓後面有遇到類似的問題的夥伴少走點彎路。

一.首先貼下效果圖吧:

android開發 視圖關聯_Android雙清單關聯和固定頭部ScrollView效果實作

如圖的效果圖是左邊清單點選之後,會滾動到左清單對應的右邊字類目清單;當滑動右邊的清單的時候,又可以反過來作用于左邊清單,實作勾選上對應的左邊清單。

1.實作思路,當左邊清單點選的時候執行下面的代碼:

mLlRight.scrollToPositionWithOffset(scrollIndex, 0);//scrollIndex就是根據左邊的點選項,計算右邊滑動的位置

網上一直有思路是根據滑動的postion是否在第一個可見的item之前,可見item之後和最後可見item之後,最後的可見item之前三種情況來處理:

if (scrollIndex <= firstItem) {

//當要置頂的項在目前顯示的第一個項的前面時

mChildRecyclerviewRight.smoothScrollToPosition(scrollIndex);

} else if (scrollIndex <= lastItem) {

//當要置頂的項已經在螢幕上顯示時,計算它離螢幕原點的距離

int top = mChildRecyclerviewRight.getChildAt(scrollIndex - firstItem).getTop();

mChildRecyclerviewRight.smoothScrollBy(0, top);

} else {

//當要置頂的項在目前顯示的最後一項的後面時

mChildRecyclerviewRight.smoothScrollToPosition(scrollIndex);

//記錄目前需要在RecyclerView滾動監聽裡面繼續第二次滾動

move = true;

}

但是我發現這樣的處理的話,能實作右邊的定位的效果,但是走else判斷的時候會觸發右邊清單的二次滾動,這個會觸發右邊清單的監聽事件,類似手指滑動右邊又重新進行左邊的定位,雖然做了各種判斷,但是在我的暴力測試下,還是會有這樣的情況出現,很頭疼,經測試還是LiearLayoutManager的方法比較靠譜。

### 2.當右邊的清單滑動的時候,給recyclerview設定滾動監聽就可以了:

@Override

public void onScrollStateChanged(RecyclerView recyclerView, int newState) {

super.onScrollStateChanged(recyclerView, newState);

//在這裡進行第二次滾動(最後的距離)

if (recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE) {

if (!mIsLeftTouch) {

leftLocation();

}

}

}

二.功能實作了,那現在咱們就談談性能問題了?

在真實情況是每個fragment的右側清單資料都會很龐大,我們以前在清單上面可以用分頁,但是現在必須一次性加載這麼多資料,會出現以下的幾個問題,針對這幾個問題,我自己有進行優化,是以将優化的方案也貼出來,旨在希望大家不僅能開發功能性的app,還要開發出性能高的app,我現在是用了700條資料進行測試,每個item有圖檔和文案。沒優化之前的使用是這個體驗,啟動是4s,如下圖:

android開發 視圖關聯_Android雙清單關聯和固定頭部ScrollView效果實作

2.1因為這個界面的tab上面有角标,這個時候通常的做法,是在網絡資料請求完成之後,再去進行ViewPager和TabLayout的初始化?

解決辦法:我們在Activity加載的時候,我們就應該對viewPager,fragment初始化好,在網絡請求拿到資料之後,我們隻需要拿到初始化的fragment和tabLayout進行重新整理資料就可以了。如下面就是在網絡請求完成之後,回調fragment提供的接口的notifyDataChange方法,執行fragment重新整理界面,同時我們對tablayout取到每一個需要指派的view,進行設值,代碼如下:

private void initVP() {

for (FragmentWithTitleBean fragmentWithTitleBean : mFragments) {

((CheckListFragment) (fragmentWithTitleBean.getFragment())).notifyDataChange();

}

//通知tablayout進行改變

for (int i = 0, size = mOrderManagerTabs.getTabCount(); i < size; i++) {

TabLayout.Tab tab = mOrderManagerTabs.getTabAt(i);

if (tab != null && tab.getCustomView() != null) {

TextView tvNum = tab.getCustomView().findViewById(R.id.tv_num);

int intNum = 0;

if (i == 0)

intNum = getCheckInfoBean().getItemAllCount();

else if (i == 1)

intNum = getCheckInfoBean().getItemDoneCount();

else if (i == 2)

intNum = getCheckInfoBean().getItemAllCount() - getCheckInfoBean().getItemDoneCount();

setTabNum(tvNum, intNum);

}

}

}

2.2這麼多的資料一次性設定給右側的recyclerView,加載肯定會很慢?

RecycerlView在加載的時候,有這樣的機制,如果是height為wrap_content的話,那麼你的recyclerview在加載的時候,會一次性将所有資料加載進來?what fuck,那這樣1000條資料同時設定,那不是卡爆了?但是當我們給recyclerview設定指定的高度的話,那麼它一開始隻會加載隻需要顯示的View,這樣不管資料多少條,那也會好很多,那這樣有思路,那麼我們接下來就是要給右側的recyclerview設定指定的高度:

private void initRightRVHeight() {

mChildRecyclerviewRight.post(new Runnable() {

@Override

public void run() {

ViewGroup.LayoutParams layoutParams = mChildRecyclerviewRight.getLayoutParams();

layoutParams.height = mParentActivity.getVpHeight();

mChildRecyclerviewRight.setLayoutParams(layoutParams);

}

});

}

2.3三個tab下面的fragment都有這麼大的資料,都加載,cpu會有點吃力吧?沒錯就是這樣!

那這樣的話,就需要用到業内的懶加載機制,相信很多人都會有解決方案,這裡我就貼下我的代碼實作吧:

public abstract class LazyFragment extends Fragment {

boolean isViewPrepared; // 辨別fragment視圖已經初始化完畢

boolean hasFetchData; // 辨別已經觸發過懶加載資料

@Override

public void setUserVisibleHint(boolean isVisibleToUser) {

super.setUserVisibleHint(isVisibleToUser);

if (isVisibleToUser) {//當目前為顯示頁面時

lazyFetchDataIfPrepared();

}

}

@Override

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

isViewPrepared = true;

}

@Override

public void onActivityCreated(@Nullable Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

lazyFetchDataIfPrepared();

}

void lazyFetchDataIfPrepared() {

// 使用者可見fragment && 沒有加載過資料 && 視圖已經準備完畢

if (getUserVisibleHint() && !hasFetchData && isViewPrepared) {

hasFetchData = true; //已加載過資料

lazyFetchData();

}

}

abstract void lazyFetchData();

}

三.總結

經過上面三步之後,你再使用過的時候,就會有第一張圖的體驗了,真是快太多了呀,從原來的4s到現在的1s開,而且滑動也明顯流暢了。

代碼已經上傳,有需要的可以去看下,github位址,您的點贊或者star是我持續開源的最大動力。