Updating fragments from Activity in a ViewPager - android

I'm new to Android developing and I would really appreciate some help here.
I'm using a fragment that contains a TextView and I'm using 5 instances of the same MyFragment class.
In the activity, i got a button and a ViewPager, and I need the button to update all the fragment instances content, whenever its clicked.
Here's the Activity
public class MainActivity extends FragmentActivity {
final static String[] CONTENT = {"a", "b"};
ViewPager pager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<MyFragment> fragments = new Vector<MyFragment>();
for(int i = 0; i < 5; i++){
MyFragment fragment = new MyFragment(CONTENT);
fragments.add(fragment);
}
PagerAdapter adapter = new PagerAdapter(this.getSupportFragmentManager(), fragments);
pager = (ViewPager) findViewById(R.id.viewpager);
pager.setAdapter(adapter);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//method that isn't working
PagerAdapter adapter = (PagerAdapter)pager.getAdapter();
for(int i = 0; i < 5; i++){
MyFragment fragment = (MyFragment) adapter.getItem(i);
fragment.textView.setText(fragment.content[1]);
}
}
});
}
}
The Fragment
public class MyFragment extends Fragment{
String[] content;
TextView textView;
public MyFragment(String[] content) {
this.content = content;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_content, container, false);
textView = (TextView) view.findViewById(R.id.textView1);
textView.setText(content[0]);
return view;
}
}
And the FragmentPagerAdapter
public class PagerAdapter extends FragmentPagerAdapter{
List<MyFragment> fragments;
public PagerAdapter(FragmentManager fm, List<MyFragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int arg0) {
return fragments.get(arg0);
}
#Override
public int getCount() {
return fragments.size();
}
}
The OnClick method gives me a NullPointerException whenever i try to access a fragment from the adapter which is less than adapter.getCurrentItem() - 1, or more than adapter.getCurrentItem() + 1.
Any idea on how to update all the fragments at the same time?
Thanks in advance.

The easiest way to update those fragments is to use your code and set the number of fragments that the ViewPager holds in memory to the number of total fragments - 1(so all fragments are valid no matter at what page you are). In your case:
pager.setOffscreenPageLimit(4); // you have 5 elements
You can still use the method from my comment with the method onPageScrollStateChanged(so the update will start the moment the user starts swiping) to see when the user is starting to swipe the pager and update the fragments to the left and right of the currently visible fragment, but this will be a bit difficult to get right so I recommend to go with the first option.
Some points regarding your code containing fragments:
If you nest the fragment class make it static so you don't tie it to the activity object.
Don't create a constructor for a Fragment besides the default one. If the framework needs to recreate the fragment it will call the default constructor and if it is not available it will throw an exception. For example, try to change the orientation of the phone/emulator and see what happens(this is one of the cases when Android will recreate the fragments). Last, use a custom name for the ViewPager's adapter, you use PagerAdapter which is the name of the super class of FragmentViewPager and it's very confusing for someone reading your code.
If you need to pass data to the Fragment you could use a creation method like the one below:
public static MyFragment newInstance(String text) {
MyFragment f = new MyFragment();
Bundle b = new Bundle();
b.putString("content", text);
f.setArguments(b);
return f;
}
The text will be available in MyFragment by using getArguments().getString("content");

Related

Same fragment called multiple time in ViewPager

