Я знаю, что в StackOverflow есть много похожих вопросов, но мой вопрос немного отличается.
У меня есть вложенная иерархия фрагментов, как в нижерасположенной структуре:
Activity
|
|
AFragment
|
(ViewPager)
| |
| |
BFragment BFragment .....
|
(ViewPager)
| |
| |
CFragment CFragment ...
|
(ViewPager)
| |
| |
DFragment DFragment ...
Теперь я хочу знать, показывает ли DFragment
пользователю или нет?
Я попробовал много решений из StackOverflow, но не смог добиться успеха.
Что я пробовал:
Я попробовал setUserVisibleHint()
, но он возвращает true
для нескольких DFragment
в иерархии выше, что является причиной ViewPager
Я также пробовал по этим ссылкам: link1, link2, link3 и т.д.... но не получили фактического решения.
Ожидание помощи. Спасибо.
UPDATE
Класс адаптера
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Ответ 1
Вы можете проверить, отображается ли View
этот фрагмент на экране устройства:
Fragment fragment = ... // retrieve from `ViewPager` adapter.
View fragmentRootView = fragment.getView();
if (fragmentRootView != null && fragmentRootView.getGlobalVisibleRect(new Rect())) {
// fragment is visible
} else {
// fragment is not visible
}
getGlobalVisibleRect()
вернет true
, если часть представления будет видна на корневом уровне.
Ответ 2
попробуйте это
@Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (visible) {
}
else
{
}
}
Ответ 3
Вы можете override
setUserVisibleHint()
в каждом фрагменте, где вы хотите проверить, видно ли это или нет с дополнительным флагом. Для примера
boolean isVisited = false;
@Override
public void setUserVisibleHint(boolean isVisibleToUser)
{
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && !isVisited )
{
isVisited = true;
}
else if(isVisited)
{
// this fragment is already in front of user
}
}
У меня была схожая проблема. Как только мне нужно было загружать данные с сервера на fragment
только тогда, когда он был перед пользователем. Я решил это, как указано выше.
Надеюсь, это тоже поможет вам.
Ответ 4
Попробуй попробовать.
Добавляет isVisible()
и дополнительный уровень проверки видимости.
ViewPager viewPager=(ViewPager)findViewById(R.id.view_pager);
final Fragment[] fragments={new DFragment(),new MyFragment()}; //add all the other fragment objects present in the view pager......
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if(fragments[position].isVisible()){
//Cool stuff to do.
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
Ответ 5
Насколько я пробовал, я думаю, что это сработает. Попробуйте и дайте мне знать.
Сделайте что-то подобное в своей деятельности.
public boolean isFragmentVisible(SwipeAdapter swipeAdapter) {
SwipeAdapter swipeAdapterA = fragmentA.getViewPagerAdapter();
BFragment bFragment = (BFragment) swipeAdapterA.getFragment(0);
if (bFragment != null) {
SwipeAdapter swipeAdapterB = bFragment.getViewPagerAdapter();
CFragment cFragment = (CFragment) swipeAdapterA.getFragment(0);
if (cFragment != null) {
SwipeAdapter swipeAdapterC = cFragment.getViewPagerAdapter();
DFragment dFragment = (DFragment) swipeAdapterA.getFragment(0);
if (dFragment != null) {
return dFragment.isFragmentVisible();
}
}
}
return false;
}
Используйте этот класс в качестве адаптера viewpager
class SwipeAdapter extends FragmentPagerAdapter {
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
private String mPagetile[];
public SwipeAdapter(FragmentManager fm, Context context) {
super(fm);
mPagetile = context.getResources().getStringArray(R.array.pageTitle);
mFragmentManager = fm;
mFragmentTags = new HashMap<>();
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new "Your_fragment";
default:
break;
}
return null;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null)
return null;
return mFragmentManager.findFragmentByTag(tag);
}
@Override
public CharSequence getPageTitle(int position) {
return mPagetile[position];
}
@Override
public int getCount() {
return "Number of fragments";
}
}
Теперь в вашем DFragment сделайте это
boolean isVisible=false;
@Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
isVisible=menuVisible;
}
public boolean isFragmentVisible(){
return isVisible;
}
Сообщите мне, если это сработает, если не сообщите мне причину.
Ответ 6
Мы можем легко отобразить, какой фрагмент теперь виден с помощью метода mViewPager.getCurrentItem()
. Этот метод дает int
номер значения, из которого мы можем найти текущий фрагмент.
- Для доступа и получения результата этого метода у нас есть два варианта:
- сделать
Static
экземпляр viewPager в действии и использовать везде, где мы хотите
- снова создайте экземпляр
viewPager
в фрагменте (findViewById()
)
и используя один из выше двух методов getCurrentItem()
в фрагменте, чтобы найти, какой фрагмент теперь виден.
Например,
- В MainActivity (активность, которая используется для ViewPager)
определить
public static ViewPager mViewPager;
- В фрагменте
MainActivity.mViewPager.getCurrentItem()
для получения текущего фрагмента
Кроме того, метод, показывающий, какой фрагмент виден:
public void whichIsVisible() {
String msg = "";
int curFragNo = MainActivity.mViewPager.getCurrentItem();
switch (curFragNo) {
case 0:
msg = "AFragment is visible.";
break;
case 1:
msg = "BFragment is visible.";
break;
case 2:
msg = "CFragment is visible.";
break;
case 3:
msg = "DFragment is visible.";
break;
}
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
(2) Для нестатического использования
- получить
viewPager
экземпляр в фрагменте ниже кода,
viewPager=(ViewPager) view.getRootView().findViewById(R.id.container);
а затем
viewPager.getCurrentItem()
, чтобы получить индекс и найти текущий видимый фрагмент в соответствии с использованием.
Ответ 7
Самый простой вариант может быть.
В onCreate
вашего DF fragment
сохраните boolean
в preferences
. я Key = is_fragment_visible_to_user
. И измените его на true
.
И в onStop
или onDestroy
(в зависимости от вашего требования) измените значение на false
. При этом вы можете легко проверить это, обратившись к значению предпочтений. В случае нескольких экземпляров вы можете сохранить тег другим ключом.
Ответ 8
Когда у меня была аналогичная проблема, я решил ее, используя этот хакерский метод "под капотом", например
Внутри FragmentPagerAdapter
добавить эту функцию.
public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return fm.findFragmentByTag(name);
}
private static String makeFragmentName(int viewId, int index) {
return "android:switcher:" + viewId + ":" + index;
}
Это не самое изящное решение, но пока оно работает
Внимание
Это частный метод, внутренний для ViewPager, который может быть изменен в любое время или для любой версии ОС.
Ответ 9
Хотя прошлое так долго, но я все еще хочу дать свою программу, потому что я обнаружил, что получить видимое состояние Фрагмента действительно нелегко. По вашему запросу вам необходимо решить следующие три вопроса:
- Как использовать FragmentPagerAdapter или FragmentStatePagerAdapter
- Как определить видимое состояние фрагмента
- Как обращаться с вложенным фрагментом
Сначала я определил базовый адаптер, чтобы помочь мне получить фрагмент в ViewPager. Если позиция и тип фрагмента изменяются в ViewPager, вы должны использовать метод public int getItemPosition(Object object) method
и public Fragment getItem(int position)
, чтобы получить правильную позицию.
/**
* Created by Kilnn on 2017/7/12.
* A abstract FragmentStatePagerAdapter which hold the fragment reference.
*/
public abstract class SmartFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
private SparseArray<WeakReference<Fragment>> registeredFragments = new SparseArray<>();
public SmartFragmentStatePagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, new WeakReference<>(fragment));
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
WeakReference<Fragment> reference = registeredFragments.get(position);
return reference != null ? reference.get() : null;
}
}
Затем я определяю базовый Фрагмент для обработки видимого состояния. Но есть небольшой недостаток в том, что вы должны указать один и тот же тип коммутатора для одного набора фрагментов.
import android.support.annotation.IntDef;
import android.support.v4.app.Fragment;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Created by Kilnn on 2017/7/12.
* A smart fragment know itself visible state.
*/
public abstract class SmartFragment extends Fragment {
private boolean isFragmentVisible;
@Override
public void onResume() {
super.onResume();
int switchType = getSwitchType();
if (switchType == ATTACH_DETACH) {
notifyOnFragmentVisible();
} else if (switchType == SHOW_HIDE) {
if (!isHidden()) {
notifyOnFragmentVisible();
}
} else if (switchType == VIEW_PAGER) {
//If the parent fragment exist and hidden when activity destroy,
//when the activity restore, The parent Fragment will be restore to hidden state.
//And the sub Fragment which in ViewPager is also be restored, and the onResumed() method will callback.
//And The sub Fragment getUserVisibleHint() method will return true if it is in active position.
//So we need to judge the parent Fragment visible state.
if (getUserVisibleHint() && isParentFragmentVisible()) {
notifyOnFragmentVisible();
}
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
int switchType = getSwitchType();
if (switchType == VIEW_PAGER) {
if (isVisibleToUser) {
//If ViewPager in ViewPager , the sub ViewPager will call setUserVisibleHint before onResume
if (isResumed()) {
notifyOnFragmentVisible();
}
} else {
notifyOnFragmentInvisible();
}
}
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
int switchType = getSwitchType();
if (switchType == SHOW_HIDE) {
if (hidden) {
notifyOnFragmentInvisible();
} else {
//Just judge for safe
if (isResumed()) {
notifyOnFragmentVisible();
}
}
}
}
@Override
public void onPause() {
super.onPause();
notifyOnFragmentInvisible();
}
private boolean isParentFragmentVisible() {
Fragment parent = getParentFragment();
if (parent == null) return true;
if (parent instanceof SmartFragment) {
return ((SmartFragment) parent).isFragmentVisible();
} else {
//TODO May be can't get the correct visible state if parent Fragment is not SmartFragment
return parent.isVisible();
}
}
public boolean isFragmentVisible() {
// Don't judge the state of the parent fragment,
// because if the parent fragment visible state changes,
// you must take the initiative to change the state of the sub fragment
// return isFragmentVisible && isParentFragmentVisible();
return isFragmentVisible;
}
public void notifyOnFragmentVisible() {
if (!isFragmentVisible) {
onFragmentVisible();
isFragmentVisible = true;
}
}
public void notifyOnFragmentInvisible() {
if (isFragmentVisible) {
onFragmentInvisible();
isFragmentVisible = false;
}
}
/**
* If this method callback, the Fragment must be resumed.
*/
public void onFragmentVisible() {
}
/**
* If this method callback, the Fragment maybe is resumed or in onPause().
*/
public void onFragmentInvisible() {
}
/**
* Fragments switch with attach/detach(replace)
*/
public static final int ATTACH_DETACH = 0;
/**
* Fragments switch with show/hide
*/
public static final int SHOW_HIDE = 1;
/**
* Fragments manage by view pager
*/
public static final int VIEW_PAGER = 2;
@Retention(RetentionPolicy.SOURCE)
@IntDef({ATTACH_DETACH, SHOW_HIDE, VIEW_PAGER})
public @interface SwitchType {
}
@SwitchType
public abstract int getSwitchType();
}
Наконец, передайте видимое состояние субфрагмента в родительском фрагменте, если вложенное использование.
public class ParentFragment extends SmartFragment{
....
@Override
public void onFragmentVisible() {
super.onFragmentVisible();
SmartFragment fragment = (SmartFragment) mAdapter.getRegisteredFragment(mViewPager.getCurrentItem());
if (fragment != null) {
fragment.notifyOnFragmentVisible();
}
}
@Override
public void onFragmentInvisible() {
super.onFragmentInvisible();
SmartFragment fragment = (SmartFragment) mAdapter.getRegisteredFragment(mViewPager.getCurrentItem());
if (fragment != null) {
fragment.notifyOnFragmentInvisible();
}
}
...
}
У меня есть тест с подключением/отсоединением, show/hide и тремя слоями ViewPager. Он работает нормально. Может быть, я должен был провести больше тестов, но для меня этого было достаточно.
Ответ 10
public static boolean isFragmentVisible(Fragment fragment) {
Activity activity = fragment.getActivity();
View focusedView = fragment.getView().findFocus();
return activity != null
&& focusedView != null
&& focusedView == activity.getWindow().getDecorView().findFocus();
}
оригинальное сообщение