引文:之前在寫一個(gè)stickScrollView的時(shí)候?qū)Σ簧偃擞幸欢ǖ膯⑹咀饔茫@次針對(duì)stickScrollView再實(shí)現(xiàn)雙列表的聯(lián)動(dòng)效果洛二,希望對(duì)后續(xù)的開(kāi)發(fā)者要實(shí)現(xiàn)同樣的效果能有一定的啟示,可能在實(shí)現(xiàn)的思路上比較簡(jiǎn)單婆翔,但是還是碰到了性能的問(wèn)題础米,也會(huì)針對(duì)我優(yōu)化的過(guò)程中提出自己優(yōu)化的思路矫渔,讓后面有遇到類似的問(wèn)題的伙伴少走點(diǎn)彎路。
一.首先貼下效果圖吧:
如圖的效果圖是左邊列表點(diǎn)擊之后让禀,會(huì)滾動(dòng)到左列表對(duì)應(yīng)的右邊字類目列表挑社;當(dāng)滑動(dòng)右邊的列表的時(shí)候,又可以反過(guò)來(lái)作用于左邊列表巡揍,實(shí)現(xiàn)勾選上對(duì)應(yīng)的左邊列表滔灶。
1.實(shí)現(xiàn)思路,當(dāng)左邊列表點(diǎn)擊的時(shí)候:
mLlRight.scrollToPositionWithOffset(scrollIndex, 0);
網(wǎng)上一直有思路是根據(jù)滑動(dòng)的postion是否在第一個(gè)可見(jiàn)的item之前吼肥,可見(jiàn)item之后和最后可見(jiàn)item之前录平,最后的可見(jiàn)item之后三種情況來(lái)處理:
if (scrollIndex <= firstItem) {
//當(dāng)要置頂?shù)捻?xiàng)在當(dāng)前顯示的第一個(gè)項(xiàng)的前面時(shí)
mChildRecyclerviewRight.smoothScrollToPosition(scrollIndex);
} else if (scrollIndex <= lastItem) {
//當(dāng)要置頂?shù)捻?xiàng)已經(jīng)在屏幕上顯示時(shí),計(jì)算它離屏幕原點(diǎn)的距離
int top = mChildRecyclerviewRight.getChildAt(scrollIndex - firstItem).getTop();
mChildRecyclerviewRight.smoothScrollBy(0, top);
} else {
//當(dāng)要置頂?shù)捻?xiàng)在當(dāng)前顯示的最后一項(xiàng)的后面時(shí)
mChildRecyclerviewRight.smoothScrollToPosition(scrollIndex);
//記錄當(dāng)前需要在RecyclerView滾動(dòng)監(jiān)聽(tīng)里面繼續(xù)第二次滾動(dòng)
move = true;
}
但是我發(fā)現(xiàn)這樣的處理的話缀皱,能實(shí)現(xiàn)右邊的定位的效果斗这,但是會(huì)觸發(fā)右邊列表的二次滾動(dòng),這個(gè)會(huì)導(dǎo)致右邊列表的二次滾動(dòng)觸發(fā)左邊列表的重新定位啤斗,雖然做了各種判斷表箭,但是在我的暴力測(cè)試下,還是會(huì)有這樣的情況出現(xiàn)钮莲,很頭疼免钻,經(jīng)測(cè)試LiearLayoutManager的方法比較靠譜,就是有個(gè)問(wèn)題數(shù)據(jù)量多的時(shí)候崔拥,左邊列表點(diǎn)擊的時(shí)候會(huì)很卡极舔,當(dāng)然了,這個(gè)在后邊有我的優(yōu)化之路链瓦。
2.當(dāng)右邊的列表滑動(dòng)的時(shí)候拆魏,給recyclerview設(shè)置滾動(dòng)監(jiān)聽(tīng)就可以了:
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//在這里進(jìn)行第二次滾動(dòng)(最后的距離)
if (recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE) {
if (!mIsLeftTouch) {
leftLocation();
}
}
}
雖然這樣聯(lián)動(dòng)的效果實(shí)現(xiàn)了盯桦,但是個(gè)人發(fā)現(xiàn)整體界面的流暢性有很大問(wèn)題,綜合原因是因?yàn)橐淮涡约虞d了右邊的列表渤刃,右邊的列表數(shù)據(jù)量太過(guò)龐大了拥峦,而且有三個(gè)tab加載這樣就導(dǎo)致整個(gè)界面異常卡頓卖子,下面就這個(gè)提下我的優(yōu)化方案:
二.其實(shí)在整體的實(shí)現(xiàn)當(dāng)中略号,真實(shí)情況是每個(gè)fragment的右側(cè)列表數(shù)據(jù)都會(huì)很龐大,我們以前在列表上面可以用分頁(yè)洋闽,但是現(xiàn)在必須一次性加載這么多數(shù)據(jù)璃哟,會(huì)出現(xiàn)以下的幾個(gè)問(wèn)題,針對(duì)這幾個(gè)問(wèn)題喊递,我自己有進(jìn)行優(yōu)化,因此將優(yōu)化的方案也貼出來(lái)阳似,旨在希望大家不僅能開(kāi)發(fā)功能性的app骚勘,還要開(kāi)發(fā)出性能高的app,我現(xiàn)在是用了700條數(shù)據(jù)進(jìn)行測(cè)試撮奏,每個(gè)item有圖片和文案俏讹。
1.沒(méi)優(yōu)化之前的使用是這個(gè)體驗(yàn),啟動(dòng)是4s:
2.問(wèn)題1:當(dāng)進(jìn)界面的時(shí)候畜吊,由于右側(cè)列表數(shù)據(jù)過(guò)于龐大泽疆,一次性加載fragment顯示很慢,將近4s玲献?
1.因?yàn)檫@個(gè)界面的tab上面有角標(biāo)殉疼,這個(gè)時(shí)候通常的做法,是在網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求完成之后捌年,再去進(jìn)行ViewPager和TabLayout的初始化瓢娜,這個(gè)時(shí)候很明顯會(huì)有一片空白的時(shí)候?
解決辦法:我們?cè)贏ctivity加載的時(shí)候礼预,我們就應(yīng)該viewPager眠砾,fragment初始化好,在網(wǎng)絡(luò)請(qǐng)求拿到數(shù)據(jù)之后托酸,我們只需要拿到初始化的fragment和tabLayout進(jìn)行刷新數(shù)據(jù)就可以了褒颈。如下面就是在網(wǎng)絡(luò)請(qǐng)求完成之后,回調(diào)fragment提供接口中的notifyDataChange方法励堡,通知fragment進(jìn)行刷新谷丸,同時(shí)我們對(duì)tablayout取到每一個(gè)需要賦值的view,進(jìn)行設(shè)值应结。
private void initVP() {
for (FragmentWithTitleBean fragmentWithTitleBean : mFragments) {
((CheckListFragment) (fragmentWithTitleBean.getFragment())).notifyDataChange();
}
//通知tablayout進(jìn)行改變
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.RecycerlView在加載的時(shí)候淤井,有這樣的機(jī)制,如果是height為wrap_content的話,那么你的recyclerview在加載的時(shí)候币狠,會(huì)一次性將所有數(shù)據(jù)加載進(jìn)來(lái)游两?what fuck,那這樣1000條數(shù)據(jù)同時(shí)設(shè)置漩绵,那不是卡爆了贱案?但是當(dāng)我們給recyclerview設(shè)置指定的高度的話,那么它一開(kāi)始只會(huì)加載部分的顯示的View止吐,這樣不管數(shù)據(jù)多少條宝踪,那也會(huì)好很多,那這樣有思路碍扔,那么我們接下來(lái)就是要給右側(cè)的recyclerview設(shè)定指定的高度瘩燥?
private void initRightRVHeight() {
mChildRecyclerviewRight.post(new Runnable() {
@Override
public void run() {
ViewGroup.LayoutParams layoutParams = mChildRecyclerviewRight.getLayoutParams();
layoutParams.height = mParentActivity.getVpHeight();
mChildRecyclerviewRight.setLayoutParams(layoutParams);
}
});
}
問(wèn)題2:我們?cè)谝贿M(jìn)來(lái),三個(gè)tab下面的fragment都有這么大的數(shù)據(jù)不同,同時(shí)執(zhí)行cpu會(huì)有點(diǎn)吃力吧厉膀,沒(méi)錯(cuò)就是這樣?那這樣的話二拐,就需要用到業(yè)內(nèi)的懶加載機(jī)制服鹅,相信他們都會(huì)解決方案,這里我貼我的實(shí)現(xiàn)吧:
public abstract class LazyFragment extends Fragment {
boolean isViewPrepared; // 標(biāo)識(shí)fragment視圖已經(jīng)初始化完畢
boolean hasFetchData; // 標(biāo)識(shí)已經(jīng)觸發(fā)過(guò)懶加載數(shù)據(jù)
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {//當(dāng)當(dāng)前為顯示頁(yè)面時(shí)
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() {
// 用戶可見(jiàn)fragment && 沒(méi)有加載過(guò)數(shù)據(jù) && 視圖已經(jīng)準(zhǔn)備完畢
if (getUserVisibleHint() && !hasFetchData && isViewPrepared) {
hasFetchData = true; //已加載過(guò)數(shù)據(jù)
lazyFetchData();
}
}
abstract void lazyFetchData();
}