RecyclerView笔记

1.嵌套在ScrollView的滑动冲突

1
2
3
4
//方式一:
recyclerView.setNestedScrollingEnabled(false);
//方式二:
重写LayoutManager的canScrollVertical,return false。

以上两种方式都是禁止RecyclerView滑动,这是非常愚蠢的做法。RecyclerView不能滑动的话,它的复用机制完全失效,一次性加载所有item,跟一个ScrollView一次加载所有item没什么区别。

忠告:ScrollView与RecyclerView尽量不要嵌套使用。

2.刷新Item中的ProgressBar

  • RecyclerView的ItemAnimator默认设置了DefaultItemAnimator,查看里面的源码可以发现局部刷新某Item是有些动画的,其中就有改变Alpha值的动画。所以刷新ProgressBar时会出现闪烁现象,因此要去掉或者自己实现ItemAnimator。
1
recyclerView.setItemAnimator(null);

3.滑到最底部

  • 这种场景默认方向是Vertical,你也可以改成Horizontal,原理一样的。
  • 判断是否到底部:
1
2
3
4
5
6
7
8
9
10
public static boolean isSlideToBottom(RecyclerView recyclerView) {
if (recyclerView == null) {
return false;
}
if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
>= recyclerView.computeVerticalScrollRange()) {
return true;
}
return false;
}
  • 滑到最底部:
1
2
3
if (isSlideToBottom(recyclerView)) {
recyclerView.smoothScrollToPosition(offset);
}

offset是系统常量。

4.记录和恢复滑动的状态

参考Activtiy的onSaveInstanceState与onRestoreInstanceState,查看RecyclerView的这两个方法。

以下为26.1.0版本源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected Parcelable onSaveInstanceState() {
SavedState state = new SavedState(super.onSaveInstanceState());
if (mPendingSavedState != null) {
state.copyFrom(mPendingSavedState);
} else if (mLayout != null) {
state.mLayoutState = mLayout.onSaveInstanceState();
} else {
state.mLayoutState = null;
}
return state;
}
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
mPendingSavedState = (SavedState) state;
super.onRestoreInstanceState(mPendingSavedState.getSuperState());
if (mLayout != null && mPendingSavedState.mLayoutState != null) {
mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
}
}

最重要的变量就是mLayout,它就是LayoutManager。LayoutManager顾名思义,就是管理布局状态的,所以保存布局状态当然要使用它了。

获取状态和保存状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Parcelable state = mLayout.onSaveInstanceState();
if (state != null) {
mLayout.onRestoreInstanceState(state);
} else {
//null处理
mRecyclerView.smoothScrollToPosition(0);
}
//smoothScrollToPosition源码
public void smoothScrollToPosition(int position) {
if (mLayoutFrozen) {
return;
}
if (mLayout == null) {
Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
+ "Call setLayoutManager with a non-null argument.");
return;
}
mLayout.smoothScrollToPosition(this, mState, position);
}

Handler的存在意义

一、Handler的实现

  • 创建:获取当前线程,通过线程的ThreadLocalMap获取Looper,关联Looper和Looper里面的MessageQueue。
  • 投递:每次封装成Message对象投递进MessageQueue。
  • 处理:这部分工作由Looper完成,它不断从MessageQueue中取Message。Message中有Callback就执行,没有就调用Message中的target.dispatchMessage(Message)方法投递给这个消息产生的Handler。

二、Handler的作用

1.切换线程,线程间通信

开发中经常使用到这个功能。

2.将事件或任务序列化

序列化最大的好处就是顺序执行,便于维护,不会出现任务错乱的情况,并且在单线程内不需要并发。以下举一个经典的例子:

  • 为什么我们可以使用View.post(Runnable)这种方式获取一个View的实际宽高?
  • View的宽高至少要通过Measure方法执行完之后才能获取,并且有可能在Layout的时候被触发重新测量。所以只有View真正显示的时候获取的宽高是最准确的。
  • View的绘制流程是借助Handler的事件处理机制。
  • View.post(Runnable)将这个任务投递到串行队列中去,因此当这个任务被执行时,前面的View绘制任务已经执行完,这时候调用View.getWidth(),View.getHeight()肯定是有值并且准确的。

Activity生命周期与finish方法

1.Activity的生命周期

图片源自官方文档
activity_lifecycle

2.finish方法

  • 在onCreate()中调用finish(),会走onDestroy()。
  • 在onStart()中调用finish(),会走onStop()。
  • 在onResume()中调用finish(),会走onPause()。

3.解释

  • 结合生命周期图,要重点理解生命周期是成对的,或者说是不同级别的。比如:onCreate和onDestroy为第一阶梯,onStart和onStop是第二阶梯,onResume和onPause是第三阶梯。对于finish方法来说,应该是尽快完成调用,从用户体验来说,这是最优策略。所以如果在第一阶梯上,那么直接走下第一阶梯就可以了,没必要再走上第二第三阶梯。
  • 为什么生命周期周期要是成对的?我的理解有两点:
    一、确保资源的正确释放。成对能保证资源生命周期正常。
    二、便于系统处理维护生命周期。