ViewPager/RecyclerView initialization best practices - android

I am developing an android weather application that includes a Tablelayout with 3 pages. The adapter is a FragmentStatePagerAdaptater and in each of them is a Fragment that contains a RecyclerView.
The application receives and processes the weather datas in an asynctask and then updates the ViewPager with the received datas.
My question is to know that it is the best practice when initializing the ViewPager, ie, I initialize the ViewPager in my MainActivity OnCreate() with empty datas in Fragment because I do not yet have the weather datas at this moment.
Once the data received I notify the ViewPager that the datas has changed and then in the FragmentStatePagerAdapter in getItemPosition() method I perform the update of the RecyclerView of the fragments. The RecyclerView are updated with a method equivalent of a contrustor that pass the new datas in the RecyclerViewAdaptater and a notifyDataSetChanged() to notify that the data has changed.
But maybe a best practice would be to wait for receiving the weather datas and then initialize the Viewpager adapter with the datas.
After if the user requests a data updates only notify the ViewPager that the data has changed as is already the case in my application.
This is the code of my approach :
MainActivity OnCreate() :
viewPager = (ViewPager)findViewById(R.id.viewpager);
viewPager.setOffscreenPageLimit(2);
SampleFragmentPagerAdapter adapter = new SampleFragmentPagerAdapter(getSupportFragmentManager());
adapter.addFrag(PageFragment.newInstance(0), "today");
adapter.addFrag(PageFragment.newInstance(1), "tomorrow");
adapter.addFrag(PageFragment.newInstance(2), "7 days");
viewPager.setAdapter(adapter);
Asynctask onPostExecute(Climat climat)
mSwipeRefreshLayout.setRefreshing(false);
if (climat != null) {
MainActivity.climat = climat;
viewPager.getAdapter().notifyDataSetChanged();
// Set page 1
viewPager.setCurrentItem(0);
}
FragmentStatePagerAdaptater :
class SampleFragmentPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>(3);
private final List<String> mFragmentTitleList = new ArrayList<>(3);
SampleFragmentPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Returns total number of pages
#Override
public int getCount() {
return mFragmentList.size();
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
Log.i(TAG, "Fragment getItem position " + position);
return mFragmentList.get(position);
}
void addFrag(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
// Refresh fragment
#Override
public int getItemPosition(Object object) {
PageFragment f = (PageFragment ) object;
if (f != null) {
f.update();
}
return super.getItemPosition(object);
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return mFragmentTitleList.get(position);
}
}
PageFragment :
public class PageFragment extends Fragment implements Updateable {
private static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
private Climat climat = null;
private RecyclerView rv1;
private RecyclerView rv2;
private RecyclerView rv3;
private ClimatAdaptateurToday climatAdaptateurTodayRv1;
private ClimatAdaptateurToday climatAdaptateurTodayRv2;
private ClimatAdaptateur climatAdaptateur;
// newInstance constructor for creating fragment with arguments
public static PageFragment newInstance(int page) {
PageFragment fragment = new PageFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
fragment.setArguments(args);
return fragment;
}
/**
* When creating, retrieve this instance's number from its arguments.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPage = getArguments() != null ? getArguments().getInt(ARG_PAGE) : 0;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
switch (mPage) {
case 0:
rv1 = (RecyclerView) view.findViewById(R.id.recyclerView);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
rv1.setHasFixedSize(true);
// use a linear layout manager
RecyclerView.LayoutManager layoutRv1 = new LinearLayoutManager(getActivity());
rv1.setLayoutManager(layoutRv1);
rv1.setItemAnimator(new DefaultItemAnimator());
climatAdaptateurTodayRv1 = new ClimatAdaptateurToday(climat, mPage);
climatAdaptateurTodayRv1.setHasStableIds(true);
rv1.setAdapter(climatAdaptateurTodayRv1);
break;
case 1:
rv2 = (RecyclerView) view.findViewById(R.id.recyclerView);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
rv2.setHasFixedSize(true);
// use a linear layout manager
RecyclerView.LayoutManager layoutRv2 = new LinearLayoutManager(getActivity());
rv2.setLayoutManager(layoutRv2);
rv2.setItemAnimator(new DefaultItemAnimator());
climatAdaptateurTodayRv2 = new ClimatAdaptateurToday(climat, mPage);
climatAdaptateurTodayRv2.setHasStableIds(true);
rv2.setAdapter(climatAdaptateurTodayRv2);
break;
case 2:
rv3 = (RecyclerView) view.findViewById(R.id.recyclerView);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
rv3.setHasFixedSize(true);
// use a linear layout manager
RecyclerView.LayoutManager layoutRv3 = new LinearLayoutManager(getActivity());
rv3.setLayoutManager(layoutRv3);
rv3.setItemAnimator(new DefaultItemAnimator());
climatAdaptateur = new ClimatAdaptateur(null,
null, null);
climatAdaptateur.setHasStableIds(true);
rv3.setAdapter(climatAdaptateur);
break;
}
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.content_main, container, false);
}
// To update fragment in ViewPager, we should implement a public method for the fragment,
// and do updating stuff in this method.
#Override
public void update() {
this.climat = MainActivity.climat;
if (climat != null) {
switch (mPage) {
case 0:
climatAdaptateurTodayRv1.updateData(climat, mPage);
climatAdaptateurTodayRv1.notifyDataSetChanged();
rv1.smoothScrollToPosition(0);
break;
case 1:
climatAdaptateurTodayRv2.updateData(climat, mPage);
climatAdaptateurTodayRv2.notifyDataSetChanged();
rv2.smoothScrollToPosition(0);
break;
case 2:
climatAdaptateur.updateData(climat.getClimatInfoDailyArray(),
climat.getTempsArray(), climat.getLieux());
climatAdaptateur.notifyDataSetChanged();
rv3.smoothScrollToPosition(0);
break;
}
}
}
}
interface Updateable {
void update();
}
Thanks for your help !!

Related

viewpager2 get adapter of current fragment cause NullPointerException

I use mAdapter.getTotalPriceInRecyclerView() to get total price in current selected page in viewpage2+Tablayout.
but it will cause NullPointerException because the mAdapter created in onCreateView method.
How could I make sure mAdapter has been initialed?
I use viewpage2 to create new Fragment (TheFragmentClass.newInstance()) rather than beginTransaction().commit
private boolean createFragment(int tabLimited) {
if (mVp2Adapter.getItemCount() >= tabLimited) {
return false;
}
String tabText = getTimeOfHMS();
SettlementProductItemFragment fragment = SettlementProductItemFragment.newInstance(); // new intance
mVp2Adapter.addFragment(tabText, fragment); // add to viewpage2's adapter
mTabSettlementProduct.selectTab(mTabSettlementProduct.getTabAt(
mVp2Adapter.getItemCount() - 1));
return true;
}
public void addFragment(String title, Fragment fragment) {
if (mFragmentTitles.contains(title)) {
Log.e(TAG, "addFragment failed: mFragmentTitles.contains(" + title + ")");
return;
}
mFragments.put(title, fragment);
mFragmentTitles.add(title);
updateHashMap();
notifyItemInserted(mFragmentTitles.size() - 1);
}
Here's my SettlementProductItemFragment class.
public class SettlementProductItemFragment extends Fragment {
private RecyclerView mRvProductInFragment;
public SettlementProductItemFragment() {
}
public static SettlementProductItemFragment newInstance() {
SettlementProductItemFragment fragment = new SettlementProductItemFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
private SettlementProductItemRecyclerViewAdapter mAdapter;
public double getTotalPriceInFragment() {
if (mAdapter == null) {
throw new NullPointerException("mAdapter(SettlementProductItemRecyclerViewAdapter) CAN NOT BE NULL");
}
return mAdapter.getTotalPriceInRecyclerView();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_settlement_product_item_list, container, false);
Log.e("getTotalPriceInFragment", "onCreateView: " + view.getClass().toString());
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
mAdapter = new SettlementProductItemRecyclerViewAdapter(context, getProducts());
mRvProductInFragment = (RecyclerView) view;
mRvProductInFragment.setLayoutManager(new LinearLayoutManager(context));
mRvProductInFragment.setAdapter(mAdapter);
}
return view;
}
}
Since you use ViewPager2 you have setOffscreenPageLimit method (offscreenPageLimit property in Kotlin) it will retain (and precreate also) your fragment when you initilize your ViewPager2.
The problem that i see in your code is that you modify items in ViewPager adapter. It isn't default snippet for using viewPager2 + tabs, so make sure you do it well, check for ViewPager2 samples by Google
Alternatively, you can create property in your SettlementProductItemFragment e.g isInitialized and observe it in your host fragment. In that way i suppose you have to use Architecture Components like ViewModel + Livedata

How to properly use ViewPager in android

I want to make a quiz application. So far I have 3 activities - home, quiz, score. Since the quiz activity contains multiple equivalent views ( image header, question and 4 answer buttons ), I did some reading and decided that
ViewPager with FragmentStatePagerAdapter show do the trick. So I made an xml template and inflated couple of test views and it was all looking good, until I started handling the user interaction.
I want to simulate a toggle button and there is only one correct answer to each question, so selecting one button should deselect the previous one ( if any ). When the button is pressed I change my Question model, then I find all 4 buttons with findViewById and reset their color filter. Then I set that filter back on my selected button. To determine which question model to update I use the current fragment position, which I have set ( using setTag, in fragment's onCreate ) in my template root view.
This is how I call my fragmets:
public Fragment getItem(int position) {
Question question = Repository.findById(position);
int correctAnswerBtnId;
switch (question.getCorrectAnswerIndex()) {
case 0: correctAnswerBtnId = R.id.quiz_answer_0_btn; break;
case 1: correctAnswerBtnId = R.id.quiz_answer_1_btn; break;
case 2: correctAnswerBtnId = R.id.quiz_answer_2_btn; break;
case 3: correctAnswerBtnId = R.id.quiz_answer_3_btn; break;
this.ACTIVITY_ROOT.setTag(question.getID());
Fragment fragment = new QuestionFragment();
Bundle args = new Bundle();
args.putSerializable(QuestionFragment.QUESTION, question);
fragment.setArguments(args);
return fragment;
}
My QuestionFragment onCreateView is as per documentation:
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle questionData) {
this.rootView = inflater.inflate(
R.layout.layout_question_template,
container,
false);
Bundle args = getArguments();
this.question = (Question) args.getSerializable(QuestionFragment.QUESTION);
populateInflatable();
rootView.findViewById(R.id.layout_question_template_root).setTag(this.question.getID());
return rootView;
}
In populateInflatable I use this.rootView to fintViewById and populate it with my question data. Then I change the color of a button, if there is selected one from the Question.
On button click I call selectAnserButton :
public void selectAnswerButton(View selectedButton) {
int questionId =
(int) this.activityRoot.findViewById(
R.id.layout_question_template_root).getTag(); //??
unSelectAllButtons();
changeColor(selectedButton);
Repository.findById(questionId).selectAnswer(selectedButton.getId());
}
Where unSelectAllButtons represents buttonToUnSelect.getBackground().clearColorFilter(); on the four buttons. and Repository is just a static class with example question data.
It all goes terribly wrong, when I have more then one view. On each fragment I inflate the same xml with same View IDs, as I have defined them. And as I now understand calling findViewById retrieves not one, but all views with that Id from my current, but also from my previous and next fragment as well. So every time I want to select my current fragment's view, I also modify the same view in the previous and next fragments as well. You can imagine how this is problematic. This makes me feel I have a fundamental mistake, because I don't think there is supposed to be more then one View with same ID.
I really don't understand how I should do this using ViewPager. At this point it feels like I'm trying to make a wood carving, but instead I am hacking the framework to pieces. There must be a better way to do this with ViewPager.
RESOLVED: Thanks to Soo Chun Jung for pointing me to the answer. In short what got it working for me was:
Passing my Question model id to each fragment with Bundle.
Storing each fragment in inside an ArrayMap with fragment position as key and fragment as value.
Getting each individual fragment from my selectAnswer function is now easy: first get the current fragment's position with myViewPager.getCurrentItem, then calling getter function which returns a fragment on the current position.
Now that I have the fragment I can easily change its button's because they are kept as private fields, assigned in the 'onCreateView` method.
Hope it's helpful~
adapter
class CustomAdapter extends FragmentPagerAdapter {
private final String[] TITLES = {"A", "B"};
private final String TAG = CustomAdapter.class.getSimpleName();
private final ArrayList<Fragment> mFragments;
private final FragmentManager fm;
public CustomAdapter(FragmentManager fm) {
super(fm);
mFragments = new ArrayList<>(getCount());
this.fm = fm;
}
#Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
#Override
public int getCount() {
return TITLES.length;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem position = " + position);
mFragments.remove(object);
super.destroyItem(container, position, object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Object object = super.instantiateItem(container, position);
mFragments.add((Fragment) object);
return object;
}
#Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem position = " + position);
if (position == 0) {
return MyFragmentA.newInstance();
} else if (position == 1) {
return MyFragmentB.newInstance();
}
return null;
}
public MyFragmentA getMyFragmentA() {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentA) {
return (MyFragmentA) f;
}
}
}
return null;
}
public MyFragmentB getMyFragmentB() {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentB) {
return (MyFragmentB) f;
}
}
}
return null;
}
}
Fragment class
public class MyFragmentB extends Fragment {
...
public updateYourUI(){
//update something
}
}
Usage
mPager = (CustomViewPager) findViewById(R.id.pager);
mAdapter = new CustomAdapter(getChildFragmentManager());
mPager.setAdapter(mAdapter);
mAdapter.getMyFragmentB().updateYourUI();
for your comment below If you only have one kind Fragment. You can modify some function like this.
public static MyFragmentB newInstance(int ID) {
MyFragmentB fragment = new MyFragmentB();
Bundle bundle = new Bundle();
bundle.putInt("ID", ID);
fragment.setArguments(bundle);
return fragment;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
myID = getArguments().getInt("ID");
....
}
public int getMyID() {
return myID;
}
public MyFragmentB getMyFragmentByID(String id) {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentB) {
MyFragmentB temp = (MyFragmentB)f;
if(temp.getID.equals(id){
return (MyFragmentB) f;
}
}
}
}
return null;
}

Access fragment variable outside of Fragment in view pager

enter image description here
Before reading the question, please refer to image.
I am using viewpager to show the fragment.
Problem
In the fragment, I have used two edittext lets say editText1, editText2 now the problem is how I will get the editText data. I can only get the editText values when user click on next button but the next button is outside of fragment. How do I access the editText outside the fragment.
Before downvoting the question, let me know the reason so that I can improve my question.
Fragment java class
// newInstance constructor for creating fragment with arguments
public static BpDetails newInstance(int page) {
BpDetails fragmentFirst = new BpDetails();
Bundle args = new Bundle();
args.putInt("someInt", page);
fragmentFirst.setArguments(args);
return fragmentFirst;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
page = getArguments().getInt("someInt", 0);
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.bp_details, container, false);
Log.i("View ",view.toString());
Log.i("DOB is ",Long.toString(Constants.dob));
systolic =(EditText) view.findViewById(R.id.systolic);
diastolic =(EditText) view.findViewById(R.id.diastolic);
return view;
}
ViewPager Activity
vpPager = (ViewPager) findViewById(R.id.view_pager);
adapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
vpPager.setAdapter(adapterViewPager);
Fragment fragment=adapterViewPager.getItem(prevPage);
if (fragment.getClass().equals(BpDetails.class)){
Log.i("Call ","Yes");
}
findViewById(R.id.btn_prev).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// checking for last page
// if last page home screen will be launched
int current = getItem(-1);
if (current!=0)
prevPage=current-1;
if (current < 4) {
// move to next screen
vpPager.setCurrentItem(current);
} else {
//final reached.
}
}
});
findViewById(R.id.btn_next).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// checking for last page
// if last page home screen will be launched
int current = getItem(+1);
if (current!=0)
prevPage=current-1;
System.out.println("Prev page "+prevPage);
if (current < 4) {
// move to next screen
Fragment prevFragment=adapterViewPager.getItem(prevPage);
} else {
//final reached.
}
}
});
}
private int getItem(int i) {
return vpPager.getCurrentItem() + i;
}
public static class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 4;
private static int mSelectedPosition;
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
//mSelectedPosition=selectedPosition;
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return BasicDetails.newInstance(0);
case 1:
return BpDetails.newInstance(1);
case 2:
return BslDetails.newInstance(2);
case 3:
return Summary.newInstance(3);
default:
return null;
}
}
}
Create two getters inside your fragment like this.
public String getSystolic(){
return this.systolic.getText().toString();
}
public String getDiastolic(){
return this.diastolic.getText().toString();
}
BpDetails fr = (BpDetails)myAdapter.getItem(myViewPager.getCurrentItem());
String systolicString = fr.getSystolic();
I had a similar issue. .getItem() instantiates a new Fragment, so upon calling myAdapter.getItem(...) you would be getting null for all elements in the Fragment, but not null for the Fragment.
When I fixed this, what I had to do was create another method inside of MyPagerAdapter called getInstantiatedFragment:
public Fragment getInstantiatedFragment(int position)
{
return fragments.get(position);
}
fragments is a new field for the class:
private ArrayList<Fragment> fragments = new ArrayList<>();
I would override getItem() (as you have done already) and change it to:
#Override
public Fragment getItem(int position)
{
switch (position) {
case 0:
BasicDetails basicDetails = BasicDetails.newInstance(0);
fragments.add(basicDetails);
return basicDetails;
...
}
where you're adding the fragment to fragments before returning, then you would call:
BpDetails fr = (BpDetails)myAdapter.getInstantiatedItem(myViewPager.getCurrentItem());
to get the instance of the created fragment and then call
String systolicString = fr.getSystolic();
if you're using the previous answer's method.
This is so that you can keep track of the instantiated fragments in fragments. I'm sure there are better ways.

In tab view I want to refresh Fragment every change

viewPager = (ViewPager) findViewById(R.id.tabanim_viewpager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabanim_tabs);
setupViewPager(viewPager);
tabLayout.setupWithViewPager(viewPager);
public void setupViewPager(ViewPager upViewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new ViewTodayFragment(),"Daily");
adapter.addFrag(new ViewWeekFragment(),"Weekly");
adapter.addFrag(new ViewMonthFragment(),"Monthly");
adapter.addFrag(new ViewYearFragment(),"Yearly");
viewPager.setAdapter(adapter);
}
I want to refresh fragment every time when tab changing.
inside fragment this is my code
public class ViewTodayFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
RecyclerView recyclerView;
RecyclerView.LayoutManager mLayoutManager;
RecyclerView.Adapter mAdapter;
// TODO: Rename and change types of parameters
View v;
ListView listview;
private ArrayList<DailyModel> mItems;
public static String datesel="a";
public ViewTodayFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
v=inflater.inflate(R.layout.fragment_view_today, container, false);
recyclerView =(RecyclerView)v.findViewById(R.id.view_today_lv_today);
recyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
loadData();
return v;
}
#Override
public void onResume() {
super.onResume();
loadData();
}
public void loadData(){
mItems=new ArrayList<DailyModel>();
List<Income> ll = null;
Log.d("ALLL",datesel);
if(datesel.equals("a")){
Calendar cw = Calendar.getInstance();
SimpleDateFormat format1 = new SimpleDateFormat("dd-MM-yyyy");
String currentDay=format1.format(cw.getTime());
ll=new IncomeHandler(getActivity()).getIncomeByThisDay(currentDay);
}else{
ll=new IncomeHandler(getActivity()).getIncomeByThisDay(datesel);
}
for (int i = 0; i < ll.size(); i++) {
DailyModel a=new DailyModel();
a.setCategory(ll.get(i).getWay());
a.setDescription(ll.get(i).getDes());
a.setType(ll.get(i).getType());
a.setBank("Bank");
a.setAmount(ll.get(i).getAmount());
mItems.add(a);
}
mAdapter = new DailyAdapter(mItems);
recyclerView.setAdapter(mAdapter);
}
}
So like this there are 4 tabs so i want to refresh every time it change the tab.I try to do that inside OnResume().But it also not working.
Create your pager adapter like this.
class PagerAdapter extends FragmentPagerAdapter {
private final List<String> fragmentTitleList = new ArrayList<>();
public PagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
public void addFragmentTitle(String title) {
fragmentTitleList.add(title);
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new ViewTodayFragment();
} else if (position == 1) {
return new ViewWeekFragment();
} else if(position == 2){
return new ViewMonthFragment();
} else{
return new ViewYearFragment();
}
}
#Override
public int getCount() {
return 4;
}
#Override
public CharSequence getPageTitle(int position) {
return fragmentTitleList.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Now you need to add fragment like this
PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager());
adapter .addFragmentTitle("Daily");
//..... add rest of the fragment title.
viewPager.setAdapter(pagerAdapter );
hope this will help you. though i didn't try this. just writing please let me know if you get any error.
I solved this type issue currently.Please reffer my Code line by line.it might be Help you.
Thanks.
ViewPager viewPager;
ViewPagerAdapter adapter;
viewPager = (ViewPager) findViewById(R.id.tabanim_viewpager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabanim_tabs);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabanim_tabs);
setupViewPager(viewPager);
tabLayout.setupWithViewPager(viewPager);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
adapter.getItem(position).onResume();
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
public void setupViewPager(ViewPager upViewPager) {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new ViewTodayFragment(),"Daily");
adapter.addFrag(new ViewWeekFragment(),"Weekly");
adapter.addFrag(new ViewMonthFragment(),"Monthly");
adapter.addFrag(new ViewYearFragment(),"Yearly");
viewPager.setAdapter(adapter);
}
Implement ViewPager.OnPageChangeListener http://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener.html
I solved a similar problem. My four tabs (a sports timer with athletes - skiers or cyclsits - starting at intervals) also needed to communicate with each other. For example once a race is started, the set up controls must be disabled.
My project was based on Google's SlidingTabsColors sample.
I created a simple, one method interface called Refreshable and each of my tabs (Fragments) implemented its refresh method.
In my SlidingTabsFragment (extends Fragment) I override instantiateItem so I can capture a reference to each tab's fragment.
// Here we can safely save a reference to the created
// Fragment, no matter where it came from (either getItem() or
// FragmentManger).
//
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
/**
* Save Fragment references in an array with the MainActivity
* so that other tabs can be refreshed (state updated) depending on
* changes to other tabs.
*/
MainActivity a = (MainActivity)getActivity();
a.addFragment((Refreshable)createdFragment);
return createdFragment;
}
My main is an observer of my data model, but yours may different.
This is the main's method to "register" the fragments.
/**
* Accepts the specified fragment and adds to the list of "Refreshable" Fragments.
* If it is already registered, it is not added a second time.
*
* #param fragment
* the Fragment (Observer) to add.
*/
public void addFragment(Refreshable fragment) {
if (fragment == null) {
throw new NullPointerException("fragment == null");
}
synchronized (this) {
if (!mRefreshables.contains(fragment))
mRefreshables.add(fragment);
}
}
In my case, this code is in the update method of my Observer. My data has changed (race started, interval countdown expired, finisher recorded) and I can refresh all tabs. I cannot call from one tab to another because of race (no pun intended) conditions.
/**
* Call refresh on every Fragment to update state.
*/
for (Refreshable refreshable : array) {
refreshable.refresh();
}
Each tab's refresh method does its own thing.
You may not need an Observer co-ordinating all tabs, but at least you'll have a reference to them and a method other than onResume (which didn't work for me either) to call and have the content refresh itself.
your answer....you must use
viewPager.setAdapter(adapter);
when your selected tab is change.

