I'm having an issue with the support library ViewPager. That ViewPager lives inside a fragment and it's composed of 3 tabs: one that will show some information, the second and third show a list of elements (so they both are beeing generated from the same fragment). When I scroll from the first to the second one everything works fine but if I try to scroll from the second one to the third something happens to the fragments and they don't show up again even the PagerTabStrip disappears when this happens. Also I tryed using the same type of fragment for the 3 tabs (the one that disappeared with the list)and everything seems to work fine, so I'm quite bugged about this. Also, the only log on the console related to the issue is this one:
W/FragmentManager: moveToState: Fragment state for StoreListFragment{3fb980e7 #2 id=0x7f0d0080 android:switcher:2131558528:2} not updated inline; expected state 3 found 2
This is the code for my parent Fragment:
public class StoreFragment extends Fragment {
#Bind(R.id.pager) ViewPager mPager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_store, container, false);
ButterKnife.bind(this, v);
setupViewPager();
return v;
}
private void setupViewPager() {
String[] titles = {getString(R.string.store_information),
getString(R.string.store_offers),
getString(R.string.store_products)
};
mPager.setAdapter(new StoresPagerAdapter(getChildFragmentManager(), titles));
}
}
This is the code for the PagerAdapter:
public class StoresPagerAdapter extends FragmentPagerAdapter {
private String[] mPageTitles;
public StoresPagerAdapter(FragmentManager fm, String[] titles) {
super(fm);
mPageTitles = titles;
}
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
return StoreInfoFragment.newInstance();
//return StoreListFragment.newInstance(position);
case 1:
return StoreListFragment.newInstance(position);
case 2:
return StoreListFragment.newInstance(position);
default:
return null;
}
}
#Override
public int getCount() {
return mPageTitles != null ? mPageTitles.length : 0;
}
#Override
public CharSequence getPageTitle(int position) {
return mPageTitles[position];
}
}
And the one for the Fragment:
public class StoreListFragment extends Fragment implements MyListListener {
#Bind(R.id.store_list) RecyclerView mStoreContentView;
private ArrayList<StoreModel> mStoreContents = new ArrayList<>();
public static StoreListFragment newInstance(int page) {
return new StoreListFragment();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStoreContents.add(new Schedule());
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_store_list, container, false);
ButterKnife.bind(this, v);
return v;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupList();
}
protected void setupList() {
LinearLayoutManager layoutManager = new LinearLayoutManager(this.getActivity());
mStoreContentView.setHasFixedSize(true);
mStoreContentView.setLayoutManager(layoutManager);
mStoreContentView.setAdapter(new ArrayAdapter(this, mStoreContents, R.layout.list_elem_locations));
}
#Override
public void onClickElement(int elementId, String elementName) {
Intent i = new Intent(this.getActivity(), DetailActivity.class);
startActivity(i);
}
}
I think your problem is here:
public static StoreListFragment newInstance(int page) {
return new StoreListFragment();
}
You're not using this page anywhere and you're getting two completely same StoreListFragments. You should add:
Bundle args = new Bundle();
args.putInt("key", page);
fragment.setArguments(args);
And then use this differentiation somewhere.
Related
I am trying to refresh the current fragment in the tabbed layout of my viewpager. I have one fragment (UserProgressFragment) that creates the ViewPager and sets the adapter (UserProgressAdapter) along with creating tabbed layout.
In my UserProgressAdapter, in the getItem() method I am returning two other fragments, (UserWeightTrackerFragment and UserCalorieCounterFragment) based on which tab i am on.
My issue is how do i refresh the fragment and update its content/view from the UserCalorieCounterFragment on a button click, because access to the viewpager and adapter are set in the UserProgressFragment? I have considered notifyDataChange for the adapter but i dont know how to call this from this class as it is set up on the UserProgressFragment class. The purpose of wanting to refresh this page is to update a specific view which is a chart, if context is needed.
I have attached the code below:
UserProgressFragment
public class UserProgressFragment extends Fragment{
public static UserProgressFragment newInstance() {
return new UserProgressFragment();
}
public UserProgressFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_user_progress, container, false);
ViewPager viewPager = view.findViewById(R.id.progress_viewpager);
viewPager.setAdapter(new UserProgressTabsAdapter(getChildFragmentManager()));
TabLayout tabLayout = view.findViewById(R.id.progress_sliding_tabs);
tabLayout.setupWithViewPager(viewPager, true);
return view;
}
}
UserProgressTabsAdapter
public class UserProgressTabsAdapter extends FragmentStatePagerAdapter {
private static final int PAGE_COUNT = 2;
private String tabTitles[] = new String[]{"Weight Tracker", "Calorie Counter"};
public UserProgressTabsAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return UserWeightTrackerFragment.newInstance(position);
case 1:
return UserCalorieCounterFragment.newInstance(position + 1);
}
return null;
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return tabTitles[position];
}
UserCalorieCounterFragment (need to refresh this one)
public class UserCalorieCounterFragment extends Fragment implements View.OnClickListener, AdapterView.OnItemSelectedListener {
public static final String ARG_PAGE = "ARG_PAGE";
public static UserCalorieCounterFragment newInstance(int page) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
UserCalorieCounterFragment fragment = new UserCalorieCounterFragment();
fragment.setArguments(args);
return fragment;
}
public UserCalorieCounterFragment() {
// Required empty public constructor
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int mPage = getArguments().getInt(ARG_PAGE);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_user_calorie_counter, container, false);
Button mAddCalories = view.findViewById(R.id.btn_add_calorie);
mAddCalories.setOnClickListener(this);
return view;
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_add_calorie:
//REFRESH/UPDATE HERE
break;
}
}
You can create a public method in your fragment which contains the view pager which will contain the notifyDataSetChanged() function. And in the view pager fragment/item, you can call getParentFragment() and type cast it to your parent/first fragment and access that public method to notifyDataSetChanged().
I am using ViewPager in my Android project, and I use FragmentStatePagerAdapter to set the pages.
class MyPageAdapter extends FragmentStatePagerAdapter {
List<Fragment> mList = new ArrayList<>();
public MyPageAdapter(FragmentManager fm) {
super(fm);
this.init();
}
private void init() {
mList.add(new FragmentOne());
mList.add(new FragmentTwo());
mList.add(new FragmentThree());
....
}
#Override
public Fragment getItem(int position) {
return mList.get(position);
}
}
And for each Fragment data will be loaded from the server once the view created, like this:
public class FragmentOne extends Fragment {
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(getViewResourceId(), container, false);
ButterKnife.bind(this, v);
setRetainInstance(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
mRecycleView.setLayotManager(linearLayoutManager);
endlessRecyclerViewScrollListener = new EndlessRecyclerViewScrollListener((LinearLayoutManager) mRecycleView.getLayoutManager()) {
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
loadByPage(page);
}
};
mRecycleView.addOnScrollListener(endlessRecyclerViewScrollListener);
return v;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mAdapter = createAdapter();
mRecycleView.setAdapter(mAdapter);
loadByPage(1); // load data from server
}
}
As shown, the Fragment contains a endless recyclerview.
So far so good. However once I change the selected view pageļ¼ I found that the data will be reloaded every time. For example, I have scroll 3 pages in FragmentOne, and then change to FragmentTwo, and when I change to FragmentOne back, FragmentOne will try load data of page 1.
Is it possible to avoid this?
you can do something like this :
private boolean isDataCalled = true;
private List<ItemModel> itemModelList;
and then
if (isDataCalled){
loadByPage(1); // Initialize itemModelList in this method
setItemAdapter(itemModelList);
isDataCalled = false;
}else {
setItemAdapter(itemModelList);
}
setItemAdapter method
private void setItemAdapter(List<ItemModel> itemModelList){
mAdapter = createAdapter(itemModelList);
mRecycleView.setAdapter(mAdapter);
}
I am trying to create an intro slider screen using fragments and ViewPager. I want to display multiple layouts in onCreateView method but don't know how to do it.
Any help please?
Main activity
public class MainActivity extends AppCompatActivity {
public ViewPager pager;
public The_fragment_adapter the_fragment_adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pager = (ViewPager) findViewById(R.id.pager);
the_fragment_adapter = new The_fragment_adapter(getSupportFragmentManager());
pager.setAdapter(the_fragment_adapter);
}
}
Fragment Class
public class The_fragment extends Fragment {
public The_fragment(){
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View my_view=inflater.inflate(R.layout.theui,container,false);
return my_view;
}
}
Adapter View
public class The_fragment_adapter extends FragmentStatePagerAdapter {
public int[] the_layouts = {R.layout.page1,R.layout.page2};
public The_fragment_adapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return new The_fragment();
}
#Override
public int getCount() {
return the_layouts.length;
}
}
A ViewPager is for paging multiple fragments, not layouts. The getItem() method should return the fragment you want for the position you want.
Create a second fragment with the layout you want:
public Fragment The_second_fragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View my_view=inflater.inflate(R.layout.page2,container,false);
return my_view;
}
}
Then change your adapter methods:
#Override
public Fragment getItem(int position) {
switch 0:
return new The_fragment();
switch 1:
return new The_second_fragment();
}
#Override
public int getCount() {
return 2;
}
See the excellent tutorial in the Android Developer's Guide. And, in the future if you follow the Java naming conventions you are more likely to get a better answer as other programmers will be able to understand your code more easily.
I use the example of a pager enter link description here
I have everything worked out but there is one problem. I have 4 different fragments. One of them contains ListView. When I click on an item in the ListView, change the global variable guestId. If then return to other fragments, these they should be changed. Because they use this variable as a parameter. But they do not change.
public class Guest extends Fragment {
...
private ViewPager pager;
private MyPagerAdapter adapter;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.guest, null);
...
if (InternetResiver.isOnline(getActivity())) {
setData();
} else {
AlertDialog alert = InternetResiver.getAlertDialog(getActivity());
alert.show();
}
return v;
}
public void startNewTread(String newId) {
guestId = newId;
Log.d("usernewid",newId);
setData();
}
private class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int pos) {
switch(pos) {
case 0: return Folio.newInstance(guestId,hotel,room);
case 1: return BalanceDetail.newInstance(guestId,hotel);
case 2: return History.newInstance(guestId,hotel);
case 3: return Record.newInstance(guestId,hotel,room);
default: return Folio.newInstance(guestId,hotel,room);
}
}
#Override
public int getCount() {
return 4;
}
}
public void setData() {
String link = String.format(......., guestId, hotel);
new MyAsincTask(getActivity()){........}.execute(link);
}
}
my fragment
public class Folio extends Fragment {
private ArrayList<FolioBean> folioBeans;
private TableLayout tableFolio;
private String guestId;
private String hotel;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.folio, null);
guestId = getArguments().getString("guestid");
hotel = getArguments().getString("hotel");
folioBeans = new ArrayList<>();
tableFolio = (TableLayout) v.findViewById(R.id.tableFolio);
if (InternetResiver.isOnline(getActivity())) {
setData();
} else {
AlertDialog alert = InternetResiver.getAlertDialog(getActivity());
alert.show();
}
return v;
}
public static Folio newInstance(String guestid,String hotel,String room) {
Folio f = new Folio();
Bundle b = new Bundle();
b.putString("guestid", guestid);
b.putString("hotel", hotel);
b.putString("room", room);
f.setArguments(b);
return f;
}
public void setData() {
String link = String.format(........., guestId, hotel);
new MyAsincTask(getActivity()){........}.execute(link);
}
}
other fragments are loaded using the same guestId
It is because ViewPager cached fragments. You need use FragmentStatePagerAdapter instead FragmentPagerAdapter, and call notifydatasetchanged() after changing guestId, and override
Override getItemPosition in your PagerAdapter like this:
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
see similar questions
Refresh images on FragmentStatePagerAdapter on resuming activity
I am using PagerSlidingTabs. I have two fragment and each fragment has one listview. I added TabListener, because i want to go first item in listview when user reselect the same tab. This is working only on the second fragment(tab). Problem is second listview's smoothScrollToPosition(0) method working but first listview's smoothScrollToPosition(0) method(all methods) not working. First listview is showing properly on screen but it's id seems to second listview's id.
PagerAdapter:
public class PagerAdapter extends FragmentPagerAdapter {
private Context mContext;
private int mPageCount;
public PagerAdapter(Context context, FragmentManager fm, int pageCount) {
super(fm);
this.mContext = context;
this.mPageCount = pageCount;
}
#Override
public int getCount() {
return mPageCount;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return Fragment1.newInstance(position);
case 1:
return Fragment2.newInstance(position);
default:
return null;
}
}}
Fragment1:
public class Fragment1 extends Fragment {
private static final String ARG_POSITION = "position";
private ListView mListView1;
private ListAdapter mAdapter;
public static Fragment1 newInstance(int position) {
Fragment1 f = new Fragment1();
Bundle b = new Bundle();
b.putInt(ARG_POSITION, position);
f.setArguments(b);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = inflater.inflate(R.layout.fragment1, container, false);
mListView1 = (ListView) mRootView.findViewById(R.id.listview1);
.
.
if (mAdapter == null) {
mAdapter = new ListAdapter(getActivity(), mListView1, R.layout.list_item, mDatas);
mListView1.setAdapter(mAdapter);
} else {
mAdapter.notifyDataSetChanged();
}
.
.
((HomeActivity) getActivity()).getPagerSlidingTabStrip().setOnTabListener(new TabListener() {
#Override
public void onTabReselected(View tab, int postion) {
mListView1.smoothScrollToPosition(0);
//mListView1's id seems to mListView2's id
//when reselect first tab, application acting like i reselect second tab.
}
});
} else {
((ViewGroup) mRootView.getParent()).removeView(mRootView);
}
return mRootView;
}}
Fragment2:
public class Fragment2 extends Fragment {
private static final String ARG_POSITION = "position";
private ListView mListView2;
private ListAdapter mAdapter;
public static Fragment2 newInstance(int position) {
Fragment2 f = new Fragment2();
Bundle b = new Bundle();
b.putInt(ARG_POSITION, position);
f.setArguments(b);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = inflater.inflate(R.layout.fragment2, container, false);
mListView2 = (ListView) mRootView.findViewById(R.id.listview2);
.
.
if (mAdapter == null) {
mAdapter = new ListAdapter(getActivity(), mListView2, R.layout.list_item, mDatas);
mListView2.setAdapter(mAdapter);
} else {
mAdapter.notifyDataSetChanged();
}
.
.
((HomeActivity) getActivity()).getPagerSlidingTabStrip().setOnTabListener(new TabListener() {
#Override
public void onTabReselected(View tab, int postion) {
mListView2.smoothScrollToPosition(0); //this line is working fine
}
});
} else {
((ViewGroup) mRootView.getParent()).removeView(mRootView);
}
return mRootView;
}}
Fragment1 and Fragment2 have only listview(id:listview1, id:listview2).
HomeActivity have PagerSlidingTabs view and viewpager.
How can i fix this problem?
When you set the listener from the second fragment, that replaces the listener from the first. That's why all the calls seem to be going to the second tab: its listener was the last one added, so it's receiving all the callbacks. One potential solution is to declare an interface, let's call it OnTabReselctedListener, that has a method onTabReselected(into position). Have your activity maintain a list of those interfaces, have your fragments implement that interface, and register those fragments as listeners in the activity. When the activity receives a reselection event, it can pass that to its children fragments; those fragments check if the position matches their own position and scroll to the top if they do. I hope that made sense; I'm writing this from my phone so I can't easily wrote sample code. If you want me to write some, let me know!