FragmentPagerAdapter override getItem() method clash - android

In order to implement sliding tabs in Android, i am following this guide: Google Play Style Tabs using TabLayout
At the point of implement the FragmentPageAdapter i am having a problem whith the "getItem()" method which is supossed to return the fragment with the associated position, in this case "PageFragment.newinstance(position + 1)". Being PageFragment a generic Fragment.
The problem itself is:
'getItem(int)' in 'com.myProject.SampleFragmentPagerAdapter' clashed
with 'getItem(int)' in 'android.support.v4.app.FragmentPagerAdapter';
attempting to use incompatible return types
Can someone figure out where the problem is?
I have attached the SampleFragmentPagerAdapter of the guide for faster checks:
public class SampleFragmentPagerAdapter extends FragmentPagerAdapter {
final int PAGE_COUNT = 3;
private String tabTitles[] = new String[] { "Tab1", "Tab2", "Tab3" };
private Context context;
public SampleFragmentPagerAdapter(FragmentManager fm, Context context) {
super(fm);
this.context = context;
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public Fragment getItem(int position) {
return PageFragment.newInstance(position + 1);
}
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return tabTitles[position];
}
}

Solved, the problem was that SampleFragmentPagerAdapter class uses android.support.v4.app.Fragment
I was using, android.app.Fragment in PageFragment class.
That resulted in getItem method of SampleFragmentPagerAdapter having a clash between types because of diferent libraries.
Solution? Change the import line from
android.app.Fragment
to
android.support.v4.app.Fragmentin PageFragment Class.

Related

getResources() not working in FragmentPagerAdapter [duplicate]

This question already has answers here:
How to get String Resource within ViewPager Adapter?
(9 answers)
Accessing resources in FragmentPagerAdapter
(3 answers)
can't use method "getString(int resId)" in FragmentPagerAdapter
(2 answers)
Using getResources() in non-activity class
(12 answers)
Closed 3 years ago.
I try to populate tab titles in FragmentPagerAdapter by strings from string.xml. I check a few different ways to do that based on StackOverlow or another sources. Below I present my newest version of code. I still have message that "Cannot Resolve method 'getResources()'. Is it a possibility to correlate tab names with string.xml?
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
public class SimpleFragmentPagerAdapter extends FragmentPagerAdapter {
final int PAGE_COUNT=5;
private int[] tabTitles = new int[] {R.string.head_character, R.string.head_training, R.string.head_evolutions, R.string.head_talismans, R.string.head_enchantments};
public SimpleFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new TrainingFragment();
} else {
return new EvoFragment();
}
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return getResources().getString(tabTitles[position]);
}
}
try to set context as a parameter in your Class contractor and use it in your getPageTitle() methode ;
public class SimpleFragmentPagerAdapter extends FragmentPagerAdapter {
private Context context;
final int PAGE_COUNT=5;
private int[] tabTitles = new int[] {R.string.head_character, R.string.head_training, R.string.head_evolutions, R.string.head_talismans, R.string.head_enchantments};
public SimpleFragmentPagerAdapter(FragmentManager fm , Context context) {
super(fm);
this.context = context;
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new TrainingFragment();
} else {
return new EvoFragment();
}
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return context.getResources().getString(tabTitles[position]);
}
}
call the getResources() method using getActivity().getApplicationContext() as return getActivity().getApplicationContext().getResources().getString(tabTitles[position]);
you only can call getResorces() method on a context so you should use getActivity().getResorces()....

Implement onClickListener on fragment inside FragmentStatePagerAdapter

Good day,
I have ViewPager, and this is adapter:
public abstract class GalleryImageAdapter extends FragmentStatePagerAdapter {
private List<? extends UrlItem> images = new ArrayList<>();
public GalleryImageAdapter(FragmentManager fm, List<? extends UrlItem> images) {
super(fm);
this.images = images;
}
#Override
public Fragment getItem(int position) {
return giveFragment(images, position);
}
public abstract Fragment giveFragment(List<? extends UrlItem> images, int position);
#Override
public int getCount() {
return images.size();
}
#Override
public Object instantiateItem(ViewGroup collection, int position) {
Fragment fragment = (Fragment) super.instantiateItem(collection, position);
return fragment;
}
}
I have a ViewPager, which represents gallery of images. I would like to handle click on current selected child (fragment) in ViewPager.
I found something about instantiateItem, but nothing about how to handle clicks in FragmentStatePagerAdapter. I got the fragment in this method but .setOnClickListener is not found
If it's not needed for your image gallery items to have their own lifecycles (and I would imagine it's not), don't use Fragments and FragmentStatePagerAdapter. Use a RecyclerView with the appropriate adapter, as explained, for example, here.

