Aktualizuj dane w ListFragment jako część Viewpagera
Używam Viewpagera zgodności V4 w Androidzie. Mój FragmentActivity ma kilka danych, które mają być wyświetlane na różne sposoby na różnych stronach w moim Viewpagerze. Do tej pory mam tylko 3 instancje tego samego ListFragment, ale w przyszłości będę miał 3 instancje różnych Listfragment. ViewPager znajduje się na pionowym ekranie telefonu, listy nie są obok siebie.
Teraz przycisk na ListFragment rozpoczyna oddzielną aktywność na całej stronie (poprzez FragmentActivity), który zwraca i FragmentActivity modyfikuje dane, zapisuje je, a następnie próbuje zaktualizować wszystkie widoki w swoim Viewpagerze. Tu utknąłem.
public class ProgressMainActivity extends FragmentActivity
{
MyAdapter mAdapter;
ViewPager mPager;
@Override
public void onCreate(Bundle savedInstanceState)
{
...
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.viewpager);
mPager.setAdapter(mAdapter);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
...
updateFragments();
...
}
public void updateFragments()
{
//Attempt 1:
//mAdapter.notifyDataSetChanged();
//mPager.setAdapter(mAdapter);
//Attempt 2:
//HomeListFragment fragment = (HomeListFragment) getSupportFragmentManager().findFragmentById(mAdapter.fragId[0]);
//fragment.updateDisplay();
}
public static class MyAdapter extends FragmentPagerAdapter implements
TitleProvider
{
int[] fragId = {0,0,0,0,0};
public MyAdapter(FragmentManager fm)
{
super(fm);
}
@Override
public String getTitle(int position){
return titles[position];
}
@Override
public int getCount(){
return titles.length;
}
@Override
public Fragment getItem(int position)
{
Fragment frag = HomeListFragment.newInstance(position);
//Attempt 2:
//fragId[position] = frag.getId();
return frag;
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE; //To make notifyDataSetChanged() do something
}
}
public class HomeListFragment extends ListFragment
{
...
public static HomeListFragment newInstance(int num)
{
HomeListFragment f = new HomeListFragment();
...
return f;
}
...
Teraz, Jak widać, moja pierwsza próba była do notifyDataSetChanged na całym FragmentPagerAdapter, a to pokazało, aby aktualizować dane czasami, ale inne mam Nielegalstateexception: nie można wykonać tej akcji po onSaveInstanceState.
Moja druga próba polegała na próbie wywołania funkcji aktualizacji w moim ListFragment, ale getId w getItem zwrócił 0. Jak na docs próbowałem przez
Uzyskanie odniesienia do FragmentManager ' a, używając findFragmentById () lub findFragmentByTag ()
Ale nie znam znacznika ani identyfikatora moich fragmentów! Mam android: id= "@ + id / ViewPager"dla ViewPager i android:id=" @android:id/list " dla mojego ListView w układzie ListFragment, ale myślę, że nie są one przydatne.
Więc, jak Mogę albo: a) aktualizować całość ViewPager bezpiecznie za jednym razem ( najlepiej zwracając użytkownika na stronę, na której był wcześniej) - jest ok, że użytkownik widzi zmianę widoku. Lub najlepiej, b) wywołaj funkcję w każdym dotkniętym ListFragment, aby ręcznie zaktualizować ListView.
Każda pomoc będzie z wdzięcznością przyjęta!10 answers
Spróbuj nagrać znacznik za każdym razem, gdy fragment jest tworzony jako instancja.
public class MPagerAdapter extends FragmentPagerAdapter {
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
public MPagerAdapter(FragmentManager fm) {
super(fm);
mFragmentManager = fm;
mFragmentTags = new HashMap<Integer, String>();
}
@Override
public int getCount() {
return 10;
}
@Override
public Fragment getItem(int position) {
return Fragment.instantiate(mContext, AFragment.class.getName(), null);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
// record the fragment tag here.
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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-24 06:44:12
Odpowiedź Barkside ' a działa z FragmentPagerAdapter
, ale nie działa z FragmentStatePagerAdapter
, ponieważ nie ustawia znaczników na fragmentach, które przekazuje FragmentManager
.
Z FragmentStatePagerAdapter
wydaje się, że możemy przejść, używając jego instantiateItem(ViewGroup container, int position)
wywołania. Zwraca odniesienie do fragmentu w pozycji position
. Jeśli FragmentStatePagerAdapter
zawiera już odniesienie do danego fragmentu, instantiateItem
po prostu zwraca odniesienie do tego fragmentu i nie wywołuje getItem()
, aby utworzyć go ponownie.
Przypuśćmy, że obecnie patrzę na fragment # 50 i chcę uzyskać dostęp do fragment #49. Ponieważ są blisko, istnieje duża szansa, że #49 będzie już utworzony.
ViewPager pager = findViewById(R.id.viewpager);
FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) pager.getAdapter();
MyFragment f49 = (MyFragment) a.instantiateItem(pager, 49)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-16 20:44:06
OK, myślę, że znalazłem sposób na wykonanie prośby b) w moim własnym pytaniu, więc podzielę się z korzyścią dla innych. Znacznik fragmentów wewnątrz Viewpagera ma postać "android:switcher:VIEWPAGER_ID:INDEX"
, Gdzie VIEWPAGER_ID
to R.id.viewpager
w układzie XML, A Indeks to pozycja w viewpagerze. Jeśli więc pozycja jest znana (np. 0), mogę wykonać w updateFragments()
:
HomeListFragment fragment =
(HomeListFragment) getSupportFragmentManager().findFragmentByTag(
"android:switcher:"+R.id.viewpager+":0");
if(fragment != null) // could be null if not instantiated yet
{
if(fragment.getView() != null)
{
// no need to call if fragment's onDestroyView()
//has since been called.
fragment.updateDisplay(); // do what updates are required
}
}
Nie mam pojęcia, czy to jest słuszny sposób na zrobienie tego, ale zrobi to, dopóki nie zaproponuje się czegoś lepszego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 13:20:20
Moim zdaniem drugie rozwiązanie na poniższej stronie, śledzenie wszystkich "aktywnych" stron fragmentów, jest lepsze: http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html
Odpowiedź z barkside jest dla mnie zbyt szorstka.Śledzisz wszystkie "aktywne" strony fragmentów. W takim przypadku strony fragmentów są śledzone w FragmentStatePagerAdapter, który jest używany przez ViewPager.
private final SparseArray<Fragment> mPageReferences = new SparseArray<Fragment>();
public Fragment getItem(int index) {
Fragment myFragment = MyFragment.newInstance();
mPageReferences.put(index, myFragment);
return myFragment;
}
Aby uniknąć trzymania odniesienia do "nieaktywne" strony fragmentów, musimy zaimplementować Destroyitem FragmentStatePagerAdapter (..."metoda": {]}
public void destroyItem(View container, int position, Object object) {
super.destroyItem(container, position, object);
mPageReferences.remove(position);
}
... a kiedy musisz uzyskać dostęp do aktualnie widocznej strony, wywołujesz:
int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);
... gdzie metoda getFragment(int) Myadaptera wygląda następująco:
public MyFragment getFragment(int key) {
return mPageReferences.get(key);
}
"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-01 11:48:46
OK, po przetestowaniu metody przez @barkside powyżej, nie mogłem go uruchomić z moją aplikacją. Potem przypomniałem sobie, że aplikacja IOSched2012 używa również viewpager
i tam znalazłem moje rozwiązanie. Nie używa żadnych identyfikatorów ani znaczników fragmentu, ponieważ nie są one przechowywane przez viewpager
w łatwo dostępny sposób.
Oto Ważne części z IOSched apps Home. Zwróć szczególną uwagę na komentarz , ponieważ w nim leży klucz.:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Since the pager fragments don't have known tags or IDs, the only way to persist the
// reference is to use putFragment/getFragment. Remember, we're not persisting the exact
// Fragment instance. This mechanism simply gives us a way to persist access to the
// 'current' fragment instance for the given fragment (which changes across orientation
// changes).
//
// The outcome of all this is that the "Refresh" menu button refreshes the stream across
// orientation changes.
if (mSocialStreamFragment != null) {
getSupportFragmentManager().putFragment(outState, "stream_fragment",
mSocialStreamFragment);
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (mSocialStreamFragment == null) {
mSocialStreamFragment = (SocialStreamFragment) getSupportFragmentManager()
.getFragment(savedInstanceState, "stream_fragment");
}
}
I przechowuj instancje Ciebie Fragments
w FragmentPagerAdapter
Tak:
private class HomePagerAdapter extends FragmentPagerAdapter {
public HomePagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return (mMyScheduleFragment = new MyScheduleFragment());
case 1:
return (mExploreFragment = new ExploreFragment());
case 2:
return (mSocialStreamFragment = new SocialStreamFragment());
}
return null;
}
Pamiętaj również, aby strzec swoich Fragment
połączeń w ten sposób:
if (mSocialStreamFragment != null) {
mSocialStreamFragment.refresh();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-14 02:26:12
Możesz skopiować FragmentPagerAdapter
i zmodyfikować kod źródłowy, dodać metodę getTag()
Na przykład
public abstract class AppFragmentPagerAdapter extends PagerAdapter {
private static final String TAG = "FragmentPagerAdapter";
private static final boolean DEBUG = false;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private Fragment mCurrentPrimaryItem = null;
public AppFragmentPagerAdapter(FragmentManager fm) {
mFragmentManager = fm;
}
public abstract Fragment getItem(int position);
@Override
public void startUpdate(ViewGroup container) {
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
String name = getTag(position);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
getTag(position));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment) object).getView());
mCurTransaction.detach((Fragment) object);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.executePendingTransactions();
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment) object).getView() == view;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
public long getItemId(int position) {
return position;
}
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
protected abstract String getTag(int position);
}
Następnie rozszerz go, Nadpisz te abstrakcyjne metody, nie musisz się bać zmiany grupy Androida
FragmentPageAdapter
kod źródłowy w przyszłości
class TimeLinePagerAdapter extends AppFragmentPagerAdapter {
List<Fragment> list = new ArrayList<Fragment>();
public TimeLinePagerAdapter(FragmentManager fm) {
super(fm);
list.add(new FriendsTimeLineFragment());
list.add(new MentionsTimeLineFragment());
list.add(new CommentsTimeLineFragment());
}
public Fragment getItem(int position) {
return list.get(position);
}
@Override
protected String getTag(int position) {
List<String> tagList = new ArrayList<String>();
tagList.add(FriendsTimeLineFragment.class.getName());
tagList.add(MentionsTimeLineFragment.class.getName());
tagList.add(CommentsTimeLineFragment.class.getName());
return tagList.get(position);
}
@Override
public int getCount() {
return list.size();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-14 03:09:39
Działa również bez problemów:
Gdzieś w układzie fragmentu strony:
<FrameLayout android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone" android:id="@+id/fragment_reference">
<View android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone"/>
</FrameLayout>
W fragmencie oncreateview ():
...
View root = inflater.inflate(R.layout.fragment_page, container, false);
ViewGroup ref = (ViewGroup)root.findViewById(R.id.fragment_reference);
ref.setTag(this);
ref.getChildAt(0).setTag("fragment:" + pageIndex);
return root;
Oraz metoda zwracania fragmentu z Viewpagera, jeśli istnieje:
public Fragment getFragment(int pageIndex) {
View w = mViewPager.findViewWithTag("fragment:" + pageIndex);
if (w == null) return null;
View r = (View) w.getParent();
return (Fragment) r.getTag();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-17 19:40:28
Alternatywnie możesz zastąpić metodę setPrimaryItem
z FragmentPagerAdapter
w następujący sposób:
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (mCurrentFragment != object) {
mCurrentFragment = (Fragment) object; //Keep reference to object
((MyInterface)mCurrentFragment).viewDidAppear();//Or call a method on the fragment
}
super.setPrimaryItem(container, position, object);
}
public Fragment getCurrentFragment(){
return mCurrentFragment;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 13:19:31
Chcę podać moje podejście, na wypadek, gdyby mogło pomóc komuś innemu:
To jest mój adapter pagera:
public class CustomPagerAdapter extends FragmentStatePagerAdapter{
private Fragment[] fragments;
public CustomPagerAdapter(FragmentManager fm) {
super(fm);
fragments = new Fragment[]{
new FragmentA(),
new FragmentB()
};
}
@Override
public Fragment getItem(int arg0) {
return fragments[arg0];
}
@Override
public int getCount() {
return fragments.length;
}
}
W mojej działalności mam:
public class MainActivity {
private ViewPager view_pager;
private CustomPagerAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter = new CustomPagerAdapter(getSupportFragmentManager());
view_pager = (ViewPager) findViewById(R.id.pager);
view_pager.setAdapter(adapter);
view_pager.setOnPageChangeListener(this);
...
}
}
Następnie, aby uzyskać bieżący fragment, robię to:
int index = view_pager.getCurrentItem();
Fragment currentFragment = adapter.getItem(index);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-28 22:35:01
To jest moje rozwiązanie, ponieważ nie muszę śledzić moich kart i muszę je wszystkie odświeżyć.
Bundle b = new Bundle();
b.putInt(Constants.SharedPreferenceKeys.NUM_QUERY_DAYS,numQueryDays);
for(android.support.v4.app.Fragment f:getSupportFragmentManager().getFragments()){
if(f instanceof HomeTermFragment){
((HomeTermFragment) f).restartLoader(b);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-25 18:44:50