One Fragment to use in multiple tabs - android

I receive a list (of Categories) from server and make tabs using SectionsPagerAdapter. I use one common Fragment within which I have a RecyclerView. After user logs in, I store Categories in TempData.productCategories and they are less than 15. When user selects a tab, I refresh the RecyclerView with the products of the selected Category. The issue is, currently I have 4 Categories. Only the first and the last ones have products (one product under each category). The first tab showing no product maybe because the second one is empty and Android automatically loads the second tab just after the first one. I want to see the products of the first category. Can anybody tell me what am I doing wrong?
The server gets my store ID and one store ID has less than 15 categories:
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
mViewPager.setCurrentItem(tab.getPosition());
//doing after a delay otherwise activity is null
final int position = tab.getPosition();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
String cat = TempData.productCategories.get(position).getName();
Log.i("Temp", "pos: " + position + ", cat: " + cat);
AnyItemFragment.updateList(cat, context);
}
}, 400);
}
Here are the other methods used in this functionality:
public static void setupListForCategory(final Activity context, final RecyclerView listView, final String category) {
List<ProductUserModel> prods = getProductsUser(category);
if(prods.isEmpty()) return;
setupList(context, listView, prods);
}
And here I update the adapter:
private static void setupList(final Activity context, RecyclerView listView, List<ProductUserModel> prods){
FastItemAdapter<ProductUserModel> p = new FastItemAdapter<>();
p.add(prods);
p.withSelectable(true);
p.withOnClickListener(new FastAdapter.OnClickListener<ProductUserModel>() {
#Override
public boolean onClick(final View v, final IAdapter<ProductUserModel> adapter, final ProductUserModel item, final int position) {
PopupUtils.getUserInputQuantity(context, item, v);
return false;
}
});
//fill the recycler view
Log.i("Temp", "updating list : " + prods.size());
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(context);
listView.setLayoutManager(layoutManager);
listView.setAdapter(p);
// p.notifyAdapterDataSetChanged();
// p.notifyDataSetChanged();
listView.invalidate();
}
private static List<ProductUserModel> getProductsUser(String category) {
List<ProductUserModel> pum = new ArrayList<>();
for(int i = 0; i < TempData.productsUser.size(); i++){
if(TempData.productsUser.get(i).getCategory().equals(category))
pum.add(TempData.productsUser.get(i));
}
return pum;
}
And the sections pager adapter in the activity:
private class SectionsPagerAdapter extends android.support.v13.app.FragmentPagerAdapter {
SectionsPagerAdapter(android.app.FragmentManager fm) {
super(fm);
}
#Override
public android.app.Fragment getItem(int position) {
return AnyItemFragment.newInstance(position-1);
}
#Override
public int getCount() {
// Show 3 total pages.
return TempData.productCategories.size();
}
#Override
public CharSequence getPageTitle(int position) {
// switch (position) {
return TempData.productCategories.get(position).getName();
// case 0:
// return getString(R.string.toys);
// case 1:
// return getString(R.string.stationaries);
// case 2:
// return getString(R.string.books);
// }
// return null;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Update: My fragment:
public class AnyItemFragment extends Fragment {
static AnyItemFragment fragment;
public static Activity context;
private static String TAG = "AllItemsFragment";
int selectedPosition = 0;
#BindView(R.id.listAll)
RecyclerView listAll;
private OnFragmentInteractionListener mListener;
public AnyItemFragment() {
// Required empty public constructor
}
public static AnyItemFragment newInstance(int categoryID) {
fragment = new AnyItemFragment();
fragment.selectedPosition = categoryID;
Log.i(TAG, "sel pos: " + fragment.selectedPosition);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_inv_any_item, container, false);
ButterKnife.bind(this, view);
context = getActivity();
return view;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(!isVisibleToUser) return;
String cat = TempData.productCategories.get(fragment.selectedPosition).getName();
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
public static void updateList(String cat, Activity context){
TempData.setupListForCategory(context, fragment.listAll, cat);
}
}

I think for starters, you should keep the flow simple and not have a static fragment instance on which you keep calling refresh.
Your code right now is difficult to read and understand.
One basic thing to note with fragments is since they are already re-usable in tandem with a FragmentStatePagerAdapter, you almost never have to deal with this hassle of having a single static instance doing all the work.
Few problems I spotted right now for example were -
You want to attach multiple fragments (as many as there are categories) so you make a new instance and update the static field
public static AnyItemFragment newInstance(int categoryID) {
fragment = new AnyItemFragment();
fragment.selectedPosition = categoryID;
Log.i(TAG, "sel pos: " + fragment.selectedPosition);
return fragment;
}
What is happening right now is since the default offscreenPageLimit on your ViewPager defaults to 1 all your updates are going to the second instance of the fragment which most probably is linked with category-2 and hence nothing renders in the first tab.
You can confirm this by adding debug break-points.
What you would ideally want to do is send the categories to your ViewPagerAdapter and based on the position set the product model list to the correct fragment itself so it knows how to render post creation.
private class SectionsPagerAdapter extends android.support.v13.app.FragmentPagerAdapter {
private final Map<String, List<ProductUserModel>> mapOfCategoryAndProductUsers;
SectionsPagerAdapter(FragmentManager fm, Map<String, List<ProductUserModel>> mapOfCategoryAndProductUsers) {
super(fm);
this.mapOfCategoryAndProductUsers = mapOfCategoryAndProductUsers;
}
#Override
public android.app.Fragment getItem(int position) {
AnyItemFragment fragment = AnyItemFragment.newInstance(position-1);
// Logic to map position to category...
String category = TempData.productCategories.get(position)
fragment.setProductUsers(mapOfCategoryAndProductUsers.get(category))
return fragment;
}
#Override
public int getCount() {
// Show 3 total pages.
return TempData.productCategories.size();
}
...
...
}
And then have the render logic inside the fragment -
private List<ProductUserModel> productUsers;
public void setProductUsers(List<ProductUserModel> productUsers) {
this.productUsers = productUsers;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setupList()
}
private void setupList() {
FastItemAdapter<ProductUserModel> p = new FastItemAdapter<>();
p.add(prods);
p.withSelectable(true);
p.withOnClickListener(new FastAdapter.OnClickListener<ProductUserModel>() {
#Override
public boolean onClick(final View v, final IAdapter<ProductUserModel> adapter, final ProductUserModel item, final int position) {
PopupUtils.getUserInputQuantity(context, item, v);
return false;
}
});
//fill the recycler view
Log.i("Temp", "updating list : " + prods.size());
LayoutManager layoutManager = new LinearLayoutManager(context);
listView.setLayoutManager(layoutManager);
listView.setAdapter(p);
}
PS: You should avoid as much as you can the use of static methods, since they make unit testing a nightmare. Also if you don't test your code before hand, I'd suggest checking out unit tests and Espresso for instrumentation test to have more re-assurance around the working of your app and be free of regression blues.
Let me know if this helped or if you'd need more explanation

Related

Issue with multiple Recyclerview inside Viewpager

I have a Viewpager with two tabs inside. The tabs are Cards and Favorite cards. I am using same fragment inside both tabs. The fragment contains a RecyclerView which I update dynamically. I add elements to both tabs one by one. I first completely add elements to first tab(Cards tab), after finishing that I add elements to second tab(Favorite card tab). My problem is when I add elements to the second tab, my RecyclerView inside first tab also get updated. Now the elements in first tab RecyclerView is the sum of existing elements plus the elements I add in the second tab. I set two different ArrayList in both the ViewPager tab. But when getItemCount() called in first tab RecyclerView, the ArrayList has the element that I added in the second tab.
I am using the same fragment with a RecyclerView in both tabs. When I want to update a single tab, I call notifyDatasetChanged() on the ViewPager. So each time RecyclerView inside the fragment is reinitialized.
This is my Viewpager adapter
public class ViewPagerWebCardsAdapter extends FragmentStatePagerAdapter {
private ArrayList<WebCardBaseResponseModel> webCardDetailList;
private ArrayList<WebCardBaseResponseModel> followingWebCardList;
private Context context;
private boolean isEmptyFavoriteWebcardList;
private boolean isEmptyWebcardsList;
public ViewPagerWebCardsAdapter(FragmentManager fm, ArrayList<WebCardBaseResponseModel> webCardDetailList,
ArrayList<WebCardBaseResponseModel> followingWebCardList,
Context context) {
super(fm);
this.webCardDetailList = webCardDetailList;
this.followingWebCardList = followingWebCardList;
this.context = context;
}
public void updateWebCardLists( ArrayList<WebCardBaseResponseModel> webCardDetailList,
ArrayList<WebCardBaseResponseModel> followingWebCardList) {
this.webCardDetailList = webCardDetailList;
this.followingWebCardList = followingWebCardList;
notifyDataSetChanged();
}
public void setEmptyFollowingCardsFragment() {
isEmptyFavoriteWebcardList = true;
notifyDataSetChanged();
}
public void setEmptyWebCardsFragment() {
isEmptyWebcardsList = true;
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
if(position == 0) {
WebCardListFragment webCardListFragment = new WebCardListFragment();
webCardListFragment.setWebCardDetailList(webCardDetailList);
webCardListFragment.setEmptyListFragment(isEmptyWebcardsList);
return webCardListFragment;
} else {
WebCardListFragment webCardListFragment = new WebCardListFragment();
webCardListFragment.setWebCardDetailList(followingWebCardList);
webCardListFragment.setEmptyListFragment(isEmptyFavoriteWebcardList);
webCardListFragment.setAsFavoritesList();
return webCardListFragment;
}
}
#Override
public int getCount() {
return Constants.NUMBER_TWO;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
if(position == 0) {
return context.getString(R.string.webcards);
} else {
return context.getString(R.string.favorite);
}
}
}
This is my Card fragment
public class WebCardListFragment extends Fragment {
#BindView(R.id.web_card_recycler_view)
RecyclerView mWebCardDetailRecyclerView;
#BindView(R.id.tv_description)
TextView tvDescription;
#BindView(R.id.progress_bar)
public ProgressBar progressBar;
private WebCardListAdapter mWebCardListAdapter;
ArrayList<WebCardBaseResponseModel> mWebCardDetailList;
WebCardActivity activity;
private boolean isFavorites;
private boolean isEmptyListFragment;
#Override
public void onAttach(Context context) {
super.onAttach(context);
activity = (WebCardActivity) context;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void setWebCardDetailList(ArrayList<WebCardBaseResponseModel> mWebCardDetailList) {
this.mWebCardDetailList = mWebCardDetailList;
}
public void setAsFavoritesList() {
this.isFavorites = true;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_webcard_list, container, false);
ButterKnife.bind(this, view);
initView();
return view;
}
private void initView() {
if(isFavorites) {
tvDescription.setText(getString(R.string.select_webcard_to_share));
}
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(activity);
mWebCardDetailRecyclerView.setLayoutManager(mLayoutManager);
mWebCardDetailRecyclerView.setHasFixedSize(true);
mWebCardDetailRecyclerView.setItemAnimator(new DefaultItemAnimator());
if(mWebCardDetailList != null && mWebCardDetailList.size() > 0) {
progressBar.setVisibility(View.GONE);
tvDescription.setVisibility(View.VISIBLE);
mWebCardListAdapter = new WebCardListAdapter(activity, mWebCardDetailList, isFavorites);
}
if(isEmptyListFragment) {
progressBar.setVisibility(View.GONE);
tvDescription.setVisibility(View.VISIBLE);
tvDescription.setText(getString(R.string.no_webcards_to_list));
}
mWebCardDetailRecyclerView.setAdapter(mWebCardListAdapter);
}
public void setEmptyListFragment(boolean isEmptyList) {
isEmptyListFragment = isEmptyList;
}
}
Finally this is my RecyclerViewadapter I use inside the fragment
public class WebCardListAdapter extends RecyclerView.Adapter<WebCardListAdapter.ViewHolder> {
private Context mContext;
private ArrayList<WebCardBaseResponseModel> mWebCardDetailList;
private boolean isFavorites;
public WebCardListAdapter(Context context, ArrayList<WebCardBaseResponseModel> webCardDetailList, boolean isFavorites) {
mContext = context;
mWebCardDetailList = webCardDetailList;
this.isFavorites = isFavorites;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_webcard_detail_layout, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
setListItemView(holder, position);
setListItemClickListener(holder);
}
#Override
public int getItemCount() {
return mWebCardDetailList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
class ViewHolder extends RecyclerView.ViewHolder {
private TextView mCardNameTextView;
private LinearLayout mWeCardLinearLayout;
#BindView(R.id.tv_phone)
TextView phone;
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
mCardNameTextView = (TextView) itemView.findViewById(R.id.webcard_name_textview);
mWeCardLinearLayout = (LinearLayout) itemView.findViewById(R.id.wed_card_title_linear_layout);
}
}
}
I want the ListView inside both tabs to be independent. When I add elements to the second tab RecyclerView, that should not reflect in the first tab RecyclerView.
The Basic difference
FragmentPagerAdapter
Good for a limited (fixed) number of items (Fragments). Why? Because it never removes a fragment instance from FragmentManager once it’s created (unless that Activity is finished). It only detaches the Views from Fragments which are currently not visible. onDestroyView() will be called on your Fragment once it’s out of reach and later onCreateView() will be called once you go back to this Fragment.
FragmentStatePagerAdapter
A FragmentStatePagerAdapter is more memory savvy. It completely removes Fragment instances from the FragmentManager once they are out of reach. The state of the removed Fragments is stored inside the FragmentStatePagerAdapter. The Fragment instance is recreated once you return back to an existing item and the state is restored. This adapter is suitable for lists with an unknown count or for lists where the items change a lot.
So FragmentPagerAdapter will solve this
I call notifyDatasetChanged() on the view pager. So each time recycler view inside the fragment is reinitialised.
Remove the override function "getItemPosition" from vew pager adapter class. Hope it will help.
Thanks for the answers, anyway this issue has got nothing to do with Recyclerview or Viewpager, it happened because of my misunderstanding on the variable reference. I change the ArrayList somewhere else inside the activity. That affects the list in recyclerview.

Implententing a swipe view using a Fragment

I am designing a constitution app and I want to use a tabbed layout with swipe view. The tabs get data from the database using a custom adapter. Since the data size (no of fragment) is unknown, I want every swipe to generate a new view which are the different chapter content from the Constitution.
I want something that looks like the dictionary app below, with those swipe labels on both sides. I am familiar with tabs but I would love to get a resource to help me achieve this, since most documentation I have seen doesn't explain this. Thanks
Modify this with your desired OutPut
onCreate
ArrayList<McqQuestionBean> mcqQuestionBeans= new ArrayList<McqQuestionBean>();
adapter = new NewsFragmentPagerAdapter(getSupportFragmentManager(),
mcqQuestionBeans, MCQTestActivity.this);
pager.setAdapter(adapter);
Base Adapter
public class NewsFragmentPagerAdapter extends FragmentStatePagerAdapter {
private ArrayList<McqQuestionBean> mcqQuestionBeans;
private McqQuestionFragment fragment;
private Activity context;
public NewsFragmentPagerAdapter(FragmentManager fm, ArrayList<McqQuestionBean> mcqQuestionBeans, Activity context) {
super(fm);
this.mcqQuestionBeans = mcqQuestionBeans;
this.context = context;
}
public void update(ArrayList<McqQuestionBean> mcqQuestionBeans) {
this.mcqQuestionBeans = mcqQuestionBeans;
notifyDataSetChanged();
}
#Override
public int getCount() {
return mcqQuestionBeans.size();
}
#Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return super.getItemPosition(object);
}
#Override
public Fragment getItem(int position) {
fragment = McqQuestionFragment.newInstance(mcqQuestionBeans.get(position), position, context);
return fragment;
}
}
Your Fragment McqQuestionFragment
public class McqQuestionFragment extends Fragment {
private int position, porrefid;
private String question;
private ArrayList<McqQuestionChoiceBean> choices;
#SuppressWarnings("unchecked")
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
position = getArguments().getInt("position");
porrefid = getArguments().getInt("porrefid");
userMarkedOn = getArguments().getInt("userMarkedOn");
question = getArguments().getString("question");
choices = (ArrayList<McqQuestionChoiceBean>) getArguments()
.getSerializable("choices");
}
public static McqQuestionFragment newInstance(
McqQuestionBean mcqQuestionBean, int position, Activity activity) {
final McqQuestionFragment f = new McqQuestionFragment();
final Bundle args = new Bundle();
args.putString("question", mcqQuestionBean.getQuestion());
args.putInt("position", position);
args.putInt("userMarkedOn", mcqQuestionBean.getUserCorrectedOn());
args.putSerializable("choices", mcqQuestionBean.getChoices());
args.putInt("porrefid", mcqQuestionBean.getPorrefid());
f.setArguments(args);
return f;
}
}

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.