Tab fragments in android

public class MyPageAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public MyPageAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
int s = this.fragments.size();
return this.fragments.size();
}
}
Text here:
if i go from tab4 to tab2 than first control go to tab2 than it go to tab1. i could not understand how can it go to tab1?
I can see you're trying to cache the fragments into memory by using the this context. But you're not supposed to, FragmentPagerAdapter class should do it for you. Google webpage and sample # FragmentPagerAdapter. Look at sample code on that page.
Code snippet:
#Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position);
}
Notes:
Notice the call to method newInstance. Implement it in your Fragment.
Need to define a fragment subclass, sample uses ArrayListFragment.

Android ViewPager prepend new View

I want to prepend a new View as first page of my ViewPager.
My adapter looks like this:
public class PagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragments;
public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int index) {
return fragments.get(index);
}
#Override
public int getCount() {
return fragments.size();
}
public void add(int i, ImageFileObject imageFile) {
ImageViewFragment f = new ImageViewFragment();
f.setImage(imageFile);
fragments.add(0, f);
notifyDataSetChanged();
}
public void add(ImageFileObject imageFile) {
ImageViewFragment f = new ImageViewFragment();
f.setImage(imageFile);
fragments.add(f);
notifyDataSetChanged();
}
}
But when calling add(0, aImageFile) the item is not prepended to the fragment list. (It's not even appended).
Any ideas?
Update: The problem that you described in comment is real, I missed it in first testing. After analyzing the source code of FragmentPagerAdapter I realized that all we need to do is to Override getItemId() in a way that it won't return same id for different fragment items. E.g. current default implementation would just return position as an id for fragment which won't work for this case:
public long getItemId(int position) {
return position;
}
I am updating this answer, please have a look. As before I have tested this code and the problem you described is not happening now.
Only thing you need is not to keep reference of fragments by yourself in List, This is done for you by FragmentPagerAdapter. And as far as I know its not a good practise either. And also even if you return POSITION_NONE from getItemPosition() as suggested by other answer(s), you will end up with Exception
Caused by: java.lang.IllegalStateException: Can't change tag of fragment.
This is because you are trying to reposition alive Fragments in your List by adding a fragment at 0th index (which causes other fragments to reposition) and ViewPager assigns tags based on position.
Keeping all this in mind, here is a tested and working modified adapter:
public class PagerAdapter extends FragmentPagerAdapter {
public static class FragmentInfo {
public String classz;
public ImageFileObject imageFile;
public static long IDS;
public long id;
public FragmentInfo(){
id = IDS++;
}
}
private final List<FragmentInfo> fragments;
private Context context;
public PagerAdapter(FragmentManager fm, List<FragmentInfo> fragments,
Context context) {
super(fm);
this.fragments = fragments;
this.context = context;
}
#Override
public Fragment getItem(int index) {
FragmentInfo info = fragments.get(index);
ImageViewFragment frag = (ImageViewFragment) Fragment.instantiate(
context, info.classz, /* null arguments*/ null);
frag.setImage(info.imageFile);
return frag;
}
#Override
public long getItemId(int position) {
return fragments.get(position).id;
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
public void add() {
FragmentInfo f = new FragmentInfo();
f.classz = ImageViewFragment.class.getName();
fragments.add(0, f);
notifyDataSetChanged();
}
}
Please change your code accordingly.
The problem is actually with notifyDataSetChanged. Just add to your Adapter:
public int getItemPosition(Object object) {
return POSITION_NONE;
}
However, I think you will get other problems when the Adapter actually tries to call getItem again. These problems will be in the lines of "Fragment Already Added".

Android FragmentStatePagerAdapter

I'm working with a FragmentStatePagerAdapter using this example.
The MyAdapter class is implemented as follows:
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);
}
}
The ListFragment class includes the following method to create a new instance:
/**
* Create a new instance of CountingFragment, providing "num"
* as an argument.
*/
static ArrayListFragment newInstance(int num) {
ArrayListFragment f = new ArrayListFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
When I create a new fragment state pager adapter in my activity, getItem is called, which in turn calls the newInstance method in the ListFragment class. This is great when I want to create a new fragment.
But it's not clear to me how to modify getItem (if even needed) to get the fragment object when it already exists and the user pages from, for example, page 2 to page 1. I'd like my Activity to retrieve that existing, previously created fragment so that it can run an inner class AsyncMethod, which resides in the fragment class.
I think the solution is much simpler than the answers provided.
If you take a look at the source of FragmentStatePagerAdapter.instantiateItem(), you'll notice that instantiateItem() handles this logic for you, and thus your implementation of getItem() should always return a new instance.
So in order to return an existing Fragment, simply do (assuming you're calling from ViewPager):
Fragment f = (Fragment) getAdapter().instantiateItem(this, getCurrentItem());
While #imiric's solution is very concise, it still bothers me that it requires knowledge of the implementation of the class. My solution involves adding the following to your adapter, which should behave nicely with a FragmentStatePagerAdapter possibly destroying fragments:
private SparseArray<WeakReference<Fragment>> mFragments = new SparseArray<>();
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment f = (Fragment) super.instantiateItem(container, position);
mFragments.put(position, new WeakReference<>(f)); // Remember what fragment was in position
return f;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
mFragments.remove(position);
}
public Fragment getFragment(int position) {
WeakReference<Fragment> ref = mFragments.get(position);
Fragment f = ref != null ? ref.get() : null;
if (f == null) {
Log.d(TAG, "fragment for " + position + " is null!");
}
return f;
}
I have implemented something similar to what you have. I extended the FragmentPagerAdapter class like so:
public class ContactsFragmentPagerAdapter extends FragmentPagerAdapter {
ActionBar mActionBar;
private List<Fragment> mFragments;
public ContactsFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
mFragments = fragments;
}
#Override
public int getCount() {
return mFragments.size();
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
public void setActionBar(ActionBar bar) {
mActionBar = bar;
}
}
Notice I have added an argument to the constructor to pass in the List of Fragment objects. This way the getItem() method of this class can return any class that extends Fragment or any of its subclasses and not just one specific class ArrayListFragment like you have done.
In the Activity where I instantiate my subclass of FragmentPagerAdapter I have passed in the list of Fragment objects:
Class the instantiates the FragmentPagerAdapter
public final class ContactManager extends Activity {
private ContactsFragmentPagerAdapter mAdapter;
private ViewPager mPager;
public ActionBar mActionBar;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contact_manager);
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, ContactsListFragment.class.getName()));
fragments.add(Fragment.instantiate(this, GroupsListFragment.class.getName()));
mAdapter = new ContactsFragmentPagerAdapter(this.getFragmentManager(), fragments);
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageScrollStateChanged(int arg0) {}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
#Override
public void onPageSelected(int arg0) {
mActionBar.getTabAt(arg0).select();
}
});
}
}
By accessing the variable "fragments", you can access a previously created Fragment so that you can run methods of that Fragment.
public class MyPagerAdapter extends FragmentStatePagerAdapter {
final Context m_context;
final WeakReference<Fragment>[] m_fragments;
public DetailPagerAdapter(FragmentManager fm) {
super(fm);
m_context = ...
m_fragments = new WeakReference[enter size here];
}
#Override
public Fragment getItem(int position) {
final Fragment fragment = instantiate your fragment;
m_fragments[position] = new WeakReference<Fragment>(fragment);
return fragment;
}
#Override
public int getCount() {
return ...
}
public Fragment getFragment(final int position) {
return m_fragments[position] == null ? null :
m_fragments[position].get();
}
}
Do not need modify the FragmentPagerAdapter, because fragments are cached by the FragmentManager. So you must need find inside it. Use this function to find the fragment by pager adapter position.
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Sample code for v4 support api.
I think this could help:
class HomePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val fragmentConstructors: List<() -> Fragment> by lazy {
listOf(
::AuctionLotsFragment,
::ChatListFragment,
::MarketHomeFragment,
::WalletSectionsFragment,
::SettingsFragment
)
}
private val fragments: HashMap<Int, WeakReference<Fragment>> by lazy {
HashMap()
}
override fun getCount(): Int = fragmentConstructors.size
override fun getItem(position: Int): Fragment {
return fragments[position]?.get() ?: this.createFragmentForPosition(position)
}
private fun createFragmentForPosition(position: Int): Fragment {
val fragment = fragmentConstructors[position].invoke()
fragments[position] = WeakReference(fragment)
return fragment
}
}

Categories

Resources