I created a viewpager and an adapter with fragments that contains 2 views. I want to make the second view in the fragment invisible when I click on the frist one. I implemented this method on my view to handle the click events
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
ImageFragment fragment = (ImageFragment) ImageGallery.fragmentmanager.findFragmentByTag("android:switcher:"+R.id.imagerpager+":"+position);
if (fragment != null) {
fragment.setTransparency();
}
return super.onSingleTapConfirmed(e);
}
All fragments I get are null. To make sure that my objects are not null, following works but for the wrong(preloaded) fragments
ImageFragment fragment = (ImageFragment) ImageGallery.fragmentmanager.findFragmentById(R.id.imagepager);
I`m quite sure I have to implement a FragmentTransition to my adapter to make fragmentmanager.findFragmentByTag work. But I could not figure out how to do that. My adapter looks like
public class GalleryAdapter extends FragmentStatePagerAdapter {
private int mCount = pictureUrlList.size();
public GalleryAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
ImageFragment fragment = ImageFragment.newInstance(position);
return fragment;
}
#Override
public int getCount() {
return mCount;
}
public void setCount(int count) {
if (count > 0 && count <= 10) {
mCount = count;
notifyDataSetChanged();
}
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Can anybody show me how to do that?
Related
I use FragmentStatePagerAdapter to manage my ViewPager. I have overwritten "getItemPosition" function. Here is my code:
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
#Override
public Fragment getItem(int position) {
Crime c = mCrimes.get(position);
return CrimeFragment.newInstance(c.getmId());
}
#Override
public int getCount() {
return mCrimes.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
});
But the problem is that when I delete the first fragment, the last fragment will be deleted, but others work well. I don't know why.
I have some sliders inside recyclerview among some other items. this is my pager adapter:
class PagerFragmentAdapter extends FragmentStatePagerAdapter {
public PagerFragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
and i call setup on binding viewholder:
setup(RecyclerView.ViewHolder holder) {
pos = holder.getAdapterPosition();
if (pagerHolder == null) {
pagerHolder = (PagerHolder) holder;
viewPager = pagerHolder.getViewPager();
adapter = new PagerFragmentAdapter(fragmentManager);
viewPager.setAdapter(adapter);
pagerHolder.bindAdapter();
} else if(adapter!=null)
adapter.notifyDataSetChanged();
}
the first slider in recyclerview is OK but others are not OK!!
non of the fragments are loaded! and if i scroll it just shows first page of pager not all of them.
I have also tried creating fragments inside getItem() method but nothing changed..
I use a PageViewer to scroll through two types of fragments.
One of them has an EditText the other multiple buttons.
Upon entering the EditText Fragment I automatically open the softkeyboard. Upon entering the Button Fragment i close it.
This works fine.
The problem is that I have multiple EditText Fragments as direct neighbours to each other.
-> I dont want the keyboard to be closed when transitioning from one EditText Fragment to the next.
For this I somehow need to predict the class to which I am transitioning next.
Is there a way to achieve this?
Note: I close the keyboard in the pageTransform method as soon as a swipe movement occurs.
EDIT
because of an request in the comments here my PagerAdapter:
public class ButtonCollectionPagerAdapter extends FragmentStatePagerAdapter {
public ButtonCollectionPagerAdapter(FragmentManager fm) {
super(fm);
}
private Fragment currentFragment;
#Override
public Fragment getItem(int i) {
Bundle args = new Bundle();
List<MultipleChoiceQuestion> multipleChoiceQuestions = QuestionTexts.getMCQuestionTexts();
List<String> questionTexts = QuestionTexts.getQuestionTexts();
if(i < multipleChoiceQuestions.size()){
String question = multipleChoiceQuestions.get(i).getText();
ArrayList<String> choices = (ArrayList<String>) multipleChoiceQuestions.get(i).getChoices();
currentFragment = ButtonFragment.newInstance(question, choices, i);
} else if(i < (multipleChoiceQuestions.size() + questionTexts.size())){
currentFragment = TextViewFragment.newInstance(questionTexts.get(i - multipleChoiceQuestions.size()), i);
} else {
currentFragment = SendFragment.newInstance(i);
}
return currentFragment;
}
#Override
public int getCount() {
return 23;
}
#Override
public CharSequence getPageTitle(int position) {
return "OBJECT " + (position + 1);
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object){
super.setPrimaryItem(container, position, object);
currentFragment = (Fragment) object;
((PagerAdapterUpdateProgress) object).onGettingPrimary(position);
}
public Fragment getCurrentFragment() {
return currentFragment;
}
}
I think the more simple way to do that is to open/close keyboard directly from your fragments. You can determine when your fragment becomes visible by overriding setUserVisibleHint method of your fragment. The code in this method for your EditText Fragment should just show keyboard, and the code for other fragment should hide keyboard.
This is an interesting question.
I believe that you can achieve what you are doing by using ViewPager.OnPageChangeListener listener especially onPageSelected(int position) method in which we could get the position of the currently selected fragment after the swipe animation has finished.
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
Log.d("ViewPager", "Page selected " + position);
onPageSelectedLogic(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
// This is because the first fragment doesn't fire the OnPageChangeListener.
onPageSelectedLogic(0);
an this is the implementation of onPageSelectedLogic method
private void onPageSelectedLogic(int position) {
// Check the type of the fragment editable vs button
if(!mPagerAdpater.isFragmentEdiatable(position)) {
// Close the Keyboard
}
}
And this is how you should implement your PagerAdapter
class CustomPagerAdapter extends FragmentStatePagerAdapter {
private SparseBooleanArray fragmentsEditable = new SparseBooleanArray();
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fargment;
// get the fragment
if(some condition) {
// Get an editable fragment
fragmentsEditable.put(position, true);
} else {
fragmentsEditable.put(position, false);
}
return fragment;
}
#Override
public int getCount() {
return NUM_PAGES;
}
public boolean isFragmentEditable(int postion) {
return fragmentsEditable.get(postion);
}
}
I have been struggling a few days with building functionality in my application that allows a user to remove a page/Fragment from a Viewpager, I have tried changing my FragmentPageAdapter to a FragmentStatePageAdapter and override my getItemPosition() to return POSITION_NONE; however this still does not allow me to remove the Fragment dynamically when I call fragments.remove(page);
pageAdapter.notifyDataSetChanged();
has anyone done this successfully before? seems like it should be really simple but it hasnt been, below is my fragment adapter, any help would go a long way thanks!
class MyPageAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragments;
public MyPageAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public float getPageWidth(int position) {
if (position == 0) {
return (0.75f);
} else {
return (1f);
}
}
#Override
public CharSequence getPageTitle(int position) {
MyFragment theFragment = (MyFragment) this.fragments.get(position);
return theFragment.returnTitle();
}
#Override
public int getCount() {
return this.fragments.size();
}
}
Use Jake Wharton's ViewPagerIndicator. It also has an open source sample application with this funcionality (add and remove fragments from options menu).
Put this into you adapter:
public void setCount(int count) {
if (count > 0 && count <= 10) {
mCount = count;
notifyDataSetChanged();
}
}
And this is adding a page:
if (mAdapter.getCount() < 10) {
mAdapter.setCount(mAdapter.getCount() + 1);
mIndicator.notifyDataSetChanged();
}
This is how to remove a page:
if (mAdapter.getCount() > 1) {
mAdapter.setCount(mAdapter.getCount() - 1);
mIndicator.notifyDataSetChanged();
}
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();
}