Android Fragments Not Updating

I am trying to create a very simple Android application that uses a FragmentPagerAdapter to swipe between three fragments. Each of the fragments contains a single EditText and has the exact same code.
The desired behavior is that when the user updates the EditText, the new value is saved to the application instance. Then, once a new fragment is selected, that new fragment should show the saved value. For some reason this is not working.
I also want the focused fragment to show the saved data when the application resumes (comes back from background). This too does not work.
I am really confused as to why something as simple as this is so difficult!
Here is my code so far:
StackOverflowDemoApplication.java:
public class StackOverflowDemoApplication extends Application {
private ApplicationData applicationData;
// the index of the last fragment that was displayed
private int lastItem = 0;
#Override
public void onCreate() {
applicationData = new ApplicationData();
}
public ApplicationData getApplicationData() {
return applicationData;
}
public int getLastItem() {
return lastItem;
}
public void setLastItem(int lastItem) {
this.lastItem = lastItem;
}
}
MainActivity.java
public class MainActivity extends ActionBarActivity implements ActionBar.TabListener {
private static final String TAG = "MainActivity";
// the application instance
private StackOverflowDemoApplication application;
// the pager adapter
private SectionsPagerAdapter pagerAdapter;
// the view pager
private ViewPager viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Save the application instance.
application = (StackOverflowDemoApplication) getApplication();
// Set up the action bar.
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
pagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(pagerAdapter);
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < pagerAdapter.getCount(); i++) {
actionBar.addTab(
actionBar.newTab()
.setText(pagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
viewPager.setCurrentItem(tab.getPosition());
pagerAdapter.notifyDataSetChanged();
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private int NUM_ITEMS = 3;
public SectionsPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
public Fragment getItem(int position) {
// get the application data instance
ApplicationData data = application.getApplicationData();
switch (position) {
case 0:
return SecondFragment.newInstance(data);
case 1:
return FirstFragment.newInstance(data);
case 2:
return ThirdFragment.newInstance(data);
default:
return null;
}
}
#Override
public int getCount() {
return NUM_ITEMS;
}
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "FIRST FRAGMENT";
case 1:
return "SECOND FRAGMENT";
case 2:
return "THIRD FRAGMENT";
default:
return null;
}
}
}
#Override
public void onResume() {
// load the previous fragment
viewPager.setCurrentItem(application.getLastItem());
super.onResume();
}
#Override
public void onPause() {
// save the last fragment we used
application.setLastItem(viewPager.getCurrentItem());
super.onPause();
}
}
FirstFragment.java
public class FirstFragment extends Fragment {
private static final String TAG = "FirstFragment";
// the activity reference
private Activity activity;
// the application data
private ApplicationData data;
// the edit text
private EditText editText;
// are we currently loading data for this fragment?
private boolean loadingData = false;
public FirstFragment(ApplicationData data) {
super();
this.data = data;
}
public static FirstFragment newInstance(ApplicationData data) {
Log.e(TAG, "New instance called");
FirstFragment fragment = new FirstFragment(data);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.e(TAG, "Creating FirstFragment view");
// inflate the view
View view = inflater.inflate(R.layout.first_fragment_layout, container, false);
// get the activity instance
activity = getActivity();
// the textview
editText = (EditText) view.findViewById(R.id.textView);
editText.addTextChangedListener(textWatcher);
// update the ui from the data
updateUIFromData();
return view;
}
public void updateUIFromData() {
// we have started loading the data
loadingData = true;
// if there is data
if (null != data) {
// set the value
if (null != data.getStringValue()) {
editText.setText(data.getStringValue());
}
}
// done loading the data
loadingData = false;
}
private void updateDataFromUi() {
data.setStringValue(editText.getText().toString());
}
private TextWatcher textWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// if we are not loading data
if (!loadingData) {
// update the data from the ui
updateDataFromUi();
}
}
#Override
public void afterTextChanged(Editable s) {
}
};
}
Try to update view when fragment get visible to user as follows:
public class MyFragment extends Fragment
#Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (visible) {
**//Get new data and update views here**
}
}
Check this tutorial, it might help you:
http://www.androidhive.info/2013/10/android-tab-layout-with-swipeable-views-1/
You don't have to call notifyDataSetChanged() inside tabSelected callback.
You should update your fragment datas in onResumer() like TextView or EditText ... etc. See example:
Oncreate....
TextView mtextview = rootview.findViewById....
#Override
public void onResume() {
if(do some thing){
mtextview.setText("It did something");
}
super.onResume();
}

