This question already has answers here:
Proper implementation of ViewPager2 in Android
(12 answers)
Closed 3 years ago.
Android Studio 3.6
I want in my activity to use 2 fragments inside ViewPager2.
Here my activity:
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
public class QRBluetoothSwipeActivity extends AppCompatActivity {
private ViewPager2 myViewPager2;
private ViewPagerFragmentAdapter myAdapter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
// Make sure this is before calling super.onCreate
setTheme(R.style.AppTheme); // show splash screen
super.onCreate(savedInstanceState);
setContentView(R.layout.qr_bluetooth_swipe_activity);
init();
}
private void init() {
myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager());
myAdapter.addFragment(new BluetoothPageFragment());
myAdapter.addFragment(new QrPageFragment());
myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
myViewPager2.setAdapter(myAdapter);
}
}
here ViewPagerFragmentAdapter
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import java.util.ArrayList;
public class ViewPagerFragmentAdapter extends FragmentStateAdapter {
private ArrayList<Fragment> arrayList = new ArrayList<>();
public ViewPagerFragmentAdapter(#NonNull FragmentManager fragmentManager) {
super(fragmentManager);
}
#NonNull
#Override
public Fragment getItem(int position) {
return arrayList.get(position);
}
public void addFragment(Fragment fragment) {
arrayList.add(fragment);
}
#Override
public int getItemCount() {
return arrayList.size();
}
}
but I get compile error:
ViewPagerFragmentAdapter is not abstract and does not override abstract method createFragment(int) in FragmentStateAdapter
How I can fix this and use FRAGMENTS in ViewPager2 ?
Thanks
FragmentStateAdapter in ViewPager2 is a little bit different than in ViewPager as follows:
Instead of implementing getCount(), implement getItemCount()
Instead of implementing getItem(int position), implement createFragment(int position)
Constructor signature is different by adding Lifecycle argument
which should be included in super as well
So replace your ViewPagerFragmentAdapter with below
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import java.util.ArrayList;
public class ViewPagerFragmentAdapter extends FragmentStateAdapter {
private ArrayList<Fragment> arrayList = new ArrayList<>();
public ViewPagerFragmentAdapter(#NonNull FragmentManager fragmentManager, #NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
public void addFragment(Fragment fragment) {
arrayList.add(fragment);
}
#Override
public int getItemCount() {
return arrayList.size();
}
#NonNull
#Override
public Fragment createFragment(int position) {
// return your fragment that corresponds to this 'position'
return arrayList.get(position);
}
}
Also make sure to import/extend the right adapter of ViewPager2
import androidx.viewpager2.adapter.FragmentStateAdapter;
UPDATE 2021
This answered the original issue of the question; However creating the FragmentStateAdapter this way is not that right generally speaking; because of:
To have a high performant ViewPager, it should keep only a few instantiated off-screen page fragments.
Keeping all the page fragments into a list is not good in terms of wasting device resources.
createFragment(), as its name implies, is intended to create a new fragment for a particular page and return it; not to return a fragment from a list that has already instantiated fragments.
Above all as per documentation createFragment()::
Provide a new Fragment associated with the specified position.
The adapter will be responsible for the Fragment lifecycle:
The Fragment will be used to display an item.
The Fragment will be destroyed when it gets too far from the
viewport, and its state will be saved. When the item is close to the
viewport again, a new Fragment will be requested, and a previously
saved state will be used to initialize it.
So, I'd suggest to change this design to include that into consideration:
Assuming your page fragment is PageFragment; then you can have single instance pattern by having static PageFragment newInstance(int position) {} method in the PageFragment, and then the createFragment() should be then:
#NonNull
#Override
public Fragment createFragment(int position) {
// return a brand new fragment that corresponds to this 'position'
return PageFragment.newInstance(position); // Create a new instance of the page fragment
}
Related
So if I have three Fragments A,B, and C.
So when I launch Fragment B I want viewpager to let me swipe from LEFT to RIGHT and it should take me back to Fragment A, but viewpager, by default lets me swipe from RIGHT to LEFT to go to Fragment A.
So basically I want viewpager to let me swipe from LEFT to RIGHT and not from RIGHT to LEFT.
Also I tried:
viewpager.setRotationY=180
That does let me do what I want but all the contents in my Fragments get flipped upside down.
The adapter I am using:
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import org.jetbrains.annotations.NotNull;
public class Adapter extends FragmentPagerAdapter {
public Adapter(FragmentManager fragmentManager){
super(fragmentManager);
}
#NonNull
#NotNull
#Override
public Fragment getItem(int position) {
if(position==0){
return new SettingsFragment();
}
if(position==1){
return new BlankFragment();
}
if(position==2){
return new VersionFragment();
}
return null;
}
#Override
public int getCount() {
return 3;
}
}
The activity where I use the adapter:
public class SettingsActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
Fragment fr=new Fragment();
getSupportFragmentManager().beginTransaction().addToBackStack(null).add(fr,"settings_fragment").commit();
runOnUiThread(new Runnable() {
#Override
public void run() {
ViewPager viewPager=findViewById(R.id.ViewPager);
viewPager.setCurrentItem(1);
viewPager.setAdapter(new Adapter(getSupportFragmentManager()));
viewPager.setOffscreenPageLimit(1);
}
});
}
}
I think your code is misleading you, because you using viewPager.setCurrentItem(1); before setAdapter(...) which you ViewPager will not go to item 1 as you expected.
So, try put that viewPager.setCurrentItem(1); after setAdapter(...)
So I want to make an app that has multiple tabs that I can slide between.
I choose the Tabbed Acitivity with Action Bar Tabs (with ViewPager) navigation tabs.
I am trying to make each tab correspond with a fragment. So I can change the lay-out of each fragment etc.
A part of my fragment code is:
public class Page1Fragment extends Fragment {
private OnFragmentInteractionListener mListener;
public Page1Fragment() {
// Required empty public constructor
}
*This is not the complete code
In my SeciontsPagerAdapter, this is my getItem method.
#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 MainActivity.PlaceholderFragment.newInstance(position + 1);
switch(position){
case 0:
return new Page1Fragment();
case 1:
return new Page2Fragment();
case 2:
return new Page3Fragment();
case 3:
return new Page4Fragment();
}
return null;
}
But I get an error? It says Page1Fragment can't be converted to a Fragment?
But I let Page1Fragment extends from Fragment?
Does anyone have any idea why this doesn't work?
Try this:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
return new GamesFragment1();//.newInstance(0);
case 1:
return new GamesFragment2();//.newInstance(1);
case 2:
return new GamesFragment3();//.newInstance(2);
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
}
Page1Fragment:
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager.PageTransformer;
There are two fragment classes either you have to use support library class or app.fragment class. don't mix it up
You need to import below
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
Did you add this ?
import android.support.v4.app.FragmentStatePagerAdapter;
SetFragmentStatePagerAdapter instead of FragmentPagerAdapter
This is killing me. I have one DemoFragment which has 3 instances created. Within these instances different data is loaded into the fragments. What I want to be able to do is have a button (within the fragment) that forces all of the Fragments to refresh or reload. This is necessary as the button also commits new data that needs to be pulled newly for every fragment.
I've search plenty of solutions, but can't get to the bottom of it. Currently I have a an activity, fragment and adapter. Below is the abridged code (as expected this loads 3 pages):
Activity (RoutineDetailsActivity.java)
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
public class RoutineDetailsActivity extends FragmentActivity {
ViewPager mViewPager;
Button mLogout;
CustomPagerAdapter mSectionsPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_routine_details);
mSectionsPagerAdapter = new CustomPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
}
Adapter (CustomPagerAdapter.java)
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import java.util.Locale;
public class CustomPagerAdapter extends FragmentStatePagerAdapter {
public CustomPagerAdapter (FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 3;
}
#Override
public Fragment getItem(int position) {
return DemoFragment.newInstance(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
}
Fragment (DemoFragment.java)
public class DemoFragment extends Fragment {
int mNum;
static DemoFragment newInstance(int num) {
DemoFragment f = new DemoFragment();
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 rootView = inflater.inflate(R.layout.fragment_routine_details, container, false);
}
#Override
public void onResume() {
// CODE CLIPPED, do loads of sh*t
Button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// New data uploaded to server
// What code to force reload the fragments?
}
});
}
So within this code, more specifically, within the button click, what do I need to do to force the all Fragments to reload and retrieve the latest data.
I've sen answers around this, but I'm sure how to relate it to here.
Thanks.
If you a willing to add a small library called EventBus: https://github.com/greenrobot/EventBus
You can do like this:
public class AnyFragment extends Fragment {
#Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
EventBus.getDefault().register(this);
}
#Override
public void onDetach() {
EventBus.getDefault().unregister(this);
super.onDetach();
}
public void onEventMainThread(UpdateUIEvent ev) {
// perform refresh
}
}
UpdateUIEvent is just a simple empty object in my case but could contain arbitrary data as well:
public class UpdateUIEvent {}
With this in place you can refresh AnyFragment from anywhere in your code by doing:
EventBus.getDefault().post(new UpdateUIEvent());
I want to stress that you can do this Anywhere, but in you case, the above line would be the only thing you would have to do in click of your button. Then all fragments listening, as shown with AnyFragment, will receive the event :)
Implement an interface in your fragment say IRefresher. Keep track of references of the 3 fragments in your activity. When button is clicked call the interface method say refreshMe() which would work as a listener/callback in the fragments and in that method refresh your fragment.
I have done it in one of my project and it works flawlessly
I am not sure whether I have understood your problem or not. From your button trigger you can call the parent activity which one is holding all the fragments. Create a method in the parent activity. Inside the method register listener (Call Interface method) which update the fragments lists. Call the parent method from fragment(s) when you need it.
I'm trying to make an application that has two fragments that are both different in function and you can swipe right to go between them. I've looked into how to do this, but no solution seems to work.
Tabs and Swipe - editing the getItem to display 3 different fragments
Im trying to implement a getitem similar to this, but as you can see in this code:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by cwallace on 11/10/14.
*/
//Implement PagerAdapter Class to handle individual page creation
class MyPagesAdapter extends FragmentPagerAdapter {
public MyPagesAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 2;
}
#Override
public Fragment getItem(int position) {
Fragment fragment = new InitialFragment();
return fragment;
}
}
I want to return a different type of fragment than the function wants, and I can't cast my Initial Fragment to the Fragment class. Am I even attempting to do this the correct way?
Let's point out some things.
can't cast my Initial Fragment, make sure that your initialFragment class extends the Fragment.
As #hitch.united suggest, in getItem() of adapter , you can return the fragment by the id in a if-else. But remember that: You should not use this function to retrieve the fragment!
I can suggest you by using the ViewPager for swipe operation.
Try this way:
class MyPagesAdapter extends FragmentPagerAdapter {
public MyPagesAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 2;
}
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch(position) {
case 0:
fragment = new InitialFragment();
break;
case 1:
fragment = new YourSecondFragment();
break;
}
return fragment;
}
}
I'm trying to make a slidescreen with viewpager and fragments so that I can load different layouts per fragment and give each page different functionality.
I followed a tutorial to accomplish this.
The error I'm getting when hovering over public Fragment getItem(int arg0): The return type is incompatible with FragmentPagerAdapter.getItem(int)
and error #2: The constructor FragmentPagerAdapter(FragmentManager) is undefined
--> getting this one when hovering super(fm);
package com.example.spui;
import android.os.Bundle;
import android.app.Fragment;
import android.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
public class MyFragmentPagerAdapter extends FragmentPagerAdapter{
final int PAGE_COUNT = 5;
/** Constructor of the class */
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
/** This method will be invoked when a page is requested to create */
#Override
public Fragment getItem(int arg0) {
MyFragment myFragment = new MyFragment();
Bundle data = new Bundle();
data.putInt("current_page", arg0+1);
myFragment.setArguments(data);
return myFragment;
}
/** Returns the number of pages */
#Override
public int getCount() {
return PAGE_COUNT;
}
}
You are using the wrong FragmentManager import. Use android.support.v4.app.FragmentManager instead.
Same problem with Fragment - use android.support.v4.app.Fragment
Note: if you are building an API11+ only application and want to use the native Fragments, then you should instead change your FragmentPagerAdapter import to android.support.v13.app.FragmentPagerAdapter.