ViewPager fragmentstatepageadapter getItem isn't passing correct position - android

I have created a viewpager layout, a class that extends FragmentActivity and a fragment. What I want is that each fragment get's passed in what position it is within the viewpager. So first viewpager is created getting the argument 0, second getting 1 etc. Then if I scroll one way or another these numbers remain a true count.
The problem is the first time a fragment is created, it seems to be created twice so the position passed is 0 then 1. However I can't scroll back but I know for sure the class is being called twice. Now as I scroll forward the position increases incrementally by one. However if I scroll back it drops immediately to three on just one page back, then continues to drop past the 1 to 0 so now I can finally see my layout for 0.
I have this:
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
PracticeFragment fragment = new PracticeFragment();
getAll.putInt("position", position);
fragment.setArguments(getAll);
return fragment;
}
#Override
public int getCount() {
return numberofQ;
}
}
It first of all runs getItem twice before even going to my fragment class so that the position is 0 then 1. Then when it gets to my fragment class it makes a layout fine, I scroll through a few (3 or 4) new pages and it adds one to the position each time then when I scroll back it says it is zero or two then the numbers continue to be just as sporadic. Finally suddenly when I scroll back to the beginning the position is again 0 so my fragment for position 0 is suddenly displayed.
I don't understand what's happening, so I'm wondering what the mistake is?
public class PracticeFragment extends Fragment {
TextView question, explain;
private ScrollView sv;
private boolean starActionBar;
private final static int version = Consts.SDKversion;
ArrayList<RadioButton> rbArray;
ArrayList<LinearLayout> lArray;
ArrayList<ImageView> ivArray;
int iRow;
SQLite info;
private String correctAnswer;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
info = new SQLite(getActivity());
starActionBar = PreferenceManager.getDefaultSharedPreferences(
getActivity()).getBoolean("star", true);
setHasOptionsMenu(true);
}
#Override
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
for (RadioButton r : rbArray) {
if (r.isChecked()) {
r.performClick();
}
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.activity_pm_fragment, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.activity_main, container, false);
lArray = new ArrayList<LinearLayout>();
rbArray = new ArrayList<RadioButton>();
ivArray = new ArrayList<ImageView>();
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay0));
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay1));
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay2));
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay3));
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay4));
for (LinearLayout l : lArray) {
l.setOnTouchListener(PracticeFragment.this);
l.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (((ViewGroup) v).getChildAt(0).isEnabled()) {
((ViewGroup) v).getChildAt(0).performClick();
}
}
});
}
rbArray.add((RadioButton) rootView.findViewById(R.id.radio0));
rbArray.add((RadioButton) rootView.findViewById(R.id.radio1));
rbArray.add((RadioButton) rootView.findViewById(R.id.radio2));
rbArray.add((RadioButton) rootView.findViewById(R.id.radio3));
rbArray.add((RadioButton) rootView.findViewById(R.id.radio4));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio0));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio1));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio2));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio3));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio4));
rootView.findViewById(R.id.bNext).setVisibility(View.GONE);
rootView.findViewById(R.id.bPrevious).setVisibility(View.GONE);
sv = (ScrollView) rootView.findViewById(R.id.svMain);
info.open();
iRow = Integer.valueOf(info.getEverything(getArguments(), getArguments().getInt("position"), "next"));
Cursor c = info.getCursor(iRow);
((TextView) rootView.findViewById(R.id.tvQuestion))
.setText((getArguments().getInt("position") + 1) + ") " + c.getString(2));
explain = (TextView) rootView.findViewById(R.id.tvExplain);
explain.setText(c.getString(9));
explain.setVisibility(View.GONE);
correctAnswer = c.getString(8);
String[] aArray = { c.getString(3), c.getString(4), c.getString(5),
c.getString(6), c.getString(7) };
c.close();
info.close();
int o = 0;
int pos = 0;
for (String s : aArray) {
LinearLayout l = lArray.get(pos);
if (s.contentEquals("BLANK")) {
l.setVisibility(View.GONE);
} else {
l.setVisibility(View.VISIBLE);
rbArray.get(pos).setText(s);
rbArray.get(pos).setOnClickListener(null);
if (o % 2 == 0) {
l.setBackgroundColor(Consts.colorAlt);
}
o++;
}
pos++;
}
return rootView;
}
}
However if I comment out everything but the viewgroup and return rootview - still the same problem.