How to Remove Fragment from FragmentPagerAdapter?

I know there are some topics about this here already but I could not find a solution which I could get to work for my case.
I have a working sliding gallery using a custom FragmentActivity and FragmentPagerAdapter which holds a list of Fragments.
Within the FragmentActivity is a ImageView "delete". If clicked, the function deleteMedia() is called which then should remove the current Fragment and the following Fragment should be displayed.
How would I have to do that in my example?
FragmentActivity:
public class GalleryPagerActivity extends FragmentActivity implements OnClickListener {
private Intent intent;
private SharedPreferences settings;
private PagerAdapter mPagerAdapter;
private ViewPager mPager;
private List<Fragment> fragments;
private List<WhiteboardMedia> wiList;
private int selectedPosition;
private LinearLayout llTop;
private TextView tvTop;
private ImageView delete;
private ImageView share;
private TextView tvCounter;
private TextView tvFilename;
private TextView tvFilesize;
private TextView tvDate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
super.setContentView(R.layout.gallery_pager);
intent = getIntent();
Type collectionType = new TypeToken<List<WhiteboardMedia>>(){}.getType();
wiList = gson.fromJson(intent.getStringExtra("wiList"), collectionType);
selectedPosition = intent.getIntExtra("position", 1);
llTop = (LinearLayout) findViewById(R.id.llTop);
llTop.setOnClickListener(this);
tvTop = (TextView) findViewById(R.id.tvTop);
tvTop.setOnClickListener(this);
delete = (ImageView) findViewById(R.id.imgDelete);
delete.setOnClickListener(this);
share = (ImageView) findViewById(R.id.imgShare);
share.setOnClickListener(this);
tvCounter = (TextView) findViewById(R.id.tvCounter);
tvFilename = (TextView) findViewById(R.id.tvFilename);
tvFilesize = (TextView) findViewById(R.id.tvFilesize);
tvDate = (TextView) findViewById(R.id.tvDate);
createContextMenu();
initDropbox();
} catch (Exception e) {
Log.e("GalleryPagerActivity", e.getLocalizedMessage());
}
}
/**
* Initialise the pager
*/
private void initialisePager() {
mPager = (ViewPager) super.findViewById(R.id.viewpager);
mPager.setAdapter(this.mPagerAdapter);
mPager.setOnPageChangeListener(new GalleryPageListener(tvCounter, tvFilename, tvFilesize, tvDate, wiList));
mPager.setCurrentItem(selectedPosition, true);
updatePage(selectedPosition);
}
public void updatePage(int position)
{
int focusedPage = position + 1;
Log.i("onPageSelected", "page selected " + position);
WhiteboardMedia wiImage = wiList.get(position);
String imageDate = "N/A";
try {
Date dateTaken= new Date(); //wiImage.getDate();
SimpleDateFormat sdf = new SimpleDateFormat("yy/MM/dd");
imageDate = sdf.format(dateTaken);
} catch (Exception e) {
}
try {
tvCounter.setText(focusedPage + "/" + wiList.size());
tvFilename.setText(wiImage.getFilename());
tvFilesize.setText(wiImage.getSize() + "a");
tvDate.setText(imageDate);
} catch (Exception e) {
}
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public void onDestroy() {
super.onDestroy();
}
private WhiteboardMedia getActiveWhiteboardImage() {
return wiList.get(mPager.getCurrentItem());
}
private final int DELETE = 1;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(1, DELETE, 2, R.string.delete).setIcon(R.drawable.menu_btn_trash);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case DELETE:
deleteMedia();
return true;
}
return super.onContextItemSelected(item);
}
#Override
public void onClick(View v) {
if (v == delete) {
deleteMedia();
}
}
private void deleteMedia() {
// TODO delete the active Fragment and display the next Fragment in the list
}
/******************************************************************************
* Context Menu
*****************************************************************************/
private void createContextMenu() {
// context menu stuff
}
#Override
protected Dialog onCreateDialog(int id) {
// stuff
}
}
FragmentPagerAdapter:
public class GalleryPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragments;
public GalleryPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
}
Thanks for help!
that is the solution I'm using:
mViewPager : is the view you are using to set you Fragment
mViewPager = (YourViewPager) findViewById(R.id.myPager);
TABLE : is just a Integer list of the position of all my Fragments
public void destroyAllItem() {
int mPosition = mViewPager.getCurrentItem();
int mPositionMax = mViewPager.getCurrentItem()+1;
if (TABLE.size() > 0 && mPosition < TABLE.size()) {
if (mPosition > 0) {
mPosition--;
}
for (int i = mPosition; i < mPositionMax; i++) {
try {
Object objectobject = this.instantiateItem(mViewPager, TABLE.get(i).intValue());
if (objectobject != null)
destroyItem(mViewPager, TABLE.get(i).intValue(), objectobject);
} catch (Exception e) {
Log.i(TAG, "no more Fragment in FragmentPagerAdapter");
}
}
}
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
if (position <= getCount()) {
FragmentManager manager = ((Fragment) object).getFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove((Fragment) object);
trans.commit();
}
}
First, I suggest that you consider altering your FragmentPagerAdapter, to look more like the sample. You normally do not hold a list of fragments, any more than an ArrayAdapter normally holds a list of Views for the rows. Normally, you create the fragments on demand, and somebody else holds the list.
Then, to delete something, delete it from your model data (what the FragmentPagerAdapter normally wraps). Make sure that getCount() will then return the right number of items. Then, call notifyDataSetChanged() on the FragmentPagerAdapter, which should trigger a redraw of the ViewPager.
I found a solution ovverriding the method "onPostResume()" of the activity and calling the notifyDataSetChanged inside that.
#Override
protected void onPostResume() {
super.onPostResume();
if(this.mCustomPagerAdapter!=null){
this.mCustomPagerAdapter.notifyDataSetChanged();
}
}
If you are using FragmentPagerAdapter for adding and removing fragments at random position(not always at the end) dynamically, there is a method you need to taken more attention which is getItemId. By default, FragmentPagerAdapter uses position combines viewId as the tag name for fragments, however the position changes if you add or remove fragments. As a result, you may get an empty page because the position you are adding is occupied by an existing fragment. To solved this problem, override getItemId.
#Override
public long getItemId(int position) {
long itemId = ...; //Provide your unique ID here according to you logic
return itemId;
}
In my case, when i try to remove one item from the adapter, i will do as follow:
// get the position of item to remove
int position = getBinding().vp.getCurrentItem();
// remove the item from adapter
adapter.removeItem(position);
adapter.notifyDataSetChanged();
// minus one from the count
totalInvoice--;
updateTitle(getBinding().vp.getCurrentItem());
if (totalInvoice == 0) {
finish();
}
// set the adapter to view pager again
getBinding().vp.setAdapter(adapter);
// smooth scroll to given position
getBinding().vp.setCurrentItem(position);
The reason that i did above is that i find that even though you removed one from the data list, but the view of fragment still exist. So you have to let the view pager instantiate the view of given position. The answer above which trying to remove all fragments doesn't work for me. So, I find out the poor way of setting adapter to view pager again.

Categories

Resources