Android Studio - Update RecyclerView from Dialog Fragment in Menu Item - android

Hey guys I really need your help. I've spent like 5 days trying to get my recyclerview to update only when the user presses OK on a dialogbox, that appears from the menu actionbar. I've tried every possible method I could think of, every method I've seen on stackoverflow, YouTube, etc. and none of them worked for me.
How do I get the recyclerview in a fragment to update after dialogbox closes? I know there are similar questions regarding updating the menu, and (recyclerviews with dialogfragments), but none of them have a combination.
Out of the countless attempts, the current code configuration posted below isn't causing any errors, however, the recyclerview remains blank. The closest attempt I had to finding a solution, was creating an adapter and setting up the recycler in onOptionsItemSelected. But obviously, it updates only when the user clicks the button, and the initial click would create a blank recyclerview.
Fragment:
(There's a lot of repeated commented code from different attempts)
public class ExerciseRoutine extends Fragment implements ExerciseRoutine_Dialog.RoutineDialogListener{
private String Routine_name, Routine_split;
private ArrayList<ExerciseRoutine_Information> Routine_information = new ArrayList<>();
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
#Override
public void sendInput(String name, String split, RecyclerView.Adapter DialogAdapter) {
Routine_name = name;
Routine_split = split;
adapter = DialogAdapter;
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_fragment, container, false);
//Report that this fragment would like to participate in populating the options menu by
//receiving a call to onCreateOptionsMenu(Menu, MenuInflater) and related methods.
//If true, the fragment has menu items to contribute.
setHasOptionsMenu(true);
recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
//BuildRecyclerView();
//recyclerView.setHasFixedSize(true); //If the Recyclerview is static
/*Routine_information.add(new ExerciseRoutine_Information(Routine_name, Routine_split));
recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
//recyclerView.setHasFixedSize(true); //If the Recyclerview is static
layoutManager = new LinearLayoutManager(getActivity());
adapter = new ExerciseRoutineAdapter(Routine_information);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);*/
return view;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.exercise_routine_menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.action_addRoutine:
ExerciseRoutine_Dialog routineDialog = new ExerciseRoutine_Dialog();
routineDialog.setTargetFragment(ExerciseRoutine.this, 1);
routineDialog.show(getFragmentManager(), "Routine Dialog");
//Routine_information.add(new ExerciseRoutine_Information(Routine_name, Routine_split));
BuildRecyclerView();
//adapter.notifyItemInserted(Routine_information.size());
//if(!Routine_name.equals("") && !Routine_split.equals("")) {
//}
}
return super.onOptionsItemSelected(item);
}
public void BuildRecyclerView(){
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
}
public void BuildAdapter(){
//adapter = new ExerciseRoutineAdapter(getContext(),Routine_information);
adapter.notifyItemInserted(Routine_information.size());
}
}
My Dialog Fragment:
public class ExerciseRoutine_Dialog extends DialogFragment{
private TextView ActionOK, ActionCANCEL;
private EditText Routine_name, Routine_split;
private RoutineDialogListener activityCommander;
private ArrayList<ExerciseRoutine_Information> Routine_information = new ArrayList<>();
private RecyclerView.Adapter adapter;
public interface RoutineDialogListener{
void sendInput(String name, String split, RecyclerView.Adapter DialogAdapter);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
activityCommander = (RoutineDialogListener) getTargetFragment();
}catch(ClassCastException e){
throw new ClassCastException(context.toString() + "Must Implement RoutineDialogListener");
}
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_dialog, container, false);
Routine_name = view.findViewById(R.id.ExerciseRoutine_DialogNameInput);
Routine_split = view.findViewById(R.id.ExerciseRoutine_DialogSplitInput);
ActionOK = view.findViewById(R.id.ExerciseRoutine_DialogAction_OK);
ActionCANCEL = view.findViewById(R.id.ExerciseRoutine_DialogAction_CANCEL);
//recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
ActionCANCEL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getDialog().dismiss();
}
});
ActionOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String name = Routine_name.getText().toString();
String split = Routine_split.getText().toString();
if(!name.equals("") && !split.equals("")) {
Routine_information.add(new ExerciseRoutine_Information(name, split));
adapter = new ExerciseRoutineAdapter(getContext(), Routine_information);
activityCommander.sendInput(name, split, adapter);
adapter.notifyItemInserted(Routine_information.size());
}
getDialog().dismiss();
}
});
return view;
}
}

