RecyclerView相比傳統的ListView無疑是一個更高級別且靈活性更強的一個控件,主要可用於數據列表展示
第一:RecyclerView的基本使用,屬於普及知識→_→
1.佈局管理
LayoutManager有三種: LinearLayoutManager(線性佈局) GridLayoutManager(表格式佈局)StaggeredGridLayoutManager(瀑布流式佈局) 三種佈局樣式,囊括了日常可能涉及到得絕大多數樣式了
2.ItemDecoration分割線定義
可自定義分割線寬度、顏色等屬性
3.ItemAnimator 動畫
控制item的增刪改的刷新動畫,默認有一個漸變的效果
4.ItemTouchHelper
對item的滑動刪除
如果你對以上4中屬性很熟悉,那麼列表在你眼裏,就不是什麼難事兒了
第二:使用中遇到的問題
本文不講解如何使用RecycclerView,主要講解所遇到的問題,只記錄目前遇到的,後續有再更新
問題一:滑動閃爍的問題
之前用RecyclerView做統計圖的時候,左右滑動一旦涉及刷新列表就會閃,前面說了ItemAnimator主要控制item的效果
可是檢查代碼之下,發現自己並沒有設置ItemAnimator,所以自然就腦補源碼了。在查看源碼之後發現RecyclerView默認就有一個動畫效果DefaultItemAnimator。
那麼DefaultItemAnimator裏面做了什麼會造成刷新一閃一閃的呢?
追溯之下發現默認動畫的runPendingAnimations(執行動畫的方法)調用了一個方法如圖:
我們進入這個方法:
重點就是這兩個地方了,默認動畫裏面設置了一個從0-1的alpha(透明度)的變化,所以我們在刷新的時候,就一定會出現閃一下的現象
解決方案:自定義DefaultItemAnimator
知道問題在哪兒了,當然就是直接貼代碼啦,具體使用時直接拷貝DefaultItemAnimator源碼,然後找到下面的方法,直接全部替換
void animateChangeImpl(final CustomDefaultItemAnimator.ChangeInfo changeInfo) {
final RecyclerView.ViewHolder holder = changeInfo.oldHolder;
final View view = holder == null ? null : holder.itemView;
final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;
final View newView = newHolder != null ? newHolder.itemView : null;
if (view != null) {
final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
getChangeDuration());
mChangeAnimations.add(changeInfo.oldHolder);
oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
oldViewAnim.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchChangeStarting(changeInfo.oldHolder, true);
}
@Override
public void onAnimationEnd(Animator animator) {
oldViewAnim.setListener(null);
view.setAlpha(1);
view.setTranslationX(0);
view.setTranslationY(0);
dispatchChangeFinished(changeInfo.oldHolder, true);
mChangeAnimations.remove(changeInfo.oldHolder);
dispatchFinishedWhenDone();
}
}).start();
}
if (newView != null) {
final ViewPropertyAnimator newViewAnimation = newView.animate();
mChangeAnimations.add(changeInfo.newHolder);
newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchChangeStarting(changeInfo.newHolder, false);
}
@Override
public void onAnimationEnd(Animator animator) {
newViewAnimation.setListener(null);
newView.setAlpha(1);
newView.setTranslationX(0);
newView.setTranslationY(0);
dispatchChangeFinished(changeInfo.newHolder, false);
mChangeAnimations.remove(changeInfo.newHolder);
dispatchFinishedWhenDone();
}
}).start();
}
}
問題二:定位不準確
我們想定位指定position的指定位置,可能會出現定位不準的問題,怎麼解決呢?
1.首先我們可以獲取到滑動的偏移值,拿我要定位到指定的item得中間
/**
* 獲取滑動值 》》滑動偏移 / 每個格子寬度
*
* @return 當前值
*/
private int getScrollPosition() {
return (int) ((double) (mRecyclerView.computeHorizontalScrollOffset() + HistoryAdapter.getItemStdWidth(mContext) / 2)
/ (double) HistoryAdapter.getItemStdWidth(mContext));
}
2.獲取中間得位置
/**
* 獲取中間位置 ITEM_NUM使我們一屏顯示的item總數,基數
*
* @return 當前值
*/
private int getMiddlePosition() {
return getScrollPosition() + (HistoryAdapter.ITEM_NUM / 2);
}
3.定位刷新
上面兩部我們獲取得到了最後應該定位的位置,具體定位代碼如此
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// 效果在暫停時顯示, 否則會導致重繪異常
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (null == mRecyclerView || mRecyclerView.getChildCount() == 0) {
return;
}
int position = getMiddlePosition();
mHistoryAdapter.highlightItem(position);
//定位到指定item
mRecyclerView.scrollToPosition(getScrollPosition());
//滾動到指定的適配器位置,從已解析的佈局獲得給定的偏移量,要更新偏移量,我們必須要通過layoutManager調用這個方法,否則系統不會主動刷新偏移量的
mLayoutManager.scrollToPositionWithOffset(getScrollPosition(), 0);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
}
});
後續有其他問題在做補充跟進,希望能幫到大家,有什麼問題的,可以給我留言!