The app is for Attendance with barcode.
The scan function work fine until i change to other fragment / activity and back to it. If that happen then the camera will scan but it will not return anything and on back press it wont show any toast (even thought it should).
If i close the app then reopen it, open this fragment and scan, it back to normal again until i change to other fragment / activity.
There isnt any error message. So i dont know where the error is.
This is parts of my code that i believe have something to do with this bug:
onCreateView:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_attendance, container, false);
fetch();
return v;
}
fetch:
private void fetch() {
FirebaseRecyclerOptions <ModelEvent> options = new FirebaseRecyclerOptions.Builder<ModelEvent>()
.setQuery(query, snapshot -> new ModelEvent(
Objects.requireNonNull(snapshot.child("eventID").getValue()).toString(),
Objects.requireNonNull(snapshot.child("eventStatus").getValue()).toString()))
.build();
adapter = new FirebaseRecyclerAdapter<ModelEvent, ViewHolderEvent>(options) {
#NonNull
#Override
public ViewHolderEvent onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_attendance, parent, false);
return new ViewHolderEvent(view);
}
//Set data on each list
#Override
protected void onBindViewHolder(#NonNull ViewHolderEvent viewHolderEvent, int i, #NonNull ModelEvent modelEvent) {
viewHolderEvent.setStringStatus(modelEvent.getEventStatus());
viewHolderEvent.buttonScan.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stringEventID = modelEvent.getEventID();
ScanOptions options = new ScanOptions();
options.setDesiredBarcodeFormats(ScanOptions.ALL_CODE_TYPES);
options.setPrompt("Scan something");
options.setOrientationLocked(true);
options.setBeepEnabled(true);
fragmentLauncher.launch(options);
}
});
}
};
recyclerViewAttendance.setAdapter(adapter);
}
for scanner:
private final ActivityResultLauncher<ScanOptions> fragmentLauncher = registerForActivityResult(new ScanContract(),
result -> {
//On Backpress
if(result.getContents() == null) {
Toast.makeText(getContext(), "Cancelled", Toast.LENGTH_LONG).show();
}
else {
//Get data from QRCode
stringQR = result.getContents();
getUserData();
setUserAttendance();
//Notification
Toast.makeText(getContext(), "Name: " + stringName, Toast.LENGTH_LONG).show();
}
});
onStart:
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
This is the full code: https://pastebin.com/e7XZN08i
So i just do many thing and ended up find what i should do.
switch (item.getItemId()) {
case R.id.nav_event:
fragmentEvent = new FragmentEvent(); //i just need to add this on all case for some reason, the tutorial i use dont do this
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragmentEvent).commit();
return true;
I also ask this on github:https://github.com/journeyapps/zxing-android-embedded/issues/695
Related
I have a BottomSheetDialogFragment and inside onCreateView i am inflating 2 different views based on a condition. Now my 1st view contains a recyclerView and the condition on which i want to switch my views is that if the list is empty then inflate view 1 else inflate view 2. Now my problem is when my list data count becomes 0 i want to call the onCreateView again so that based on the condition my 2nd view would be inflated. But i don't know how am i supposed to do it or rather is this the right way to do it.
My Code -
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if (this.documentCount > 0) {
View v = inflater.inflate(R.layout.bottom_sheet_layout_document_attachments_added, container, false);
documentArrayList = dataToRender.get(fieldPosition).getAttachments();
Button attachMore = (Button) v.findViewById(R.id.btn_attachMore);
if(documentCount == 5){
attachMore.getBackground().setAlpha(128);
}else{
attachMore.getBackground().setAlpha(255);
}
attachMore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try{
if(documentCount == 5){
PageLoader.showToast(getResources().getString(R.string.document_limit_message));
}else{
pickDocuments();
}
}
catch (Exception e){
Sentry.captureException(e);
}
}
});
Button btnCloseDrawer = (Button) v.findViewById(R.id.btn_closeDrawer);
btnCloseDrawer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dismiss();
}
});
recyclerview_documents = v.findViewById(R.id.recyclerview_documents);
adapter_documents = new DocumentAdapter(this, documentArrayList, fieldPosition);
recyclerview_documents.setAdapter(adapter_documents);
recyclerview_documents.setLayoutManager(new LinearLayoutManager(mContext));
return v;
} else {
View v = inflater.inflate(R.layout.bottom_sheet_layout_document, container, false);
TextView txtview_attach = (TextView) v.findViewById(R.id.txtview_attach);
txtview_attach.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try{
permissionManager.checkPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE, new PermissionManager.PermissionAskListener() {
#Override
public void onNeedPermission() {
ActivityCompat.requestPermissions(getActivity(), new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE
}, REQUEST_EXTERNAL_STORAGE);
}
#Override
public void onPermissionPreviouslyDenied() {
showDocumentRational();
}
#Override
public void onPermissionPreviouslyDeniedWithNeverAskAgain() {
dialogForSettings(getResources().getString(R.string.permission_denied), getResources().getString(R.string.permission_denied_document_access_message));
}
#Override
public void onPermissionGranted() {
pickDocuments();
}
});
}
catch (Exception e){
Sentry.captureException(e);
}
}
});
return v;
}
}```
Can't use more then one views for one fragment, but you can create a fragment wrapper that depends on the conditions, will show or create the fragment you need with the view you need.
for example WrapperFragment checking list size and creating Fragment1, when list size changed Wrapper fragment removung from fragment manager Fragment1 and add Fragment2
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);
}
}
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"/>
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?
I am having the "child already has a parent" error close to what a lot of people have been getting, however, I am getting it doing something different (as far as my searches have shown me). I am assuming that the way I am adding the fragment is not necessarily wrong, but I cannot for the life of me figure out how the fragment I am adding already has a parent.
I am launching from my main activity using a fragment to be loaded into a FrameLayout. Here is the onCreate from my main activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
realm = Realm.getDefaultInstance();
if (savedInstanceState == null) {
}
tripsFragment = new CarTripsOverview();
getSupportFragmentManager().beginTransaction().add(R.id.content_container, tripsFragment).commit();
}
And here are the methods which run for CarTripsOverview (which is a Fragment):
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
setUpRealm();
}
void setUpRealm() {
realm = Realm.getDefaultInstance();
RealmQuery<Car> carQuery = realm.where(Car.class);
RealmResults<Car> carResults = carQuery.findAll();
carAdapter = new CarArrayAdapter(getActivity(),0, carResults, true);
RealmQuery<Trip> tripQuery = realm.where(Trip.class);
RealmResults<Trip> tripResults = tripQuery.findAll();
tripAdapter = new TripAdapterRealm(getActivity(), 0, tripResults, true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_car_trips_overview, container, true);
// Bind all of the views with Butter Knife
ButterKnife.bind(this, view);
setUpSwipeMenu();
// Add my event listeners
tripListView.setAdapter(tripAdapter);
carSpinner.setAdapter(carAdapter);
return view;
}
void setUpSwipeMenu() {
SwipeMenuCreator creator = new SwipeMenuCreator() {
#Override
public void create(SwipeMenu menu) {
SwipeMenuItem deleteItem = new SwipeMenuItem(getContext());
deleteItem.setBackground(new ColorDrawable(Color.RED));
deleteItem.setWidth(MathHelper.dipToPixels(getContext(), 90));
deleteItem.setTitle("Delete");
deleteItem.setTitleColor(Color.WHITE);
deleteItem.setTitleSize(18);
menu.addMenuItem(deleteItem);
}
};
tripListView.setMenuCreator(creator);
tripListView.setOnMenuItemClickListener(new SwipeMenuListView.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(int position, SwipeMenu menu, int index) {
Trip trip = tripAdapter.getItem(position);
switch (index) {
case 0:
delete(trip);
tripAdapter.notifyDataSetChanged();
break;
}
return false;
}
});
}
My biggest problem is that I just don't understand quite where I am adding the child to the parent where it would already have a parent.
All of my source code can be found here.
Instead of:
View view = inflater.inflate(R.layout.fragment_car_trips_overview, container, true);
use:
View view = inflater.inflate(R.layout.fragment_car_trips_overview, container, false);
Notice the last parameter is false now. If you use true, the view you just created will be automatically added to the container (second parameter of inflate). With fragments, always use false in onCreateView(...).