Your current approach seems to be to pass the RecyclerView.Adapter to the DialogFragment and try to insert the new data on-the-spot. I think this is a problematic setup. The dialog's purpose is to offer some means for the users to enter the required data, period. It should not be tasked with the job of managing the RecyclerView or its Adapter because that way your components will be very tightly coupled:
Imagine that first you use a ListView in your implementation, and suddenly someone decides to ban every ListView from your app (maybe for performance reasons) and has you exchange them all for RecyclerViews. Then your approach would force you to change the code for the DialogFragment (it would have to cater to a different type of Adapter). A more loosely coupled implementation would enable you to make changes to one component without having to rewrite too many others.
That's why I won't try to make your code work as-is, instead I'd like to show you another way:
Because the RecyclerView is part of the Fragment's UI, the Fragment is the place where code related to managing the RecyclerView belongs. It is basically possible to have the Adapter as an inner class of the Fragment but I prefer having it as a standalone class if the code gets a little longer, and also because it enforces "decoupling".
Interfaces play a very important part in good architecture, so the DialogFragment will still make use of an interface to send its data. But it's up to the class which actually implements the interface (here: the Fragment) to pass the data to any interested third parties, e.g. the RecyclerView.Adapter (The Adapter in turn could have its own interface to publish important events like clicks on list items).
Having said that, here are some code snippets:
The DialogFragment
public class ExerciseRoutine_Dialog extends DialogFragment {
private EditText Routine_name, Routine_split;
public interface RoutineDialogListener{
/**
* There is some new ExerciseRoutine_Information
*/
void sendInput(String name, String split);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_dialog, container, false);
Routine_name = view.findViewById(R.id.ExerciseRoutine_DialogNameInput);
Routine_split = view.findViewById(R.id.ExerciseRoutine_DialogSplitInput);
TextView actionOK = view.findViewById(R.id.ExerciseRoutine_DialogAction_OK);
TextView actionCANCEL = view.findViewById(R.id.ExerciseRoutine_DialogAction_CANCEL);
actionCANCEL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getDialog().dismiss();
}
});
actionOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String name = Routine_name.getText().toString();
String split = Routine_split.getText().toString();
if(!name.equals("") && !split.equals("")) {
// just send the input to the main Fragment
RoutineDialogListener listener = getListener();
if(listener != null) {
listener.sendInput(name, split);
}
}
getDialog().dismiss();
}
});
return view;
}
/**
* Tries to find a suitable listener, examining first the hosting Fragment (if any) and then the Activity.
* Will return null if this fails
* #return x
*/
private RoutineDialogListener getListener(){
RoutineDialogListener listener;
try{
Fragment onInputSelected_Fragment = getTargetFragment();
if (onInputSelected_Fragment != null){
listener = (RoutineDialogListener) onInputSelected_Fragment;
}
else {
Activity onInputSelected_Activity = getActivity();
listener = (RoutineDialogListener) onInputSelected_Activity;
}
return listener;
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
return null;
}
}
The Fragment:
public class ExerciseRoutine extends Fragment implements ExerciseRoutine_Dialog.RoutineDialogListener{
public static final String ROUTINE_DIALOG = "Routine Dialog";
private ArrayList<ExerciseRoutine_Information> routineInformations = new ArrayList<>();
private RecyclerView.Adapter adapter;
public static ExerciseRoutine instance(){
return new ExerciseRoutine();
}
#Override
public void sendInput(String name, String split) {
routineInformations.add(new ExerciseRoutine_Information(name, split));
adapter.notifyItemInserted(routineInformations.size());
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_fragment, container, false);
setHasOptionsMenu(true);
RecyclerView recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter = new ExerciseRoutineAdapter(getContext(), routineInformations);
// So far you have a RecyclerView with an empty List.
recyclerView.setAdapter(adapter);
return view;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.exercise_routine_menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.action_addRoutine:
showDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
private void showDialog(){
ExerciseRoutine_Dialog routineDialog = new ExerciseRoutine_Dialog();
routineDialog.setTargetFragment(ExerciseRoutine.this, 1);
routineDialog.show(getFragmentManager(), ROUTINE_DIALOG);
}
}

Related

Android: Recyclerview in Bottom Navigation Bar Fragment populated with SQLite will not update