I have a View pager. The user can choose how many differents pages he can have.
The pages are all the same layout but it will just load different data.
Here is my fragment adapter :
public class FragmentAdapter extends FragmentPagerAdapter
{
private final List<Fragment> lstFragment = new ArrayList<>();
private final List<String> lstTitles = new ArrayList<>();
public FragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return lstFragment.get(i);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return lstTitles.get(position);
}
#Override
public int getCount() {
return lstTitles.size();
}
public void AddFragment (Fragment fragment , String title)
{
lstFragment.add(fragment);
lstTitles.add(title);
}
}
And here is the code to call the fragment multiple time :
FragAdapter = new FragmentAdapter(getSupportFragmentManager());
viewPager = (ViewPager) findViewById(R.id.main_tabs_pager);
toolbar = (Toolbar) findViewById(R.id.main_page_toolbar);
tabLayout = (TabLayout) findViewById(R.id.main_tabs);
String[] Fragments = {"Frag1", "Frag2", "Frag3", "Frag4"};
for (int i=0; i<Fragments.length; i++)
{
((FragmentAdapter) FragAdapter).AddFragment(new MenuFragment(),Fragments[i]);
}
viewPager.setAdapter(FragAdapter);
tabLayout = (TabLayout) findViewById(R.id.main_tabs);
tabLayout.setupWithViewPager(viewPager);
So it works fine. But the only problem is that I don't know how to know the difference in code between the differents fragments.
Exemple :
The frag1 must load 5 pictures about the sea
The frag2 must load 8 pictures about the sun
How can I tell the fragment what to do? I tried to pass in the constructeur the arguments by exemple
public MenuFragment(int numberofpictures, String picturesthemes)
{
// Required empty public constructor
}
but the constructors must be empty because it is not called again when fragment is destroyed and recreated...
does anyone has an idea? thanks
UPDATE
I don't know if that is the good way but here is the way I did it :
In main activity I created :
for (int i=0; i<Fragments.length; i++)
{
Bundle parameters = new Bundle();
parameters.putInt("myInt", i);
Fragment menuFragment = new MenuFragment();
menuFragment.setArguments(parameters);
((FragmentAdapter) FragAdapter).AddFragment(menuFragment, Fragments[i]);
}
Which give a everyfragment the the int i which is a reference to the title.
Then I simply wrote this function :
public String getName (int i)
{
return Fragments[i];
}
which return the title based on the int that the fragment got thanks to the bundle
Then, In the MenuFragment() I used this :
private void fillinthelist()
{
myInt = getArguments().getInt("myInt");
String test = ((MainActivity) getActivity()).getName(myInt);
ListOfProgrammes.add(new Modele_carte(test));
}
so it gets the int from the bundle and make a like to it thanks to the function in MainActivity
Is it the good way to do it? It seems to work
You can attach a Bundle containing the parameters with setArguments(Bundle) :
Bundle parameters = new Bundle();
parameters.putInt("myInt", <int_value>);
Fragment menuFragment = new MenuFragment();
menuFragment.setArguments(arguments);
((FragmentAdapter) FragAdapter).AddFragment(menuFragment, Fragments[i]);
A common practice is to build and attach the Bundle in a fragment's class static factory method.
The fragment can use getArguments() to retrieve the parameters.
private int myInt;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myInt = getArguments().getInt("myInt");
}

Using viewpager with specific fragments

I am creating an app that is using a viewpager to slide between 4 specific fragments.
All the examples of viewpager I have read so far, create new fragments each time the getPosition method of FragmentPagerAdapter is called. So it's something like:
return FragmentA.newInstance();
What I have done is the following:
In the main activity
public static final int FRAGMENTS = 4;
public static final String FRAGMENT_LIST ="LIST";
public static final String FRAGMENT_SETTINGS = "SETTINGS";
public static final String FRAGMENT_MAP = "MAP";
public static final String FRAGMENT_TICKET = "TICKET";
MainAdapter _adapter;
ViewPager _pager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.add(new FragmentMap(), FRAGMENT_MAP)
.add(new FragmentList(), FRAGMENT_LIST)
.add(new FragmentTicket(), FRAGMENT_TICKET)
.add(new FragmentSettings(), FRAGMENT_SETTINGS)
.commit();
}
_adapter = new MainAdapter(getSupportFragmentManager());
_pager = (ViewPager) findViewById(R.id.pager);
_pager.setAdapter(_adapter);
}
and in the adapter:
public class MainAdapter extends FragmentPagerAdapter {
FragmentManager _manager;
public MainAdapter(FragmentManager fm) {
super(fm);
_manager = fm;
}
#Override
public Fragment getItem(int position) {
return _manager.getFragments().get(position);
}
#Override
public int getCount() {
return ActivityMain.FRAGMENTS;
}
}
This raises an exception because the adapter is trying to change the tag of each fragment in getItem
My questions are:
a) Is it incorrect to always use the same fragment every time? I have seen no example that uses the above method or a similar one, they always create a new instance in the getItem method
b) If I wish for fragments to have some persistence, then does that mean that I should store the data that should be held by each fragment in static variables and always create new instances that use those variables?
a) You must create a new instance in the getItem() method, this method is not called every time you switch fragment from your viewpager.
I recommend you to use your adapter like
public class MainAdapter extends FragmentPagerAdapter {
public MainAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
case 0 :
return new FragmentMap();
case 1 :
return new FragmentList();
case 2 :
return new FragmentTicket();
case 3 :
return new FragmentSettings();
default :
return null;
}
#Override
public int getCount() {
return ActivityMain.FRAGMENTS;
}
}
b) Fragments in FragmentPagerAdapter are persistents, and they will be recreate only if you switch several fragment in your ViewPager. You can set the refresh limit by _pager.setOffscreenPageLimit(3); for exemple if you never want to recreate your fragments in your case.
For more information : http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setOffscreenPageLimit(int)
a) I think it's perfectly ok. They always create ne instance because it is easier and in many cases (like image gallery) it is better.
b) Static variables get lost if you send the app to backround and then return to it. Shared prefferences or bundle should do the work.
You should use FragmentStatePagerAdapter. If you fragmentstatepager adapter, you can remove or add fragment dynamically.

