如何确定何时在ViewPager中可见碎片
问题: ViewPager
片段onResume()
在片段实际可见之前被触发。
例如,我有2个片段与ViewPager
和FragmentPagerAdapter
。 第二个片段仅适用于授权用户,我需要让用户在片段变得可见时使用警报对话框login。
但ViewPager
创build第二个片段时,第一个是可见的,以便caching第二个片段,并使其在用户开始滑动时可见。
所以onResume()
事件在第二个片段变得可见之前很久了。 这就是为什么我试图find一个事件,当第二个片段变得可见时触发,在适当的时刻显示对话框。
如何才能做到这一点?
更新 :Android支持库(修订11)终于修复了用户可见的提示问题 ,现在如果你使用支持库的片段,那么你可以安全地使用getUserVisibleHint()
或覆盖setUserVisibleHint()
来捕获更改描述gorn的答案。
更新1这是getUserVisibleHint()
一个小问题。 该值默认为true
。
// Hint provided by the app that this fragment is currently visible to the user. boolean mUserVisibleHint = true;
因此,在调用setUserVisibleHint()
之前尝试使用它时可能会出现问题。 作为一种解决方法,您可以像这样在onCreate
方法中设置值。
public void onCreate(@Nullable Bundle savedInstanceState) { setUserVisibleHint(false);
过时的答案:
在大多数情况下, ViewPager
只显示一个页面,但是如果您在Android Support Library pre-r11
中使用FragmentStatePagerAdapter
Android Support Library pre-r11
,则预caching片段也会处于“可见”状态(实际上不可见)。
我重写:
public class MyFragment extends Fragment { @Override public void setMenuVisibility(final boolean visible) { super.setMenuVisibility(visible); if (visible) { // ... } } // ... }
为了捕获片段的焦点状态,我认为这是您所说的“可见性”最适合的状态,因为ViewPager中只有一个片段实际上可以将其菜单项与父活动的项目放在一起。
如何确定何时在ViewPager中可见碎片
你可以通过在你的Fragment
覆盖setUserVisibleHint
来完成以下工作:
public class MyFragment extends Fragment { @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { } else { } } }
这似乎恢复了您所期望的正常的onResume()
行为。 它与按Home键离开应用程序,然后重新进入应用程序播放良好。 onResume()
不连续调用两次。
@Override public void setUserVisibleHint(boolean visible) { super.setUserVisibleHint(visible); if (visible && isResumed()) { //Only manually call onResume if fragment is already visible //Otherwise allow natural fragment lifecycle to call onResume onResume(); } } @Override public void onResume() { super.onResume(); if (!getUserVisibleHint()) { return; } //INSERT CUSTOM CODE HERE }
这是使用onPageChangeListener
另一种方法:
ViewPager pager = (ViewPager) findByViewId(R.id.viewpager); FragmentPagerAdapter adapter = new FragmentPageAdapter(getFragmentManager); pager.setAdapter(adapter); pager.setOnPageChangeListener(new OnPageChangeListener() { public void onPageSelected(int pageNumber) { // Just define a callback method in your fragment and call it like this! adapter.getItem(pageNumber).imVisible(); } public void onPageScrolled(int arg0, float arg1, int arg2) { // TODO Auto-generated method stub } public void onPageScrollStateChanged(int arg0) { // TODO Auto-generated method stub } });
有时会在 onCreateView()
之前调用setUserVisibleHint()
,有时之后会导致麻烦。
为了克服这个问题,你需要在setUserVisibleHint()
方法中检查isResumed()
。 但在这种情况下,我意识到setUserVisibleHint()
被调用, 只有当Fragment被恢复和可见,而不是创build时。
所以如果你想在Fragment visible
时候更新一些东西,把你的更新函数放在onCreate()
和setUserVisibleHint()
:
@Override public View onCreateView(...){ ... myUIUpdate(); ... } .... @Override public void setUserVisibleHint(boolean visible){ super.setUserVisibleHint(visible); if (visible && isResumed()){ myUIUpdate(); } }
更新:仍然我意识到myUIUpdate()
有时被调用两次,原因是,如果你有3个选项卡,这个代码是在第二个选项卡上,当你第一次打开第一个选项卡,第二个选项卡也创build,即使它不可见和myUIUpdate()
被调用。 然后,当您滑动到第二个选项卡时,将myUIUpdate()
if (visible && isResumed())
,因此myUIUpdate()
可能会在一秒钟内调用两次。
另一个问题是!visible
在setUserVisibleHint
!visible
setUserVisibleHint
被调用1)当你离开片段屏幕时2)在它被创build之前,当你第一次切换到分片屏幕时。
解:
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()
仅在创build并显示片段时调用,而不是在继续时调用。 当你在第一个选项卡时,它也解决了问题,第二个选项卡即使不可见也被创build。 这解决了这个问题,并检查当onCreate
时,碎片屏幕是否可见。
fragmentOnCreated
:确保片段不可见,并且在您第一次创build片段时不会调用。 所以现在这个if子句只在你从片段中滑出时被调用。
更新你可以像这样把所有这些代码放在BaseFragment
代码中,并覆盖方法。
覆盖FragmentPagerAdapter
子类中的setPrimaryItem()
。 我用这个方法,效果很好。
@Override public void setPrimaryItem(ViewGroup container, int position, Object object) { // This is what calls setMenuVisibility() on the fragments super.setPrimaryItem(container, position, object); if (object instanceof MyWhizBangFragment) { MyWhizBangFragment fragment = (MyWhizBangFragment) object; fragment.doTheThingYouNeedToDoOnBecomingVisible(); } }
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); } }
为此重写Fragment.onHiddenChanged()
。
public void onHiddenChanged(boolean hidden)
当片段的隐藏状态(由
isHidden()
返回)已更改时调用。 片段开始不隐藏; 这将被称为每当片段改变状态。参数
hidden
–boolean
:如果片段现在隐藏,则为真;如果不可见,则为false。
我发现onCreateOptionsMenu
和onPrepareOptionsMenu
方法仅在片段真正可见的情况下被调用。 我找不到像这些行为的任何方法,我也尝试OnPageChangeListener
但它不适合的情况下,例如,我需要一个variables在onCreate
方法初始化。
所以这两个方法可以用于解决这个问题,特别是对于短期和短期工作。
我认为,这是更好的解决scheme,但不是最好的。 我会用这个,但同时等待更好的解决scheme。
问候。
另一个解决scheme在这里重写了由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(); } } }
在片段内添加以下代码
@Override public void setMenuVisibility(final boolean visible) { super.setMenuVisibility(visible); if (visible && isResumed()) { } }
public abstract class FragmentHelpLoadDataWhenVisible extends Fragment { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,Bundle savedInstanceState) { if(getUserVisibleHint()){ // fragment is visible loadData(); } return super.onCreateView(inflater, container, savedInstanceState); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser && isResumed()) { // fragment is visible and have created loadData(); } } public void loadData(){ // data for fragment when it visible here } }
你需要setUserVisibleHint
和onCreateView
loadData()
,因为setUserVisibleHint
可以在onCreateView
之前调用,所以如果我们总是在setUserVisibleHint
loadData()
,我们可以得到NullPointerException
例如,我的viewpager有3个选项卡(Tab1,Tab2,Tab3)。
我第一次去这个viewpager时,函数会按顺序调用
Tab1 => setUserVisibleHint (isVisibleToUser = false) (isResumed = false) Tab2 => setUserVisibleHint (isVisibleToUser = false) (isResumed = false) Tab1 => setUserVisibleHint (isVisibleToUser = true) (isResumed = false) Tab1 => onCreateView (getUserVisibleHint() = true) Tab2 => onCreateView (getUserVisibleHint() = false)
在这种情况下, loadData()
被调用到Tab1
onCreateView
中(我们不会得到任何NullPointerException
)
然后我到Tab2,函数会按照顺序调用
Tab3 => setUserVisibleHint (isVisibleToUser = false) (isResumed = false) Tab1 => setUserVisibleHint (isVisibleToUser = false) (isResumed = true) Tab2 => setUserVisibleHint (isVisibleToUser = true) (isResumed = true) Tab3 => onCreateView (getUserVisibleHint() = false)
在这种情况下, loadData()
在Tab2
setUserVisibleHint
中被调用,就像我们所期望的那样
请注意, setUserVisibleHint(false)
不会在活动/片段停止处调用。 您仍然需要检查开始/停止以正确register/unregister
任何听众/等。
此外,如果您的片段以不可见状态启动,您将获得setUserVisibleHint(false)
你不想unregister
那里,因为你从来没有注册过这种情况。
@Override public void onStart() { super.onStart(); if (getUserVisibleHint()) { // register } } @Override public void onStop() { if (getUserVisibleHint()) { // unregister } super.onStop(); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser && isResumed()) { // register if (!mHasBeenVisible) { mHasBeenVisible = true; } } else if (mHasBeenVisible){ // unregister } }
当我试图让viewpager中的片段在屏幕上让用户看到时触发一个定时器,我遇到了这个问题。
定时器总是在用户看到片段之前开始。 这是因为片段中的onResume()
方法在我们可以看到片段之前被调用。
我的解决scheme是在onResume()
方法中进行检查。 当片段8是视图寻呼机当前片段时,我想调用某个方法“foo()”。
@Override public void onResume() { super.onResume(); if(viewPager.getCurrentItem() == 8){ foo(); //Your code here. Executed when fragment is seen by user. } }
希望这可以帮助。 我看到这个问题popup很多。 这似乎是我见过的最简单的解决scheme。 许多其他人不适合较低的API等。
我遇到过同样的问题。 ViewPager
执行其他片段生命周期事件,我无法改变这种行为。 我用碎片和可用的animation写了一个简单的寻呼机。 SimplePager
我们有一个MVP的特殊情况,片段需要通知演示者视图已经变得可见,并且演示者由Dagger在fragment.onAttach()
注入。
setUserVisibleHint()
是不够的,我们已经检测到需要解决的3个不同的情况( onAttach()
被提及,以便知道何时可以使用演示者):
-
片段刚刚创build。 系统进行以下调用:
setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null onAttach() ... onResume()
-
已经创build片段,并按下主页button。 当将应用程序恢复到前台时,这被称为:
onResume()
-
定位更改:
onAttach() // presenter available onResume() setUserVisibleHint()
我们只希望知道提示一次,让我们这样做:
@Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_list, container, false); setHasOptionsMenu(true); if (savedInstanceState != null) { lastOrientation = savedInstanceState.getInt(STATE_LAST_ORIENTATION, getResources().getConfiguration().orientation); } else { lastOrientation = getResources().getConfiguration().orientation; } return root; } @Override public void onResume() { super.onResume(); presenter.onResume(); int orientation = getResources().getConfiguration().orientation; if (orientation == lastOrientation) { if (getUserVisibleHint()) { presenter.onViewBecomesVisible(); } } lastOrientation = orientation; } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (presenter != null && isResumed() && isVisibleToUser) { presenter.onViewBecomesVisible(); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(STATE_LAST_ORIENTATION, lastOrientation); }
我在使用FragmentStatePagerAdapters
和3个选项卡时遇到了同样的问题。 当第一个标签被点击时,我必须显示一个Dilaog,并在点击其他标签时隐藏它。
重写setUserVisibleHint()
本身无助于查找当前的可见片段。
从第三个标签—–>第一个标签点击。 它为第二个片段和第一个片段触发两次。 我把它与isResumed()方法结合起来。
@Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); isVisible = isVisibleToUser; // Make sure that fragment is currently visible if (!isVisible && isResumed()) { // Call code when Fragment not visible } else if (isVisible && isResumed()) { // Call code when Fragment becomes visible. } }
在片段中使用GestureDetector.OnGestureListenerand
,当发生这种情况时,您将知道“this”片段是可见的
在片段中使用手势检测器
我在这里处理可见状态: 检查片段目前是否可见或不在android? 。 我将Fragment的开关types拆分为三种方式,如果嵌套使用,则在其父Fragment中处理子Fragment的可见状态。 它为我工作。
我覆盖了关联的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,并且只有前两个片段应显示,直到满足某些条件,则通过将TrimmedPages设置为1来覆盖页面计数,并且它应该只显示前两个页面。
这对于页面来说很好,但是对于开始或者中间的页面不会有帮助(尽pipe有很多方法可以做到这一点)。
- 如何模仿材质devise凸起的button风格,即使是前棒棒糖(减去特殊效果)?
- 如何在Eclipse中更改Android应用程序的图标?
- 当你testingActionBarActivity的时候,你需要使用Theme.Appcompat主题
- Android:我如何获得所有可用networking运营商的GSM信号强度
- 如何将日志输出从logcatredirect到Android设备上的SD卡?
- 可以从工作线程调用NoticationManager.notify()吗?
- 在Android中布局被销毁的方法
- 如何在Android中通过URL加载ImageView?
- 从XML格式的string资源中设置TextView文本