Attempting to use a ViewPager with FragmentPagerAdapter with all dynamic GUI elements (i.e. no XML). Yes I have a good reason and not just because I feel like it :) Cannot find anything in the docs that suggests this should not work.
The only difference between working and not working is having the ViewGroup (LinearLayout) and ViewPager defined in XML as opposed to created in the onCreate(). Gives the following error:
java.lang.IllegalArgumentException: No view found for id 0xffffffff for fragment PageFragment{41936480 #0 id=0xffffffff android:switcher:-1:0}
Code:
public class DynoPagerActivity extends FragmentActivity {
private static class MyFragmentPagerAdapter extends FragmentPagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
return PageFragment.newInstance("My Message " + index);
}
#Override
public int getCount() {
return 4;
}
}
private ViewPager mViewPager;
private MyFragmentPagerAdapter mMyFragmentPagerAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
setContentView(ll);
mViewPager = new ViewPager(this);
mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mMyFragmentPagerAdapter);
ll.addView(mViewPager);
//setContentView(ll); tried here as well...
}
}
and:
public class PageFragment extends Fragment {
public static PageFragment newInstance(String title) {
PageFragment pageFragment = new PageFragment();
Bundle bundle = new Bundle();
bundle.putString("title", title);
pageFragment.setArguments(bundle);
return pageFragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// https://android-review.googlesource.com/#/c/31261/ - bug in Support lib
setUserVisibleHint(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
TextView textView = new TextView(container.getContext());
textView.setText(getArguments().getString("title"));
return textView;
}
}
Seems to be an issue with how/when fragments are created, but failing to find the connection...
The ViewPager results in a call to findViewById() for each "page" which tries to get the parent (the ViewPager ID), which always fails for Views that do not have their ID set. When you programmatically create Views, they get an ID of -1, which after browsing the Android platform source code, always returns null/fail for findViewByID().
Adding an ID makes it work, but then you need to manage IDs yourself safely to avoid issues. Add this to code above to make it work in the onCreate() method:
mViewPager.setId(888889);
Related
I am new into Android and I like to code. I just got the requirement to show the screen like this.
User Inerface
My understanding is, there is an Activity which contains ViewPager and ViewPager having multiple Fragments which opened as Popup Dialog.
So I created Activity like this.
public class MainActivity extends AppCompatActivity {
private ViewPager viewPagerMain;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
findViewByIds();
setupImageSlider();
}
private void findViewByIds()
{
viewPagerMain = (ViewPager) findViewById(R.id.viewPagerMain);
}
private void setupImageSlider()
{
viewPagerMain.setAdapter(new MyStatePagerAdapter(getSupportFragmentManager()));
}
static class MyStatePagerAdapter extends FragmentStatePagerAdapter
{
public MyStatePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return OfferFragment.init();
}
#Override
public int getCount() {
return 4;
}
}
}
For now just to show dummy pages I am passing fix size as 4 in getCount()
Here is my OfferFragment class
public class OfferFragment extends Fragment {
private View view;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_offer, container, false);
return view;
}
public static OfferFragment init() {
OfferFragment offersFragment = new OfferFragment();
Bundle args = new Bundle();
offersFragment.setArguments(args);
return offersFragment;
}
}
Here are my some thoughts why I created init() method in Fragment class. As I don't know how many pages will have to be shown, in that case creating Fragment object may cause exception.
I know there is DialogFragment class which shows Fragment as Dialog. but in my case I am calling init() from Activity class and inside init() creating object of Fragment class.
So if I extend DialogFragment then, I have to call setShowDialog() method inside onCreate() of Fragment and it give me below error.
Caused by: java.lang.IllegalStateException: DialogFragment can not be attached to a container view
Kindly let me know how to Create Fragment in ViewPager and show as Popup
Thanks in Advance
In my application the fragment activity holds two fragments, Fragment A and Fragment B. Fragment B is a view pager that contains 3 fragments.
In my activity, to prevent that the fragment is recreated on config changes:
if(getSupportFragmentManager().findFragmentByTag(MAIN_TAB_FRAGMENT) == null) {
getSupportFragmentManager().beginTransaction().replace(R.id.container, new MainTabFragment(), MAIN_TAB_FRAGMENT).commit();
}
Code for Fragment B:
public class MainTabFragment extends Fragment {
private PagerSlidingTabStrip mSlidingTabLayout;
private LfPagerAdapter adapter;
private ViewPager mViewPager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
this.adapter = new LfPagerAdapter(getChildFragmentManager());
this.mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
this.mViewPager.setAdapter(adapter);
this.mSlidingTabLayout = (PagerSlidingTabStrip) view.findViewById(R.id.sliding_tabs);
this.mSlidingTabLayout.setViewPager(this.mViewPager);
}
}
Code for the adapter:
public class LfPagerAdapter extends FragmentPagerAdapter {
private static final int NUM_ITEMS = 3;
private FragmentManager fragmentManager;
public LfPagerAdapter(FragmentManager fm) {
super(fm);
this.fragmentManager = fm;
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
Log.d("TEST","TEST");
switch (position) {
case 1:
return FragmentC.newInstance();
case 2:
return FragmentD.newInstance();
default:
return FragmentE.newInstance();
}
}
}
My problem is that I am not able to retain the state of the view pager an its child fragments on orientation changes.
Obviously this is called on every rotation:
this.adapter = new LfPagerAdapter(getChildFragmentManager());
which will cause the whole pager to be recreated, right? As a result
getItem(int position)
will be called on every rotation and the fragment will be created from scratch and losing his state:
return FragmentC.newInstance();
I tried solving this with:
if(this.adapter == null)
this.adapter = new LfPagerAdapter(getChildFragmentManager());
in onViewCreated but the result was that on rotation the fragments inside the pager where removed.
Any ideas how to correctly retain the state inside the pager?
You will need to do 2 things to resolve the issue:
1) You should use onCreate method instead of onViewCreated to instantiate LfPagerAdapter;
i.e.:
public class MainTabFragment extends Fragment {
private PagerSlidingTabStrip mSlidingTabLayout;
private LfPagerAdapter adapter;
private ViewPager mViewPager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
this.adapter = new LfPagerAdapter(getChildFragmentManager());
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
this.mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
this.mViewPager.setAdapter(adapter);
this.mSlidingTabLayout = (PagerSlidingTabStrip) view.findViewById(R.id.sliding_tabs);
this.mSlidingTabLayout.setViewPager(this.mViewPager);
}
}
2) You will need to extend FragmentStatePagerAdapter instead of FragmentPagerAdapter
Android will automatically recreate your activity on configuration without this line android:configChanges="keyboardHidden|orientation|screenSize" in you manifest so that you can handle onConfiguration change yourself.
The only way then is to use onSaveInstanceState() in both your activityFragement to save viewPager state(current position for example) and in fragments where you need to save stuff
Example on how you can save current position of viewpager and restore it onConfiguration change
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
int position = mViewPager.getCurrentItem();
outState.Int("Key", position );
}
#Override //then restore in on onCreate();
public void onCreated(Bundle savedInstanceState) {
super.onCreated(savedInstanceState);
// do stuff
if (savedInstanceState != null) {
int position= savedInstanceState.getInt("Key");
mViewPager.setCurrentItem(position)
}
}
Of course, this is a very basic example.
Ps: to restore in fragment use onActivityCreated() instead of onCreate() method.
Here is another example on how to retain state : Click me!
I am using PagerSlidingTab Library for ViewPager. And I want to change Fragment while scrolling of tabs. It is working fine. Check out my code.
I am using AsynTask() on each Fragment.
When the App opens with the MainActivity, First Fragment is attached to the activity, But It shows two AsynTask() dialog message, one from First and another from Second Fragment. And When I scroll to second tab, It shows dialog message of Third Fragment.
So, If I scroll from left to right in tabs, the Fragment right to the current fragment is displayed and if i scroll from right to left, the Fragment left to the current Fragment is displayed.
Please help me to solve the problem.
My Code:
public class PageSlidingTabStripFragment extends Fragment {
public static final String TAG = PageSlidingTabStripFragment.class
.getSimpleName();
public static PageSlidingTabStripFragment newInstance() {
return new PageSlidingTabStripFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.pager, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
PagerSlidingTabStrip tabs = (PagerSlidingTabStrip) view
.findViewById(R.id.tabs);
ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
MyPagerAdapter adapter = new MyPagerAdapter(getChildFragmentManager());
pager.setAdapter(adapter);
tabs.setViewPager(pager);
}
public class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(android.support.v4.app.FragmentManager fm) {
super(fm);
}
private final String[] TITLES = { "Instant Opportunity", "Events",
"Experts" };
#Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
#Override
public int getCount() {
return TITLES.length;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new InstantOpportunity();
case 1:
return new Events();
case 2:
return new Experts();
default:
break;
}
return null;
}
}
}
Explanation:
It turns out there is an easier implementation for scrollable tabs which doesn't involve another library. You can easily implement tabs into your app using normal Android code straight from the default SDK.
The Code
Main Class:
public class PageSlidingTabStripFragment extends Fragment {
//Variables
private ViewPager viewPager;
private PagerTitleStrip pagerTitleStrip;
public PageSlidingTabStripFragment() {
// Required empty public constructor
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//Find your pager declared in XML
viewPager = (ViewPager) getView().findViewById(R.id.pager);
//Set the viewPager to a new adapter (see below)
viewPager.setAdapter(new MyAdapter(getFragmentManager()));
//If your doing scrollable tabs as opposed to fix tabs,
//you need to find a pagerTitleStrip that is declared in XML
//just like the pager
pagerTitleStrip = (PagerTitleStrip)
getView().findViewById(R.id.pager_title_strip);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.[your layout name here], container, false);
}
}
Adapter:
//Note: this can go below all of the previous code. Just make sure it's
//below the last curly bracket in your file!
class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int arg0) {
Fragment fragment = null;
if (arg0 == 0) {
fragment = new InstantOpportunity();
}
if (arg0 == 1) {
fragment = new Events();
}
if (arg0 == 2) {
fragment = new Experts();
}
return fragment;
}
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) {
return "Instant Opportunity";
}
if (position == 1) {
return "Events";
}
if (position == 2) {
return "Experts";
}
return null;
}
}
Conclusion:
I hope this helps you understand another way to make scrollable tabs! I have examples on my Github Page about how to make each type (That being Fixed or Scrollable).
Links:
Fixed Tabs Example - Click Here
Scrollable Tabs Example - Click Here
Hope this helps!
Edit:
When asked what to import, make sure you select the V4 support fragments.
please use this example..its very easy.i already implement that.
reference link
hope its useful to you.its best example of pager-sliding-tabstrip.
Use
framelayout compulsory:
FrameLayout fl = new FrameLayout(getActivity());
fl.addView(urFragementView);
and then set your fragement view in this framelayout.
I have an Activity with a ViewPager containing multiple fragments. how can i now access a TextView in one of that fragments to change its text from the main activity? I tried multiple ways and they all ended in a NullPointerException.
Activity:
public class SummonerOverview extends SherlockFragmentActivity implements TabListener, OnPageChangeListener {
private ViewPager mPager;
private PagerAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.summoner_overview);
initialize();
}
private void initialize() {
// initialize Pager
mPager = (ViewPager) findViewById(R.id.viewpager);
mAdapter = new PagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(1);
mPager.setOnPageChangeListener(this);
}
}
PagerAdapter:
public class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
frags = new Fragment[3];
frags[0] = new StatisticsFragment(0);
frags[1] = new RatingsFragment(1);
frags[2] = new HistoryFragment(2);
}
private final int NUM_PAGES = 3;
Fragment[] frags;
#Override
public Fragment getItem(int arg0) {
if (arg0 == 0)
return frags[0];
else if (arg0 == 1)
return frags[1];
else
return frags[2];
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
The Fragment:
public class StatisticsFragment extends SherlockFragment {
public StatisticsFragment(int fragNr) {
this.fragNr = fragNr;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_overview_statistics, container, false);
return v;
}
}
The Textview in the StatisticsFragment is labeled with an id in the fragment_overview_statistics.xml, but when i try
TextView tv = (TextView) findViewById(R.id.id_of_the_textview)
tv.setText("text");
from within the onCreate() Method of the Activity after the initialize() Method, i get an Exception.
Okay, after another hour of googling, i think i solves my own question (although i'm sure there is a better method to solve that)
I now hold all the Data that is displayed by the fragments in the MainActivity and make the Fragmets consume this Data in their onCreateView() Method by using public Methods of the Activity.
In the PagerAdapter, i overwrote getItemPosition() to:
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Now, everytime i update some of the Data, i also call mAdapter.notifyDataSetChanged() which leads into a recreation of all fragments.
It works, although it looks like a poor hack for me. Im sure there must be a better solution because now i have to recreate all fragments for changing one TextView of one Fragment, what is surely not the way it is intended to be done.
I am trying to show a FragmentDialog ( created and shown as a dialog NOT added as content in a view hierarchy) where there is a ViewPager whose content is given by a FragmentPagerAdapter (provides Fragments consisting of an image).
The code works perfect when showing ViewPager + FragmentPagerAdapter from a FragmentActivity, but get the following exception when doing it from a FragmentDialog:
"IllegalArgumentException: No view found for id 0x7f040077 for fragment SimpleFragment..."
Here is my code:
A SherlockFragmentActivity with a button to create and show the dialog.
public class BorrameActivity extends SherlockFragmentActivity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.one_act);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
showTheDialog();
}});
}
private void showTheDialog(){
AchGalleryDialog newFragment = AchGalleryDialog.newInstance(achs);
newFragment.show(getSupportFragmentManager(), "dialog");
}
The FragmentDialog:
public class AchGalleryDialog extends DialogFragment{
public AchGalleryDialog(){
}
public static AchGalleryDialog newInstance(){
AchGalleryDialog f = new AchGalleryDialog();
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_user_result, container);
getDialog().setTitle("Hola tronco");
//content to show in the fragments
int[] images = new int[]{R.drawable.d1, R.drawable.d2, R.drawable.d3};
ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
MyFragmentAdapter adapter = new MyFragmentAdapter(getFragmentManager(),images);
pager.setAdapter(adapter);
return view;
}
}
This is the very simple MyFragmentPagerAdapter,
I put only the getItem() method, and nullPointer checks:
#Override
public Fragment getItem(int position) {
return MySimpleFragment.newInstance(images[position]);
}
And finally SimpleFragment:
public class SimpleFragment extends Fragment{
int id;
public static SimpleAchFragment newInstance(int imgId){
SimpleFragment f = new SimpleFragment();
Bundle args = new Bundle();
args.putLong(ID_BUNDLE, imgId);
f.setArguments(args);
return f;
}
public SimpleAchFragment(){
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.id = getArguments() != null ? getArguments().getInt(ID_BUNDLE) : 0;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.simple_fragment, container, false);
FragmentActivity mAct = getActivity();
ImageView img = (ImageView) v.findViewById(R.id.image);
img.setImageDrawable(mAct.getResources().getDrawable(id));
return v;
}
}
More info, if the content passed to the adapter ( an int array with 3 ints) has length zero, then the adapter doesn't try to create any Fragment so dialogs appears correctly but empty (as expected).
The Exception is thrown at SimpleFragment.onCreateView() at the time of inflating.
The id referenced in the exception (as not found) correspond to ViewPager 's id, with is properly defined in R.layout.simple_fragment.
I have try also to build the Dialog with an AlertDialog.builder and also directly with Dialog() contructor, but get the same behaviour.
Try this:
In class AchGalleryDialog
MyFragmentAdapter adapter = new MyFragmentAdapter(getChildFragmentManager(),images);
instead of
MyFragmentAdapter adapter = new MyFragmentAdapter(getFragmentManager(),images);
Because of this:
http://developer.android.com/about/versions/android-4.2.html#NestedFragments
Hope this will help!