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()....
Related
This question already has answers here:
TabLayout Tab Title text in Lower Case
(6 answers)
Closed 6 years ago.
I found a question in tablayout,the title of TableLayout are always show in capital letters,how to solve this problem?
TableLayout title
I get the title text form strings.xml,and the adapter is following:
static class Adapter extends FragmentPagerAdapter {
private final List<Fragment> mFragments = new ArrayList<>();
private final List<String> mFragmentTitles = new ArrayList<>();
Adapter(FragmentManager fm) {
super(fm);
}
void addFragment(Fragment fragment, String title) {
mFragments.add(fragment);
mFragmentTitles.add(title);
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
#Override
public int getCount() {
return mFragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitles.get(position);
}
}
public static Fragment create() {
return new SearchFragment();
}
}
strings.xml
<string name="Repositories">Repositories</string>
<string name="Code">Code</string>
<string name="Issues">Issues</string>
<string name="Users">Users</string>
Replace your code with this
it will work
import android.support.v4.app.FragmentPagerAdapter;
import java.util.ArrayList;
import java.util.List;
static class Adapter extends FragmentPagerAdapter {
private final List<Fragment> mFragments = new ArrayList<>();
private final List<String> mFragmentTitles = new ArrayList<>();
Adapter(FragmentManager fm) {
super(fm);
}
void addFragment(Fragment fragment, String title) {
mFragments.add(fragment);
mFragmentTitles.add(title);
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
#Override
public int getCount() {
return mFragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitles.get(position).toLowerCase();
}
}
public static Fragment create() {
return new SearchFragment();
}
}
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.
I got the screenslider to work from the android Screenslider,
put a webview in and got it to load the urls i assign to it.
It works fine but now i'd like to control the private static final int NUM_PAGES = 5;
and dynamically change it from a sqlite database numrowcount when i move to this activity.
I tried wrappers and then some but it will crash everytime.
My knowledge of java is somewhat limited elas.
Now i know this works in the oncreate
Bundle bundle = new Bundle();
bundle.putString("urlData", urlData);
ScreenSlidePageFragment fragobj = new ScreenSlidePageFragment();
fragobj.setArguments(bundle);
So i assign the url thats being used to get content within the fragment.
Now how do i control the private static final int NUM_PAGES = 5,
and reassign it to whatever number i want or get from the database?
I just cant get why this is such a difficult thing to create
or find on the web.
Seems to me it should be a non final static number.
I know i am totaly not getting it, but someone does ;)
Anybody any idea?
this is the code:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
...
public class ScreenSlidePagerActivity extends FragmentActivity {
/**
* The number of pages (wizard steps) to show in this demo.
*/
private static final int NUM_PAGES = 5;
private ViewPager mPager;
private PagerAdapter mPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_slide);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
}
#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 {
// Otherwise, select the previous step.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
/**
* A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
* sequence.
*/
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;
}
}
}
Mm i turn out to be easy just make it non final and slip in a variable (int count) with the constructor.
public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public int NUM_COUNT;
#Override
public int getCount() {
return NUM_COUNT;
}
public ScreenSlidePagerAdapter(FragmentManager fm,int count) {
super(fm);
this.NUM_COUNT = count;
}
#Override
public Fragment getItem(int position) {
return ScreenSlidePageFragment.create(position);
}
}
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".
I'm making an adroid application with viewpager and fragments. I want to make an option to add or remove fragment pages to the pager dynamically. I have a custom FragmentPagerAdapter:
public class MyAdapter extends FragmentPagerAdapter implements TitleProvider{
protected final List<PageFragment> fragments;
/**
* #param fm
* #param fragments
*/
public MyAdapter(FragmentManager fm, List<PageFragment> fragments) {
super(fm);
this.fragments = fragments;
}
public void addItem(PageFragment f){
fragments.add(f);
notifyDataSetChanged();
}
public void addItem(int pos, PageFragment f){
for(int i=0;i<fragments.size();i++){
if(i>=pos){
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag(getFragmentTag(i))).commit();
}
}
fragments.add(pos,f);
notifyDataSetChanged();
pager.setAdapter(this);
pager.setCurrentItem(pos);
}
public void removeItem(int pos){
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag(getFragmentTag(pos))).commit();
for(int i=0;i<fragments.size();i++){
if(i>pos){
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag(getFragmentTag(i))).commit();
}
}
fragments.remove(pos);
notifyDataSetChanged();
pager.setAdapter(this);
if(pos<fragments.size()){
pager.setCurrentItem(pos);
}else{
pager.setCurrentItem(pos-1);
}
}
#Override
public Fragment getItem(int position) {
return fragments.get(position).toFragment();
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
private String getFragmentTag(int pos){
return "android:switcher:"+R.id.pager+":"+pos;
}
public String getTitle(int position) {
String name = fragments.get(position).getName();
if(name.equals(""))
return "- "+position+" -";
return name;
}
}
I can remove any fragment except the 0. when I try to remove it I got a NullPointerException on the notifyDataSetChanged(); or on the pager.setAdapter(this); if i comment out the notify.
I also got a NullPointerException when I try to insert a new page. When I add the new page to the end of the list it's working fine.
I even tried to readd the fragments in the insert with this after the fragments.add(pos,f)
for(int i=0;i<fragments.size();i++){
if(i>=pos){
getSupportFragmentManager().beginTransaction().add(fragments.get(i).toFragment(),getFragmentTag(i-1)).commit();
}
}
if I use getFragmentTag(i-1) I got nullpointer again. With using just i I got illegalstateexception because can not modify fragment's tag. With beginTransaction().add(pager.getId,fragments.get(i).toFragment()) it is still nullpointer...
My question is: what am I doing wrong, and how can it be done properly? (and maybe: from where notyfyDataSetChanged() get the data when causes nullpointerexception?)
This is what I used to get around the problem with fragments being tagged when instantiated and the fact that previously tagged fragments cannot change position within the ViewPager when trying to add or remove pages, i.e. getting the exception:
java.lang.IllegalStateException: Can't change tag of fragment
MyFragment{4055f558 id=0x7f050034 android:switcher:2131034164:3}: was
android:switcher:2131034164:3 now android:switcher:2131034164:4
This adapter is created with a list of all pages upfront and you can then use setEnabled(int position, boolean enabled) to disable or enable certain pages which hides or shows these pages in the ViewPager.
It works by maintaining an internal list of all fragments but exposing to the ViewPager and mapping the positions of enabled fragments only.
public class DynamicFragmentPagerAdapter extends FragmentPagerAdapter {
public final ArrayList<Fragment> screens = new ArrayList<Fragment>();
private Context context;
private List<AtomicBoolean> flags = new ArrayList<AtomicBoolean>();
public DynamicFragmentPagerAdapter(FragmentManager fm, Context context, List<Class<?>> screens) {
super(fm);
this.context = context;
for(Class<?> screen : screens)
addScreen(screen, null);
notifyDataSetChanged();
}
public DynamicFragmentPagerAdapter(FragmentManager fm, Context context, Map<Class<?>, Bundle> screens) {
super(fm);
this.context = context;
for(Class<?> screen : screens.keySet())
addScreen(screen, screens.get(screen));
notifyDataSetChanged();
}
private void addScreen(Class<?> clazz, Bundle args) {
screens.add(Fragment.instantiate(context, clazz.getName(), args));
flags.add(new AtomicBoolean(true));
}
public boolean isEnabled(int position) {
return flags.get(position).get();
}
public void setEnabled(int position, boolean enabled) {
AtomicBoolean flag = flags.get(position);
if(flag.get() != enabled) {
flag.set(enabled);
notifyDataSetChanged();
}
}
#Override
public int getCount() {
int n = 0;
for(AtomicBoolean flag : flags) {
if(flag.get())
n++;
}
return n;
}
#Override
public Fragment getItem(int position) {
return screens.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE; // To make notifyDataSetChanged() do something
}
#Override
public void notifyDataSetChanged() {
try {
super.notifyDataSetChanged();
} catch (Exception e) {
Log.w("", e);
}
}
private List<Fragment> getEnabledScreens() {
List<Fragment> res = new ArrayList<Fragment>();
for(int n = 0; n < screens.size(); n++) {
if(flags.get(n).get())
res.add(screens.get(n));
}
return res;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
// We map the requested position to the position as per our screens array list
Fragment fragment = getEnabledScreens().get(position);
int internalPosition = screens.indexOf(fragment);
return super.instantiateItem(container, internalPosition);
}
#Override
public CharSequence getPageTitle(int position) {
Fragment fragment = getEnabledScreens().get(position);
if(fragment instanceof TitledFragment)
return ((TitledFragment)fragment).getTitle(context);
return super.getPageTitle(position);
}
public static interface TitledFragment {
public String getTitle(Context context);
}
}
I have a solution that works with a non-fragment based adapter. Take a look at my post and see if it helps.
I can remove any fragment except the 0.
For me it works if just use a >= condition whitin the removeItem's for-loop:
if (i >= pos){
fm.beginTransaction().remove(fragments.get(i).getFragment()).commit();
}