New to android and recently learned swipe views a bit. In my app, it has 17 chapters and in each chapter, there will be 30 pages containing some text data, a user can swipe through. I need to use FragmentStatePagerAdapter to save memory but I need to know that do I need to make 510 Fragment objects, means 510 XML layout? I just need to change the text in each page, and I've seen many using switch statement like this
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new FragmentOne();
case 1:
return new FragmentTwo();
default:
break;
}
return null;
}
Do I/Should I write 30 cases in each chapter? or is there a better way to do this? I've Googled and seen lots of Youtube videos but couldn't find the solution. I request everyone that if you're answering or commenting, do explain your codes cause I believe in learning, not in copy pasting.
Using FragmentStatePagerAdapter is right approach here. It will manage the fragment states based on the viepager offscreenLimit() you set.
In your case, you can have just one fragment and an arrayList of your text and given different text based on position.
Something like this..
List<String> yourList = new ArrayList<>();
#Override
public Fragment getItem(int position) {
return new YourFragment(yourList.get(position));
}
It's easy peasy man!
You need only one Fragment object, here's the code
public class FragmentChild extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
// INFLATE THE LAYOUT THAT EACH FRAGMENT OBJECT WILL HAVE, PUT IT IN A VIEW
View root = inflater.inflate(R.layout.couplets, container, false);
// RECEIVE THE BUNDLE DATA SENT (ARGUMENTS)
Bundle args = getArguments();
// CREATE AN ARRAY LIST OF STRINGS THAT WILL HOLD TEXT
ArrayList<String> someText = new ArrayList<>();
someText.add("one");
someText.add("two");
someText.add("three");
TextView txt = (TextView) root.findViewById(R.id.text_view);
txt.setText(someText.get(args.getInt("position")));
return root;
}
custom pager adapter
class MyFragmentAdapter extends FragmentStatePagerAdapter {
MyFragmentAdapter(FragmentManager fm) {super(fm);}
#Override
public Fragment getItem(int position) {
Bundle args = new Bundle();
args.putInt("position", position);
Fragment fragment = new FragmentChild();
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
return 3;
}
}
And finally host activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chapter_one);
// FIND THE VIEWPAGER AND SET THE CUSTOM ADAPTER TO IT TO PROVIDE CHILD PAGES
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
}
}
Related
I have seen a lot of answered questions about this, but none about what I exactly want so here it goes (if there's an answered thread about this I'd appreciate it):
I want to create a kind of "level selection" app, where you basically have to swipe from right to left in order to be able to see the next list of levels, however I want to do it WITHOUT tabs (haven't found out how to do it yet).
Thanks.
EDIT: Solved it by simply using a ViewPager without even bothering or paying attention to the ActionBar part Android tells you to add (I just created my few fragments, my viewpager, and this last one did the rest, didn't even need to use a gesture detector for swipes as viewpager already provides this animation).
Any ViewPager Tutorial teaching you how to swipe between tabs basically has all the information I needed :) Thanks everyone!
For doing this you shiuld use ViewPager
Its easy !
Layout ViewPager
<android.support.v4.view.ViewPager
android:id="#+id/vpPager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v4.view.ViewPager>
Define Fragments
public class FirstFragment extends Fragment {
// Store instance variables
private String title;
private int page;
// newInstance constructor for creating fragment with arguments
public static FirstFragment newInstance(int page, String title) {
FirstFragment fragmentFirst = new FirstFragment();
Bundle args = new Bundle();
args.putInt("someInt", page);
args.putString("someTitle", title);
fragmentFirst.setArguments(args);
return fragmentFirst;
}
// Store instance variables based on arguments passed
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
page = getArguments().getInt("someInt", 0);
title = getArguments().getString("someTitle");
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_first, container, false);
TextView tvLabel = (TextView) view.findViewById(R.id.tvLabel);
tvLabel.setText(page + " -- " + title);
return view;
}
}
Setup FragmentPagerAdapter
public class MainActivity extends FragmentActivity {
// ...
public static class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// 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 FirstFragment.newInstance(0, "Page # 1");
case 1: // Fragment # 0 - This will show FirstFragment different title
return FirstFragment.newInstance(1, "Page # 2");
case 2: // Fragment # 1 - This will show SecondFragment
return SecondFragment.newInstance(2, "Page # 3");
default:
return null;
}
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
}
}
Apply the Adapter
public class MainActivity extends FragmentActivity {
FragmentPagerAdapter adapterViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
ViewPager vpPager = (ViewPager) findViewById(R.id.vpPager);
adapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
vpPager.setAdapter(adapterViewPager);
}
// ...
}
Setup OnPageChangeListener
// Attach the page change listener inside the activity
vpPager.setOnPageChangeListener(new OnPageChangeListener() {
// This method will be invoked when a new page becomes selected.
#Override
public void onPageSelected(int position) {
Toast.makeText(HomeActivity.this,
"Selected page position: " + position, Toast.LENGTH_SHORT).show();
}
// This method will be invoked when the current page is scrolled
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Code goes here
}
// Called when the scroll state changes:
// SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING
#Override
public void onPageScrollStateChanged(int state) {
// Code goes here
}
});
if you need more info see this link (it also explain how to use tabs) :
https://github.com/codepath/android_guides/wiki/ViewPager-with-FragmentPagerAdapter
You have to use HorizontalScrollView (http://developer.android.com/reference/android/widget/HorizontalScrollView.html) which will manage horizontall scroll by himself. Juste place other view inside and you're good.
Edit: Solved it by simply using a ViewPager without even bothering or paying attention to the ActionBar part Android tells you to add (I just created my few fragments, my viewpager, and this last one did the rest, didn't even need to use a gesture detector for swipes as viewpager already provides this animation).
Any ViewPager Tutorial teaching you how to swipe between tabs basically has all the information I needed :) Thanks everyone!
I'm kinda confused about the whole Fragment-way-of-thinking. I've followed a tutorial on how to create a ViewPager with Fragments like the Google Play app.
I have TabFragment class like this one:
public class SwipeyTabFragment extends SherlockFragment {
#Override
public void onCreate(Bundle b) {
super.onCreate(b);
Log.e("FRAGMENT: ", "Hello World!");
}
public static Fragment newInstance(String title) {
SwipeyTabFragment f = new SwipeyTabFragment();
Bundle args = new Bundle();
args.putString("title", title);
f.setArguments(args);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_swipeytab, null);
final String title = getArguments().getString("title");
((TextView) root.findViewById(R.id.text)).setText(title);
return root;
}
}
I know that the onCreateView method initialize the layout and the controlls like Button, ListView and so on.
Over to my FragmentAdapter
private class SwipeyTabsPagerAdapter extends FragmentPagerAdapter implements SwipeyTabsAdapter {
private final Context mContext;
public SwipeyTabsPagerAdapter(Context context, FragmentManager fm) {
super(fm);
this.mContext = context;
}
#Override
public Fragment getItem(int position) {
return SwipeyTabFragment.newInstance(TITLES[position]);
}
#Override
public int getCount() {
return TITLES.length;
}
public TextView getTab(final int position, SwipeyTabs root) {
TextView view = (TextView) LayoutInflater.from(mContext).inflate(R.layout.swipey_tab_indicator, root, false);
view.setText(TITLES[position]);
view.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mViewPager.setCurrentItem(position);
}
});
return view;
}
}
This will just construct a new Tab based on a String-Array, which will set the text and the header of the Fragment.
So this is where I get confused. Say for instance that I want several fragments with different layout, and different ways of interacting when the user presses on Button, Picture or whatever. How can I do so?
Thanks.
All the 'user presses button' stuff is handled in the fragments, you can call back to the Activity of course when you need to (see here).
You have to create different fragment classes for different layouts, logic. etc. and return them to the ViewPager in getItem. You could have a FirstPageFragment and a SecondPageFragment, then return them (depending on the index) in getView. This only makes sense if those fragments have different functionalities of course.
Hope it's clear what I mean ;)
EDIT: as to your comment:
I don't know what exactly you want to do, but you have your SwipeyTabFragment already defined in it's own file. Take this then, modify it, give it another layout and other functionality, then call it OtherFragment or whatever. Let's say you want to have 2 different 'pages' in your App - the getCount() method in your adapter defines the amount of 'pages' in your ViewPager, so let's let it return two.
In the getItem() method, if position is 0, let it return your SwipeyFragment, else (position is 1) let it return your new OtherFragment. Now you have a ViewPager with 2 different Fragments that can serve totally different purposes.
For an application which I am currently building in Java for Android I am looking for the following. I have a set with lets say 10 database records. For each row i want to show an activity (which is every time the same activity). The activity has some fields for each row to be updated.
Lets say for example you have a record set with contacts and now you want to loop through al 10 contacts and update the data.
Now I am not sure how this should work in android. Should I use fragments for this? How should i model this. Some pseudo code would help.
Since this is a custom app I am developing for ICS
Thanks very much
Create FragmentActivity with ViewPager.
ViewPager uses Adapter to display same fragments with different data.
For example, if you store in database images ids which you want to display, the code will look like this.
public class MyActivity extends FragmentActivity {
public static class MyFragment extends Fragment {
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
final ImageView content = new ImageView(getActivity());
content.setImageResource(getArguments().getInt("img"));
return content;
}
}
private class MyPagerAdapter extends FragmentStatePagerAdapter {
private final ArrayList<Integer> imgs;
public AppTourPagerAdapter(final FragmentManager fm, final ArrayList<Integer> imgs) {
super(fm);
this.imgs = imgs;
}
#Override
public int getCount() {
return imgs.size();
}
#Override
public Fragment getItem(final int position) {
final MyFragment fragment = new MyFragment();
final Bundle args = new Bundle();
args.putInt("img", imgs.get(position));
fragment.setArguments(args);
return fragment;
}
}
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
final ViewPager pager = (ViewPager) findViewById(R.id.pager);
final ArrayList<Integer> imgIds = ... // get values from database
pager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), imgIds));
}
}
Use a viewpager with a viewpager adapter. then create a layout with a view pager, after that create a layout that will contain the items in the view pager you wish to flip through. In the adapter inflate the layout you wish to flip through. There are tons of examples online. Google it. You can copy paste 90% of what, you need to make it work.
I want to create a screen with 4 tabs. But initially only first tab have to be seen.
Based on an action in the content ( which is a fragment) I need to create a new tab with different layout. Consider this an application as a form form applying to something. So tabs represents Steps 1,2,3,4. So once I complete the Step 1 I will click on a button which creates a new tab Step2.
I don't want to implement it by launching activities because I need to maintain previous tabs. So how do I catch a button click in a fragment and add a tab at runtime?
create a new tab with different layout
See this Example
How do you set Android ViewPager to encompass only one View or Layout?
The question is not related to you but Example is Valid and related to One part of your question
EDITED
This is how you can link fragments with your tabs.
class TestFragmentAdapter extends FragmentPagerAdapter {
private int mCount = TABS.length;
public TestFragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
MainSummary f1 = new MainSummary();
MainActivity f2 = new MainActivity();
if(TABS[position].equals("Summary")){
return f1;
}else if(TABS[position].equals("Activity")){
return f2;
}else{
return null;
}
}
#Override
public int getCount() {
return mCount;
}
#Override
public CharSequence getPageTitle(int position) {
return TABS[position];
}
}
And for each fragment you can have separate class and layout (R.layout.main_summary) like this
public final class MainSummary extends ListFragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.main_summary, container, false);
return view;
}
}
There are some useful sample projects here https://github.com/github/android
You can find all answers of your question if you study these samples
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");