I have a BottomNavigationBar with 3 fragments. In the first fragment, I try to put SQLite data into a recyclerview. It works fine except for the fact that I need to switch between the Navigation Bar items in order to see the refreshed recyclerview. When I use a handler with postDelayed however, it does show the refreshed recyclerview if I set around 1 sec of delay. 0.2 secs wont work already.
Even though this is still very generic: is there any best practice for this? It seems to me that I need to use AsyncTask which has been -however- deprecated.
Thanks!
Simon
HomeFragment
public class HomeFragment extends Fragment {
private HomeViewModel homeViewModel;
private Context context;
private CardView cardview;
private LinearLayout.LayoutParams layoutparams;
private TextView textview;
private RelativeLayout relativeLayout;
private myDbAdapter helper;
RecyclerView myView;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
View root = inflater.inflate(R.layout.fragment_home, container, false);
helper = new myDbAdapter(getContext());
myView = (RecyclerView) root.findViewById(R.id.recyclerview_home);
RecyclerViewAdapter3 adapter = new RecyclerViewAdapter3(new ArrayList<String>(Arrays.asList(helper.classes())));
myView.setHasFixedSize(true);
myView.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
myView.setLayoutManager(llm);
homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
#Override
public void onChanged(#Nullable String s) {
textView.setText(s);
}
});
return root;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
public void refresh(View v){
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
myView = (RecyclerView) v.findViewById(R.id.recyclerview_home);
helper = new myDbAdapter(v.getContext());
ArrayList<String> classes = new ArrayList(Arrays.asList(helper.classes()));
ArrayList<String> subClasses = new ArrayList(Arrays.asList(helper.subClasses()));
RecyclerViewAdapter3 adapter = new RecyclerViewAdapter3(classes);
myView.setHasFixedSize(true);
myView.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(v.getContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
myView.setLayoutManager(llm);
}
}, 1000); //time in millis
}
}
RecyclerViewAdapter3
public class RecyclerViewAdapter3 extends RecyclerView.Adapter<RecyclerViewAdapter3.MyViewHolder> {
public ArrayList<String> classArrayList;
public ArrayList<String> subClassArrayList;
myDbAdapter helper;
public RecyclerViewAdapter3(ArrayList<String> classArrayList){
this.classArrayList = classArrayList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View listItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview, parent, false);
return new MyViewHolder(listItem);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.class.setText(classArrayList.get(position));
holder.delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
helper = new myDbAdapter(v.getContext());
helper.delete(classArrayList.get(position));
HomeFragment homeFragment = new HomeFragment();
homeFragment.refresh(v.getRootView());
}
});
holder.selectButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}});}
#Override
public int getItemCount() {
return classArrayList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
private TextView class;
private Button selectButton;
private ImageView delete;
public MyViewHolder(View itemView) {
super(itemView);
class = (TextView)itemView.findViewById(R.id.name);
selectButton = (Button) itemView.findViewById(R.id.selectButton);
delete = (ImageView) itemView.findViewById(R.id.delete);
}
}
}
Thanks for posting your code :)
There are a fair few things that can go wrong in your code as it is right now, and I can't really pinpoint what causes it to work when you use postDelay. I'm going to list a few, which you can look into:
From your onClick() inside your ViewHolder
HomeFragment homeFragment = new HomeFragment();
homeFragment.refresh(v.getRootView());
You should really not instantiate your fragments like this. You can instead pass a callback from your fragment to your adapter (eg.: View.OnClickListener)
You keep re-instantiating your adapter and your helper needlessly. You should create your adapter only once, set it as your recycler view adapter, and save it in a member variable.
Proposed solution
I see that you're already using ViewModel, so you're on a great path for a less error-prone screen, so I suggest that you move your db query-ing logic to your view model. If you're using raw SQLite (instead of Room), you can extend AndroidViewModel, so you'll have access to a context right away. And as you do with your homeViewModel.getText(), you should expose the classes array as live data, observe it, and submit the new list to your adapter.
For submitting your list to your adapter I suggest using ListAdapter, this will provide you a submitList method for submitting the list in the fragment, and inside the adapter, you will have a getItem(int position) method, which you can query inside the onBindViewHolder method.
Inside your fragment, it'll look something like this:
ClassAdapter adapter = null;
View onCreateView(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
View root = inflater.inflate(R.layout.fragment_home, container, false);
adapter = new ClassAdapter(
new ClassDeleteCallback() {
#Override
void onClassDeleted(Class class) {
// inside view model we can modify db state, than refresh live data
viewModel.deleteClass(class);
}
},
new ClassSelectedCallback() {
// follows same pattern of above
}
);
RecyclerView rv = root.findViewById(R.id.my_rv);
rv.setAdapter(adapter);
rv.setLayoutManager(new LinearLayoutManager(getContext());
homeViewModel.getClasses().observe(getViewLifecycleOwner(), new Observer<List<Class>>() {
#Override
public void onChanged(#Nullable List<Class> classes) {
adapter.submitList(classes);
}
});
homeViewModel.refreshClasses();
return root;
}
I can highly recommend for you to study this project a bit, because it covers lot of the basics which can lead to a much stabler app: Sunflower sample app
I think you should read a bit more about the architecture components, and then go through some code-labs and stuff, and have another go with this screen starting from square one, because it will be easier than fixing the current state :)
I hope this was helpful, and not too discouraging!

Android - RecyclerView multiplies itself every time device is rotated

