Как узнать, виден ли фрагмент?

Я использую библиотеку поддержки v4, и мои вопросы: как узнать, видим ли фрагмент? и как я могу изменить свойства Макета, раздутые во Фрагменте? Заранее спасибо.

---Редактировать---

Я использую фрагменты, как в учебнике Android разработчиков с FragmentActivity

15 ответов

Решение

Вы должны быть в состоянии сделать следующее:

MyFragmentClass test = (MyFragmentClass) getSupportFragmentManager().findFragmentByTag("testID");
if (test != null && test.isVisible()) {
     //DO STUFF
}
else {
    //Whatever
}

И то и другое isVisible() а также isAdded() вернуть true как только Fragment создан, и даже на самом деле не виден. Единственное решение, которое действительно работает:

if (isAdded() && isVisible() && getUserVisibleHint()) {
    ... do your thing
}

Это делает работу. Период.

Если вы хотите знать, когда использование смотрит на фрагмент, вы должны использовать

yourFragment.isResumed()

вместо

yourFragment.isVisible()

Прежде всего isVisible() уже проверяет isAdded() так что нет необходимости звонить обоим. Во-вторых, не из этих двух означает, что пользователь фактически видит ваш фрагмент. Только isResumed() гарантирует, что ваш фрагмент находится перед пользователем, и пользователь может взаимодействовать с ним, если это то, что вы ищете.

Вы можете попробовать так:

Fragment currentFragment = getFragmentManager().findFragmentById(R.id.fragment_container);

или же

Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);

В этом случае вы проверяете, является ли currentFragment экземпляром YourFragment

if (currentFragment instanceof YourFragment) {
     Log.v(TAG, "your Fragment is Visible");
}

Вы можете переопределить setMenuVisibility следующим образом:

@Override
public void setMenuVisibility(final boolean visible) {
   if (visible) {
      //Do your stuff here
   }

   super.setMenuVisibility(visible);
}

getUserVisibleHint() сбывается только тогда, когда фрагмент виден и виден

Одна вещь, о которой нужно знать, это то, что isVisible() возвращает видимое состояние текущего фрагмента. Существует проблема в библиотеке поддержки, где, если у вас есть вложенные фрагменты, и вы скрываете родительский фрагмент (и, следовательно, все дочерние элементы), дочерний элемент все еще говорит, что он виден.

isVisible() является окончательным, поэтому не может переопределить, к сожалению. Мой обходной путь должен был создать BaseFragment Класс, который расширяет все мои фрагменты, а затем создайте метод следующим образом:

public boolean getIsVisible()
{
    if (getParentFragment() != null && getParentFragment() instanceof BaseFragment)
    {
        return isVisible() && ((BaseFragment) getParentFragment()).getIsVisible();
    }
    else
    {
        return isVisible();
    }
}

я делаю isVisible() && ((BaseFragment) getParentFragment()).getIsVisible(); потому что мы хотим вернуть false, если какой-либо из родительских фрагментов скрыт.

Это, кажется, делает трюк для меня.

ArticleFragment articleFrag = (ArticleFragment)
            getSupportFragmentManager().findFragmentById(R.id.article_fragment);

    if (articleFrag != null && articleFrag.isVisible()) {

        // Call a method in the ArticleFragment to update its content
        articleFrag.updateArticleView(position);
    }

см. http://developer.android.com/training/basics/fragments/communicating.html

На всякий случай, если вы используете макет фрагмента с ViewPager (TabLayout), вы можете легко запросить текущий (передний) фрагмент с помощью метода ViewPager.getCurrentItem(). Это даст вам индекс страницы.

Сопоставление индекса страницы с фрагментом [класс] должно быть простым, поскольку вы уже выполнили сопоставление в адаптере, производном от FragmentPagerAdapter.

      int i = pager.getCurrentItem();

Вы можете зарегистрироваться для получения уведомлений об изменении страницы,

      ViewPager pager = (ViewPager) findViewById(R.id.container);
pager.addOnPageChangeListener(this);

Конечно, вы должны реализовать интерфейс ViewPager.OnPageChangeListener

      public class MainActivity 
    extends AppCompatActivity
    implements ViewPager.OnPageChangeListener
{
    public void onPageSelected (int position)
    {
        // we get notified here when user scrolls/switches Fragment in ViewPager -- so
        // we know which one is in front.
        Toast toast = Toast.makeText(this, "current page " + String.valueOf(position), Toast.LENGTH_LONG);
        toast.show();
    }

    public void onPageScrolled (int position, float positionOffset, int positionOffsetPixels) {
    }

    public void onPageScrollStateChanged (int state) {
    }
}

Мой ответ здесь может немного отличаться от вопроса. Но как новичок в приложениях для Android я столкнулся именно с этой проблемой и нигде не нашел ответа. Итак, разработал вышеупомянутое решение и разместил его здесь - возможно, кто-то сочтет его полезным.