Viewpager gets the wrong page

I have looked in several topics like this but didn't see how to fix my problem
I have a resturant for say.. with dynamic number of categories.. I put all the categories in a list.. and create fragments by from those categories
so I cant just
case 0 : fragment0
case 1 :frament 1
because I dont know how much categories I have untill runtime
class MyPageAdapter extends FragmentStatePagerAdapter {
private List<MyFragment> fragments = new ArrayList<>();
private List<menuCat> Categories = new ArrayList<>();
public MyPageAdapter(FragmentManager fm, List<menuCat> Categories) {
super(fm);
this.Categories = Categories;
for (int i = 0; i<Categories.size();i++)
{
fragments.add(MyFragment.newInstance(Categories.get(i)));
}
}
#Override
public String getPageTitle(int position)
{
return Categories.get(position).catName();
}
#Override
public MyFragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
MyFragment.class
public class MyFragment extends Fragment {
public static final MyFragment newInstance(menuCat category)
{
Bundle bun = new Bundle();
bun.putString("category", category.toJson());
MyFragment f = new MyFragment();
f.setArguments(bun);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_my, container, false);
String json = getArguments().getString("category");
menuCat category = menuCat.fromJson(json);
System.out.println(category.catName());
ArrayList<Card> cards = new ArrayList<Card>();
for(menuItem item : category.getItems())
{
Card card = new Card(getActivity());
// Create a CardHeader
CardHeader header = new CardHeader(getActivity());
// Add Header to card
header.setTitle(item.getName());
card.setTitle(item.getPrice());
card.addCardHeader(header);
CardThumbnail thumb = new CardThumbnail(getActivity());
//thumb.setDrawableResource(listImages[i]);
//card.addCardThumbnail(thumb);
cards.add(card);
}
CardArrayAdapter mCardArrayAdapter = new CardArrayAdapter(getActivity(), cards);
CardListView listView = (CardListView) getActivity().findViewById(R.id.myList);
if (listView != null) {
listView.setAdapter(mCardArrayAdapter);
}
return v;
}
}
this is my adapter, my problem is for example
if I am in page 1 and I need page 3 data.. if I will go to page 2.. it will display data of page 3 OR if I go to page 5 then go backward.. to 4 it will also show data of page 3.. I mean it gets me like the data of the next page instead of current one.
Creating all of your fragments in the constructor is very poor design, as you're creating references to these objects which will later be attached to an Activity, but when they are detached, you continue to hold the reference. In the end, this is going to cause you a lot of frustration with memory leaks.
Is it not possible to simply remove fragments and change your methods to the following:
#Override
public MyFragment getItem(int position) {
return MyFragment.newInstance(Categories.get(position));
}
#Override
public int getCount() {
return Categories.size();
}
I'm not certain this will solve all of your problems, but it is a start.

Categories

Resources