Android Set Text of TextView in Fragment that is in FragmentPagerAdapter

This one is driving me nuts. Basically, I want to create a ViewPager and add a few Fragments to it. Then, all I want to do, it set a value in one of the Fragment's TextViews. I can add the Fragments fine, and they attach, but when I go to findViewById() for one of the TextViews in the first Fragment it throws a NullPointerException. I, for the life of me, can't figure out why.
Here's my code so far, let me know if more is needed please.
public class SheetActivity extends FragmentActivity {
// /////////////////////////////////////////////////////////////////////////
// Variable Declaration
// /////////////////////////////////////////////////////////////////////////
private ViewPager viewPager;
private PagerTitleStrip titleStrip;
private String type;
private FragmentPagerAdapter fragmentPager; //UPDATE
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sheet);
viewPager = (ViewPager) findViewById(R.id.viewPager);
titleStrip = (PagerTitleStrip) findViewById(R.id.viewPagerTitleStrip);
// Determine which type of sheet to create
Intent intent = getIntent();
this.type = intent.getStringExtra("type");
FragmentManager manager = getSupportFragmentManager();
switch (type) {
case "1":
viewPager.setAdapter(new InstallAdapter(manager));
break;
case "2":
viewPager.setAdapter(new InstallAdapter(manager));
break;
}
fragmentPager = (FragmentPagerAdapter) viewPager.getAdapter(); //UPDATE
}
#Override
public void onResume() {
super.onResume();
fragmentPager.getItem(0).setText("something"); //UPDATE
}
class MyAdapter extends FragmentPagerAdapter {
private final String[] TITLES = { "Title1", "Title2" };
private final int PAGE_COUNT = TITLES.length;
private ArrayList<Fragment> FRAGMENTS = null;
public MyAdapter(FragmentManager fm) {
super(fm);
FRAGMENTS = new ArrayList<Fragment>();
FRAGMENTS.add(new FragmentA());
FRAGMENTS.add(new FragmentB());
}
#Override
public Fragment getItem(int pos) {
return FRAGMENTS.get(pos);
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public CharSequence getPageTitle(int pos) {
return TITLES[pos];
}
}
}
All of Fragments I created only have the onCreateView() method overridden so I can display the proper XML layout. Other than that they are 'stock'. Why can't I interact with elements in any of the Fragments?
UPDATE:
So do something like this?
public class FragmentA extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle inState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
public void setText(String text) {
TextView t = (TextView) getView().findViewById(R.id.someTextView); //UPDATE
t.setText(text);
}
}
XML LAYOUT FOR FRAGMENT A
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/someTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="22sp" />
</LinearLayout>
The TextView is located in the fragments layout, not in the ViewPagers or the PagerAdapter, that is causing the NPE. Now, you have 2 options.
The first is the easiest, you should simple move your code for changing the text into the corresponding fragment's class, FragmentA in this case.
Secondly, you could make the TextView into FragmentA static, so it can be accessed by other classes. So your code would look something like this:
....
TextView myText;
#Override
public View onCreateView(....) {
myLayout = ....;
myText = myLayout.findViewById(yourID);
....
}
And then you would change the text from somewhere else (if it's really necessary):
FragmentA.myText.setText("new text");
Explaining method 2
Use the following in your Fragment.
public static void setText(String text) {
TextView t = (TextView) getView().findViewById(R.id.someTextView);
t.setText(text);
}
Then change the text like:
FragmentA.setText("Lulz");
Unless you are planning to change the value at runtime, you can pass the value into the fragment as a parameter. It is done my using a Bundle and passing it as args into a Fragment, which then retrieves it from it's args. More info here. If you implement this, your instantiation of new Fragments might look something like this:
public InstallAdapter(FragmentManager fm) {
super(fm);
FRAGMENTS = new ArrayList<Fragment>();
FRAGMENTS.add(FragmentA.newInstance("<text to set to the TextView>"));
FRAGMENTS.add(FragmentB.newInstance("<text to set to the TextView>"));
}
If, however, you are planning to update the value at runtime (it will change as user is running the app), then you want to use an Interface to channell communication between your fragment and your activity. Info here. This is what it might look like:
//Declare your values for activity;
ISetTextInFragment setText;
ISetTextInFragment setText2;
...
//Add interface
public interface ISetTextInFragment{
public abstract void showText(String testToShow);
}
...
//your new InstallAdapter
public InstallAdapter(FragmentManager fm) {
super(fm);
FRAGMENTS = new ArrayList<Fragment>();
Fragment fragA = new FragmentA();
setText= (ISetTextInFragment)fragA;
FRAGMENTS.add(fragA);
Fragment fragB = new FragmentB();
setText2= (ISetTextInFragment)fragB;
FRAGMENTS.add(fragB);
}
//then, you can do this from your activity:
...
setText.showText("text to show");
...
and it will update your text view in the fragment.
While it can be done "more easily", these methods are recomended because they reduce chances of bugs and make code a lot more readable and maintainable.
EDIT: this is what your Fragment should look like (modified your code):
public class FragmentA extends Fragment implements ISetTextInFragment {
TextView myTextView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle inState) {
View v = inflater.inflate(R.layout.fragment_a, container, false);
myTextView = (TextView)v.findViewbyId(R.id.someTextView)
return v;
}
#Override
public void showText(String text) {
myTextView.setText(text);
}
}
If after that you are still getting a null pointer exception, your TextView is NOT located where it needs to me, namely in the R.layout.fragment_a filem, and it needs to be located there. Unless you are calling the interface method BEFORE the fragment finished loading, of course.
This line:
TextView t = (TextView) findViewById(R.id.someTextViewInFragmentA);
is looking for the view in your ParentActivity. Of course it wont find it and that's when you get your NPE.
Try something like this:
Add a "tag" to your fragments when you add them
Use someFragment = getSupportFragmentManager().findFragmentByTag("your_fragment_tag")
Get the view of the fragment
fragmentView = someFragment.getView();
And finally find your TextView and set the text
TextView t = (TextView) fragmentView.findViewById(R.id.someTextViewInFragmentA);
t.setText("some text");
How about to change this line
TextView t = (TextView) getView().findViewById(R.id.someTextView); //UPDATE
to
TextView t = (TextView) getActivity().findViewById(R.id.someTextView); //UPDATE
then you can try to update "t" with .setText("some_string") inside "SheetActivity".

