I am going through the Android Developer guide on View Paging with fragments and am stuck at populating the fragment from my main onCreate method. I think I am confused as to what methods get called when to populate the fragment as I am getting null values.
My Fragment Class
public class CatalogFragment extends Fragment {
private String filename;
private String author;
private String title;
public CatalogFragment(String f, String t, String a){
filename = f;
author = a;
title = t;
Log.d("frag", f+" | "+t+" | "+a);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.catalog_fragment, container, false);
ImageView imgView = (ImageView) rootView.findViewById(R.id.imgFrag);
Bitmap bmp = BitmapFactory.decodeFile(filename);
imgView.setImageBitmap(bmp);
TextView txtView = (TextView)rootView.findViewById(R.id.title_text);
txtView.setText(author+": "+title);
return rootView;
}
}
My onCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_catalog_view);
//Instantiate a ViewPager and a PagerAdapter
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
//populate the fragment
//I was thinking I could pass the fragment class the necessary information to put an imageview and text view in place.
List<Poster> posters = db.getAllPosters();
for (Poster p : posters) {
final String filename = p.getPosterFilename();
//Create a new Fragment?
new CatalogFragment(filename, p.getPosterTitle(), p.getPresenterFname()+" "+p.getPresenterLname());
}
}
However it appears that getItem is what is actually being called to populate the fragment. I think. If I put null in for the arguments that is what I get. If I try to omit this line eclipse starts to complatin. So I am stuck.
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter{
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
// TODO Auto-generated constructor stub
}
#Override
public Fragment getItem(int position){
return new CatalogFragment(null, null, null);
}
#Override
public int getCount(){
return NUM_PAGES;
}
}
The Adapter is what creates the fragments in the view pager, you do not need to create them yourself.
Here is what the Adapter and Activity#onCreate could look like to give you the correct result.
Notice how we are telling the adapter where to get it's data.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_catalog_view);
//Instantiate a ViewPager and a PagerAdapter
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPagetAdapter.setData(db.getAllPosters()); // Provide Poster Data to Adapter
mPager.setAdapter(mPagerAdapter);
}
In the adapter we tell it how to build it's fragments:
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
List<Poster> posterList = new ArrayList<Poster>(0);
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
// TODO Auto-generated constructor stub
}
public void setData(List<Poster> posters) {
posterList.addAll(posters);
}
#Override
public Fragment getItem(int position){
Poster poster = posterList.get(position)
// FRAGMENTS SHOULD NOT IMPLEMENT A CONTRUCTOR!!!
// This will lead to other issue down the line.
return new CatalogFragment(poster.getPosterFilename(), poster.getPosterTitle(), poster.getPresenterFname()+" "+poster.getPresenterLname());
}
#Override
public int getCount(){
return posterList.size();
}
}
```
As a side-note, you should not have a fragment constructor with arguments. See this other SO question for an explanation: Do fragments really need an empty constructor?
Related
I'm creating an app with vertical page adapter using FragmentStatePagerAdapter. The big issue i'm facing is, data is not displayed on the textview on first app launch but is displayed on scrolling the page. I believe the fragment view is delaying to create textview because, on my LoadAlbumDataCompleted() function inside Fragmentone.class, i'm able to print the data returned or also output via toast but is not getting populated to the textview.
Kindly help.
MainActivity.class
public class MainActivity extends FragmentActivity implements LoadAalbumsTotalListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAdapter = new MainActivityVSlideAdapter(this, getSupportFragmentManager(), NUMBER_OF_PAGES);
mPager.setAdapter(mAdapter);
LoadTotalAlbumsNum.BindAlbumsTotalListener(this);
}
#Override
public void OnLoadAlbumsCompleted(String total) {
if(total.trim().equalsIgnoreCase("")){
NUMBER_OF_PAGES=0;
}else{
NUMBER_OF_PAGES=Integer.parseInt(total.trim());
}
mAdapter = new MainActivityVSlideAdapter(this, getSupportFragmentManager(), NUMBER_OF_PAGES);
mPager.setAdapter(mAdapter);
}
}
MainActivityVSlideAdapter.class Adapter
public class MainActivityVSlideAdapter extends FragmentStatePagerAdapter {
static int NUMBER_OF_PAGES;
private Context con;
public MainActivityVSlideAdapter(Context con, FragmentManager fm, int NUMBER_OF_PAGES) {
super(fm);
this.con=con;
this.NUMBER_OF_PAGES=NUMBER_OF_PAGES;
}
#Override
public int getCount() {
return NUMBER_OF_PAGES;
}
#Override
public Fragment getItem(int position) {
return FragmentOne.newInstance(position);
}
}
Fragmentone.class
public class FragmentOne extends Fragment implements LoadAlbumDataListener {
private static final String MY_NUM_KEY = "num";
private int mNum;
private TextView SaloonName;
private TextView location;
// You can modify the parameters to pass in whatever you want
public static FragmentOne newInstance(int num) {
FragmentOne f = new FragmentOne();
Bundle args = new Bundle();
args.putInt(MY_NUM_KEY, num);
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//get argument from
mNum = getArguments() != null ? getArguments().getInt(MY_NUM_KEY) : 0;
session=new Session(getActivity());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_one, container, false);
Methods methods=new Methods(getActivity());
// v.setBackgroundColor(mColor);
SaloonName = v.findViewById(R.id.SaloonName);
location=v.findViewById(R.id.location);
new LoadAlbumData(getActivity()).execute(getString(R.string.urlAddress)+"load-album-data.php", String.valueOf(mNum));
LoadAlbumData.BindLoadAlbumDataListener(this);
return v;
}
#Override
public void LoadAlbumDataCompleted(String s) {
JSONArray jsonPicsArray = null;
JSONObject jsonObj;
String BusinessLocation=null;
try {
jsonPicsArray = new JSONArray(s);
businessName = jsonObj.getString("businessName");
BusinessLocation = jsonObj.getString("location");;
}
Toast.makeText(getActivity(), businessName, Toast.LENGTH_LONG).show();
SaloonName.setText(businessName);
location.setText(BusinessLocation);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
From your code, it looks like, you are not setting the LoadAlbumDataListener correctly. Instead of statically setting the listener. Pass reference in constructor of your LoadAlbumData Asynctask
remove this line
LoadAlbumData.BindLoadAlbumDataListener(this);
replace
new LoadAlbumData(getActivity()).execute(getString(R.string.urlAddress)+"load-album-data.php", String.valueOf(mNum));
with
new LoadAlbumData(getActivity(), this).execute(getString(R.string.urlAddress)+"load-album-data.php", String.valueOf(mNum));
Modify your asynctask to have reference of LoadAlbumDataListener
Also as a good practice, never store strong reference of activity or fragment in your asynctask. Use WeakReference.
Hello, I'm trying to implement an Activity with tabs. Some tabs contain a fragment that shows simple list, others a tree view like list.
The fragments with simple list work fine, while I'm having troubles with the one which shows tree view like list: the tree are build properly, but I get them in the wrong tabs. In the first tab I always see the content of the second one, and swiping back or expanding/collapsing the tree produces a new unwanted substitution of the whole tree.
I'm struggling to understand what I'm doing wrong. I hope you can suggest me what to do. Thanks in advance!
Here's my code:
Pager Adapter:
public class PagerAdapter extends FragmentStatePagerAdapter {
private final List<ListFragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public PagerAdapter(FragmentManager manager)
{
super(manager);
}
#Override
public ListFragment getItem(int index) {
return mFragmentList.get(index);
}
#Override
public int getCount()
{
return mFragmentList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
public void addFragment(int index, boolean tree) {
Bundle b=new Bundle();
String category= Items.getCategoryByIndex(index);
b.putString(Items.CATEGORY,category);
ListFragment fragment;
if(!tree) {
fragment = new ContentFragment();
} else {
fragment = new TreeFragment();
}
fragment.setArguments(b);
mFragmentList.add(fragment);
mFragmentTitleList.add(Items.getCategoryByIndex(index));
}
}
This is the TreeFragment class:
public class TreeFragment extends TreeViewFragment {
TreeListAdapter mAdapter;
public TreeFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle b = getArguments();
String category = (String) b.get(Items.CATEGORY);
GlobalVariables.dataList = Items.getTreeNodeByCategory(category);
GlobalVariables.nodes = TreeViewLists.LoadInitialNodes(GlobalVariables.dataList);
TreeViewLists.LoadDisplayList();
mAdapter = new TreeListAdapter(inflater.getContext());
setListAdapter(mAdapter);
return super.onCreateView(inflater, container, savedInstanceState);
}
}
and in the activity:
public class MyActivity extends AppCompatActivity {
private ViewPager mViewPager;
private PagerAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mAdapter = new PagerAdapter(getSupportFragmentManager());
// (...) Content creation (...)
mAdapter.addFragment(0, true);
mAdapter.addFragment(1, true);
mAdapter.addFragment(2, true);
mAdapter.addFragment(3, true);
mAdapter.addFragment(4, false);
mAdapter.addFragment(5, false);
mViewPager.setAdapter(mAdapter);
TabLayout tabs = (TabLayout) findViewById(R.id.tabs);
tabs.setupWithViewPager(mViewPager);
}
I have an activity that show data stored in database in a view pager organized in fragments.
When the adapter init the View Pager it call a method in each fragment to pass the id of the database entry to retrive and show:
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment different title
firstFragment = Fragment.newInstance(1, "Page # 1");
firstFragment.setID(ctx, id);
return firstFragment;
case 1: // Fragment # 0 - This will show FirstFragment different title
secondFragment = Fragment.newInstance(1, "Page # 2");
secondFragment.setID(ctx, id);
return secondFragment;
case 2: // Fragment # 0 - This will show FirstFragment
thirdtFragment = Fragment.newInstance(1, "Page # 3");
thirdtFragment.setID(context, id);
return thirdtFragment;
default:
return null;
}
}
when the method onCreateView of the fragment get called it retrive the data from the database and init all the view displaying the data to the user:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
DataManager dataManager = new DataManager(mContext);
DataModel data = dataManager.getData(mID);
.....
All seems to work fine except when the user restart the app that was for example paused when inside this activity: when the activity got restarted I get a fatal error accessing the data, as if the private variable (of the fragment) containing the ID of the data got deleted from the garbage collector.
Is my approach correct? How can I prevent the error generated when the activity get restarted?
During onPause and onResume Fragment and Activity is managed by android so you may not retrieve the member variables so to overcome this problem on your Fragment's newInstance method use something like
public static Fragment newInstance(int id, String page){
Bundle bundle = new Bundle();
bundle.putString("page", page);
bundle.putInt("ID", page);
setArguments(bundle);
}
and on your onCreateView callback use something like
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
int mID = getArguments().getInt("ID");
DataManager dataManager = new DataManager(mContext);
DataModel data = dataManager.getData(mID);
...
So, I approached my problem by doing the following:
Create a view pager that has a list of Fragments and their respective titles. This view pager adapter should have a method to add fragments to the list. Here is my code:
public class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
private int mItemId;
public ViewPagerAdapter(FragmentManager manager, int id) {
super(manager);
mItemId = id;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFrag(Fragment fragment, String title) {
Bundle bundle = new Bundle();
bundle.putInt("itemId", mItemId);
fragment.setArguments(bundle);
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}}
Now, inside your Fragment, you can extract the bundle extra which can help you when reading some data from your database for example if it is an id for table column. This could be your fragment class:
public class InfoFragment extends Fragment {
private Activity mActivity;
private CustomerDbObject customer;
private int customerId;
public InfoFragment() {
// Required empty public constructor
}
#Override
public void onAttach(Context ctx){
super.onAttach(ctx);
if (ctx instanceof Activity){
this.mActivity = (Activity) ctx;
}
customerId = getArguments().getInt("itemId");
Realm realm = Realm.getInstance(ctx);
RealmQuery<Customer> query = realm.where(Customer.class);
query = query.equalTo("account_id", customerId);
customer = query.findFirst();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.customer_fragment_info, container, false);
ButterKnife.bind(this, view);
return view;
}}
With these two pieces in place, what we need now is our activity that will display the viewpager with tabs! Remember, it is inside this same activity that we provide the extra (itemId) to the viewpager instance then the viewpager will pass it to the fragment in that order.
public class CustomerActivity extends AppCompatActivity {
private int mSelectedCustomer = 5; //this value could be extracted for an intent that is passed from a Recyclerviewer etc
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_customer_details);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
final ViewPager mViewPager = (ViewPager) findViewById(R.id.viewPager);
setupViewPager(mViewPager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(mViewPager);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager(), mSelectedCustomer);
adapter.addFrag(new InfoFragment(), getString(R.string.info_tab));
}
}
Now that we have all the pieces together, the important extra (data) being passed around should always be available to the Fragment through the viewpager adapter which gets it subsequently from the activity.
When I tested this, it worked and I hope it works for you as well. Please let me know how it goes!!
I have a viewpager with 2 fragment pages inside it. Now i want to access the views (textview,buttons) from the first fragment page but everytime Iam getting Nullpointer exception
check out my code
Fragment i want to access its views:
public class ListPagerFragment extends Fragment
{
ListView listview;
TextView text;
private ViewPager pager;
public static ListPagerFragment newInstance(int message)
{
ListPagerFragment f = new ListPagerFragment();
// pass the text message received from main activity into a bundle to be
// delivered to the new fragment
Bundle bdl = new Bundle();
bdl.putInt("num", message);
f.setArguments(bdl);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.list_pager_fragment, container, false);
return v;
}
public void fillList(Nameslist list)
{
listview = (ListView) getView().findViewById(R.id.listView1);
if (listview == null)
System.out.println("listview null"); //this should not be null but
//it is thus the nullpointerexception
if (listview.getAdapter() == null)
{
MyListAdapter listAdapter = new MyListAdapter(getActivity(), R.layout.listview_item, list.getVenues());
listview.setAdapter(listAdapter);
}
else
{
((MyListAdapter) listview.getAdapter()).updateAdapter(list.getnames());
}
}
PagerAdapter code
public class MyPagerAdapter extends FragmentStatePagerAdapter
{
private int mCount = 1;
public MyPagerAdapter(FragmentManager fm)
{
super(fm);
// TODO Auto-generated constructor stub
}
#Override
public Fragment getItem(int position)
{
switch (position)
{
case 1:
return ResultsPagerFragment.newInstance(position);
}
return ListPagerFragment.newInstance(position);
}
#Override
public int getCount()
{
// TODO Auto-generated method stub
return mCount;
}
}
and main finally activity code
private ViewPager pager;
private MyPagerAdapter pagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(pagerAdapter);
}
and later somewhere in the activity i have the code that should trigger the fillList from Fragment to do the job
Fragment frag = pagerAdapter.getItem(0); //this should give me the Fragment i want right?
((ListPagerFragment)frag).fillList(result);
What am I missing here?
Ok I found it seems like the problem was in my custom FragmentStatePagerAdapter
All I had to do is create the fragments when activity is created and put them into an ArrayList which it will be later passed to MyPagerAdapter
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
frags = new ArrayList<Fragment>();
frags.add(ListPagerFragment.newInstance(0));
frags.add(ResultsPagerFragment.newInstance(1));
pagerAdapter = new MyPagerAdapter(getSupportFragmentManager(),frags);
pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(pagerAdapter);
}
And then modify the MyPagerAdapter like so:
public class MyPagerAdapter extends FragmentStatePagerAdapter
{
private List<Fragment> fragments;
public MyPagerAdapter(FragmentManager fm, List<Fragment> frags)
{
super(fm);
this.fragments = frags;
}
#Override
public Fragment getItem(int position)
{
return fragments.get(position);
}
}
kind of silly isn't it? I don't understand the point but it worked
That's probably because you are trying to use the views from your Activity, before your ListPagerFragment.onCreateView() has inflated your xml layout. Move what's inside your onCreate() and some code of fillList() to onCreateView(), something like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.list_pager_fragment, container, false);
listview = (ListView) v.findViewById(R.id.listView1);
pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
pager = (ViewPager) v.findViewById(R.id.pager);
pager.setAdapter(pagerAdapter);
return v;
}
After your fragments onCreateView() has inflated the view, you can try to fill it.
And don't forget to clear your view references and unregister your listview listener(if you set one) in onDestroyView() to avoid memory leaks, e.g.
#Override
public void onDestroyView() {
super.onDestroyView();
listview.setOnItemClickListener(null);
...
listview = null;
}
read about the fragment's lifecycle --> Fragment's Lifefycle
I have a listview containing item apple, banana, orange ...in second screen activity
when i click on particular item on apple it navigates to Details screen
here output has to appear like apple ..if i swipe page then banana and for nextswipe orage
apple-->banana-->orange
but am getting output like apple-->apple-->apple.
Intent detailIntent =new Intent(SecondScreen.this, Details.class);
startActivity(detailIntent);
public class MyApplication extends Application {
ArrayList<String> totalvalue = new ArrayList<String>();
}
public class Details extends FragmentActivity{
MyApplication app;
ViewPager pager;
MyFragmentPagerAdapter pagerAdapter;
public static int position ;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.detail);
app = ((MyApplication) getApplicationContext());
pager = (ViewPager) findViewById(R.id.pager);
FragmentManager fm = getSupportFragmentManager();
pagerAdapter = new MyFragmentPagerAdapter(fm,app.totalvalue);
pager.setAdapter(pagerAdapter);
}
public static class MyFragmentPagerAdapter extends FragmentPagerAdapter {
private static ArrayList<String> temperList;
public MyFragmentPagerAdapter(FragmentManager fm, ArrayList<String> totalvalue ) {
super(fm);
this.temperList = totalvalue;
}
public Fragment getItem(int position) {
return ThingFragment.newInstance((temperList.get(position)));
}
#Override
public int getCount(){
return temperList.size();
}
private static class ThingFragment extends Fragment {
private String name1;
static ThingFragment newInstance(String string ) {
ThingFragment f = new ThingFragment();
Bundle args = new Bundle();
args.putString("name",temperList.get(position) );
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
name1 = getArguments().getString("name");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.myfragment_layout, container, false);
TextView t = (TextView)v.findViewById(R.id.prodcutt);
t.setText(""+ name1);
return v;
}
}
}
}
args.putString("name",temperList.get(position) ); shouldn't be possible inside your ThingFragment as its static.
That should be args.putString("name",string);
Also your temperList shouldn't be static in your adapter (no reason to, your passing it as a param) just make it final - private final ArrayList<String> temperList;
If that doesn't fix it please post more structured separated code so we can see how your application is built up a bit more. The adapter looks fine, so it maybe a case that your writing/overwriting the array at some point.