導(dǎo)讀
- 問題描述
- 代碼分析及修改
- 性能測(cè)試
問題描述
數(shù)據(jù)2000多條,雙Recycleview聯(lián)動(dòng)滑動(dòng)伪朽,左右各一個(gè)轴咱,滑動(dòng)左邊的右邊也動(dòng),滑動(dòng)右邊左邊也動(dòng)烈涮;
先滑動(dòng)左邊的Recycleview-rcvLeft朴肺,兩個(gè)列表滑動(dòng)過程中(重點(diǎn)),去滑動(dòng)右邊的Recycleview-rcvRight
這時(shí)就會(huì)報(bào)錯(cuò)坚洽,堆內(nèi)存溢出8M (StackOverflowError)戈稿,同理先滑右過程中去滑左也是崩潰
首先看下下需求,設(shè)計(jì)稿如下圖
紅色部分左右滑動(dòng),藍(lán)色部分上下滑動(dòng)讶舰,所以紅色部分使用了HorizontalScrollView+RecyclerView鞍盗;綠色為兩個(gè)RecyclerView:
實(shí)際效果
代碼梳理
網(wǎng)上方式
mLeftScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
rcvRight.scrollBy(dx, dy);
}
}
};
rcvLeft.addOnScrollListener(mLeftScrollListener);
mRightScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
rcvLeft.scrollBy(dx, dy);//報(bào)StackOverflowError
}
}
};
rcvRight.addOnScrollListener(mRightScrollListener);
網(wǎng)上上下聯(lián)動(dòng)都是這套代碼需了,可能數(shù)據(jù)量比較小,滑動(dòng)時(shí)都沒發(fā)現(xiàn)StackOverflowError這個(gè)異常般甲;
解決辦法:在滑動(dòng)rcvLeft時(shí)讓rcvRight的父布局去攔截rcvRight的觸摸事件肋乍,在滑動(dòng)rcvRight時(shí)讓rcvLeft的父布局去攔截rcvLeft的觸摸事件;
所以重寫了右邊的HorizontalScrollView和左邊父布局RelativeLayout敷存;
TouchHorizontalScrollView.java:
public class TouchHorizontalScrollView extends HorizontalScrollView {
public TouchHorizontalScrollView(Context context) {
super(context);
}
public TouchHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TouchHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (intercept) {
return true;
}
return super.onInterceptTouchEvent(ev);
}
/**
* 是否攔截子View
*/
private boolean intercept=false;
public boolean isIntercept() {
return intercept;
}
public void setIntercept(boolean intercept) {
this.intercept = intercept;
}
TouchRelativeLayout.java :
public class TouchRelativeLayout extends RelativeLayout {
public TouchRelativeLayout(Context context) {
super(context);
}
public TouchRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TouchRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (intercept) {
return true;
}
return super.onInterceptTouchEvent(ev);
}
/**
* 是否攔截子View
*/
private boolean intercept=false;
public boolean isIntercept() {
return intercept;
}
public void setIntercept(boolean intercept) {
this.intercept = intercept;
}
}
處理后代碼:
mLeftScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//newState 0:停止墓造,1、2表示滑動(dòng)锚烦,再滑動(dòng)時(shí)去攔截右側(cè)RecyclerView 的觸摸事件
horizontalScrollview.setIntercept(newState != 0);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
rcvRight.scrollBy(dx, dy);
}
}
};
rcvLeft.addOnScrollListener(mLeftScrollListener);
mRightScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//newState 0:停止觅闽,1、2表示滑動(dòng)挽牢,在滑動(dòng)時(shí)去攔截左側(cè)RecyclerView 的觸摸事件
rl_left.setIntercept(newState != 0);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
rcvLeft.scrollBy(dx, dy);//報(bào)StackOverflowError
}
}
};
rcvRight.addOnScrollListener(mRightScrollListener);
這樣處理完之后稍微有點(diǎn)小瑕疵:在滑動(dòng)左側(cè)后谱煤,滑動(dòng)右側(cè)是不生效的(因?yàn)橛|摸事件被攔截了),但是總比報(bào)StackOverflowError 崩潰強(qiáng)禽拔;
記得在onDestroy時(shí)移除滑動(dòng)事件監(jiān)聽,不然在滑動(dòng)時(shí)刘离,關(guān)閉頁面會(huì)報(bào)空指針
@Override
protected void onDestroy() {
try {
if (rcvLeft != null) {
rcvLeft.removeOnScrollListener(mLeftScrollListener);
}
if (rcvRight != null) {
rcvRight.removeOnScrollListener(mRightScrollListener);
}
mLeftScrollListener=null;
mRightScrollListener=null;
}catch (Exception e){
e.printStackTrace();
}
super.onDestroy();
}
最后測(cè)試下這個(gè)滑動(dòng)的性能,看看有沒有掉幀的現(xiàn)象
打開https://perfdog.qq.com/,進(jìn)行測(cè)試,通過windows 客戶端 鏈接手機(jī)進(jìn)行數(shù)據(jù)收集,兩次之前的代碼測(cè)試,一次修改的代碼測(cè)試,選擇好進(jìn)行對(duì)比
藍(lán)色的線是我們修復(fù)代碼后的曲線
FPS
JANK