问题:ViewPager中的Fragment onResume()在片段实际可见之前被激发。
例如,我有2个带有ViewPager和FragmentPagerAdapter的片段。第二个片段仅对授权用户可用,我需要在片段可见时要求用户登录(使用警告对话框)。
但是,当第一个片段可见时,ViewPager创建第二个片段,以便缓存第二个碎片,并在用户开始滑动时使其可见。
因此,onResume()事件在第二个片段可见之前很久就被激发了。这就是为什么我试图找到一个事件,当第二个片段变得可见时,该事件会触发,以便在适当的时候显示对话框。
如何做到这一点?
setUserVisibleHint()有时会在onCreateView()之前调用,有时会在之后调用。
要克服这个问题,您需要在setUserVisibleHint()方法中检查isResumed()。但在本例中,我意识到只有当Fragment恢复并可见时,才会调用setUserVisibleHint(),而不是在Created时。
因此,如果您想在Fragment可见时更新某些内容,请将更新函数放在onCreate()和setUserVisibleHint()中:
@Override
public View onCreateView(...){
...
myUIUpdate();
...
}
....
@Override
public void setUserVisibleHint(boolean visible){
super.setUserVisibleHint(visible);
if (visible && isResumed()){
myUIUpdate();
}
}
UPDATE:我仍然意识到myUIUpdate()有时会被调用两次,原因是,如果您有3个选项卡,而这段代码位于第二个选项卡上,当您第一次打开第一个选项卡时,第二个标签也会被创建,即使它不可见,并且会调用myUIUpdate)。然后,当您滑动到第二个选项卡时,将调用if(visible&&isResumed())中的myUIUpdate(),因此,myUIUpdate)可能会在一秒内被调用两次。
另一个问题是!setUserVisibleHint中的visible同时被调用:1)当您退出片段屏幕时;2)在创建片段屏幕之前,当您第一次切换到片段屏幕时。
解决方案:
private boolean fragmentResume=false;
private boolean fragmentVisible=false;
private boolean fragmentOnCreated=false;
...
@Override
public View onCreateView(...){
...
//Initialize variables
if (!fragmentResume && fragmentVisible){ //only when first time fragment is created
myUIUpdate();
}
...
}
@Override
public void setUserVisibleHint(boolean visible){
super.setUserVisibleHint(visible);
if (visible && isResumed()){ // only at fragment screen is resumed
fragmentResume=true;
fragmentVisible=false;
fragmentOnCreated=true;
myUIUpdate();
}else if (visible){ // only at fragment onCreated
fragmentResume=false;
fragmentVisible=true;
fragmentOnCreated=true;
}
else if(!visible && fragmentOnCreated){// only when you go out of fragment screen
fragmentVisible=false;
fragmentResume=false;
}
}
说明:
fragmentResume,fragmentVisible:确保仅在创建片段并可见时才调用onCreateView()中的myUIUpdate(),而不是在恢复时调用。它还解决了当您处于第一个选项卡时的问题,即使第二个选项卡不可见,也会创建它。这解决了这个问题,并检查在onCreate时片段屏幕是否可见。
fragmentOnCreated:确保片段不可见,并且在第一次创建片段时不会被调用。所以现在,这个if子句只在从片段中滑出时被调用。
使现代化您可以像这样将所有这些代码放在BaseFragment代码中,并重写方法。
在版本androidx.fragment:frage:1.1.0的ViewPager2和ViewPager中,您可以使用onPause和onResume回调来确定用户当前可见的片段。当片段可见时调用onResume回调,当片段停止可见时调用onPause回调。
在ViewPager2的情况下,这是默认行为,但同样的行为可以很容易地为旧的好ViewPager启用。
要在第一个ViewPager中启用此行为,必须将FragmentPagerAdapter.behavior_RESUME_ONLY_CURRENT_FRAGMENT参数作为FragmentPagerAdapter构造函数的第二个参数传递。
FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
注意:在android jetpack的新版本Fragment中,setUserVisibleHint()方法和带有一个参数的FragmentPagerAdapter构造函数现在已被弃用。
我重写了关联的FragmentStatePagerAdapter的Count方法,并让它返回总计数减去要隐藏的页数:
public class MyAdapter : Android.Support.V13.App.FragmentStatePagerAdapter
{
private List<Fragment> _fragments;
public int TrimmedPages { get; set; }
public MyAdapter(Android.App.FragmentManager fm) : base(fm) { }
public MyAdapter(Android.App.FragmentManager fm, List<Android.App.Fragment> fragments) : base(fm)
{
_fragments = fragments;
TrimmedPages = 0;
}
public override int Count
{
//get { return _fragments.Count; }
get { return _fragments.Count - TrimmedPages; }
}
}
因此,如果最初有3个片段添加到ViewPager中,并且在满足某些条件之前只应显示前2个片段,则通过将TrimmerdPages设置为1来覆盖页面计数,并且它应只显示前两个页面。
这对结尾的页面很有用,但对开头或中间的页面没有帮助(尽管有很多方法)。
package com.example.com.ui.fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.com.R;
public class SubscribeFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_subscribe, container, false);
return view;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// called here
}
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
只有这对我有用!!而setUserVisibleHint(…)现在已被弃用(我在末尾附上了文档),这意味着大多数其他答案已被弃用;-)
public class FragmentFirewall extends Fragment {
/**
* Required cause "setMenuVisibility(...)" is not guaranteed to be
* called after "onResume()" and/or "onCreateView(...)" method.
*/
protected void didVisibilityChange() {
Activity activity = getActivity();
if (isResumed() && isMenuVisible()) {
// Once resumed and menu is visible, at last
// our Fragment is really visible to user.
}
}
@Override
public void onResume() {
super.onResume();
didVisibilityChange();
}
@Override
public void setMenuVisibility(boolean visible) {
super.setMenuVisibility(visible);
didVisibilityChange();
}
}
已测试并与NaviagationDrawer一起使用,TheisMenuVisible()将始终返回true(onResume()似乎足够了,但我们也希望支持ViewPager)。
setUserVisibleHint已弃用。如果重写此方法,则传入true时实现的行为应移动到Fragment.onResume(),传入false时实现的操作应移动到Fragment.onPause()。
kris larson在这里发布的另一个替代pageradapter中setPrimaryItem的解决方案几乎对我有效。此外,我还从片段中的视图等获得了NPE,因为在最初几次调用此方法时,还没有准备好。通过以下更改,这对我有效:
private int mCurrentPosition = -1;
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
if (position == mCurrentPosition) {
return;
}
if (object instanceof MyWhizBangFragment) {
MyWhizBangFragment fragment = (MyWhizBangFragment) object;
if (fragment.isResumed()) {
mCurrentPosition = position;
fragment.doTheThingYouNeedToDoOnBecomingVisible();
}
}
}