ViewPager+FragmentPagerAdapter 被符号为过期
Tab
页是绝大多数项目中很常见的样式了,假如Tab
选用的是ViewPager+FragmentPagerAdapter
的方式来布局,运用如下:
val mViewPager: ViewPager by id(R.id.m_pager)
val mViewPagerAdapter = ChapterViewPagerAdapter(childFragmentManager)
mViewPager.adapter = mViewPagerAdapter
注:其间ChapterViewPagerAdapter
对应的FragmentPagerAdapter
完成。
源码浅析
FragmentPagerAdapter
点进去发现现在的运用方式现已被符号为过期了,源码如下:
private final int mBehavior;
@Deprecated
public FragmentPagerAdapter(@NonNull FragmentManager fm) {
this(fm, BEHAVIOR_SET_USER_VISIBLE_HINT);
}
public FragmentPagerAdapter(@NonNull FragmentManager fm,
@Behavior int behavior) {
mFragmentManager = fm;
mBehavior = behavior;
}
之前运用的一个参数的结构办法现已被符号位Deprecated
了,系统引荐运用2个参数的结构办法,所以直接来看第2个参数的含义是什么:
@Retention(RetentionPolicy.SOURCE)
@IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
private @interface Behavior { }
@Deprecated
public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
FragmentPagerAdapter
结构办法第2个参数传入的是一个int
值而且只能传入上面对应的两个值:BEHAVIOR_SET_USER_VISIBLE_HINT、BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
。
FragmentPagerAdapter(FragmentManager fm)
默许给咱们传入的是BEHAVIOR_SET_USER_VISIBLE_HINT
,此值也被符号为Deprecated
了,要点来看BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
的含义,先来看一下被赋值的mBehavior
都在哪里运用了,按关键字搜一下:
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
} else {
fragment.setUserVisibleHint(false);
}
}
return fragment;
}
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
} else {
mCurrentPrimaryItem.setUserVisibleHint(false);
}
}
fragment.setMenuVisibility(true);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
} else {
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
instantiateItem()
是初始化Fragment
时调用的办法,setPrimaryItem()
是替换显现的Fragment
时履行,逻辑很简单,以instantiateItem()
为例,
看下mBehavior
做了哪些改动:
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
} else {
fragment.setUserVisibleHint(false);
}
能够看到假如FragmentPagerAdapter
结构函数中传入的是BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
,则会履行FragmentTransaction.setMaxLifecycle()
,否则会履行咱们熟悉的Fragment.setUserVisibleHint()
办法,继续看setMaxLifecycle()
用来干什么的。
@NonNull
public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment,
@NonNull Lifecycle.State state) {
addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state));
return this;
}
第2个参数传入的是Lifecycle.State
,其实是依靠 Jetpack Lifecycle
生命周期来办理Fragment
的状况,望文生义,setMaxLifecycle
是为了设置Fragment
的最大状况、
-
Fragment状况值:
INITIALIZING、CREATED、ACTIVITY_CREATED、STARTED、RESUMED
-
Lifecycle State:生命周期状况,包含
DESTROYED、INITIALIZED、CREATED、STARTED、RESUMED
,两者联系: 以FragmentTransaction.setMaxLifecycle()
参数传入Lifecycle.State.STARTED
为例: -
Lifecycle.State.STARTED
对应Fragment
的STARTED
状况,假如当时Fragment
状况低于STARTED
,那么Fragment
的状况会变为STARTED
,以当时Fragment
状况为CREATED
为例,接下来会依次履行onCreateView()、onActivityCreate()和onStart()
办法; - 假如当时
Fragment
状况高于STARTED
,也便是RESUMED
,那么Fragment
的状况会被强制降为STARTED
,接下来会履行onPause()
办法。 - 假如当时
Fragment
的状况恰好为STARTED
,那么就什么都不做。
定论
看到这里,基本知道如何替换setUserVisibleHint()了,列一下定论:
- 运用
FragmentPagerAdapter
时直接运用两个参数的结构办法FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
- 本质上是经过
FragmentTransaction的setMaxLifecycle()
办法来替代setUserVisibleHint()
办法完成Fragment
的懒加载作用。instantiateItem()
中setMaxLifecycle()
设置的Fragment
状况为STARTED
,即经过ViewPager.offscreenPageLimit
设置提前初始化时,接近的Fragment
最多履行到onStart()
办法,不会再履行onResume()
办法了。 - 假如需要
Fragment
只履行一次对应逻辑且Fragment
从头加载View
时需要重置,之前通常会经过setUserVisibleHint(isVisibleToUser: Boolean)
里经过isVisibleToUser
以及自定义的isFirstLoad
去判断,现在能够直接将逻辑写到onResume
中,形如:
private var isFirstLoad: Boolean = true //是否第一次加载
@Override
public void onResume() {
super.onResume();
if (isFirstLoad) {
//......
isFirstLoad = false;
}
}
//对应Fragment的CREATED状况
@Override
public void onDestroyView() {
super.onDestroyView();
isFirstLoad = true;
}
举个栗子
//LazyViewPagerAdapter.kt
class LazyViewPagerAdapter(fm: FragmentManager) :
FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getCount(): Int = 3
override fun getItem(position: Int): Fragment {
return LazyFragment.newInstance(position.toString())
}
}
//MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var mViewPager: ViewPager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mViewPager = findViewById(R.id.view_pager)
val adapter = LazyViewPagerAdapter(supportFragmentManager)
mViewPager.adapter = adapter
mViewPager.offscreenPageLimit = 1 //默许是1 小于1的时分会被置为1
}
}
//LazyFragment.kt
class LazyFragment : Fragment() {
private var position: String? = null
private var param2: String? = null
private lateinit var mTvContent: TextView
override fun onAttach(context: Context) {
super.onAttach(context)
arguments?.let {
position = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
log("Fragment$position: onAttach()")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
log("Fragment$position: onCreate()")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_lazy, container, false)
mTvContent = view.findViewById(R.id.tv_content)
mTvContent.text = position.toString()
log("Fragment$position: onCreateView()")
return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
log("Fragment$position: onActivityCreated()")
}
override fun onStart() {
super.onStart()
log("Fragment$position: onStart()")
}
override fun onResume() {
super.onResume()
log("Fragment$position: onResume()")
}
override fun onPause() {
super.onPause()
log("Fragment$position: onPause()")
}
override fun onStop() {
super.onStop()
log("Fragment$position: onStop()")
}
override fun onDestroyView() {
super.onDestroyView()
log("Fragment$position: onDestroyView()")
}
override fun onDestroy() {
super.onDestroy()
log("Fragment$position: onDestroy()")
}
override fun onDetach() {
super.onDetach()
log("Fragment$position: onDetach()")
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
log("Fragment$position: setUserVisibleHint()->$isVisibleToUser")
}
companion object {
@JvmStatic
fun newInstance(param1: String = "") =
LazyFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, "")
position = param1
}
}
}
}
不传BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT时
LazyViewPagerAdapter
结构参数中没有传 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
,日志如下:
能够看到,setUserVisibleHint()
会履行,且当时显现Fragment0
的时分,Fragment1
的onResume()
也履行了。
传入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT时
LazyViewPagerAdapter
结构参数中传入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
参数,日志如下:
能够看到,假如把BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT参数去掉,setUserVisibleHint()不再履行,且当时显现Fragment0的时分,Fragment1的履行到onStart()之后不再履行。对应了instantiateItem()
的 mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED)
其他
测试中的Fragment
是androidx.fragment:fragment:1.1.0
版别,且运用的是ViewPager
。在Fragment
高版别(如测试运用1.3.6版别)中,FragmentPagerAdapter
整个类现已被符号为过期了,引荐直接运用ViewPager2
完成懒加载作用。