I created two ViewPagers in my activity and what I'm trying to do is to share the same data between them.
The ViewPager 1 shows a fragment for every string in a list named "A".
Every fragment has a simple layout with a TextView.
The ViewPager 2 shows a fragment for every string from the same list "A".
Every fragment has a simple layout with an EditText.
I'm trying to obtain this behavior:
1) VP 1 is visible and the user can swipe every fragment populated with the strings from the list "A".
2) VP 2 is invisible.
3) After the onClick event on the TextView, I'd like to hide VP 1 and show VP 2 with the same string content but in an EditText instead of TextView.
4) The user can modify the content of the EditText.
5) After 4) the user can press a button in the toolbar and come back to VP 1 with the content updated.
I'm using the FragmentStatePagerAdapter.
I was thinking to save data directly in a DB or in a file.
Is it possible to create something like that?
How can I implement the event that after tapping on fragment "n" in VP1 reaching the fragment "n" in VP2?
How can I update the content in VP 1 after the mod in VP 2?
The code at the moment is quite simple so I didn't post it here, by the way is very similar to this one.
Thanks
Why don't you just use one ViewPager, with the fragment having a TextView and an EditText. The EditText's visibility is set to "GONE". On click of the TextView set it's visibility to "GONE" and the EditText's to "VISIBLE". When the toolbar button is clicked do the opposite and also populate the new text to the appropriate TextView.
The Object-Oriented approach is using an interface. You can create an interface in your activity for the fragments to communicate with.
ViewPagerComm.class:
public interface ViewPagerComm {
String getData();
void setData(String data);
}
Activity:
public class FragmentStatePagerSupport extends Activity, implements ViewPagerComm {
String data;
...
#Override
public void setData(String data){
this.data = data;
}
#Override
public String getData(){
return data;
}
}
...
}
In the adapter:
public static class MyAdapter extends FragmentStatePagerAdapter {
ViewPagerComm callback;
public MyAdapter(FragmentManager fm, ViewPagerComm callback) {
this.callback = callback;
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position, callback);
}
}
In your Fragments:
public static class ArrayListFragment extends ListFragment {
ViewPagerComm callback;
static ArrayListFragment newInstance(int num, ViewPagerComm callback) {
ArrayListFragment f = new ArrayListFragment();
f.callback = callback;
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
}
Now, whenever a fragment calls setData(aString) the parent activity will hold the data. Whenever a fragment calls getData(), the data will be retrieved from the activity.
Just declare Variable in your Activity (your Viewpager Activity)
public class TestActivity extends AppCompatActivity {
public String test;
}
And access it in your Fragment (within code)
((TestActivity)getActivity()).test
Related
I have an Activity "A" which contains a view pager that contains 5 fragments.
One of the fragment has over 7-8 fragments added programmatically.
Each fragment is instantiated using the static method instance() defined in the respective fragment.
for(int i = 0 ; i < 10; i++) {
CustomFragment fragment = (CustomFragment) Class.forName(classNameList.get(i)).instance();
getFragmentManager().beginTransaction().add(R.id.parentLinearLayout, fragment, fragmentTag).commitAllowingStateLoss();
}
Everything works fine when this activity is launched and traversed across different fragments.
But if I launch an activity "B" and return to activity "A". All the other fragments in the view pager load fine except the fragment that contains the set of fragments. It shows up as a blank screen. What could be the problem?
You are using a constructor to instantiate your Fragment and pass your parameters, and when Android re-instantiates the Fragment it doesn't use your constructor, and hence your parameters are disregarded. You should use Arguments to pass your parameters. This is an example of how you can do so:
Inside MyFragment.java:
private static final String KEY_ITEM = "item";
public static MyFragment getFragment(String myItem) {
MyFragment fragment= new MyFragment ();
Bundle args = new Bundle();
args.putString(KEY_ITEM, myItem);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String myItem = getArguments().getString(KEY_ITEM);
}
Inside your ViewPager adapter:
#Override
public Fragment getItem(int position) {
return MyFragment.getFragment("Your parameter here.");
}
Of course, I used a single String as a parameter, you can add as much parameters as you'd like.
I have a FragmentPagerAdapter for a viewPager Which initially has only one Fragment in it. I want to dynamically add a new Fragment to the adapter when user swipes from right to left, and dynamically remove a Fragment when user swipes from left to right.I have tried to use this library https://github.com/commonsguy/cwac-pager but in that library we have an option to add and remove fragments on button clicks. I have tried to add a OnPageChangeListener to the viewpager but the callback methods ( onPageScrolled and onPageScrollStateChanged) are being called more than once which results in addition of more than one fragment to the FragmentPagerAdapter. So please shed some light on how to do this.
#dora: i think in your case FragmentStatePagerAdapter will help you. I have mentioned its use below as per my understanding.I hope it will help you in taking decision.
There are two ways to implement ViewPager:
• FragmentStatePagerAdapter
• FragmentPagerAdapter
FragmentStatePagerAdapter class consumes less memory, because it destroys fragments, as soon as they are not visible to user, keeping only saved state of that fragment
FragmentPagerAdapter: when there are less number of fragments. But using AndroidFragmentPagerAdapter for large number of fragments would result choppy and laggy UX.
Number of page hold by a viewPager?
The number of items that any ViewPager will keep hold of is set by the setOffscreenPageLimit() method. The default value for the offscreen page limit is 3. This means ViewPager will track the currently visible page, one to the left, and one to the right. The number of tracked pages is always centered around the currently visible page.
Please follow this link for code: http://www.truiton.com/2013/05/android-fragmentpageradapter-example/
I know this post is old, but I struggled to figure this out so I'll answer it anyway.
You want to use FragmentStatePagerAdapter and override getItemPosition(). Create a list of stuff you want to pass down to the fragment, call notifyDataSetChanged(), and you're all set!
Here's the adapter:
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
List<String> mKeyList = new ArrayList<>();
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return PlaceholderFragment.newInstance(mKeyList.get(position));
}
#Override
public int getCount() {
return mKeyList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return "SCOUT " + (getCount() - position);
}
public void add(int position, String key) {
mKeyList.add(position, key);
notifyDataSetChanged();
}
}
And here's the fragment:
public static class PlaceholderFragment extends Fragment {
private static final String ARG_SCOUT_KEY = "scout_key";
public static PlaceholderFragment newInstance(String key) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putString(ARG_SCOUT_KEY, key);
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.current_scout_fragment, container, false);
//getArguments().getString(ARG_SCOUT_KEY));
return rootView;
}
}
I want to dynamically add a new Fragment to the adapter when user swipes from right to left, and remove dynamically remove a Fragment when user swipes from left to right.
AFAIK, that will not be supported by any PagerAdadpter. It certainly will not be supported by ArrayPagerAdapter. The page needs to exist, otherwise you cannot swipe to it. You cannot swipe first, then add the page later.
Moreover, I have never found a use case for your proposed pattern that could not be handled by having the page be in the adapter, but not populating the page (i.e., whatever the expensive work is that you appear to be trying to avoid) until the swipe begins.
As viewpager preload +1 and -1 of the fragment that is used in it. I've a loading of data using asyntask that i wish to execute only when i am on the page itself.
Where should i execute the function? oncreate does not seems to work for me
You can implement ViewPager.OnPageChangeListener and run your AsyncTask in onPageSelected().
For example:
public class MyActivity implements ViewPager.OnPageChangeListener {
#Override
public void onPageSelected(int position) {
new MyAsyncTask().execute();
}
}
However, as Tyczj pointed out in the comments, this defeats the purpose of a ViewPager trying to keep Views loaded. This feature is designed to make your app look smooth, and without it your Views will look empty (or take on their default appearance) while you load your data.
The solution provided by Tanis should work, there's however one think that should be taken in consideration. Since the AsyncTask is started from Activity, you may encounter some issues when dealing with configuration changes. Perhaps starting the AsyncTask dirrectly from the fragment will make more sense.
The solution then would be to make the currently displayed fragment aware that he is the fragment displayed now.
Firstly, you should have a method in your Activity that will return the position of current fragment from ViewPager:
public class MainActivity extends Activity{
//....
public int getViewPagerCurrentIndex() {
return pager.getCurrentItem();
}
}
Secondly, in your PagerAdapter in getItem() method pass the position of current item as an argument to the fragment:
public class MyPagerAdapter extends FragmentStatePagerAdapter {
//....
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(position);
}
}
Lastly, check in MyFragment that both position returned from MainActivity and position received when the fragment was instantiated match. If they match then this instance of fragment is visible:
public static MyFragment newInstance(int position) {
MyFragment fragment = new MyFragment ();
Bundle args = new Bundle();
args.putInt(KEY_POSITION, position);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (getArguments() != null) {
fragmentPosition = getArguments().getInt(KEY_POSITION);
}
}
// It does not matter where this method is called, the AsyncTask will be started only for the currently visible fragment.
private void executeAsyncTask(){
MainActivity mainActivity = (MainActivity)getActivity();
if (mainActivity.getViewPagerCurrentIndex() == fragmentPosition ) {
new MyAsyncTask().execute();
}
}
I am new to using a ViewPager, but the initial screen on my app is a ListView that the user can add/remove new items to, then clicking on one of the items brings them to a "details" fragment based on an id that is passed. I'd like for the user to also be able to swipe from the listing through to each of the details.
I have the ViewPager working, except the id's are always off by one. This might be my lack of understanding of ViewPagers, but if I put a breakpoint in the onCreateView of the details fragment, the breakpoint is hit when the app loads and the id that is passed is the first id. So, say the ids are 1,2,3,4, when the app loads, the id on app start-up in onCreateView is 1. When I perform the initial swipe from the listing to the first details fragment, the id is 2 (when I would expect it to be 1).
This is what I have so far:
Main.class (this initial activity on app start-up)
public class Main extends SherlockFragmentActivity
{
private static int NUMBER_OF_PAGES;
private ViewPager mViewPager;
private MyFragmentPagerAdapter mMyFragmentPagerAdapter;
private static List<Fragment> fragments;
#Override
public void onCreate(final Bundle icicle)
{
super.onCreate(icicle);
setContentView(R.layout.main);
mViewPager = (ViewPager)findViewById(R.id.viewpager);
mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mMyFragmentPagerAdapter);
final List<Integer> ids = GetIds(); //loads ids to popular the viewpager
NUMBER_OF_PAGES = ids.size();
fragments = new ArrayList<Fragment>();
fragments.add(new ListingFragment()); //initial screen
for(Integer id : ids)
fragments.add(DetailsFragment.newInstance(id));
}
private static class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
return fragments.get(index);
}
#Override
public int getCount() {
return NUMBER_OF_PAGES;
}
}
}
DetailsFragment.class
public class DetailsFragment extends SherlockFragmentActivity
{
private int detailId;
private List<Integer> items;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
public void onActivityCreated(final Bundle icicle)
{
super.onActivityCreated(icicle);
}
public static DetailsFragment newInstance(int id) {
DetailsFragment lf = new DetailsFragment();
Bundle bundle = new Bundle();
bundle.putInt("id", id);
lf.setArguments(bundle);
return lf;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.details, container, false);
detailId = getArguments().getInt("id"); //this id is always off
items = GetItems();
return view;
}
}
I have the ViewPager working, except the id's are always off by one.
This might be my lack of understanding of ViewPagers, but if I put a
breakpoint in the onCreateView of the details fragment, the breakpoint
is hit when the app loads and the id that is passed is the first id.
So, say the ids are 1,2,3,4, when the app loads, the id on app
start-up in onCreateView is 1. When I perform the initial swipe from
the listing to the first details fragment, the id is 2 (when I would
expect it to be 1).
Maybe I don't understand the problem you're facing but the code you posted(again, if that is the full code you use(currently it will not compile as your DetailsFragments extends SherlockFragmentActivity (?!))) can't do what you say. The way you setup the fragments will make the them have the proper ids.
What you're seeing it's most likely due to the way the ViewPager works, which can be misleading. When you first start the app you'll see the ListingFragment. Now, the ViewPager, in order to provide a smooth swipe for the user will also load(by default) one additional fragment on each side of the current visible fragment(so when the app starts the ViewPager will load page 0(ListingFragment) and the next(to the right as we don't have anywhere to go left)) page, 1(the DetailsFragment with the id 1). When you swipe from the ListingFragment to the first DetailsFragment(which has the id 1) the ViewPager will automatically create the second DetailsFragment(which has the id 2) in advance(again, in order to be able to swipe right away to it). Now, if you have a Log statement in the onCreateView in the DetailsFragment(to see the id) it's normal that you see the second id as you swipe from the ListingFragment to the first DetailsFragment because that Log will not appear from the first DetailsFragment(with id 1, which is already created) being created, it will appear because the second DetailsFragment(with id 2) is being created in advance.
Also, revise your code, as you introduced a subtle bug. You set the size of the adapter to
NUMBER_OF_PAGES which is initialized with ids.size() but then you add the ListingFragment which will make the DetailsFragment with the last id(from ids) not appear at all.
Is it possible to use one fragment in a viewpager multiple times? I am trying to build a dynamically updated UI using ViewPager.
I want to use the same design, basically the same fragment with different data for every page, like a listview adapter.
You can instantiate the same Fragment class for every page in your ViewPager, passing the position of the ViewPager to control what to display. Something like that:
public class MyFragment extends Fragment {
private int mIndex;
public MyFragment(int index) {
mIndex = index;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
switch(mIndex){
case 0:
// do you things..
case 1:
// etcetera
}
}
}
then, in you FragmentPagerAdapter:
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
return new MyFragment(position);
}
}
That way you can reuse most of your code changing only what you need in the switch/case statement.
You misunderstood the concept of class versus object of class. Your MyFragment.java source code defines class which you turn into "living thing" each time you instantiate it with new operator (new MyFragment();) - this creates object which is an instance of your class. Unless you intentionally prevent this (by i.e. using Singleton pattern) your can make as many instances of the class as you like, same way you are allowed to make as many i.e. cakes using single recipe. And this applies to fragments as well.
So as long as you create separate object (aka said instance) of your class for each page, you should be able to do what you want.