I have a landscape configuration for one of my app's activities. This activity contains a fragment and this fragment contains one textview and one recyclerview. Everytime when i switch in between portrait and landscape modes, the recyclerview leaves the view of itself like however it was before i turned the device. It might be a little difficult to understand what i try to ask here, so i recorded a gif for that.
https://giphy.com/gifs/3Wv7NAtT8ezP1SQhDu
This is my activity
public class RecipeStepsActivity extends AppCompatActivity {
static Recipe recipe;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recipe_steps);
if (StepDetailActivity.SDA_TAG.equals(StepDetailActivity.NEGATIVE))
recipe = getIntent().getParcelableExtra("recipe");
Bundle b = new Bundle();
b.putParcelable("recipe", recipe);
ActionBar ab = getSupportActionBar();
if (ab != null)
ab.setTitle(recipe.getName());
RecipeStepsFragment recipeStepsFragment = new RecipeStepsFragment();
recipeStepsFragment.setArguments(b);
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().add(R.id.frame_layout_steps, recipeStepsFragment).commit();
}
}
This is my fragment
public class RecipeStepsFragment extends Fragment {
#BindView(R.id.recipe_steps_rv)
RecyclerView recyclerView;
#BindView(R.id.ingredients_tv)
TextView tv_ingredients;
List<Step> steps;
public RecipeStepsFragment(){}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recipe_steps, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
List<Ingredients> ingredients;
Recipe recipe = getArguments().getParcelable("recipe");
steps = recipe.getSteps();
initRecyclerView();
ingredients = recipe.getIngredients();
String ingredientsAppended = "INGREDIENTS" + "\n\n";
if (ingredients == null){
ingredientsAppended = "Not Available";
} else {
for (int a = 0 ; a < ingredients.size() ; a++) {
Ingredients i = ingredients.get(a);
ingredientsAppended += String.valueOf(i.getQuantity()) + " " +
i.getMeasure() + " " +
i.getIngredient();
if (a+1 != ingredients.size()){
ingredientsAppended += '\n';
}
}
}
tv_ingredients.setText(ingredientsAppended);
if(savedInstanceState != null){
recyclerView.scrollToPosition(savedInstanceState.getInt("position"));
}
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
outState.putInt("position", recyclerView.getVerticalScrollbarPosition());
super.onSaveInstanceState(outState);
}
private void initRecyclerView(){
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
RecipeStepsRecyclerAdapter recipeStepsRecyclerAdapter =
new RecipeStepsRecyclerAdapter(steps, new RecipeStepsRecyclerAdapter.ClickListener() {
#Override
public void onItemClick(int clickedItemPosition) {
Intent intentToStepDetail = new Intent(getActivity(), StepDetailActivity.class);
Step step = steps.get(clickedItemPosition);
intentToStepDetail.putExtra("step", step);
startActivity(intentToStepDetail);
}
}, getContext());
recyclerView.setAdapter(recipeStepsRecyclerAdapter);
recipeStepsRecyclerAdapter.notifyDataSetChanged();
}
}
This is my adapter
public class RecipeStepsRecyclerAdapter extends RecyclerView.Adapter<RecipeStepsRecyclerAdapter.ViewHolder> {
private List<Step> stepList;
private LayoutInflater mInflater;
final private ClickListener clickListener;
public RecipeStepsRecyclerAdapter(List<Step> stepList, ClickListener clickListener, Context context){
this.stepList = stepList;
this.clickListener = clickListener;
mInflater = LayoutInflater.from(context);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recipe_steps_recyclerview_adapter, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Step step = stepList.get(position);
String stepContent = step.getShortDescription();
holder.listingNumber.setText(String.valueOf(position+1));
holder.stepContent.setText(stepContent);
}
#Override
public int getItemCount() {
return stepList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
TextView listingNumber;
TextView stepContent;
private ViewHolder(View itemView) {
super(itemView);
listingNumber = itemView.findViewById(R.id.list_number_tv);
stepContent = itemView.findViewById(R.id.step_content_tv);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
int clickedPosition = getAdapterPosition();
clickListener.onItemClick(clickedPosition);
}
}
public interface ClickListener{
void onItemClick(int clickedItemPosition);
}
}
Layout files are as I already explained above. I guess nothing special to post here.
Thanks in advance.
The problem is that you add a new fragment to the Activity every time it is created. Here's the end of your onCreate(...):
RecipeStepsFragment recipeStepsFragment = new RecipeStepsFragment();
recipeStepsFragment.setArguments(b);
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().add(R.id.frame_layout_steps, recipeStepsFragment).commit();
The FragmentManager keeps a reference to the fragment you add to it even if the host Activity is destroyed. Thus, you keep adding new instances of the RecipeStepsFragment eventually overlaying each other and producing the seen behavior.
Don't worry, the fix is pretty simple: use replace(...) instead of add(...):
fm.beginTransaction().replace(R.id.frame_layout_steps, recipeStepsFragment).commit();
P.S.: Note, however, that replacing the current fragment with a new one every time the host activity is destroyed is not a good idea. You should check either if the savedInstanceState is null (which indicates that it's a non-recreated Activity) or if the given fragment is already added (define a tag when adding the fragment and try finding it in onCreate(...) before replacing the old one).
Every time you change the rotation of the screen your activity is destroyed and recreated. That is the reason why your list is populating with new items each time when you switch between your orientation.
So it's important you only instantiate your objects once, and not keep recreating them each time your app is recreated. Which then adds them to your list.
You can use onSaveInstanceState and onRestoreInstanceState.
If you want your app to allow orientation change, apply logic so your list don't get new items each time when activity is created.
Put configChanges attribute in your activity in menifest file
<activity android:name=".VideoActivity" android:configChanges="keyboardHidden|orientation|screenSize"/>

RecyclerView in single fragment with Firebase data

Application
I am building an Android application wherein order for certain item will be taken from customers. These order can have 4 different status : Pending, Confirmed, Completed and Cancelled. I have written necessary firebase rule for the same.
My initial design was listing up all the orders in single activity, HomeActivity, and everything looked good. But then I decided to change the design to tabs and viewpager for each status of the order in the same HomeActivity. I am using one of the best library, smart tab layout to generate my views.
Current Design
I planned to keep one single Fragment and update the adapter of recyclerview accordingly.
HomeActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
Global.replaceFragmentWithAnimation(new HomeTabFragment(), getSupportFragmentManager(), R.id.frame_container);
}
Global.replaceFragmentWithAnimation is a static method which just replaces the fragment to necessary container.
HomeTabFragment.java
public class HomeTabFragment extends Fragment implements SmartTabLayout.TabProvider {
public HomeTabFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home_tab, container, false);
final ViewPager viewPager = view.findViewById(R.id.viewpager);
final SmartTabLayout viewPagerTab = view.findViewById(R.id.viewpagertab);
viewPagerTab.setCustomTabView(this);
FragmentPagerItems pages = new FragmentPagerItems(getContext());
pages.add(FragmentPagerItem.of("Pending", OrderListsFragment.class));
pages.add(FragmentPagerItem.of("Confirmed", OrderListsFragment.class));
pages.add(FragmentPagerItem.of("Cancelled", OrderListsFragment.class));
pages.add(FragmentPagerItem.of("Completed", OrderListsFragment.class));
FragmentStatePagerItemAdapter adapter = new FragmentStatePagerItemAdapter(
getActivity().getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(0);
viewPagerTab.setViewPager(viewPager);
viewPagerTab.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
}
});
return view;
}
#Override
public View createTabView(ViewGroup container, int position, PagerAdapter adapter) {
LayoutInflater inflater = LayoutInflater.from(container.getContext());
View tab = inflater.inflate(R.layout.custom_tab_icon_and_notification_mark, container, false);
TextView txtTab = tab.findViewById(R.id.txtTitle);
switch (position) {
case 0:
txtTab.setText(getResources().getString(R.string.pending_tab_text));
break;
case 1:
txtTab.setText(getResources().getString(R.string.confirmed_tab_text));
break;
case 2:
txtTab.setText(getResources().getString(R.string.cancelled_tab_text));
break;
case 3:
txtTab.setText(getResources().getString(R.string.completed_tab_text));
break;
default:
throw new IllegalStateException("Invalid pos - " + position);
}
return tab;
}
}
and finally this is my OrderListFragment
OrderListFragment.java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_order_lists, container, false);
recycler_view_order_list = view.findViewById(R.id.recycler_view_order_list); //declared outside
emptyView = view.findViewById(R.id.empty_view);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
recycler_view_order_list.setItemAnimator(new DefaultItemAnimator());
recycler_view_order_list.setLayoutManager(mLayoutManager);
loadOrders();
return view;
}
public void loadOrders() {
if (recycler_view_order_list != null) {
recycler_view_order_list.setHasFixedSize(true);
}
final DatabaseReference orderTableRef = Global.getDatabase().getReference(Constants.ORDERS_TABLE);
final DatabaseReference customerRef = Global.getDatabase().getReference(Constants.CUSTOMERS_TABLE);
orderTableRef.keepSynced(true);
customerRef.keepSynced(true);
final Query orderList = orderTableRef.orderByChild("status").equalTo("Pending");
//I hardcoded Pending value to make sure everything looks good.
Global.adapter = new FirebaseRecyclerAdapter<Order, OrderItemHolder>(
Order.class,
R.layout.order_item_view,
OrderItemHolder.class,
orderList
) {
#Override
protected void populateViewHolder(final OrderItemHolder viewHolder, final Order model, final int position) {
if(model.getCustId()!=null) {
customerRef.child(model.getCustId()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Customers customers = dataSnapshot.getValue(Customers.class);
//set Customer name
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
if ("Pending".equals(model.getStatus())) {
//Some UI updates for Pending
}
if ("Completed".equals(model.getStatus())) {
//Some UI updates for Pending
}
if ("Cancelled".equals(model.getStatus())) {
//Some UI updates for Pending
}
if ("Confirmed".equals(model.getStatus())) {
//Some UI updates for Pending
}
}
}
};
orderTableRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!dataSnapshot.hasChildren())
emptyView.setVisibility(View.VISIBLE);
else
emptyView.setVisibility(View.GONE);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
RecyclerView.AdapterDataObserver mObserver = new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
emptyView.setVisibility(View.GONE);
}
#Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
if (Global.adapter.getItemCount() == 0)
emptyView.setVisibility(View.VISIBLE);
else
emptyView.setVisibility(View.GONE);
}
};
Global.adapter.registerAdapterDataObserver(mObserver);
recycler_view_order_list.setAdapter(Global.adapter);
}
Very long code but I've shortened it as much as possible. I've couple of problems here.
The data never gets displayed even though it is fetched in orderList. when debugged, it never hits populateViewHolder of FirebaseRecyclerAdapter. When I did some research on this issue, All I found was to add RecyclerView.AdapterDataObserver and register it to Global.adapter which I've already implemented. I am still confused as in why the data is not fetched. addListenerForSingleValueEvent for orderTableRef hits anyhow and if (!dataSnapshot.hasChildren()) condition within evaluates to false thus hiding emptyView message. Few suggestions from GitHub issue also stated to remove recycler_view_order_list.setHasFixedSize(true); but it did not help either.
How can I pass different order status for different tabs? I tried doing below within:
HomeTabFragment.java
Bundle bundle=new Bundle();
bundle.putString("orderType","Pending");
pages.add(FragmentPagerItem.of("Pending", OrderListsFragment.class,bundle));
bundle.putString("orderType","Confirmed");
pages.add(FragmentPagerItem.of("Confirmed", OrderListsFragment.class,bundle));
bundle.putString("orderType","Cancelled");
pages.add(FragmentPagerItem.of("Cancelled",OrderListsFragment.class,bundle));
bundle.putString("orderType","Completed");
pages.add(FragmentPagerItem.of("Completed", OrderListsFragment.class,bundle));
Overriding FragmentPagerItems's add method and passing budnle with different value for same key but then it ended up passing last value i.e. Completed during every initialization.
Note - FragmentPagerItems is an utility extension available with the tabs library used.
Could anyone point me in the right direction here?

