Fragment 可见性监听方案,多种 case 完美兼容,我凭什么拿到了阿里、腾讯、今日头条 3 家大厂 offer
AndroidX 的适配(也是一个坑)
在 AndroidX 当中,FragmentAdapter?和 FragmentStatePagerAdapter?的构造方法,添加一个 behavior?参数实现的。
如果我们指定不同的?behavior,会有不同的表现。
当?behavior?为 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 时,ViewPager?中切换?Fragment,setUserVisibleHint?方法将不再被调用,他会确保?onResume?的正确调用时机。
当?behavior?为 BEHAVIOR_SET_USER_VISIBLE_HINT,跟之前的方式是一致的,我们可以通过?setUserVisibleHint?结合?fragment?的生命周期来监听。
//FragmentStatePagerAdapter 构造方法
public?FragmentStatePagerAdapter(@NonNull?FragmentManager?fm,
@Behavior?int?behavior)?{
mFragmentManager?=?fm;
mBehavior?=?behavior;
}
//FragmentPagerAdapter 构造方法
public?FragmentPagerAdapter(@NonNull?FragmentManager?fm,
@Behavior?int?behavior)?{
mFragmentManager?=?fm;
mBehavior?=?behavior;
}
@IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT,?BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
private?@interface?Behavior?{?}
既然是这样,我们就很好适配呢,直接在?onResume?中调用 checkVisibility?方法,判断当前?Fragment?是否可见。
回过头,Behavior?是如何实现的呢?
以?FragmentStatePagerAdapter?为例,我们一起来看看源码。
@SuppressWarnings({"ReferenceEquality",?"deprecation"})
@Override
public?void?setPrimaryItem(@NonNull?ViewGroup?container,?int?position,?@NonNull?Object?object)?{
Fragment?fragment?=?(Fragment)object;
if?(fragment?!=?mCurrentPrimaryItem)?{
if?(mCurrentPrimaryItem?!=?null)?{
//当前显示 Fragment
mCurrentPrimaryItem.setMenuVisibility(false);
if?(mBehavior?==?BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)?{
if?(mCurTransaction?==?null)?{
mCurTransaction?=?mFragmentManager.beginTransaction();
}
//最大生命周期设置为 STARTED,生命周期回退到 onPause
mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem,?Lifecycle.State.STARTED);
}?else?{
//可见性设置为 false
mCurrentPrimaryItem.setUserVisibleHint(false);
}
}
//将要显示的 Fragment
fragment.setMenuVisibility(true);
if?(mBehavior?==?BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)?{
if?(mCurTransaction?==?null)?{
mCurTransaction?=?mFragmentManager.beginTransaction();
}
//最大?生命周期设置为 RESUMED
mCurTransaction.setMaxLifecycle(fragment,?Lifecycle.State.RESUMED);
}?else?{
//可见性设置为 true
fragment.se?tUserVisibleHint(true);
}
//赋值
mCurrentPrimaryItem?=?fragment;
}
}
代码比较简单很好理解。
当?mBehavior?设置为 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 会通过?setMaxLifecycle?来修改当前 Fragment 和将要显示的 Fragment 的状态,使得只有正在显示的?Fragment 执行到 onResume()?方法,其他?Fragment?只会执行到 onStart()?方法,并且当?Fragment?切换到不显示状态时触发?onPause()方法。
当?mBehavior?设置为 BEHAVIOR_SET_USER_VISIBLE_HINT?时,会当 frament?可见性发生变化时调用?setUserVisibleHint()?,也就是跟我们上面提到的第一种懒加载实现方式一样。
====================================================================================
这种 case 也是比较常见的,比如?ViewPager?嵌套 ViewPager,再嵌套?Fragment。
宿主 Fragment 在生命周期执行的时候会相应的分发到子 Fragment 中,但是 setUserVisibleHint 和 onHiddenChanged 却没有进行相应的回调。试想一下,一个 ViewPager 中有一个 FragmentA 的 tab,而 FragmentA 中有一个子 FragmentB,FragmentA 被滑走了,FragmentB 并不能接收到 setUserVisibleHint 事件,onHiddenChange 事件也是一样的。
那有没有办法监听到宿主的?setUserVisibleHint?和 ,onHiddenChange?事件呢?
方法肯定是有的。
第一种方法,宿主?Fragment?提供可见性的回调,子 Fragment?监听改回调,有点类似于观察者模式。难点在于子 Fragment?要怎么拿到宿主?Fragment。
第二种 case,宿主?Fragment?可见性变化的时候,主动去遍历所有的 子 Fragment,调用 子?Fragment?的相应方法。
第一种方法
总体思路是这样的,宿主?Fragment?提供可见性的回调,子 Fragment?监听改回调,有点类似于观察者模式。也有点类似于 Rxjava 中下游持有。
第一步,我们先定义一个接口。
interface?OnFragmentVisibilityChangedListener?{
fun?onFragmentVisibilityChanged(visible:?Boolean)
}
第二步,在?BaseVisibilityFragment?中提供 addOnVisibilityChangedListener 和 removeOnVisibilityChangedListener?方法,这里需要注意的是,我们需要用一个 ArrayList?来保存所有的?listener,因为一个宿主?Fragment?可能有多个子?Fragment。
当?Fragment?可见性变化的时候,会遍历?List?调用 OnFragmentVisibilityChangedListener?的 onFragmentVisibilityChanged?方法 **。
open?class?BaseVisibilityFragment?:?Fragment(),?View.OnAttachStateChangeListener,
OnFragmentVisibilityChangedListener?{
private?val?listeners?=?ArrayList<OnFragmentVisibilityChangedListener>()
fun?addOnVisibilityChangedListener(listener:?OnFragmentVisibilityChangedListener?)?{
listener?.apply?{
listeners.add(this)
}
}
fun?removeOnVisibilityChangedListener(listener:?OnFragmentVisibilityChangedListener?)?{
listener?.apply?{
listeners.remove(this)
}
}
private?fun?checkVisibility(expected:?Boolean)?{
if?(expected?==?visible)?return
val?parentVisible?=
if?(localParentFragment?==?null)?parentActivityVisible
else?localParentFragment?.isFragmentVisible()??:?false
val?superVisible?=?super.isVisible()
val?hintVisible?=?userVisibleHint
val?visible?=?parentVisible?&&?superVisible?&&?hintVisible
if?(visible?!=?this.visible)?{
this.visible?=?visible
listeners.forEach?{?it?->
it.onFragmentVisibilityChanged(visible)
}
onVisibilityChanged(this.visible)
}
}
第三步,在?Fragment attach?的时候,我们通过 getParentFragment?方法,拿到宿主?Fragment,进行监听。这样,当宿主?Fragment?可见性变化的时候,子?Fragment 能感应到。
override?fun?onAttach(context:?Context)?{
super.onAttach(context)
val?parentFragment?=?parentFragment
if?(parentFragment?!=?null?&&?parentFragment?is?BaseVisibilityFragment)?{
this.localParentFragment?=?parentFragment
info("onAttach,?localParentFragment?is?$localParentFragment")
localParentFragment?.addOnVisibilityChangedListener(this)
}
checkVisibility(true)
}
第二种方法
第二种方法,它的实现思路是这样的,宿主?Fragment?生命周期发生变化的时候,遍历子?Fragment,调用相应的方法,通知生命周期发生变化。
//当自己的显示隐藏状态改变时,调用这个方法通知子 Fragment
private?void?notifyChildHiddenChange(boolean?hidden)?{
if?(isDetached()?||?!isAdded())?{
return;
}
FragmentManager?fragmentManager?=?getChildFragmentManager();
List<Fragment>?fragments?=?fragmentManager.getFragments();
if?(fragments?==?null?||?fragments.isEmpty())?{
return;
}
for?(Fragment?fragment?:?fragments)?{
if?(!(fragment?instanceof?IPareVisibilityObserver))?{
continue;
}
((IPareVisibilityObserver)?fragment).onParentFragmentHiddenChanged(hidden);
}
}
================================================================
/**
*?Created?by?jun?xu?on?2020/11/26.
*/
interface?OnFragmentVisibilityChangedListener?{
fun?onFragmentVisibilityChanged(visible:?Boolean)
}
/**
*?Created?by?jun?xu?on?2020/11/26.
*?支持以下四种?case
*?1.?支持?viewPager?嵌套?fragment,主要是通过?setUserVisibleHint?兼容,
*??FragmentStatePagerAdapter?BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT?的?case,因为这时候不会调用?setUserVisibleHint?方法,在?onResume?check?可以兼容
*?2.?直接?fragment?直接?add,?hide?主要是通过?onHiddenChanged
*?3.?直接?fragment?直接?replace?,主要是在?onResume?做判断
*?4.?Fragment?里面用?ViewPager,?ViewPager?里面有多个?Fragment?的,通过?setOnVisibilityChangedListener?兼容,前提是一级?Fragment?和?二级?Fragment?都必须继承??BaseVisibilityFragment,?且必须用?FragmentPagerAdapter?或者?FragmentStatePagerAdapter
*?项目当中一级?ViewPager?adapter?比较特殊,不是?FragmentPagerAdapter,也不是?FragmentStatePagerAdapter,导致这种方式用不了
*/
open?class?BaseVisibilityFragment?:?Fragment(),?View.OnAttachStateChangeListener,
OnFragmentVisibilityChangedListener?{
companion?object?{
const?val?TAG?=?"BaseVisibilityFragment"
}
/**
*?ParentActivity 是否可见
*/
private?var?parentActivityVisible?=?false
/**
*?是否可见(Activity 处于前台、Tab 被选中、Fragment 被添加、Fragment 没有隐藏、Fragment.View 已经 Attach)
*/
private?var?visible?=?false
private?var?localParentFragment:?BaseVisibilityFragment??=
null
private?val?listeners?=?ArrayList<OnFragmentVisibilityChangedListener>()
fun?addOnVisibilityChangedListener(listener:?OnFragmentVisibilityChangedListener?)?{
listener?.apply?{
listeners.add(this)
}
}
fun?removeOnVisibilityChangedListener(listener:?OnFragmentVisibilityChangedListener?)?{
listener?.apply?{
listeners.remove(this)
}
}
override?fun?onAttach(context:?Context)?{
info("onAttach")
super.onAttach(context)
val?parentFragment?=?parentFragment
if?(parentFragment?!=?null?&&?parentFragment?is?BaseVisibilityFragment)?{
this.localParentFragment?=?parentFragment
localParentFragment?.addOnVisibilityChangedListener(this)
}
checkVisibility(true)
}
override?fun?onDetach()?{
info("onDetach")
localParentFragment?.removeOnVisibilityChangedListener(this)
super.onDetach()
checkVisibility(false)
localParentFragment?=?null
}
override?fun?onResume()?{
info("onResume")
super.onResume()
onActivityVisibilityChanged(true)
}
override?fun?onPause()?{
info("onPause")
super.onPause()
onActivityVisibilityChanged(false)
}
/**
*?ParentActivity 可见性改变
*/
protected?fun?onActivityVisibilityChanged(visible:?Boolean)?{
parentActivityVisible?=?visible
checkVisibility(visible)
}
/**
*?ParentFragment 可见性改变
*/
override?fun?onFragmentVisibilityChanged(visible:?Boolean)?{
checkVisibility(visible)
}
override?fun?onCreate(savedInstanceState:?Bundle?)?{
info("onCreate")
super.onCreate(savedInstanceState)
}
override?fun?onViewCreated(
view:?View,
savedInstanceState:?Bundle?
)?{
super.onViewCreated(view,?savedInstanceState)
//?处理直接?replace?的?case
view.addOnAttachStateChangeListener(this)
}
/**
*?调用?fragment?add?hide?的时候回调用这个方法
*/
override?fun?onHiddenChanged(hidden:?Boolean)?{
super.onHiddenChanged(hidden)
checkVisibility(hidden)
}
/**
Tab 切换时会回调此方法。对于没有 Tab 的页面,[Fragment.getUserVisibleHint]默认为 true。
*/
override?fun?setUserVisibleHint(isVisibleToUser:?Boolean)?{
info("setUserVisibleHint?=?$isVisibleToUser")
super.setUserVisibleHint(isVisibleToUser)
checkVisibility(isVisibleToUser)
}
override?fun?onViewAttachedToWindow(v:?View?)?{
info("onViewAttachedToWindow")
checkVisibility(true)
}
override?fun?onViewDetachedFromWindow(v:?View)?{
info("onViewDetachedFromWindow")
v.removeOnAttachStateChangeListener(this)
checkVisibility(false)
}
/**
*?检查可见性是否变化
*?@param?expected 可见性期望的值。只有当前值和 expected 不同,才需要做判断
*/
private?fun?checkVisibility(expected:?Boolean)?{
if?(expected?==?visible)?return
val?parentVisible?=
if?(localParentFragment?==?null)?parentActivityVisible
else?localParentFragment?.isFragmentVisible()??:?false
val?superVisible?=?super.isVisible()
val?hintVisible?=?userVisibleHint
val?visible?=?parentVisible?&&?superVisible?&&?hintVisible
info(
String.format(
"==>?checkVisibility?=?%s??(?parent?=?%s,?super?=?%s,?hint?=?%s?)",
visible,?parentVisible,?superVisible,?hintVisible
)
)
if?(visible?!=?this.visible)?{
总结
算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。
这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲
《Android 学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。
部分资料展示:




有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂 Offer 的距离更加近。
评论