initialize the getAll every time as a new object in getItem()
make your fragment class static
and create one method in PracticeFragment
static PracticeFragment newInstance(int num) {
PracticeFragment f = new PracticeFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
and change in adapter
#Override
public Fragment getItem(int position) {
return PracticeFragment.newInstance(position);
}

Subclassing it fixed the problem!

Related

How to properly use ViewPager in android

I want to make a quiz application. So far I have 3 activities - home, quiz, score. Since the quiz activity contains multiple equivalent views ( image header, question and 4 answer buttons ), I did some reading and decided that
ViewPager with FragmentStatePagerAdapter show do the trick. So I made an xml template and inflated couple of test views and it was all looking good, until I started handling the user interaction.
I want to simulate a toggle button and there is only one correct answer to each question, so selecting one button should deselect the previous one ( if any ). When the button is pressed I change my Question model, then I find all 4 buttons with findViewById and reset their color filter. Then I set that filter back on my selected button. To determine which question model to update I use the current fragment position, which I have set ( using setTag, in fragment's onCreate ) in my template root view.
This is how I call my fragmets:
public Fragment getItem(int position) {
Question question = Repository.findById(position);
int correctAnswerBtnId;
switch (question.getCorrectAnswerIndex()) {
case 0: correctAnswerBtnId = R.id.quiz_answer_0_btn; break;
case 1: correctAnswerBtnId = R.id.quiz_answer_1_btn; break;
case 2: correctAnswerBtnId = R.id.quiz_answer_2_btn; break;
case 3: correctAnswerBtnId = R.id.quiz_answer_3_btn; break;
this.ACTIVITY_ROOT.setTag(question.getID());
Fragment fragment = new QuestionFragment();
Bundle args = new Bundle();
args.putSerializable(QuestionFragment.QUESTION, question);
fragment.setArguments(args);
return fragment;
}
My QuestionFragment onCreateView is as per documentation:
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle questionData) {
this.rootView = inflater.inflate(
R.layout.layout_question_template,
container,
false);
Bundle args = getArguments();
this.question = (Question) args.getSerializable(QuestionFragment.QUESTION);
populateInflatable();
rootView.findViewById(R.id.layout_question_template_root).setTag(this.question.getID());
return rootView;
}
In populateInflatable I use this.rootView to fintViewById and populate it with my question data. Then I change the color of a button, if there is selected one from the Question.
On button click I call selectAnserButton :
public void selectAnswerButton(View selectedButton) {
int questionId =
(int) this.activityRoot.findViewById(
R.id.layout_question_template_root).getTag(); //??
unSelectAllButtons();
changeColor(selectedButton);
Repository.findById(questionId).selectAnswer(selectedButton.getId());
}
Where unSelectAllButtons represents buttonToUnSelect.getBackground().clearColorFilter(); on the four buttons. and Repository is just a static class with example question data.
It all goes terribly wrong, when I have more then one view. On each fragment I inflate the same xml with same View IDs, as I have defined them. And as I now understand calling findViewById retrieves not one, but all views with that Id from my current, but also from my previous and next fragment as well. So every time I want to select my current fragment's view, I also modify the same view in the previous and next fragments as well. You can imagine how this is problematic. This makes me feel I have a fundamental mistake, because I don't think there is supposed to be more then one View with same ID.
I really don't understand how I should do this using ViewPager. At this point it feels like I'm trying to make a wood carving, but instead I am hacking the framework to pieces. There must be a better way to do this with ViewPager.
RESOLVED: Thanks to Soo Chun Jung for pointing me to the answer. In short what got it working for me was:
Passing my Question model id to each fragment with Bundle.
Storing each fragment in inside an ArrayMap with fragment position as key and fragment as value.
Getting each individual fragment from my selectAnswer function is now easy: first get the current fragment's position with myViewPager.getCurrentItem, then calling getter function which returns a fragment on the current position.
Now that I have the fragment I can easily change its button's because they are kept as private fields, assigned in the 'onCreateView` method.
Hope it's helpful~
adapter
class CustomAdapter extends FragmentPagerAdapter {
private final String[] TITLES = {"A", "B"};
private final String TAG = CustomAdapter.class.getSimpleName();
private final ArrayList<Fragment> mFragments;
private final FragmentManager fm;
public CustomAdapter(FragmentManager fm) {
super(fm);
mFragments = new ArrayList<>(getCount());
this.fm = fm;
}
#Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
#Override
public int getCount() {
return TITLES.length;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem position = " + position);
mFragments.remove(object);
super.destroyItem(container, position, object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Object object = super.instantiateItem(container, position);
mFragments.add((Fragment) object);
return object;
}
#Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem position = " + position);
if (position == 0) {
return MyFragmentA.newInstance();
} else if (position == 1) {
return MyFragmentB.newInstance();
}
return null;
}
public MyFragmentA getMyFragmentA() {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentA) {
return (MyFragmentA) f;
}
}
}
return null;
}
public MyFragmentB getMyFragmentB() {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentB) {
return (MyFragmentB) f;
}
}
}
return null;
}
}
Fragment class
public class MyFragmentB extends Fragment {
...
public updateYourUI(){
//update something
}
}
Usage
mPager = (CustomViewPager) findViewById(R.id.pager);
mAdapter = new CustomAdapter(getChildFragmentManager());
mPager.setAdapter(mAdapter);
mAdapter.getMyFragmentB().updateYourUI();
for your comment below If you only have one kind Fragment. You can modify some function like this.
public static MyFragmentB newInstance(int ID) {
MyFragmentB fragment = new MyFragmentB();
Bundle bundle = new Bundle();
bundle.putInt("ID", ID);
fragment.setArguments(bundle);
return fragment;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
myID = getArguments().getInt("ID");
....
}
public int getMyID() {
return myID;
}
public MyFragmentB getMyFragmentByID(String id) {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentB) {
MyFragmentB temp = (MyFragmentB)f;
if(temp.getID.equals(id){
return (MyFragmentB) f;
}
}
}
}
return null;
}

Viewpager Adapter getItem always called for index 0 and 1

I was just wondering if it is the normal behaviour of viewpager and its adapter to always call the getItem() method for index 0 and 1, even if I immediately set a current position.
Here is my code:
mNewsPagerAdapter = new NewsDetailPagerAdapter(getChildFragmentManager());
mNewsPagerAdapter.updateNewsList(news);
mViewPager = (ViewPager) mView.findViewById(R.id.horizontal_view_pager);
mViewPager.setPageMargin(2);
mViewPager.setPageMarginDrawable(R.color.black);
mViewPager.setAdapter(mNewsPagerAdapter);
mViewPager.setCurrentItem(mCurrentPositionPager, false);
If I switch from my overview activity to my detail activity with this viewpager, the adapter always calls the getItem() method for position 0 and 1 and after that the getItem() method for the position of mOriginalPosition and its neighbors. I was wondering if this is the correct behaviour or if I missed something to implement it in a right way. Thanks for your help :)
Edit: Added my adapter code
public class NewsDetailPagerAdapter extends FragmentStatePagerAdapter {
private SparseArray<Fragment> mPageReferenceMap = new SparseArray<Fragment>();
private ArrayList<News> mNewsList;
public NewsDetailPagerAdapter(FragmentManager fm) {
super(fm);
}
/**
* Setzt die neuen News.
**/
public void updateNewsList(ArrayList<News> list) {
mNewsList = list;
}
#Override
public Fragment getItem(int position) {
Log.d("debug", "getItem position:" + position);
News newsItem = mNewsList.get(position);
NavigationFragment fragment = new NavigationFragment();
mPageReferenceMap.put(position, fragment);
return fragment;
}
#Override
public int getCount() {
return mNewsList.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
public Fragment getFragment(int position) {
return mPageReferenceMap.get(position);
}
}
It is normal (and intelligent in my opinion).
ViewPager class has one property named mOffscreenPageLimit with default value of 1. This number determines how many pages on the left and on the right of the current page that the Viewpager will preload. For instance, you have 10 pages, current position is 5 and mOffcreenPageLimit is 1, the page at position 4 and 6 will be loaded.
You could change this property by calling this method
viewpager. setOffscreenPageLimit(int)
If you pass in an integer that is smaller than 1, it has no effect.
Yes, this is the normal behaviour of the ViewPager, because it will always try to stay ahead of the user by rendering tabs that limit with the drawing area. I personally don't recommend creating a custom ViewPager as you are almost sure to break functionality unless you really know what you are doing. Your adapter class should look something like this:
public class YourCustomPagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragmentList = new ArrayList<>();
private List<String> titleList = new ArrayList<>();
public WizardPagerAdapter(FragmentManager fm) {
super(fm);
}
public void addFragment(Fragment fragment, String title) {
fragmentList.add(fragment);
titleList.add(title);
}
#Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public CharSequence getPageTitle(int position) {
super.getPageTitle(position);
return titleList.get(position);
}
#Override
public int getCount() {
return fragmentList.size();
}
}
and you should add your fragments as such:
#Override
public void onCreate(Bundle savedInstanceState) {
...
YourCustomPagerAdapter adapter = new YourCustomPagerAdapter (getSupportFragmentManager());
adapter.addFragment(FragmentOne.newInstance(), "Frag 1");
adapter.addFragment(FragmentTwo.newInstance(), "Frag 2");
viewPager.setAdapter(adapter);
...
}
Actually this is the normale behavior. In fact, as soos as you associate the ViewPager with the adapter, the adapter creates the first visibile layout (index 0) end the next one (index 1). This is done by default in the "setAdapter". Then, when you set a different position, the adapter will instantiate the fragment at the selected index, the previous one and the next one.
This is the usual ViewPager setAdapter code:
public void setAdapter(PagerAdapter adapter) {
if (mAdapter != null) {
mAdapter.setViewPagerObserver(null);
mAdapter.startUpdate(this);
for (int i = 0; i < mItems.size(); i++) {
final ItemInfo ii = mItems.get(i);
mAdapter.destroyItem(this, ii.position, ii.object);
}
mAdapter.finishUpdate(this);
mItems.clear();
removeNonDecorViews();
mCurItem = 0;
scrollTo(0, 0);
}
final PagerAdapter oldAdapter = mAdapter;
mAdapter = adapter;
mExpectedAdapterCount = 0;
if (mAdapter != null) {
if (mObserver == null) {
mObserver = new PagerObserver();
}
mAdapter.setViewPagerObserver(mObserver);
mPopulatePending = false;
final boolean wasFirstLayout = mFirstLayout;
mFirstLayout = true;
mExpectedAdapterCount = mAdapter.getCount();
if (mRestoredCurItem >= 0) {
mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
setCurrentItemInternal(mRestoredCurItem, false, true);
mRestoredCurItem = -1;
mRestoredAdapterState = null;
mRestoredClassLoader = null;
} else if (!wasFirstLayout) {
populate();
} else {
requestLayout();
}
}
if (mAdapterChangeListener != null && oldAdapter != adapter) {
mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);
}
}
In order to change the ViewPager behavior, you could extend the classic ViewPager overriding the setAdapter method and set the mCurrItem to the desired position.
I hope it helped
Edit:
After different tests, we found a solution.
If the ViewPager adapter is set after ViewPager layout become visible, items 0 and 1 are load.
If you want to avoid this behavior but you can't set the adapter before the layout become visible (because you are waiting for data), than you can use this workaround:
1) Set the ViewPager visibility initially to GONE
2) After you receive all the data, you update the adapter and you set the current item value
3) Finally you set the ViewPager visibility to VISIBLE
Here you can find an example:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.detail_overview_fragment, container, false);
final int position = getArguments().getInt("position");
final ViewPager viewPager = (ViewPager) v.findViewById(R.id.viewpager);
viewPager.setVisibility(View.GONE);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
viewPager.setAdapter(new PagerAdapter(getChildFragmentManager()));
viewPager.setCurrentItem(position);
viewPager.setVisibility(View.VISIBLE);
}
},5000);
return v;
}
i think the error is the adapter:
/**
* Setzt die neuen News.
**/
public void updateNewsList(ArrayList<News> list) {
//mNewsList = list;
mNewsList.clear();
mNewsList.addAll(list);
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
this.notifyDataSetChanged();
}
error reason :this list is diffent entity for that adapter.

When creating two instances of the same fragment and registering them only the second one created gets event

I have created two fragments in a ViewPager , when I click on first one , Second fragment is taking the click.
This issue puts me in another position, when I create two instance from same fragment but with different data.
{
#Override
public Fragment getItem(int index) {
switch (index) {
case 1:
return FragmentBrandList.getInstance(tabs.getBrandList2(), 19,
title);
case 0:
return FragmentBrandList.getInstance(tabs.getBrandList1(), 19,
title);
}
return null;
}
#Override
public int getCount() {
return 2;
}
}
After creating ViewPager , both the fragments get created correctly , but when I click on any thing in the first fragment , the click event gets fired in second fragment not in the first fragment.
EDIT
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 1:
return FragmentBrandList.getInstance(tabs.getBrandList2(), 19,
title);
case 0:
return FragmentBrandList.getInstance(tabs.getBrandList1(), 19,
title);
}
return null;
}
#Override
public int getCount() {
return 2;
}
in FragmentBrandList
public class FragmentBrandList extends Fragment {
ArrayList<Brand> brandList = new ArrayList<Brand>();
int discoverID;
RecyclerView listView;
LinearLayoutManager mLayoutManager;
public static FragmentBrandList getInstance(ArrayList<Brand> brandList,
int discoverID, String title) {
FragmentBrandList frag = new FragmentBrandList();
Bundle b = new Bundle();
b.putSerializable("brandList", brandList);
b.putInt("discoverID", discoverID);
b.putString("title", title);
frag.setArguments(b);
return frag;
}
public FragmentBrandList() {
}
String title = "";
View v;
boolean isInflated = false;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (v == null) {
v = inflater.inflate(R.layout.fragment_list_view_brownbg,
container, false);
isInflated = true;
} else {
isInflated = false;
((ViewGroup) v.getParent()).removeView(v);
}
return v;
}
MainActivity activity;
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (isInflated) {
activity = (MainActivity) getActivity();
initView();
}
}
public void initView(){
title = getArguments().getString("title");
discoverID = getArguments().getInt("discoverID");
listView = (RecyclerView) v.findViewById(R.id.listView);
mLayoutManager = new LinearLayoutManager(getActivity());
listView.setItemAnimator(new DefaultItemAnimator());
listView.setHasFixedSize(true);
listView.setLayoutManager(mLayoutManager);
listView.setAdapter(new BrandListRecAdapter(getActivity(),
R.layout.single_item_listview, brandList));
}
#Override
public void onResume() {
// handle on click
((BrandListRecAdapter) listView.getAdapter())
.setOnItemClickListener(new ItemClickListener() {
#Override
public void onItemClickListener(final int pos, View v) {
activity.replaceCurrentFragment(
FragmentBrandDetails.getInstance(
brandList.get(pos), "bank"), true,
true);
}}
EDIT
i think problem cause
when create second fragment , listview.onclick is overwrite first one !!
how can solve this peb?
EDIT
thank you to every one try to help me
problem is already because i use same adapter and same fragment
when second fragment created it is overwrite on item click
so when click in item is called second one !!!
Just put this android:clickable="true" in every fragment layout, and this will not happen again.
This is just an educated guess, but because a ViewPager will always create at least one extra Fragment on either side of the currently visible fragment, you may be creating two virtually identical Fragments in parallel, assigning them both onItemClickListeners in onResume and as such they are both responding to item clicks when an item is pressed on either Fragment.
You could try moving the onItemClickListener to the ViewHolder in your Adapter, rather assigning it in onResume. In addition, I wonder what a Brand object looks like in your RecyclerView, and whether it wouldn't be simpler to pass the current ViewPager page as a parameter in getInstance, and use this to access an Array containing the information necessary to fill your RecyclerView rows.
Here is a very brief example of how your ViewHolder may look:
class MyRecyclerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
public MyRecyclerViewHolder(View itemView) {
itemView.setOnClickListener(this);
//etc.

Nested fragments: java.lang.IllegalArgumentException: No view found for id

I have a fragment A, containing a listview. To this listview I add a listheader containing a ViewPager that pages through childfragments.
When the user clicks an item in the list, the listfragment A gets replaced by a detail-view-fragment of that listitem.
I want the user to be able to go back to the list by clicking the back button.
So far everything works, except when the user presses the back button to pop the detail fragment from the stack to get back to the listview fragment A, the app crashes with an
java.lang.IllegalArgumentException: No view found for id 0x7f06002e (com.makamedia.hockeyweb:id/news_header_pager) for fragment NewsHeaderFragment{41f7b6f8 #0 id=0x7f06002e android:switcher:2131099694:0}
My suspicion is, that maybe the nested fragments for the viewpager in the listheader get recreated before the viewpager gets recreated, thus crashing the app, but I am not sure.
Any help is appreciated!
My ViewPagerAdapter for the listheader-viewpager (removed some unrelated code):
public class NewsHeaderAdapter extends FragmentPagerAdapter {
private int mCount;
public final NewsListAdapter mListAdapter;
public NewsHeaderAdapter(FragmentManager fm, int count, long autoSwipeInterval, NewsListAdapter adapter) {
super(fm);
this.mCount = count;
this.mListAdapter = adapter;
}
#Override
public Fragment getItem(int pos) {
return NewsHeaderFragment.getNew(this.mListAdapter.getItem(pos));
}
public void setCount(int newCount){
if(newCount < 1){
this.mCount = 1;
} else if(newCount >= this.mListAdapter.getCount()){
this.mCount = this.mListAdapter.getCount();
} else {
this.mCount = newCount;
}
}
#Override
public int getCount() {
return mCount;
}
#Override
public CharSequence getPageTitle(int position) {
return this.mListAdapter.getItem(position).getTitle();
}
}
My news detail fragment (pretty straight forward):
public class NewsHeaderFragment extends Fragment {
private NewsItem mNewsItem;
private TextView mHeaderNewsBigTitle;
private ImageView mHeaderNewsBigImage;
// Convenience method for creating a new fragment with parameters
public static NewsHeaderFragment getNew(NewsItem item){
NewsHeaderFragment fragment = new NewsHeaderFragment();
Bundle args = new Bundle();
args.putSerializable(Constants.SIG_NEWS_ITEM, item);
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.row_big_news, container, false);
Bundle newsHeaderArgs = getArguments();
mNewsItem = (NewsItem)newsHeaderArgs.getSerializable(Constants.SIG_NEWS_ITEM);
setupUI(rootView);
fillUI();
return rootView;
}
private void fillUI() {
mHeaderNewsBigTitle.setText(mNewsItem.getTitle());
Picasso.with(getActivity()).load(mNewsItem.getImageBig2x()).into(mHeaderNewsBigImage);
}
private void setupUI(View rootView) {
mHeaderNewsBigTitle = (TextView) rootView.findViewById(R.id.news_big_title);
mHeaderNewsBigImage = (ImageView) rootView.findViewById(R.id.news_big_img);
}
}
My viewpager is declared in xml in a row-layout and added like so:
private void addHeaderPager(int count) {
if(mNewsListAdapter != null && mNewsListAdapter.getCount()>0) {
if (count >= mNewsListAdapter.getCount()) {
count = mNewsListAdapter.getCount() - 1;
}
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mHeader = (RelativeLayout) inflater.inflate(R.layout.row_big_news_pager, null);
mHeaderPager = (ViewPager) mHeader.findViewById(R.id.news_header_pager);
mHeaderPagerAdapter = new NewsHeaderAdapter(getChildFragmentManager(), count, 6000, mNewsListAdapter);
mHeaderPager.setOffscreenPageLimit(count);
mHeaderPager.setAdapter(mHeaderPagerAdapter);
// Bind the title indicator to the adapter
CirclePageIndicator circleIndicator = (CirclePageIndicator) mHeader.findViewById(R.id.news_header_pager_indicator);
circleIndicator.setViewPager(mHeaderPager);
mNewsListView.addHeaderView(mHeader);
}
}
Are you sure tha you use the right FragmentManager in addHeaderPager()?
I normally use getFragmentManager() and if there is a parent fragment I have to use getParentFragment().getFragmentManager() - if I don't I get the same error ("No view found for id") when trying to replace the current visible fragment.

The FragmentPagerAdapter update the pages wrongly?

I am having strange issue in FragmentStatePagerAdapter.when i swipe front ,it works gud,when i swipe back it skip 2 fragments.how to resolve this?
is there any way to get current item no?
NavigationPagerAdapter
public static class NavigationPagerAdapter extends FragmentStatePagerAdapter {
public NavigationPagerAdapter(android.support.v4.app.FragmentManager fm ) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new NaviagtionFragment();
Bundle args = new Bundle();
args.putInt("position", i); // Our object is just an integer :-P
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
// For this contrived example, we have a 100-object collection.
return 100;
}
#Override
public CharSequence getPageTitle(int position) {
return "OBJECT " + (position );
}
in fragment
public static class NaviagtionFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.item_pager, container, false);
Bundle args = getArguments();
int m= args.getInt("position");
Toast.makeText(c, ""+m, Toast.LENGTH_SHORT).show();//here i tracked the position of fragments when i swipe front ,its increasing 1,2,3,4,5,6,7 but when i swipe back it will go directly 7 -5 th position..
}}}
Toast is very slow and it is not good solution when you are swiping views, because it remains on the screen longer and can not reach to show all messages, so its better to check position in LogCat. Try this for items position and see position with Log:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNum = getArguments() != null ? getArguments().getInt("position") : 1;
Log.i("tag", "Item clicked: " + mNum);
}

Categories

Resources