Not Getting page Count on Activity from Fragment Adapter and Fragment Class

I have one details activity. Inside this, I'm using paging Controller with
<android.support.v4.view.ViewPager ..../> for fragment details view.
My code:
ViewPager pager = (ViewPager) findViewById(R.id.pagerHandbookDetails);
/** Getting fragment manager */
FragmentManager fm = getSupportFragmentManager();
/** Instantiating FragmentPagerAdapter */
DetailsFragmentAdapter pagerAdapter = new DetailsFragmentAdapter(fm,cases);
/** Setting the pagerAdapter to the pager object */
pager.setAdapter(pagerAdapter);
pager.setCurrentItem(0);
Then, Details Fragment Adapter Class extends FragmentPagerAdapter
int PAGE_COUNT = 9;
#Override
public Fragment getItem(int arg0) {
DetailsFragment detailsFragment = new DetailsFragment();
Bundle data = new Bundle();
Log.d("Current Page", "Page " + arg0);
data.putInt("current_page", arg0);
detailsFragment.setArguments(data);
return detailsFragment;
}
Class Details Fragment extends Fragment
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle data = getArguments();
int mCurrentPage = data.getInt("current_page", 0);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
layoutPaging = (LinearLayout) v.findViewById(R.id.layoutPagingView);
for(int i = 0; i < 9; i++){
ImageView image1 = new ImageView(layoutPaging.getContext());
image1.setTag(i);
if(i == mCurrentPage)
{
image1.setImageResource(R.drawable.black_dot);
}
else
{
image1.setImageResource(R.drawable.white_dot);
}
layoutPaging.addView(image1);
}
.......
}
It's working fine like this.
So whenever I swipe page, It'll obviously move all content of details fragment page. So it'll also move my custom view layout for paging. And it looks odd.
I want this layout static and change image content as per swipe pages.
If I'm taking this layout in main details class, then I'm not getting current page's value for changing an image from fragment adapter. It's only changing values inside fragment details class.
I'm stuck here.
you can add page indicators in your layout that is provided by:
https://github.com/Papercloud/SimpleViewPagerIndicator
you need to just place the control and set your viewpager controller's object to SimpleViewPagerIndicator.You can find sample code and implementation steps there
Using these classes, you can get indicators that will not scroll with pages
Just try this implement SimpleOnPageChangeListener for listen the page changes.
private static class PageListener extends SimpleOnPageChangeListener{
public void onPageSelected(int position) {
int currentPage = position;
}
}
And inside your ViewPagerActivity just write
private PageListener pageListener;
inside the onCreate() method do like this
pageListener=new PageListener()
pager.setOnPageChangeListener(pageListener);

