How to add Fragments in ViewPager to TabLayout from a web service
Clash Royale CLAN TAG#URR8PPP
How to add Fragments in ViewPager to TabLayout from a web service
Hello guys Im trying to add tabs to my TabLayout-ViewPager-Fragment
based application dynamically.The TabLayout
uses a custom view with an ImageView
on top of a TextView
.
TabLayout-ViewPager-Fragment
TabLayout
ImageView
TextView
What I want now is to add tabs according to the number of JSONObjects
in my JSONArray
created from a PHP - MySql
web service.I tried the method below in my PagerAdapter
but it throws java.lang.IllegalStateException: Can't change tag of fragment TstFragdf80bc8 #0 id=0x7f080157 android:switcher:2131231063:0: was android:switcher:2131231063:0 now android:switcher:2131231063:1
.
JSONObjects
JSONArray
PHP - MySql
PagerAdapter
java.lang.IllegalStateException: Can't change tag of fragment TstFragdf80bc8 #0 id=0x7f080157 android:switcher:2131231063:0: was android:switcher:2131231063:0 now android:switcher:2131231063:1
The method is as in my Adapter below
public class FrgAdapter extends FragmentStatePagerAdapter
private List<Fragment> mFragmentList = new ArrayList<>();
private List<String> mFragmentTitleList = new ArrayList<>();
public FrgAdapter(FragmentManager fm)
super(fm);
public FrgAdapter(FragmentManager fm, List<Fragment> mFragmentList, List<String> mFragmentTitleList)
super(fm);
this.mFragmentList = mFragmentList;
this.mFragmentTitleList = mFragmentTitleList;
@Override
public Fragment getItem(int position)
return mFragmentList.get(position);
@Override
public int getCount()
return mFragmentList.size();
public void addFragment(Fragment fragment, String title)
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
public void insertFromJSONObject(JSONObject jsonObject, Fragment fragment, CircularImageView imageView, TextView textView) throws JSONException
JSONArray dataArray = jsonObject.getJSONArray("data");
for (int i = 0; i < dataArray.length(); i++)
JSONObject sectionObj = (JSONObject) dataArray.get(i);
JSONArray sectionsArray = sectionObj.getJSONArray("section");
for (int j = 0; j < sectionsArray.length(); j++)
JSONObject obj = (JSONObject) sectionsArray.get(j);
Picasso.get().load(obj.getString("imag")).into(imageView);
mFragmentList.add(fragment);
mFragmentTitleList.add(obj.getString("name"));
textView.setText(obj.getString("name"));
notifyDataSetChanged();
@Override
public CharSequence getPageTitle(int position)
return null;
Here is where the method is called in my activity to add the fragments:
private void setUpViewPager(ViewPager viewPager, View view)
adapter = new FrgAdapter(getSupportFragmentManager());
/*adapter.addFragment(new MleFrag(), "Men");
adapter.addFragment(new FmlFrag(), "Women");
adapter.addFragment(new ChdFrag(), "Children");
adapter.addFragment(new AceFrag(), "Accessories");
*/
String url = "https://44091ee6.ngrok.io/Glam/men.json";
AndroidNetworking.get(url)
.setPriority(Priority.MEDIUM)
.build()
.getAsJSONObject(new JSONObjectRequestListener()
@Override
public void onResponse(JSONObject response)
try
adapter.insertFromJSONObject(response, new TstFrag(), view.findViewById(R.id.circularImageView), view.findViewById(R.id.text_header));
adapter.notifyDataSetChanged();
catch (JSONException e)
e.printStackTrace();
@Override
public void onError(ANError anError)
Toast.makeText(getApplicationContext(), anError.toString(), Toast.LENGTH_LONG).show();
);
adapter.notifyDataSetChanged();
viewPager.setAdapter(adapter);
and here is my Fragment
.In my mind I want to reuse this one Fragment
for each of the times I will be requiring one.
Fragment
Fragment
public class TstFrag extends Fragment
public TstFrag()
@Override
public void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
return inflater.inflate(R.layout.layout_cht, container, false);
imageView
oh sorry I was trying it out without the imageview but let me update the code
– Thekamble
Aug 6 at 6:35
Check this answer
– Khaled Lela
Aug 6 at 7:31
2 Answers
2
You may try this:
adapter.insertFromJSONObject(response, view.findViewById(R.id.circularImageView), view.findViewById(R.id.text_header));
adapter.notifyDataSetChanged();
and
public void insertFromJSONObject(JSONObject jsonObject, CircularImageView imageView, TextView textView) throws JSONException
JSONArray dataArray = jsonObject.getJSONArray("data");
for (int i = 0; i < dataArray.length(); i++)
JSONObject sectionObj = (JSONObject) dataArray.get(i);
JSONArray sectionsArray = sectionObj.getJSONArray("section");
for (int j = 0; j < sectionsArray.length(); j++)
JSONObject obj = (JSONObject) sectionsArray.get(j);
Picasso.get().load(obj.getString("imag")).into(imageView);
mFragmentList.add(new TstFrag());
textView.setText(obj.getString("name"));
//mFragmentTitleList.add(obj.getString("name"));
notifyDataSetChanged();
The basic idea here is to create a new instance of TstFrag
for each entry of the FrgAdapter
.
TstFrag
FrgAdapter
I took long to reply because I had already figured this out.Its working but now another issue is I get the tabs but they are empty.i.e the image and text are not loaded.I will accept this answer however for anyone who might have te same issue
– Thekamble
Aug 6 at 11:09
BTW, You shall load image in/after fragment
onCreateView
– Khaled Lela
Aug 6 at 11:59
onCreateView
Image is loaded I think simultaneously with fragment oncreateview because I call the method to get the images at the same time when Im attaching viewpager to adapter.By the way till now I havent got it to work.The images and text are not showing despite the number of fragments crreated matching the length of my JSONArray
– Thekamble
Aug 6 at 13:53
If you want just to cache images while paring
JSON
to be. ready when user slide pager then use Picasso.get().load(imageUrl).fetch(callback)
– Khaled Lela
Aug 6 at 22:27
JSON
Picasso.get().load(imageUrl).fetch(callback)
@Thekamble Check my answer update on how to pass data to fragment and handle rendering UI there on fragment
onCreateView
– Khaled Lela
Aug 6 at 23:00
onCreateView
Consider NOT to add fragments to viewPager
manually but just implement getItem
&& instantiateItem
methods on viewPager adapter. Then viewPager will control the instantiation
of new TstFrag
or just reuse created one. Check this answer for complete adapter.
viewPager
getItem
instantiateItem
instantiation
TstFrag
@Override
public Fragment getItem(final int pos)
return TstFrag.getInstance(dataList.get(pos));
@NonNull
@Override
public Object instantiateItem(ViewGroup container, int position)
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
@Override
public void destroyItem(ViewGroup container, int position, Object object)
registeredFragments.remove(position);
super.destroyItem(container, position, object);
Update
public class DataModel implements Serializable
private String name;
private String image;
// GETTER && SETTER
Replace
private List<String> mFragmentTitleList = new ArrayList<>();
private List<String> mFragmentTitleList = new ArrayList<>();
With
private List<DataModel> dataList = new ArrayList<>();
insertFromJSONObject
dataList.clear();
for (int j = 0; j < sectionsArray.length(); j++)
JSONObject obj = (JSONObject) sectionsArray.get(j);
final DataModel model = new DataModel();
model.setName(obj.getString("name"));
model.setImage(obj.getString("imag"));
// Picasso.get().load(obj.getString("imag")).into(imageView);
// No need to load image here just fetch for cache if you want.
Picasso.get().load(model.getImage()).fetch();
// mFragmentList.add(fragment); // no need handle adding fragment here as mentioned above.
dataList.add(dataModel);
// textView.setText(obj.getString("name")); // It's not good to update UI here but update there with fragment on create called mention below.
// notifyDataSetChanged(); DON'T call this inside the loop but after finish your stuff.
// Just notify here...
notifyDataSetChanged();
TstFrag
public class TstFrag extends Fragment
private DataModel model;
public TstFrag()
public static TstFrag getInstance(DataModel model)
TstFrag fragment = new TstFrag();
Bundle bundle = new Bundle();
bundle.putSerializable("mData", model);
fragment.setArguments(bundle);
return fragment;
@Override
public void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
model = (DataModel)getArguments().getSerializable("mData");
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
final View rootView = inflater.inflate(R.layout.layout_cht, container, false);
// findViewById() for imageView and nameTextView
Picasso.get().load(model.getImage()).into(imageView);
nameTextView.setText(model.getName());
I already got it working using @Lino's answer but an upvote because this method seems solid.Actually I saw such a suggestion earlier but wanted to proceed with the other method.Thanks for the fast response though
– Thekamble
Aug 6 at 11:12
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
where is defined the
imageView
variable? Also, please post the code of TstFrag. Thanks– Lino
Aug 5 at 18:21