Initializing and Updating Fragment's Data with ViewPager Bundle or Getters - android

I am having some difficulties grasping the correct way to create fragments using the ViewPager, initialize their data and then eventually update their data.
The challenge is where do I stick the data structure? Currently it is passed in from a previous activity into my main activity as a bundle and initialzed like so (Where mTitles is a list of my tab titles and the Fragment titles, and mData is a hashmap containing a list of data for each fragment
HashMap<String, ArrayList<Parcelable>> mData = new HashMap<String, ArrayList<Parcelable>>();
ArrayList<String> mTitles = new ArrayList<String>()
private void initializeData(Bundle extras, String[] keys) {
for (int i = 0; i < keys.length; i++) {
mData.put(keys[i], extras.getParcelableArrayList(keys[0]));
}
if (viewPager != null) {
setupViewPager(viewPager, keys);
}
}
Then I call the setupViewPager to initialize the fragments and their data from the data structure.
public class FragmentObserver extends Observable {
#Override
public void notifyObservers() {
setChanged(); // Set the changed flag to true, otherwise observers won't be notified.
super.notifyObservers();
}
#Override
public void notifyObservers(Object object) {
setChanged(); // Set the changed flag to true, otherwise observers won't be notified.
super.notifyObservers(object);
}
}
private void setupViewPager(final ViewPager viewPager, String[] tabs) {
Adapter adapter = new Adapter(getSupportFragmentManager());
for (int i = 0; i < tabs.length; i++) {
Bundle b1 = new Bundle();
b1.putString("title", tabs[i]);
Log.d("ADAPTER: ", "putting " + tabs[i]);
b1.putParcelableArrayList(tabs[i], mData.get(tabs[i]));
ListFragment f1 = new ListFragment();
f1.setArguments(b1);
mTitles.add(tabs[i]);
adapter.addFragment(f1, tabs[i]);
}
viewPager.setAdapter(adapter);
}
class Adapter extends FragmentPagerAdapter {
private List<Fragment> mFragments = new ArrayList<>();
private List<String> mFragmentTitles = new ArrayList<>();
private Observable mObservers = new FragmentObserver();
public Adapter(FragmentManager fm) {
super(fm);
}
public void addFragment(Fragment fragment, String title) {
this.mFragments.add(fragment);
this.mFragmentTitles.add(title);
}
public Fragment getItemByTitle(String title) {
int index = this.mFragmentTitles.indexOf(title);
return (index > 0) ? getItem(index) : null;
}
#Override
public Fragment getItem(int position) {
Fragment f = this.mFragments.get(position);
mObservers.addObserver((Observer) f);
return f;
}
#Override
public int getCount() {
return this.mFragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
return this.mFragmentTitles.get(position);
}
public void updateFragments(SessionInfo session) {
mObservers.notifyObservers(session);
}
}
Fragment Code:
ArrayList<Info> mDatas;
public ListFragment(){
super();
}
#Override
public void update(Observable observable, Object data) {
View root = getView();
// Update your views here.
Log.d("OBSERVER", "called update for: " + ((SessionInfo) data).getActivity());
Log.d("OBSERVER", "I am " + title);
// SimpleStringRecyclerViewAdapter a = ((SimpleStringRecyclerViewAdapter)rv.getAdapter());
// a.insertValueAt(0, (SessionInfo) data);
//
// a.notifyItemInserted(0);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rv = (RecyclerView) inflater.inflate(
R.layout.fragment_session_list, container, false);
String title = getArguments().getString("title");
mDatas = getArguments().getParcelableArrayList(title);
// SessionInfo first = sessions.get(0);
// Log.i("FRAG", first.getActivity());
this.title = title;
return rv;
}
The problem and my question is what happens when I need to update a fragments data structure? Currently it is stored in the Activity class so I update it there, and then call the observer update method to pass the data to the fragment. But I get a null pointer error if I need to update Fragment 3 and I have not scrolled my ViewPager to 3 yet so it is not initialized and of course has no Observer yet. How do I update that fragments data?
Is this Observer pattern pron to memory leaks?
Should I just be holding a reference to Activity and call a getter method for my data rather then sending it as a bundle?

You can refer this tutorial: https://github.com/codepath/android_guides/wiki/ViewPager-with-FragmentPagerAdapter
Basically pass your HashMap> mData to Adapter and from adapter setArguments() with relevant data on each fragment. That way relevant data stay with relevant object.