Изменить: вы можете объединить этот метод с LiveData, на который подписываются фрагменты. Далее, если вы укажете своим фрагментам индекс страницы в качестве аргумента конструктора, вы можете создать простую функцию amIvisible() в своем классе фрагментов.

В MainActivity:

      private final MutableLiveData<Integer> current_page_ld = new MutableLiveData<>();
public LiveData<Integer> getCurrentPageIdx() { return current_page_ld; }

public void onPageSelected(int position) {
    current_page_ld.setValue(position);
}

public class MyPagerAdapter extends FragmentPagerAdapter
{
    public Fragment getItem(int position) {
        // getItem is called to instantiate the fragment for the given page: But only on first
        // creation -- not on restore state !!!
        // see: https://stackoverflow.com/a/35677363/3290848
        switch (position) {
            case 0:
                return MyFragment.newInstance(0);
            case 1:
                return OtherFragment.newInstance(1);
            case 2:
                return XYFragment.newInstance(2);
        }
        return null;
    }
}

Во фрагменте:

          public static MyFragment newInstance(int index) {
        MyFragment fragment = new MyFragment();
        Bundle args = new Bundle();
        args.putInt("idx", index);
        fragment.setArguments(args);
        return fragment;
    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mPageIndex = getArguments().getInt(ARG_PARAM1);
        }
        ...
    }
    
    public void onAttach(Context context)
    {
        super.onAttach(context);
        MyActivity mActivity = (MyActivity)context;

        mActivity.getCurrentPageIdx().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer data) {
                if (data == mPageIndex) {
                    // have focus
                } else {
                    // not in front
                }
            }
        });
    }

Добавляя сюда некоторую информацию, которую я испытал:

fragment.isVisible только работает ( true/false) когда ты replaceFragment() в противном случае, если вы работаете с addFragment(), isVisible всегда возвращается true находится ли фрагмент позади другого фрагмента.

Ни одно из вышеперечисленных решений не помогло мне. Однако следующее работает как шарм:

<code>override fun setUserVisibleHint(isVisibleToUser: Boolean)</code>

Попробуйте, если у вас только один Fragment

if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
                    //TODO: Your Code Here
                }

getUserVisibleHint устарел, и у меня были проблемы с isVisible правда, когда другой фрагмент был addЭд перед ним. Это определяет видимость фрагмента на заднем стеке, используя его представление. Это может быть полезно, если ваша проблема связана с другими фрагментами в заднем стеке.

Расширение просмотра для определения того, отображается ли представление на экране: (см. Также отображается Как узнать,ли представление на экране в Android? )

      fun View.isVisibleOnScreen(): Boolean {
    if (!isShown) return false
    val actualPosition = Rect().also { getGlobalVisibleRect(it) }
    val screenWidth = Resources.getSystem().displayMetrics.widthPixels
    val screenHeight = Resources.getSystem().displayMetrics.heightPixels
    val screen = Rect(0, 0, screenWidth, screenHeight)
    return Rect.intersects(actualPosition, screen)
}

Затем определил прослушиватель заднего стека из фрагмента, наблюдая за верхним фрагментом в стеке (добавленным последним)

      fun Fragment.setOnFragmentStackVisibilityListener(onVisible: () -> Unit) {
    val renderDelayMillis = 300L
    parentFragmentManager.addOnBackStackChangedListener {
        Handler(Looper.getMainLooper()).postDelayed({
            if (isAdded) {
                val topStackFragment = parentFragmentManager.fragments[parentFragmentManager.fragments.size - 1]
                if (topStackFragment.view == view && isVisible && view!!.isVisibleOnScreen()) {
                    onVisible.invoke()
                }
            }
        }, renderDelayMillis)
    }
}

Прослушиватель заднего стека вызывается до того, как представление будет готово, поэтому потребовалась сколь угодно небольшая задержка. Лямбда вызывается, когда представление становится видимым.

В Котлине

если вы используетеFragmentPagerAdapterи с тех порgetUserVisibleHint()устарел в API 29, я предлагаю вам добавить параметр поведенияBEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTв вашем FragmentPagerAdapter следующим образом:

      FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

затем в вашем фрагменте вы можете проверить их состояние жизненного цикла:

      if(lifecycle.currentState == Lifecycle.State.RESUMED) {
  // do something when fragment is visible
}

Я использовал Android BottomNavigationView и управлял фрагментами с помощьюFragmentTransactions.hide(frag)иFragmentTransaction.show(frag). Итак, чтобы определить, виден фрагмент или нет, я использовал следующее:

      abstract class BaseFragment : Fragment() {

    open fun onFragmentVisible(){
        
    }
    
    override fun onStart() {
        super.onStart()
        if (!isHidden){
            onFragmentVisible()
        }
    }

    override fun onHiddenChanged(hidden: Boolean) {
        super.onHiddenChanged(hidden)
        if (!hidden){
            onFragmentVisible()
        }
    }
}

Вы можете расширить BaseFragment в своем фрагменте и реализовать егоonFragmentVisibleфункция.

Другие вопросы по тегам