Pass data between fragments

I have one MainActivity and two fragments. In FragmentA I have a recycler view. If I click on "add" button there, the FragmentB is open. The thing I would like to is to write text into some EditTexts and send data back to FragmentA (and render that data in the recycler view). Could you suggest me something please? Thanks
FragmentB
public class NewContactFragment extends Fragment {
EditText name, number, email;
public String mName;
public String mNumber;
public String mEmail;
boolean isFavourite = false;
public NewContactFragment() {
// Required empty public constructor
}
public static NewContactFragment newInstance() {
NewContactFragment fragment = new NewContactFragment();
Bundle bundle = new Bundle();
fragment.setArguments(bundle);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
setHasOptionsMenu(true);
super.onCreate(savedInstanceState);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
//set title
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.new_contact);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_new_contact, container, false);
name = (EditText) view.findViewById(R.id.ed_name);
number = (EditText) view.findViewById(R.id.ed_number);
email = (EditText) view.findViewById(R.id.ed_email);
mName = name.getText().toString();
mNumber = number.getText().toString();
mEmail = email.getText().toString();
return view;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.new_contact_menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_done:
//TODO: save editTexts and return to ContactListFragment
break;
case R.id.action_favourite:
getActivity().invalidateOptionsMenu();
//Toast.makeText(getContext(), "isFavourite is: " + isFavourite, Toast.LENGTH_SHORT).show();
break;
}
return super.onOptionsItemSelected(item);
}
FragmentA
public class ContactListFragment extends Fragment implements View.OnClickListener {
private static final String TAG = "newcontact";
FloatingActionButton fabButton;
SearchView searchView;
RecyclerView recyclerView;
ContactsAdapter contactsAdapter;
List<Contact> mContact = new ArrayList<>();
public static ContactListFragment newInstance() {
Bundle args = new Bundle();
ContactListFragment fragment = new ContactListFragment();
fragment.setArguments(args);
return fragment;
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contact_list, container, false);
searchView = (SearchView) view.findViewById(R.id.search_view);
fabButton = (FloatingActionButton) view.findViewById(R.id.fab_button);
fabButton.setOnClickListener(this);
recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mContact = SugarRecord.listAll(Contact.class);
contactsAdapter = new ContactsAdapter(getActivity(), mContact);
recyclerView.setAdapter(contactsAdapter);
inputFilter();
return view;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
//show actionBar
((MainActivity) getActivity()).getSupportActionBar().show();
//show title
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.app_name);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
public void inputFilter() {
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
contactsAdapter.filterList(newText);
return true;
}
});
}
#Override
//Fab button listener
public void onClick(View v) {
((MainActivity) getActivity()).showFragment(new NewContactFragment(), TAG);
}
Fragments should generally only communicate with their direct parent activity. Fragments communicate through their parent activity allowing the activity to manage the inputs and outputs of data from that fragment coordinating with other fragments or activities. Think of the Activity as the controller managing all interaction with each of the fragments contained within.
A few exceptions to this are dialog fragments presented from within another fragment or nested child fragments. Both of these cases are situations where a fragment has nested child fragments and that are therefore allowed to communicate upward to their parent (which is a fragment).
The important thing to keep in mind is that fragments should not directly communicate with each other and should generally only communicate with their parent activity. Fragments should be modular, standalone and reusable components. The fragments allow their parent activity to respond to intents and callbacks in most cases.
There are three ways a fragment and an activity can communicate:
Bundle - Activity can construct a fragment and set arguments
Methods - Activity can call methods on a fragment instance
Listener - Fragment can fire listener events on an activity via an interface
In other words, communication should generally follow these principles:
Activities can initialize fragments with data during construction
Activities can pass data to fragments using methods on the fragment instance
Fragments can communicate up to their parent activity using an interface and listeners
Fragments should pass data to other fragments only routed through their parent activity
Fragments can pass data to and from dialog fragments
Fragments can contain nested child fragments
Read more about Fragment and its communication at Creating and Using Fragments