Related

Is there a way to dynamically create fragments and display data to them using the same layout?

I have written code that fetches a JSON object from a remote endpoint. For this question, I am passing in a mock object which is an array of objects.
My goal is to deconstruct the object and for each element of the array, create a fragment within a ViewPager. Each fragment should display the id of the object it is representing.
The object looks like this:
{'data':[{'id':1},{'id':2}]}
If I uncomment the code in FragmentClass, each fragment will display "test." However, if I try to set the text based on the value I pass via arguments, I run into this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.os.Bundle.get(java.lang.String)' on a null object reference
The code for FragmentClass is:
public class FragmentClass extends Fragment {
private TextView txtId;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = (ViewGroup) inflater.inflate(
R.layout.fragment_screen_slide, container, false);
txtId = (TextView) rootView.findViewById(R.id.txtId);
txtId.setText(getArguments().get("id").toString());
// txtId.setText("test");
return rootView;
}
}
The code for the ViewPager class, ScreenSliderPagerActivity is:
public class ScreenSliderPagerActivity extends FragmentActivity {
private ViewPager mPager;
private PagerAdapter pagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
Intent intent = getIntent();
String data = intent.getStringExtra("data");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_slide);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager(), data);
mPager.setAdapter(pagerAdapter);
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public int NUM_PAGES; //making this a public variable within the inner class allows me to dynamically change the number of fragments created. In this instance, 2 will be created, one for each element of the array.
public ScreenSlidePagerAdapter(FragmentManager fm, String data) {
super(fm);
try {
JSONObject json = new JSONObject(data);
JSONArray jsonArray = json.getJSONArray("data");
NUM_PAGES = jsonArray.length();
Log.d("NUM_PAGES", String.valueOf(NUM_PAGES)); //2
FragmentTransaction ft = fm.beginTransaction();
Bundle args = new Bundle();
// I think the problem is something in the loop, but I cannot seem to figure another way to do this.
for (int i = 0; i < NUM_PAGES; i++){
JSONObject obj = (JSONObject) jsonArray.get(i);
FragmentClass fragment = new FragmentClass();
ft.add(R.id.pager, fragment);
args.putString("id", obj.get("id").toString());
fragment.setArguments(args);
}
ft.commit();
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public Fragment getItem(int position) {
return new FragmentClass();
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
Finally, my MainActivity containing the mock object is:
public class MainActivity extends AppCompatActivity {
private Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.btn);
}
public void handleClick(View view){
String mockData = "{'data':[{'id':1},{'id':2}]}";
Intent intent = new Intent(MainActivity.this, ScreenSliderPagerActivity.class);
intent.putExtra("data",mockData);
MainActivity.this.startActivity(intent);
}
}
FragmentStatePagerAdapters don't work that way. You can't add your fragments in constructor of your adapter to the fragment manager and expect it to be added in the right place. You have to return the constructed fragment with the arguments in getItem(int) method:
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public int NUM_PAGES; //making this a public variable within the inner class allows me to dynamically change the number of fragments created. In this instance, 2 will be created, one for each element of the array.
private ArrayList<FragmentClass> mFragments = new ArrayList<>();
public ScreenSlidePagerAdapter(FragmentManager fm, String data) {
super(fm);
try {
JSONObject json = new JSONObject(data);
JSONArray jsonArray = json.getJSONArray("data");
NUM_PAGES = jsonArray.length();
Log.d("NUM_PAGES", String.valueOf(NUM_PAGES)); //2
for (int i = 0; i < NUM_PAGES; i++){
JSONObject obj = (JSONObject) jsonArray.get(i);
Bundle args = new Bundle();
FragmentClass fragment = new FragmentClass();
args.putString("id", obj.get("id").toString());
fragment.setArguments(args);
mFragments.add(fragment);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
#Override
public int getCount() {
return NUM_PAGES;
}
}

Unable to get position of selected tab on viewpager

How many tabs will be created it depends on web service. It means I cannot discover how many Tabs are going to be Created until web service is called.
The tabs contain the products which I want to show in grid view.
In my project I have ShopProductsPageFragments.java where tabs get created. Please have look below code :
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CatPosition = getArguments().getInt("CatPosition");
StoreID = getArguments().getString("StoreID");
System.out.println("getStoreID in ShopProductsPageFragments="+ StoreID);
System.out.println("getCatPosition in ShopProductsPageFragments="+ CatPosition);
try {
ShopCategoryData = (GetProductCategoriesByStoreResponsePojo) getArguments().getSerializable("ShopCatNames");
}catch (Exception e){
e.printStackTrace();
}
assert ShopCategoryData != null;
List<Datum> shopcatdata = ShopCategoryData.getData();
for (int i = 0; i < shopcatdata.size(); i++) {
System.out.println("ShopCategoryData in ShopProductsPageFragments "+ shopcatdata.get(i).getCatName());
}
ShopProductsPageView = inflater.inflate(R.layout.activity_product_page_fragment ,container ,false);
viewPager = (ViewPager)ShopProductsPageView.findViewById(R.id.product_viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout)ShopProductsPageView.findViewById(R.id.product_tabs);
tabLayout.setupWithViewPager(viewPager);
return ShopProductsPageView;
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getFragmentManager());
List<Datum> shopcatdata = ShopCategoryData.getData();
for (int i = 0; i < shopcatdata.size(); i++) {
CommanShopProductFragment commanShopProductFragment = CommanShopProductFragment.newInstance(i);
String CatName = shopcatdata.get(i).getCatName();
Bundle bundle = new Bundle();
bundle.putString("StoreID",StoreID);
bundle.putString("CatName",CatName);
commanShopProductFragment.setArguments(bundle);
System.out.println("ShopCategoryData in ShopProductsPageFragments "+ shopcatdata.get(i).getCatName());
adapter.addFrag(commanShopProductFragment, shopcatdata.get(i).getCatName());
}
adapter.notifyDataSetChanged();
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(CatPosition);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
FragmentManager fragmentManager;
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
fragmentManager = manager;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
void addFrag(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Here, you can see how tabs are created. I am using same fragment for showing data in Tabs as follows:
public class CommanShopProductFragment extends Fragment {
private static final String ARG_POSITION = "position";
private int position;
View CategoryTabFragmentView;
GetStoreProductsByCategoriesPresenterImpl presenter;
RestClient service;
GridView gridView;
List<Datum> shopProduct;
ProductByCategoryGridViewAdapter mAdapter;
public CommanShopProductFragment() {
// Required empty public constructor
}
public static CommanShopProductFragment newInstance(int position) {
CommanShopProductFragment f = new CommanShopProductFragment();
Bundle b = new Bundle();
b.putInt(ARG_POSITION, position);
f.setArguments(b);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String CatName = getArguments().getString("CatName");
String StoreID = getArguments().getString("StoreID");
assert CatName != null;
System.out.println("CommanShopProductFragment >>>>>>>> CatName="+CatName);
assert StoreID != null;
System.out.println("CommanShopProductFragment >>>>>>>> StoreID="+StoreID);
CategoryTabFragmentView = inflater.inflate(R.layout.activity_category_tab_fragment ,container ,false);
service = ((LilubiApplication) getActivity().getApplication()).getNetworkService();
presenter = new GetStoreProductsByCategoriesPresenterImpl(this, service);
String page = "1", itemsPerPage = "10";
try {
presenter.GetStoreProductsByCategories(CatName, StoreID, page, itemsPerPage);
}catch (Exception e){
e.printStackTrace();
}
return CategoryTabFragmentView;
}
public void getStoreProductsByCategories(ProductByCategoriesResponsePojo productByCategoriesResponsePojo){
System.out.println("CategoryTabFragment in getMessage="+productByCategoriesResponsePojo.getMessage());
System.out.println("CategoryTabFragment in getStatus="+productByCategoriesResponsePojo.getStatus());
// prepared arraylist and passed it to the Adapter class
shopProduct = productByCategoriesResponsePojo.getData();
mAdapter = new ProductByCategoryGridViewAdapter(getActivity(),shopProduct);
// Set custom adapter to gridview
gridView = (GridView) CategoryTabFragmentView.findViewById(R.id.category_tab_view_grid_view);
gridView.setAdapter(mAdapter);
}
Now what I want is when user selects a tab then list of products should be displayed according to selected category from the tabs.
All product data also comes from web service. Let me know if I missed any thing to explain. Thank you.
I am editing my previous answer
Edit:
you can use viewpager.getCurrentItem() to get current position //include this in your activity with viewpager

ViewPager + RecyclerView issue in android

Hi I have Tablayout with Viewpager and i am using Fragment for tablayout. Now in every Tablayout fragments I have Recyclerview and displaying items.Please See this my json response
http://pastebin.com/nUswad9s
here in "typeMaster": array i have categories "typeName": "Dogs", and i am displaying typenames in tablayout i have 4 tablayout, and inside typemaster i have subcategoreis named "catMaster": and i am trying to display catmaster data in recyclerview,but the issue is in every fragment it shows last data "catName": "Vitamins & Minerals",
Activity
public class CategoriesActivity extends AppCompatActivity{
private Header myview;
private ArrayList<SubcategoryModel> subct;
private ArrayList<CategoryModel> filelist;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.categoris_activity);
filelist = (ArrayList<CategoryModel>)getIntent().getSerializableExtra("categorylist");
System.out.println("Category list size"+filelist.size());
myview = (Header) findViewById(R.id.categorisactivity_headerView);
myview.setActivity(this);
TabLayout tabLayout = (TabLayout) findViewById(R.id.cat_tab_layout);
for(int i = 0; i < filelist.size(); i++){
subct=filelist.get(i).getItems();
for(int j=0;j<subct.size();j++)
{
}
System.out.println("SubCategory list size"+subct.size());
}
for(int i = 0; i < filelist.size(); i++){
tabLayout.addTab(tabLayout.newTab().setText(filelist.get(i).getCategory_typename()));
ArrayList<SubcategoryModel> subct=filelist.get(i).getItems();
for(int j=0;j<subct.size();j++)
{
}
}
Bundle bundleObject = new Bundle();
bundleObject.putSerializable("key", filelist);
FirstFragment ff=new FirstFragment();
ff.setArguments(bundleObject);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
final ViewPager viewPager = (ViewPager) findViewById(R.id.categories_pager);
CategoriesAdapter mPagerAdapter = new CategoriesAdapter(getSupportFragmentManager(),tabLayout.getTabCount());
viewPager.setAdapter(mPagerAdapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
public class CategoriesAdapter extends FragmentStatePagerAdapter {
ArrayList<CategoryModel> catlist;
int numoftabs;
public CategoriesAdapter(FragmentManager fm, int numoftabs) {
super(fm);
this.numoftabs = numoftabs;
}
#Override
public Fragment getItem(int position) {
Log.v("adapter", "getitem" + String.valueOf(position)+subct.size());
return FirstFragment.create(position,subct);
}
#Override
public int getCount() {
return numoftabs;
}
}
}
Fragment
public class FirstFragment extends Fragment {
// Store instance variables
public static final String ARG_PAGE = "page";
private int mPageNumber;
private Context mContext;
private int Cimage;
private ArrayList<SubcategoryModel> subcatlist;
private RecyclerView rcylervw;
private ArrayList<CategoryModel> filelist;
ArrayList<SubcategoryModel> subct;
public static FirstFragment create(int pageNumber,ArrayList<SubcategoryModel> subct){
FirstFragment fragment = new FirstFragment();
Bundle args = new Bundle();
args.putInt(ARG_PAGE, pageNumber);
args.putSerializable("key", subct);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt(ARG_PAGE);
subct= (ArrayList<SubcategoryModel>) getArguments().getSerializable("key");
System.out.println("Frag Category list size"+subct.size());
/* for(int i = 0; i < filelist.size(); i++){
subct=filelist.get(i).getItems();
for(int j=0;j<subct.size();j++)
{
}
System.out.println("Frag SubCategory list size"+subct.size());
}*/
// image uri get uri of image that saved in directory of app
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater
.inflate(R.layout.test, container, false);
rcylervw=(RecyclerView)rootView.findViewById(R.id.subcategory_recycler_view);
rcylervw.setHasFixedSize(true);
MyAdapter adapter = new MyAdapter(subct);
rcylervw.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
rcylervw.setLayoutManager(llm);
return rootView;
}
// this method is not very important
}
MyAdapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList<SubcategoryModel> mDataset;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public MyViewHolder(View v) {
super(v);
mTextView = (TextView) v.findViewById(R.id.subcategory_text);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<SubcategoryModel> myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_subcategory, parent, false);
// set the view's size, margins, paddings and layout parameters
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.mTextView.setText(mDataset.get(position).getSubCategory_name());
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
Output i am getting right now
As you can see it shows same result "Vitamin & Minerals" in every tabs..i want different subcategories instead of same.
I see a lot of problems with your code, but let's get your UI displaying the subcategories since that's your main concern.
Change the getItem in your adapter to this:
#Override
public Fragment getItem(int position) {
ArrayList<SubcategoryModel> subcategories = filelist.get(position).getItems();
Log.v("adapter", "getitem" + String.valueOf(position)+subcategories.size());
return FirstFragment.create(position,subcategories);
}
What caused the problem:
Let's focus on ArrayList<SubcategoryModel> subct in your activity:
First your code did this:
for(int i = 0; i < filelist.size(); i++){
ArrayList<SubcategoryModel> subct=filelist.get(i).getItems();
// for(int j=0;j<subct.size();j++) ...
}
So at the end of this loop subct is set the subcategories of the last category in filelist.
After that, you did another loop to load the tabs, but that used a different subct variable that was declared inside the loop, and that had no effect on the subct field of your activity.
Then you created your view pager and adapter.
In your pager adapter you had this:
#Override
public Fragment getItem(int position) {
Log.v("adapter", "getitem" + String.valueOf(position)+subct.size());
return FirstFragment.create(position,subct);
}
Since subct was set to the last category's subcategories from the loop before, every single fragment created was receiving those subcategories, no matter what position (category) the fragment was for. All I did was change the code to go back to filelist and get the correct category (and subcategories) for the position of the fragment being created.
When you're writing code, you think about what you want the code to do. However, at the point where you run the code and discover you have a problem, you have to forget what you wanted the code to do, then pretend you're the computer and run the code in your head. You want to understand what effect every line of code is having. When you do it that way it's easier to find the problem.
Problem:
There's no way to pass a Serializable ArrayList in a Bundle. Look at the docs page here Bundle docs
Solution:
Change your SubCategoryModel to implement Parcelable and then use bundle.putParcelableArrayList(key, list) and bundle.getParcelableArrayList(key) to pass the ArrayList to the FragmentArgs and get them from the Fragment
this is the main logic of your code I guess... Try it and let me know if you need more help or you find it helpful...
private void parseJsonData() {
try {
listDogs.clear();
JSONArray jsonArray = new JSONArray(loadJSONFromAsset());
JSONObject firstJsonobject = jsonArray.optJSONObject(0);
JSONArray itemListJsonArray = firstJsonobject.optJSONArray("itemList");
JSONObject secondJsonobject = itemListJsonArray.optJSONObject(0);
JSONArray typeMasterArray = secondJsonobject.optJSONArray("typeMaster");
JSONObject thirdJsonobject = typeMasterArray.optJSONObject(0);
JSONArray catMasterArray = thirdJsonobject.optJSONArray("catMaster");
for(int i=0; i<catMasterArray.length(); i++) {
JSONObject jsonObject = catMasterArray.optJSONObject(i);
ModelClass modelClass = new ModelClass();
modelClass.setTypeId(jsonObject.optString("catID"));
modelClass.setTypeName(jsonObject.optString("catName"));
listDogs.add(modelClass);
}
RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(getActivity(), listDogs);
recyclerView.setAdapter(recyclerViewAdapter);
} catch (JSONException e) {
e.printStackTrace();
}
}
Note: To parse data for dogs category I've passed 0 position in variable thirdJsonobject. Pass 1 for cats and 2 for horse and you will find your desired output
Screenshots:
Dogs category
Cats category
Horse category

Instance of same fragment destroy layout of first one in ViewPager?

I am trying to fetch results from sqllite db in ViewPager using Adapater class
public class AppDetailPagerAdapter extends android.support.v4.app.FragmentStatePagerAdapter {
private List<AppPagingData> mData;
public AppDetailPagerAdapter(FragmentManager fm, List<AppPagingData> data) {
super(fm);
this.mData = data;
}
#Override
public Fragment getItem(int i) {
sCurrentPosition = i;
Fragment fragment = AppDetailFragment.newInstance(mData, i);
return fragment;
}
#Override
public int getCount() {
return mData.size();
}
#Override
public CharSequence getPageTitle(int position) {
return "OBJECT " + (position + 1);
}
}
And my fragment is
public class AppDetailFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private List<AppPagingData> mData;
private int mCurrentPosition;
private int mToken;
private static final String EXTRA_KEY_APP_DATA = "EXTRA_KEY_APP_DATA";
private static final String EXTRA_KEY_APP_CURR_POSITION = "EXTRA_KEY_APP_CURR_POSITION";
public static AppDetailFragment newInstance(ArrayList<AppPagingData> param1, int currentPosition) {
AppDetailFragment fragment = new AppDetailFragment();
Bundle args = new Bundle();
args.putParcelableArrayList(EXTRA_KEY_APP_DATA, param1);
args.putInt(EXTRA_KEY_APP_CURR_POSITION, currentPosition);
fragment.setArguments(args);
return fragment;
}
public AppDetailFragment() {
// Required empty public constructor
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().restartLoader(mToken, null, this);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (loader.getId() == mToken) {
ViewGroup oocsGroup = (ViewGroup) getActivity().findViewById(R.id.oocsGroup);
// Remove all existing timings (except 1 ie header)
//I think this line remove childs for all fragment????
for (int i = oocsGroup.getChildCount() - 1; i >= 1; i--) {
oocsGroup.removeViewAt(i);
}
} else {
cursor.close();
}
}...
Now, the problem is my fragment linear layout items get deleted, as android call my fragment second instance. for e.g. if i select 1st item second will called automatically.
How to avoid layout of first instance to destry because of second.
As your adapter creates a new instance of fragment at getItem(), you are probably loosing the previous fragment to the garbage collector.
You could keep your fragments in an array (or ArrayList) and return fragment from the array at getItem().
like:
// keep created instances of AppDetailFragments
private AppDetailFragment[] frags;
public AppDetailPagerAdapter(FragmentManager fm, List<AppPagingData> data) {
super(fm);
this.data = data;
frags = new AppDetailFragment[data.size()];
// init all frags
for (int i=0;i<data.size();i++) {
frags[i] = AppDetailFragment.newInstance(data, i);
}
}
#Override
public Fragment getItem(int i) {
sCurrentPosition = i;
return frags[i]; // may want to check for arrayindexoutofboundsEx..
}
This way you will keep a reference to all created fragments.
note:
You no longer need to keep the data as for getCount you can return length of frags-array.

Communicating between fragments

Below is an screenshot from my app.
This screen is a fragment that has sliding tabs layout. It will hold another fragment that will show data in listview. The problem is, in order to load data the value selected from the spinner need to pass within the fragment in tab. I am not getting idea how to do this. One approach would be the tab fragment would implement a callback and within that callback data should be loaded. But I am not getting how to register that callback in onItemSelected of spinner.
Note: All fragments within the tab will show data in listview only, so I have created a common fragment.
This is my code so far:
Fragment for the screenshot
public class BuyListingFragment2 extends BaseFragment {
private Context ctx;
private Spinner vehicle_type;
private ArrayList<ListingTabModel> mListingTabs = new ArrayList<ListingTabModel>();
private ArrayAdapter<String> spinnerAdapter;
private ArrayList<String> vehicleTypeSpinnerlist;
private int spinnerPosition;
private SlidingTabLayout sliding_tabs;
private BuyListingPagerAdapter buyListingPagerAdapter;
public static BuyListingFragment2 newInstance(String category,
int position, String preselectedFilters) {
BuyListingFragment2 fragment = new BuyListingFragment2();
Bundle args = new Bundle();
args.putString("vehicle_type", category);
args.putInt("spinner_position", position);
fragment.setArguments(args);
return fragment;
}
public BuyListingFragment2() {
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.vehicleType = getArguments().getString("vehicle_type");
this.selectedVehicle = this.vehicleType;
this.spinnerPosition = getArguments().getInt("spinner_position");
ArrayList<CategoryType> vehicleTypeList = RegistrationResponse
.getInstance().getVehicleTypeList();
spinnerAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, vehicleTypeList);
buyListingPagerAdapter = new BuyListingPagerAdapter(
getChildFragmentManager(), mListingTabs);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ctx = getActivity();
vehicle_type = (Spinner) view.findViewById(R.id.vehicle_type);
vehicle_type.setAdapter(spinnerAdapter);
vehicle_type.setSelection(spinnerPosition, false);
if (mListingTabs.isEmpty()) {
String[] tabNames = getResources().getStringArray(
R.array.listing_tab_names);
for (int i = 0; i < tabNames.length; i++) {
String tabName = tabNames[i];
ListingTabModel mListingTabModel = new ListingTabModel();
mListingTabModel.setTagName(tabName);
mListingTabs.add(mListingTabModel);
}
}
buyListingPagerAdapter.notifyDataSetChanged();
listing_layout_viewpager = (ViewPager) view
.findViewById(R.id.listing_layout_viewpager);
listing_layout_viewpager.setAdapter(buyListingPagerAdapter);
sliding_tabs = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
sliding_tabs.setDistributeEvenly(true);
sliding_tabs.setViewPager(listing_layout_viewpager);
vehicle_type.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
spinnerPosition = position;
//How to register listener here
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
}
Common Fragment inside Tab
public class ListingFragment extends BaseFragment implements
OnSpinnerDataSelected {
private InfiniteListView mListView;
private BuyListingListAdapter buyListingAadapter;
private RobotoLightTextView emptyMessage;
private int currentPageNumber = 1;
private int totalPages;
private HashMap<String, String> params = new HashMap<String, String>();
private int apiCallCount = 0;
private Context ctx;
private String vehicleType;
private ProgressBar progressBar;
public ListingFragment() {
}
public static ListingFragment newInstance(ListingTabModel mListingTabModel) {
ListingFragment mFragment = new ListingFragment();
Bundle bundle = new Bundle();
// bundle.putBoolean("is_grid_view", mListingTabModel.isShowGridView());
// bundle.putString("vehicle_type", mListingTabModel.getVehicleType());
mFragment.setArguments(bundle);
return mFragment;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ctx = getActivity();
emptyMessage = (RobotoLightTextView) view
.findViewById(R.id.empty_message);
mListView = (InfiniteListView) view.findViewById(R.id.lstVw_buy);
boolean isGrid = getArguments().getBoolean("is_grid_view");
vehicleType = getArguments().getString("vehicle_type");
buyListingAadapter = new BuyListingListAdapter(ctx,
mVehicleListingList, isGrid);
mListView.setAdapter(buyListingAadapter);
progressBar = new ProgressBar(ctx);
}
#Override
public int getLayoutId() {
return R.layout.layout_messages;
}
#Override
public void onSpinnerDataSelected(String vehicleCategory) {
// TODO: fetch listing data
}
}
Callback implemented by the ListingFragment
public interface OnSpinnerDataSelected {
void onSpinnerDataSelected(String vehicleCategory);
}
FragmentStatePagerAdapter
public class BuyListingPagerAdapter extends FragmentStatePagerAdapter {
ArrayList<ListingTabModel> mFragmentsList;
public BuyListingPagerAdapter(FragmentManager fm,
ArrayList<ListingTabModel> mFragmentsList) {
super(fm);
this.mFragmentsList = mFragmentsList;
}
#Override
public Fragment getItem(int index) {
ListingFragment listingFragment = ListingFragment
.newInstance(mFragmentsList.get(index));
return listingFragment;
}
#Override
public int getCount() {
return mFragmentsList.size();
}
#Override
public CharSequence getPageTitle(int position) {
String tagName = mFragmentsList.get(position).getTagName();
tagName = tagName.replace("_", " ");
return tagName;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return object == view;
}
}
When using one activity and multiple fragments, I suggest to let the Fragment manage the UI and use the Activity has a controller/model.
Workflow for a spinner to communicate with other fragments :
Register the spinner listener in Frag1
Register a data listener from Frag2 in Activity
OnItemSelected from Frag1 prevent Activity from the Spinner value change
Activity received the spinner change value
Activity call Frag2 listener to prevent Frag2 of the spinner change
Frag2 receive spinner change, do your stuff
Here is a litle schema
I would base everything on an event bus like Otto. IMHO, Fragments were meant to be decoupled from hosting activities and such, but all the interfaces and callbacks end up creating spaghetti code. Otto lets you post event on a common bus -- the receiver doesn't need to be tied to the sender via some listener/callback mechanism. Plus, it works great in conjunction with dependency injection, see Dagger.

Categories

Resources