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) {
}
});
后续有其他问题在做补充跟进,希望能帮到大家,有什么问题的,可以给我留言!