ViewPager fragments are all referencing the same RecyclerView and/or Adapter

I have a Fragment that contains a RecyclerView to display events for a given day. I am using a ViewPager to separate the Fragments into multiple days; A Fragment for Saturday's events and a Fragment for Sunday's events.
However, it appears that both Fragments are referencing the same RecyclerView and/or Adapter, as it is only the last tab (in this case, Sunday) whose events are shown.
In my specific case, Saturday has two events, and Sunday has no events. Both Fragments have empty RecyclerViews. To confirm my theory that it was caused by the last tab, I switched the date. This caused both RecyclerViews to have two events (the ones from Saturday).
Here is the relevant code for the individual Fragments:
public class EventListFragment extends Fragment{
private EventAdapter mEventAdapter;
private static final String DATE_ARG = "eventDate";
public static EventListFragment newInstance(LocalDate date){
EventListFragment eventListFragment = new EventListFragment();
Bundle args = new Bundle();
args.putSerializable(DATE_ARG, date);
eventListFragment.setArguments(args);
return eventListFragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_event_list, container, false);
// Setup recyclerview
RecyclerView eventRecyclerView = (RecyclerView) view.findViewById(R.id.event_recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
eventRecyclerView.setLayoutManager(layoutManager);
// Get date
LocalDate eventDate = (LocalDate) getArguments().getSerializable(DATE_ARG);
// Set adapter
mEventAdapter = new EventAdapter(getActivity(), getEvents(eventDate));
eventRecyclerView.setAdapter(mEventAdapter);
return view;
}
}
getEvents() is just a private function to return events for a given date. I have used the debugger as well as unit tests to verify that it works properly. The debugger shows that it pulls the proper list for each Fragment, but as I explained they are not displayed properly.
Here is the relevant code for the parent Fragment:
public class EventFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_event, container, false);
// Get and set up viewpager
final ViewPager viewPager = (ViewPager) view.findViewById(R.id.event_view_pager);
EventFragmentAdapter eventFragmentAdapter = new EventFragmentAdapter(getFragmentManager(), getEventDates());
viewPager.setAdapter(eventFragmentAdapter);
// Get and set up tablayout
final TabLayout tabLayout = (TabLayout) view.findViewById(R.id.event_tabs);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
}
});
return view;
}
}
Similar to the last one, getEventDates() just pulls the dates that events are taking place. For testing purposes at the moment, I am hard coding a returned list of dates as we don't have our database set up yet. I pulled this method out because I want the app to be able to function again in 2016, which may have different dates:
private List<LocalDate> getEventDates(){
List<LocalDate> eventDates = new ArrayList<>();
eventDates.add(new LocalDate(2015, 10, 17));
eventDates.add(new LocalDate(2015, 10, 18));
return eventDates;
}
The last bit of relevant code is for the FragmentStatePagerAdapter I am using for my ViewPager:
public class EventFragmentAdapter extends FragmentStatePagerAdapter {
private List<LocalDate> mEventDates;
public EventFragmentAdapter(FragmentManager fragmentManager, List<LocalDate> eventDates){
super(fragmentManager);
this.mEventDates = eventDates;
}
#Override
public Fragment getItem(int i) {
return EventListFragment.newInstance(mEventDates.get(i));
}
#Override
public int getCount() {
return mEventDates.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mEventDates.get(position).dayOfWeek().getAsText();
}
}
Any ideas why both lists are always the same, and are based on the last tab in the ViewPager? I assume that somehow they are referencing the same RecyclerView or the same RecyclerViewAdapter, but I don't have any static fields for those so I am not sure how it is happening.
A long hunt and an anti-climactic solution(as with most difficult bugs). Also a bit unfair since the bug isn't in the code posted above, I had to hunt down your git project to figure it out. The bug is in EventAdapter:
public class EventAdapter extends RecyclerView.Adapter<EventAdapter.ViewHolder> {
private static final List<Event> mEvents;
private final Context mContext;
public EventAdapter(Context context, List<Event> events){
this.mContext = context;
mEvents = events;
}
...
}
mEvents is static!... so it's shared across all instances of mEvents. This explains the bug perfectly since updates to it last will set the values for all EventAdapters.
It looks like you made mEvents static so that you could access it within your ViewHolders. Instead you can just store the individual Event within the ViewHolder and drop the dangerous static modifier. On the flip-side, hooray for Open Source Projects!
I've seen your code and I totally agree with Travor - you're using a static member, that is replaced every time you create a new Adapter (and so it gets just the last page data). I've modified your project a little bit, to make it work properly. Take a look at it, hope it can be useful.
EventFragment: replace getFragmentManager with getChildFragmentManager since you need EventListFragment to be handle by EventFragment fragment manager and not by the activity fragment manager
public class EventFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_event, container, false);
// Get and set up viewpager
final ViewPager viewPager = (ViewPager) view.findViewById(R.id.event_view_pager);
EventFragmentAdapter eventFragmentAdapter = new EventFragmentAdapter(getChildFragmentManager(), getEventDates());
viewPager.setAdapter(eventFragmentAdapter);
// Get and set up tablayout
final TabLayout tabLayout = (TabLayout) view.findViewById(R.id.event_tabs);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
}
});
return view;
}
/**
* Retrieves the event dates for the hackathon so that the proper events can be displayed.
* #return
*/
private List<LocalDate> getEventDates(){
List<LocalDate> eventDates = new ArrayList<>();
eventDates.add(new LocalDate(2015, 10, 17));
eventDates.add(new LocalDate(2015, 10, 18));
return eventDates;
}
}
EventListFragment - I've modified the query, since the sqlite query doesn't work with my locale (italian)
public class EventListFragment extends Fragment{
private EventAdapter mEventAdapter;
private static final String TAG = EventListFragment.class.getSimpleName();
private static final String DATE_ARG = "eventDate";
public static EventListFragment newInstance(LocalDate date){
EventListFragment eventListFragment = new EventListFragment();
Bundle args = new Bundle();
args.putSerializable(DATE_ARG, date);
eventListFragment.setArguments(args);
return eventListFragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_event_list, container, false);
// Setup recyclerview
RecyclerView eventRecyclerView = (RecyclerView) view.findViewById(R.id.event_recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
eventRecyclerView.setLayoutManager(layoutManager);
// Get date
LocalDate eventDate = (LocalDate) getArguments().getSerializable(DATE_ARG);
// Set adapter
mEventAdapter = new EventAdapter(getEvents(eventDate));
eventRecyclerView.setAdapter(mEventAdapter);
Log.v(TAG, eventRecyclerView.toString());
return view;
}
/**
* Retrieves the events for the given date for the fragment.
*/
private List<Event> getEvents(LocalDate date){
List<Event> returnList = new ArrayList<>();
String dateString = Utility.getDBDateString(date);
List<String> dateList = new ArrayList<>();
Cursor cursor = getActivity().getContentResolver().query(
GHContract.EventEntry.CONTENT_URI,
new String[]{ "*", "substr(" + GHContract.EventEntry.COLUMN_TIME + ",0,11)" },
"substr(" + GHContract.EventEntry.COLUMN_TIME + ",0,11) = ? ",
new String[]{dateString},
GHContract.EventEntry.COLUMN_TIME
);
while(cursor.moveToNext()){
returnList.add(new Event(cursor));
dateList.add(cursor.getString(cursor.getColumnIndex(GHContract.EventEntry.COLUMN_TIME)));
}
cursor.close();
return returnList;
}
}
EventAdapter - removed the static keyword and the reference to the activity context (you need to get the context somewhere else)
public class EventAdapter extends RecyclerView.Adapter<EventAdapter.ViewHolder> {
private List<Event> mEvents;
public EventAdapter(List<Event> events){
mEvents = events;
}
/**
* Inflates the view for Event items.
*/
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_event, parent, false);
return new ViewHolder(view);
}
/**
* Binds the data for an event to its view.
*/
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Event event = mEvents.get(position);
holder.timeView.setText(Utility.getTimeString(event.getTime()));
holder.titleView.setText(event.getTitle());
holder.locationView.setText(event.getLocation());
// If reminder time is not null, show check mark. If it is, show plus.
if(event.getReminderTime() != null){
holder.alarmView.setImageDrawable(holder.itemView.getResources().getDrawable(R.drawable.ic_alarm_on));
} else{
holder.alarmView.setImageDrawable(holder.itemView.getResources().getDrawable(R.drawable.ic_add_alarm));
}
}
/**
* Returns the size of the adapter.
*/
#Override
public int getItemCount() {
return mEvents.size();
}
/**
* Retains a reference to the view so `findViewById` calls are only made once for the adapter.
*/
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public final TextView timeView;
public final TextView titleView;
public final TextView locationView;
public final ImageView alarmView;
public ViewHolder(View view){
super(view);
timeView = (TextView) view.findViewById(R.id.event_time);
titleView = (TextView) view.findViewById(R.id.event_title);
locationView = (TextView) view.findViewById(R.id.event_location);
alarmView = (ImageView) view.findViewById(R.id.event_add_reminder);
alarmView.setOnClickListener(this);
}
/**
* Handles the click a user makes on the alarm image view.
*/
#Override
public void onClick(View v) {
OnEventReminderClickListener activity = (OnEventReminderClickListener) v.getContext();
activity.onEventReminderClicked(mEvents.get(getPosition()));
}
}
/**
* Interface to call back to the activity when an alarm is clicked for an event item.
*/
public interface OnEventReminderClickListener{
void onEventReminderClicked(Event event);
}
}
And finally the app/build.gradle, since you need to get the same version for all support libraries (recycler, card and so on)
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.adammcneilly.grizzhacks"
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.1'
compile 'joda-time:joda-time:2.7'
compile 'com.android.support:recyclerview-v7:22.2.1'
compile 'com.android.support:cardview-v7:22.2.1'
compile 'com.android.support:design:22.2.1'
}
Could you try this?
public class EventFragmentAdapter extends FragmentStatePagerAdapter {
private List<LocalDate> mEventDates;
private List<EventListFragment> mFragments;
public EventFragmentAdapter(FragmentManager fragmentManager, List<LocalDate> eventDates){
super(fragmentManager);
this.mEventDates = eventDates;
this.mFragments = new ArrayList<>;
for (LocalDate date : this.mEventDates) {
this.mFragments.add(EventListFragment.newInstance(date));
}
}
#Override
public Fragment getItem(int i) {
return mFragments.get(i);
}
#Override
public int getCount() {
return mEventDates.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mEventDates.get(position).dayOfWeek().getAsText();
}
}
Then you check into each item of mFragents if they have the expected content.

Categories

Resources