Access TextView in ViewPager from Activity

I have an Activity with a ViewPager containing multiple fragments. how can i now access a TextView in one of that fragments to change its text from the main activity? I tried multiple ways and they all ended in a NullPointerException.
Activity:
public class SummonerOverview extends SherlockFragmentActivity implements TabListener, OnPageChangeListener {
private ViewPager mPager;
private PagerAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.summoner_overview);
initialize();
}
private void initialize() {
// initialize Pager
mPager = (ViewPager) findViewById(R.id.viewpager);
mAdapter = new PagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(1);
mPager.setOnPageChangeListener(this);
}
}
PagerAdapter:
public class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
frags = new Fragment[3];
frags[0] = new StatisticsFragment(0);
frags[1] = new RatingsFragment(1);
frags[2] = new HistoryFragment(2);
}
private final int NUM_PAGES = 3;
Fragment[] frags;
#Override
public Fragment getItem(int arg0) {
if (arg0 == 0)
return frags[0];
else if (arg0 == 1)
return frags[1];
else
return frags[2];
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
The Fragment:
public class StatisticsFragment extends SherlockFragment {
public StatisticsFragment(int fragNr) {
this.fragNr = fragNr;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_overview_statistics, container, false);
return v;
}
}
The Textview in the StatisticsFragment is labeled with an id in the fragment_overview_statistics.xml, but when i try
TextView tv = (TextView) findViewById(R.id.id_of_the_textview)
tv.setText("text");
from within the onCreate() Method of the Activity after the initialize() Method, i get an Exception.
Okay, after another hour of googling, i think i solves my own question (although i'm sure there is a better method to solve that)
I now hold all the Data that is displayed by the fragments in the MainActivity and make the Fragmets consume this Data in their onCreateView() Method by using public Methods of the Activity.
In the PagerAdapter, i overwrote getItemPosition() to:
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Now, everytime i update some of the Data, i also call mAdapter.notifyDataSetChanged() which leads into a recreation of all fragments.
It works, although it looks like a poor hack for me. Im sure there must be a better solution because now i have to recreate all fragments for changing one TextView of one Fragment, what is surely not the way it is intended to be done.

Categories

Resources