Android Fragments in a ViewPager - android

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.

Related

moving from one fragment to another using viewpager

So currently my code can move between objects from the same fragment, but I want to move between different fragments that have different layouts.What code do I need to add to viewpager to make it work? Do I need to make use of a FragentManager? Can anyone guide me on how to go about it? Thanks.
Below if my code:
ScreenSlidePagerActivity.java
public class ScreenSlidePagerActivity extends FragmentActivity {
private static final int NUM_PAGES = 5;
private ViewPager mPager;
private PagerAdapter pagerAdapter;
/**
* The pager adapter, which provides the pages to the view pager widget.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slide_screen_viewpager);
//declare viewpager and pageradapter
mPager = findViewById(R.id.ViewPageSlide);
pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(pagerAdapter);
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0){
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
}
else {
mPager.setCurrentItem(mPager.getCurrentItem() -1 );
}
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int position) {
return new ScreenSlidePageFragment();
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
ScreenSlidePageFragment.java
public class ScreenSlidePageFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.slide_content_page, container, false
);
return rootView;
}
}
You have only one class i.e.,ScreenSlidePageFragment that extends fragments. If you want different layouts for that, its better if you create different classes that inflates different layouts. eg: if you want two layouts, create two classes and both classes should inflate different layouts. The changes need to be done are :
//inside ScreenSlidePagerAdapter
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new ScreenSlidePageFragment();
case 1:
return new NewClass();
//and so on
}
}
You have to create the new Class similar to ScreenSlidePageFragment. The only change is inflate a different layout.
public class NewClass extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.new_layout, container, false
);
return rootView;
}
}
You can create a new_layout similar to slide_content_page and customize it as you want. You can also increase the no of fragment objects and layout as you wish.
But a new way of doing this things has come. Its better if you extend FragmentStateAdapter instead of FragmentStatePagerAdapter. This is more easy and efficient. You have to override createFragment in this case instead of getItem. Ignore of you are okay with it.
Hope this is the question you have asked and this helps. Thankyou.

notifyDataSetChanged Method Takes Away Smooth Scrolling From Tabs

I am currently using Material Design in an Android app that I am making. In this app, I am using the Material Design tab layout to display some information that I am receiving. However when I tap the tabs, the animation is not smooth, and it is very abrupt. Sliding to go to the other tab, however is very smooth.
mTabLayout = (TabLayout) findViewById(R.id.chem_tab_layout);
mGenericAdapter = new GenericPagerAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.view_pager);
mPager.setAdapter(mGenericAdapter);
//Notice how the Tab Layout links with the Pager Adapter
mTabLayout.setTabsFromPagerAdapter(mGenericAdapter);
//Notice how The Tab Layout and View Pager object are linked
mTabLayout.setupWithViewPager(mPager);
mPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout){
#Override
public void onPageSelected(int position) {
mGenericAdapter.notifyDataSetChanged();
}
});
That is my code for setting the adapter, etc.
This is my custom adapter code for the tabs:
class GenericPagerAdapter extends FragmentStatePagerAdapter {
public GenericPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
ChemGridActivity.MyFragment myFragment = new ChemGridActivity.MyFragment();
return myFragment;
}
#Override
public int getCount() {
return 3; //returns number of tabs that need to be created
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) return "Chemistry";
if (position == 1) return "Mathematics";
if (position == 2) return "Physics";
else return null;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
I feel that the choppy transition between tabs is caused by the overriden method onPageSelected method when I add onPageChangeListener. What do I add to this method to make tapping on tabs a smoother animation?
Without knowing much about the internals of your classes, I imagine the problem is not that you have a listener, but what you are doing inside that listener.
In the case of most adapters notifyDataSetChanged() will cause it to re-render the entire view again (including all pages).
Seeing as you haven't specified what the intent here with the notification is, it's hard to tell you how you can do this in an alternative way, but you do need to do something less intensive if you want the animation to remain smooth.
I suspect you just want to change which fragment is shown, in which case just use the FragmentManager where necessary, remembering to reuse fragments which have already been seen once.
EDIT Based on additional info in comments
#Override
public Fragment getItem(int position) {
//POSITION_SOMETHINHG would be one of a set of constants to indicate hwa to display
return ChemGridActivity.MyFragment.newInstance(ChemGridActivity.MyFragment.POSITION_SOMETHINHG);
}
public class ChemGridActivity.MyFragment ... {
private static final String KEY_DISPLAY_TYPE = "KEY_DISPLAY_TYPE";
public static final int POSITION_SOMETHINHG = 11111;
public static MyFragment newInstance(int display) {
MyFragment f = new MyFragment();
Bundle bund = new Bundle();
bund.putInt(KEY_DISPLAY_TYPE, display);
f.setArguments(bund);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
mDisplay = args.getInt(KEY_DISPLAY_TYPE, 0);
}
}
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.my_layout, container, false);
//TODO: change something based on mDisplay
return view;
}

ViewPager - Events in all page occurs only in last page

I have a viewPager with say 4 pages. All 4 pages uses same Xml. When i do an event in 1st page somehow it always triggers in the last page.
Here is my PagerAdapter
#Override
public Object instantiateItem(ViewGroup container, int pos) {
View desktopView;
OnTouchListener tl = null;
desktopView = act.getLayoutInflater().inflate(
act.getViewPagerLayout(groupName), null);
RelativeLayout rr_appContainer, rr_dialogContainer;
ImageView rr_home_container = (ImageView) desktopView
.findViewById(R.id.imageView_forClick);
Button buttonChange = (Button)desktopView.findViewById(R.id.B1);
Button buttonDelete = (Button)desktopView.findViewById(R.id.B2);
rr_appContainer = (RelativeLayout) desktopView
.findViewById(R.id.rr_home_container);
rr_dialogContainer = (RelativeLayout) desktopView
.findViewById(R.id.rr_dialogView);
..........
buttonDelete.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
deletestuff();
}
buttonChange.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
changeColorOfStuff();
}
.....
return desktopView;
}
What is happening is, When i click on buttonChange from 1st page it supposed to change the color of text on 1st page, but actually it is changing color of the last page. Similarly buttonDelete is deleting color from last page.
Regardless of what page i am in, its reflecting those changes on last page.
Any help would be appreciated.
From the context given here, the deleteStuff() and changeColorOfStuff() can only be members of the Fragment/Activity that owns the adapter, or the adapter itself. So these methods can only act on members of those classes. ViewPager asks the adapter for the fragments it is going to display. However, the text in the fragment being shown by the ViewPager belong to the that fragment. To act on that text, you need a method that's a member of that fragment. The usual way to do this is to use a custom fragment. For example:
Custom Fragment (inner class):
public static class CustomFragment extends Fragment {
//members of the fragment
TextView yourTextView;
...
public static CustomFragment newInstance(int pos) {
CustomFragment fragment = new CustomFragment();
//get whatever info you need for this page
Bundle args = getInfoSomehow(pos);
fragment.setArguments(args)
return fragment;
}
#Override
public View onCreateView(Layout inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(....
yourTextView = root.findViewById(...) //this is the text view you want to change things in
//all the stuff you're currently doing in instantiateItem()
return root;
}
private void deleteStuff() {
//whatever you need to do. But notice that here it's acting on the TextView that belongs to this particular fragment
}
private void changeColorOfStuff() {...}
...
}
Then in your instantiateItem(...)
#Override
public Object instantiateItem(ViewGroup container, int pos) {
return CustomFragment.newInstance(pos);
}

Android:- Fragments and Tabs

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

How to refresh current view in ViewPager

I am using ViewPager with views V1, V2, V3 ..... I am trying to set visibility of a LinearLayout used in each view, by clicking on a button. Through this code it apply the change on the next view instead of the current view. e.g. I am on V5. When I click it hides/show the object on V6. If I am going backwards from V6 to V5, then it applies the change on V4.
Here is the code:
public class FragmentStatePagerSupport extends FragmentActivity {
static final int NUM_ITEMS = 10;
MyAdapter mAdapter;
ViewPager mPager;
static int mNum;
private Button btn_zoom;
static LinearLayout LL_Head;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(5);
btn_zoom = (Button)findViewById(R.id.btn_zoom);
btn_zoom.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
if (LL_Head.getVisibility() == View.VISIBLE) {
LL_Head.setVisibility(View.GONE);
}else{
LL_Head.setVisibility(View.VISIBLE);
}
}
});
.
.
.
}
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position);
}
}
public static class ArrayListFragment extends ListFragment {
static ArrayListFragment newInstance(int num) {
ArrayListFragment f = new ArrayListFragment();
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNum = getArguments() != null ? getArguments().getInt("num") : 1;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.sura_vpager, container, false);
TextView tv1=(TextView) v.findViewById(R.id.txtHead);
tv1.setText("Fragment #" + mNum);
LL_Head = (LinearLayout)v.findViewById(R.id.LL_Head);
return v;
}
Please advise
Thanks
In order to make a fluent experience the ViewPager not only loads the view you are currently looking at, but also the adjacent views. That means, that if you are scrolling from position 0 to position 1, what actually happens is that position 2 is loaded, so it will be ready when you scroll on. This is why the change is applied to the "next" view, rather than the current one (if you scroll from view 2 to 1, then view 0 is created).
Since you are setting the static LinearLayout in OnCreate, then it's only the last view to be created that is changed - and this will only ever be the one you are looking at, if you have scrolled to the end of the pager. Instead you should keep track of which fragment the user is looking at (ViewPager.setOnPageChangeListener()) and cache the fragment in your adapter. You then know which fragment position you want, and when you ask for it, you will just return the one you previously created (don't create a new one, then it won't work :)).
Or, the tl;dr version:
LL_Head is almost always set to be the next fragment, not the current one. Don't set it statically. Cache it in your PagerAdapter and reget it when you need it.
Edit:
Alternatively you may want to have the fragments listen to an event of sorts, which tells them whether they should show or hide the layout in question. Otherwise it will only be the current fragment that is affected by this, rather than all fragments.
The numbering in Java starts from 0. Thus when you want to set the 5th item, you have to call mPager.setCurrentItem(4);
Hope this helps!

Categories

Resources