How to add Fragments in ViewPager to TabLayout from a web service

The name of the pictureThe name of the pictureThe name of the pictureClash 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);





where is defined the imageView variable? Also, please post the code of TstFrag. Thanks
– Lino
Aug 5 at 18:21



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.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

Creating a leaderboard in HTML/JS