I am having problems updating my RecyclerView with new data. If I press a confirmation button on a CardView in the first tab, the card should get added to the second tab but it won't update it there until I rotate the screen. I get the data for the card from reading a text file. Please advise me how to call the notifyDataSetChange method after I have added the new data to my text file. I have tried everything and all I get is NullPointerExceptions. The RecyclerViews are in fragments and I use FragementStatePagerAdapter.
I'll put some of my classes here. Ask if you need more information.
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag) {
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag);
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag) {
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
SectionsPagerAdapter.java
public class SectionsPagerAdapter extends FragmentStatePagerAdapter{
private ViewPager viewPager;
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public void addFragment(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
}
CompletedFragment.java
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed");
recyclerView.setAdapter(adapter);
return view;
}
}
}
EDIT:
Added the code for the second fragment, which is the one that should be updated after the onClick at RecyclerViewHolder-class.
You have to add a function in your adapter for adding data:
public void addData(String title, String desc, String point, String date) {
this.mListTitle.add(title);
this.mListDesc.add(desc);
this.mListPoints.add(point);
this.mListDates.add(date);
notifyDataSetChanged();
}
If you want to enable animations call notifyItemInserted() instead of notifyDataSetChanged();
It is important that you add a String to every list because in your onBindViewHolder() you get the item to display from every list with list.get(position). Otherwise you'll get a IndexOutOfBoundsException.
You can create an interface and use as a callback. Send it as a parameter of the RecyclerViewAdapter and then to your RecyclerViewHolder. When the item should be added you call the callback that will get you back to your fragment. There you can read the file again and call notifyDataSetChanged.
I know i explain pretty bad so i will try to change your code so that it does what i said:
this will be your RecyclerViewAdapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
private Runnable callback;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag, Runnable callBack) {
//add the callback here(Runnable) and save it into a local variable
this.callback=callback;
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag, callback);
//send the callback to your viewHolder
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag, Runnable callback) {
//ADD the callback to the parameters list here
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
//call the callback
callback.run();
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
And this will be your fragment:
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed", new Runnable() {
#Override
public void run() {
//here read the list again and call notifyDataSetChanged on your recycler
}
});
);
recyclerView.setAdapter(adapter);
return view;
}
}
}
Hope it helps and it works for you. If i did anything wrong, let me know, i can't run the code right now so...
edited, i forgot to add code in the callback
Related
My fragment has a Recycler View. Therefore I have a RecyclerView Adapter too. From this Adapter, I am opening an AlertDialog. When I click OK, I need to pass the onclick event from my DialogFragment back to my RecyclerView Adapter.
Currently, I am doing it like here, but this passes the event back to the activity and not to the RecyclerView Adapter.
public class FreshwaterRecyclerViewAdapter extends RecyclerView.Adapter<FreshwaterRecyclerViewAdapter.ViewHolder> implements BiotopeDialogFragment.NoticeDialogListener {
private List<Biotope> data;
private LayoutInflater layoutInflater;
FreshwaterRecyclerViewAdapter(Context context, List<Biotope> data) {
this.layoutInflater = LayoutInflater.from(context);
this.data = data;
}
//The dialog fragment receives a reference to this Activity through the
//Fragment.onAttach() callback, which it uses to call the following methods
//defined by the NoticeDialogFragment.NoticeDialogListener interface
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
notifyItemInserted(getItemCount()-1);
}
#Override
public void onDialogNegativeClick(DialogFragment dialog) {
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
if (viewType == R.layout.biotope_cardview){
itemView = layoutInflater.inflate(R.layout.biotope_cardview, parent, false);
} else {
itemView = layoutInflater.inflate(R.layout.biotope_add_button, parent, false);
}
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
if (position == data.size()) {
holder.imageButtonAddBiotope.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentManager fragmentManager = ((AppCompatActivity) layoutInflater.getContext()).getSupportFragmentManager();
DialogFragment dialog = new BiotopeDialogFragment();
dialog.show(fragmentManager, "NoticeDialogFragment");
}
});
} else {
holder.textViewBiotopeTitle.setText(getItem(position).name);
Picasso.get().load(Uri.parse(getItem(position).imageUri)).into(holder.imageViewBiotope);
LastValuesRecyclerViewAdapter recyclerAdapter = new LastValuesRecyclerViewAdapter(layoutInflater.getContext(), getData());
holder.recyclerViewLastValues.setLayoutManager(new LinearLayoutManager(layoutInflater.getContext(), LinearLayoutManager.HORIZONTAL, false));
holder.recyclerViewLastValues.setAdapter(recyclerAdapter);
}
}
//total number of rows
#Override
public int getItemCount() {
return data.size() + 1; //+1 for the add button
}
#Override
public int getItemViewType(int position) {
return (position == data.size()) ? R.layout.biotope_add_button : R.layout.biotope_cardview;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private TextView textViewBiotopeTitle;
private ImageView imageViewBiotope;
private RecyclerView recyclerViewLastValues;
private ImageButton imageButtonAddBiotope;
public ViewHolder(View view) {
super(view);
textViewBiotopeTitle = (TextView) view.findViewById(R.id.textViewBiotopeTitle);
imageViewBiotope = (ImageView) view.findViewById(R.id.imageViewBiotopeCardview);
recyclerViewLastValues = (RecyclerView) view.findViewById(R.id.recyclerViewLastValues);
imageButtonAddBiotope = (ImageButton) view.findViewById(R.id.imageButtonAddBiotope);
}
}
Biotope getItem(int id) {
return data.get(id);
}
private List<String> getData() {
List<String> data = new ArrayList<>();
data.add("PO4");
data.add("NO3");
return data;
}
}
This is my dialog.
public class BiotopeDialogFragment extends DialogFragment {
private NoticeDialogListener listener;
public interface NoticeDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}
//Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
//Verify that the host activity implements the callback interface
try {
//Instantiate the NoticeDialogListener so we can send events to the host
listener = (NoticeDialogListener) context;
} catch (ClassCastException e) {
//The activity doesn't implement the interface, throw exception
throw new ClassCastException("FreshwaterRecyclerViewAdapter must implement NoticeDialogListener | Context: " + context.toString());
}
}
public static final String TAG = "biotope_dialog_fragment";
private ActivityResultLauncher<Intent> activityResultLauncher;
private Uri imageUri = null;
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext());
LayoutInflater inflater = requireActivity().getLayoutInflater();
//Inflate and set the layout for the dialog
//Pass null as the parent view because its going in the dialog layout
View view = inflater.inflate(R.layout.fragment_dialog_biotope, null, false);
builder.setView(view);
View colorPickerPreviewView = view.findViewById(R.id.colorPickerPreviewView);
ColorPickerView colorPickerView = view.findViewById(R.id.colorPickerView);
ImageView imageViewBiotope = view.findViewById(R.id.imageViewBiotopePreview);
TextInputEditText textFieldBiotopeName = view.findViewById(R.id.textFieldBiotopeName);
builder.setTitle("New biotope")
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
BiotopeDatabase database = BiotopeDatabase.getDbInstance(requireContext().getApplicationContext());
Biotope biotope = new Biotope();
if (textFieldBiotopeName.getText() != null) {
biotope.name = textFieldBiotopeName.getText().toString();
} else {
biotope.name = "";
}
if (imageUri != null) {
biotope.imageUri = imageUri.toString();
} else {
biotope.imageUri = "";
}
biotope.color = colorPickerView.getColor();
database.biotopeDao().insertAll(biotope);
//Send the positive button event back to the host activity
listener.onDialogPositiveClick(BiotopeDialogFragment.this);
}
})
.setNegativeButton("noke", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
//Send the negative button event back to the host activity
listener.onDialogNegativeClick(BiotopeDialogFragment.this);
Objects.requireNonNull(BiotopeDialogFragment.this.getDialog()).cancel();
}
});
return builder.create();
}
public static BiotopeDialogFragment display(FragmentManager fragmentManager) {
BiotopeDialogFragment fragment = new BiotopeDialogFragment();
fragment.show(fragmentManager, TAG);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
This is my fragment building the RecyclerView. Alternatively, I can pass the event back to the fragment if it is not possible to pass it to the adapter.
public class BiotopesFragment extends Fragment {
private FreshwaterRecyclerViewAdapter recyclerAdapter;
public static BiotopesFragment newInstance(String param1, String param2) {
BiotopesFragment fragment = new BiotopesFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_biotopes, container, false);
RecyclerView recyclerViewFreshwater = (RecyclerView) root.findViewById(R.id.recyclerViewFreshwater);
recyclerAdapter = new FreshwaterRecyclerViewAdapter(getContext(), getData());
recyclerViewFreshwater.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
recyclerViewFreshwater.setAdapter(recyclerAdapter);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL);
recyclerViewFreshwater.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.START | ItemTouchHelper.END, 0) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();
int toPosition = target.getAdapterPosition();
Collections.swap(getData(), fromPosition, toPosition);
recyclerView.getAdapter().notifyItemMoved(fromPosition, toPosition);
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
}
});
itemTouchHelper.attachToRecyclerView(recyclerViewFreshwater);
return root;
}
private List<Biotope> getData() {
BiotopeDatabase database = BiotopeDatabase.getDbInstance(requireContext().getApplicationContext());
BiotopeDao biotopeDao = database.biotopeDao();
return biotopeDao.getAll();
}
}
Ideal Way to do this to create all UI component in Fragment not in the adapter . Create an interface to handle events in the fragment and provide callback to the fragment from adapter. now your Fragment should create all the UI component .
Now coming to the
How to provide callback from dialog fragment to Fragment.
You can use setTargetFragment but its deprecated . Now you can use setFragmentResultListener instead of setTargetFragment(), its the safest way i think. Once you get the result back in fragment you can call any method of your adapter.
I am a beginner at android development. I tried to display data on Recycle View using MVVM. When I opened the fragment the first time it is empty, the second time it showed data. Adapter not called the first time. How to fix this.
Fragment class
public class fragmentEmployee extends Fragment {
private FragmentEmployeeBinding binding;
private ViewGroup viewGroup;
private View view;
private RecyclerView mRecycleView;
private UserAdapter mAdapter;
private EmployeeRegisterViewModel viewModel;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_employee, viewGroup, false);
view = binding.getRoot();
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Toast.makeText(getContext(), "onViewCreated", Toast.LENGTH_SHORT).show();
mRecycleView = binding.recyleViewEmployee;
viewModel = new ViewModelProvider(this).get(EmployeeRegisterViewModel.class);
viewModel.init();
initRecycleView();
viewModel.getEmployee().observe(getViewLifecycleOwner(), new Observer<List<employeeModel>>() {
#Override
public void onChanged(List<employeeModel> employeeModels) {
mAdapter.notifyDataSetChanged();
}
});
}
private void initRecycleView() {
mAdapter = new UserAdapter(getContext(), viewModel.getEmployee().getValue());
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
mRecycleView.setLayoutManager(layoutManager);
mRecycleView.setAdapter(mAdapter);
}
}
UserAdapter class
public class UserAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
public List<employeeModel> employeeModelList= new ArrayList<>();
public Context context1;
public UserAdapter(Context context,List<employeeModel> employeeModels){
employeeModelList = employeeModels;
context1 = context;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_listitem,parent,false);
ViewHolder viewHolder=new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
(ViewHolder)holder).username.setText(employeeModelList.get(position).getName());
}
#Override
public int getItemCount() {
return employeeModelList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView username;
public ViewHolder(#NonNull View itemView) {
super(itemView);
username=itemView.findViewById(R.id.userText);
}
}
}
Try reordering like this.
Remove
mAdapter = new UserAdapter(getContext(), viewModel.getEmployee().getValue());
from initRecycleView
mAdapter = new UserAdapter(getContext(), viewModel.getEmployee().getValue());
viewModel.getEmployee().observe(getViewLifecycleOwner(), new Observer<List<employeeModel>>() {
#Override
public void onChanged(List<employeeModel> employeeModels) {
mAdapter.notifyDataSetChanged();
}
});
initRecycleView();
mAdapter.notifyDataSetChanged();
Above method will work if the list declared inside adapter has any change. So, calling above method without changing adapter's list is useless.
Add a method to UserAdapter class to update list data. Like:
public void updateData(List<employeeModel> updatedList){
employeeModelList.clear();
employeeModelList.addAll(updatedList);
notifyDataSetChanged();
}
And call this method inside observer. Like:
viewModel.getEmployee().observe(getViewLifecycleOwner(), new
Observer<List<employeeModel>>() {
#Override
public void onChanged(List<employeeModel> employeeModels) {
mAdapter.updateData(employeeModels);
}
});
where are you get your network data?
If your get in here -> viewModel.init();
I think it just timing problem.
Just change code sequence
initRecycleView();
viewModel.getEmployee().observe(getViewLifecycleOwner(), new Observer<List<employeeModel>>() {
#Override
public void onChanged(List<employeeModel> employeeModels) {
mAdapter.notifyDataSetChanged();
}
});
viewModel.init();
I'm creating an app for ordering food, and I created a Dish class and a Dish array adapter.
Currently you can add quantity for a specific dish by clicking on the view.
However, I wish to have 2 buttons to add or sub the quantity.
How can I make the 2 buttons for each dish, without the need to write for each element its ows button code? is there a way to make an "add" and "sub" methods and that the listview will know on which view it was clicked and by that update its quantity?
public Dish(String dishName, int dishPrice, int Image, int quantity) {
mdishName = dishName;
mdishPrice = dishPrice;
mdishPic = Image;
mquantity = quantity;
}
public class DishAdapter extends ArrayAdapter<Dish> {
public DishAdapter(Activity context, ArrayList<Dish> dishes){
super(context, 0, dishes);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View listItemView = convertView;
if(listItemView == null){
listItemView = LayoutInflater.from(getContext()).inflate(R.layout.list_layout, parent, false);
}
final Dish currenDish = getItem(position);
TextView dishName = (TextView) listItemView.findViewById(R.id.dishName);
dishName.setText(currenDish.getDishName());
TextView dishPrice = (TextView) listItemView.findViewById(R.id.dishPrice);
dishPrice.setText(String.valueOf(currenDish.getDishPrice()));
ImageView image = (ImageView) listItemView.findViewById(R.id.dishPic);
image.setImageResource(currenDish.getDishPic());
image.setVisibility(View.VISIBLE);
TextView quantity = (TextView) listItemView.findViewById(R.id.quantity);
quantity.setText(String.valueOf(currenDish.getQuantity()));
return listItemView;
}
public class DesertsActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.deserts_activity);
final ArrayList<Dish> dishes = new ArrayList<Dish>();
dishes.add(new Dish("Number Cake",180, R.drawable.cake_number, 0));
dishes.add(new Dish("Ear of Haman", 40, R.drawable.ozen_haman, 0));
dishes.add(new Dish("Alphachores", 35, R.drawable.alphachores, 0));
dishes.add(new Dish("Snow Cookies", 35, R.drawable.snow_cookies, 0));
DishAdapter adapter = new DishAdapter(this, dishes);
final ListView listView = (ListView) findViewById(R.id.deserts_list);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
dishes.set(position, new Dish(dishes.get(position).getDishName(),
dishes.get(position).getDishPrice(), dishes.get(position).getDishPic(),
dishes.get(position).getQuantity()+1));
TextView quantity = (TextView) view.findViewById(R.id.quantity);
quantity.setText(String.valueOf(dishes.get(position).getQuantity()));
}
});
}
}
You have to create a separate Adaptor Class and then apply button click listeners on the buttons. I am attaching the image for the reference.
and My product Adaptor Is
public class ProductAdaptor extends RecyclerView.Adapter<ProductAdaptor.ViewHolder> {
List<ProductsItem> productsItems;
Context context;
public ProductAdaptor(Context context, List<ProductsItem> productsItems)
{
this.context = context;
this.productsItems = productsItems;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.single_product_design,viewGroup, false);
return new ProductAdaptor.ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
final ProductsItem productsItem = productsItems.get(i);
viewHolder.productname.setText(productsItem.getProductName());
Glide.with(context).load(productsItem.getProductImageUrl()).into(viewHolder.productImage);
viewHolder.stock.setText(productsItem.getStock());
viewHolder.farmername.setText(productsItem.getFarmerName());
// add listener on single Item
viewHolder.btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "Clicked", Toast.LENGTH_LONG).show();
}
});
viewHolder.btnPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// apply code to increment the number
// first of all get the value form text counter and increment after that bind on the UI
}
});
viewHolder.btnMinus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// first of all get the value form text counter and decrement after that bind on the UI
}
});
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public int getItemCount() {
return productsItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder
{
TextView productname;
ImageView productImage;
TextView stock, farmername, specility,qty;
Button btnPlus, btnMinus;
LinearLayout btnAdd;
public ViewHolder(#NonNull View itemView)
{
super(itemView);
productname = itemView.findViewById(R.id.productname);
productImage = itemView.findViewById(R.id.productimage);
stock = itemView.findViewById(R.id.stcokValue);
farmername = itemView.findViewById(R.id.farmerName);
qty = itemView.findViewById(R.id.qty);
specility = itemView.findViewById(R.id.specility);
btnPlus = itemView.findViewById(R.id.plusbutton);
btnMinus = itemView.findViewById(R.id.minusbtn);
btnAdd = itemView.findViewById(R.id.add_button);
}
}
}
I'm trying to populate a RecyclerView using data stored in a ArrayList. When the RecyclerView loads each piece of data is repeted 4x in each row.
I've tried a whole variety of solutions I've found, but none seem to resolve the issue.
After some debugging the data in 'mData', appears to be correct, so that would lead me to believe that this issue is 'onBindViewHolder'?
Adapter
public class EventsRecyclerViewAdapter extends RecyclerView.Adapter<EventsRecyclerViewAdapter.ViewHolder> {
private List<String> mData;
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
// data is passed into the constructor
public EventsRecyclerViewAdapter(List<String> data) {
this.mInflater = LayoutInflater.from(MainActivity.mainActivity);
this.mData = data;
}
// inflates the row layout from xml when needed
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recyclerview_row, parent, false);
return new ViewHolder(view);
}
// binds the data to the TextView in each row
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.keyID.setText(mData.get(position));
holder.lockID.setText(mData.get(position));
holder.eventTime.setText(mData.get(position));
holder.eventType.setText(mData.get(position));
}
// total number of rows
#Override
public int getItemCount() {
return mData.size();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView keyID;
TextView lockID;
TextView eventTime;
TextView eventType;
ViewHolder(View itemView) {
super(itemView);
keyID = itemView.findViewById(R.id.keyIDTV);
lockID = itemView.findViewById(R.id.lockIDTV);
eventTime = itemView.findViewById(R.id.eventDateTV);
eventType = itemView.findViewById(R.id.eventTypeTV);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
Fragment
public class KeyEvents extends Fragment {
public static KeyInfo newInstance() {
KeyInfo fragment = new KeyInfo();
return fragment;
}
EventsRecyclerViewAdapter adapter;
ArrayList<String> eventsList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventsOperationHandler ev = new EventsOperationHandler();
eventsList = new ArrayList<>();
for (int i=0; i<ev.getEvents().size(); i++) {
eventsList.add(ev.getEvents().get(i).get(0));
eventsList.add(ev.getEvents().get(i).get(1));
eventsList.add(ev.getEvents().get(i).get(2));
eventsList.add(ev.getEvents().get(i).get(3));
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_event_info, container, false);
RecyclerView recyclerView = view.findViewById(R.id.rvEvents);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.mainActivity));
adapter = new EventsRecyclerViewAdapter(eventsList);
recyclerView.setAdapter(adapter);
return view;
}
}
In your 'onBindViewHolder' you are setting same data in every textView of yours
// binds the data to the TextView in each row
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.keyID.setText(mData.get(position));
holder.lockID.setText(mData.get(position));
holder.eventTime.setText(mData.get(position));
holder.eventType.setText(mData.get(position));
}
Here you are setting mData.get(position) to every textView in the holder
A solution would be creating a Pojo class for your recyclerView
Create Pojo Event class
class Event{
public String keyID;
public String lockID;
public String eventTime;
public String eventType;
}
Create a list of 'Event' in your fragment
public class KeyEvents extends Fragment {
public static KeyInfo newInstance() {
KeyInfo fragment = new KeyInfo();
return fragment;
}
EventsRecyclerViewAdapter adapter;
ArrayList<Event> eventsList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventsOperationHandler ev = new EventsOperationHandler();
eventsList = new ArrayList<>();
for (int i=0; i<ev.getEvents().size(); i++) {
Event event = new Event();
event.keyID = ev.getEvents().get(i).get(0);
event.lockID = ev.getEvents().get(i).get(1);
event.eventTime = ev.getEvents().get(i).get(2);
event.eventType = ev.getEvents().get(i).get(3);
eventsList.add(event);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_event_info, container, false);
RecyclerView recyclerView = view.findViewById(R.id.rvEvents);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.mainActivity));
adapter = new EventsRecyclerViewAdapter(eventsList);
recyclerView.setAdapter(adapter);
return view;
}
}
Modify your recyclerView to handle to 'Event' class
public class EventsRecyclerViewAdapter extends RecyclerView.Adapter<EventsRecyclerViewAdapter.ViewHolder> {
private List<Event> mData;
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
// data is passed into the constructor
public EventsRecyclerViewAdapter(List<Event> data) {
this.mInflater = LayoutInflater.from(MainActivity.mainActivity);
this.mData = data;
}
// inflates the row layout from xml when needed
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recyclerview_row, parent, false);
return new ViewHolder(view);
}
// binds the data to the TextView in each row
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Event event = mData.get(position);
holder.keyID.setText(event.keyID);
holder.lockID.setText(event.lockId);
holder.eventTime.setText(event.eventTime);
holder.eventType.setText(event.eventType);
}
// total number of rows
#Override
public int getItemCount() {
return mData.size();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView keyID;
TextView lockID;
TextView eventTime;
TextView eventType;
ViewHolder(View itemView) {
super(itemView);
keyID = itemView.findViewById(R.id.keyIDTV);
lockID = itemView.findViewById(R.id.lockIDTV);
eventTime = itemView.findViewById(R.id.eventDateTV);
eventType = itemView.findViewById(R.id.eventTypeTV);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
That's it, this should work perfectly.
I am actually working on a group project and I want to develop a functionnality for our application. My goal is to have a list of several items with their images and when I click on an Item of that list I want to have a text pop in the midle of the screen related to that particular item. I'm afraid I might be using the wrong technical Tools to do so. I am actually using a csv file for the list details, an adapter and a viewHolder for the list. Since I have no idea on what is wrong and what to do I link a big part of my code so you can check how I did until now. I can also give you my xml files if you need to check them out, a really big thanks in advance to all the answers and time spent on my problem
I already managed to have my list of items with the title and the picture (text from csv file) of each list item but I'm stuck on how to show a specific text for each ViewHolder.
this is my Adapter
public class Adapter extends RecyclerView.Adapter<ViewHolder> {
List<Departement> list;
Activity activity;
public Adapter(List<Departement> list, Activity activity) {
this.list = list;
this.activity = activity;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int itemType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.departement,viewGroup,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
Departement departement = list.get(position);
viewHolder.bind(departement, activity);
}
#Override
public int getItemCount() {
return list.size();
}
}
my ViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView textViewView;
private ImageView imageView;
public ViewHolder(View itemView) {
super(itemView);
textViewView = (TextView) itemView.findViewById(R.id.text);
imageView = (ImageView) itemView.findViewById(R.id.image);
}
public void bind(Departement departement, Activity activity){
textViewView.setText(departement.getText());
String uri = departement.getImageUrl();
int imageResource = activity.getResources().getIdentifier(uri, null, activity.getPackageName());
Drawable res = activity.getResources().getDrawable(imageResource);
imageView.setImageDrawable(res);
}
}
each item of the list is a Departement
public class Departement {
private String text;
private String imageUrl;
public Departement(String text, String imageUrl) {
this.text = text;
this.imageUrl = imageUrl;
}
public String getText() {
return text;
}
public String getImageUrl() {
return imageUrl;
}
public void setText(String text) {
this.text = text;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}
and finally my fragment
public class FragmentEspecesProches extends Fragment {
public final static char SEPARATOR=',';
private RecyclerView recyclerView;
private List<Departement> departementsList = new ArrayList<>();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = inflater.inflate(R.layout.fragment_especes_proches, container, false);
ajouterDepartements();
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new GridLayoutManager(this,2));
recyclerView.setAdapter(new Adapter(departementsList, getActivity()));
return view;
}
private void ajouterDepartements() {
ArrayList<String> lines = new ArrayList<>();
ArrayList<String[]> data = new ArrayList<>();
String sep = new Character(SEPARATOR).toString();
lines = UtilitaireResultat.readFile(getActivity().getResources().openRawResource(R.raw.departement));
for(String line : lines){
String[] oneData = line.split(sep);
data.add(oneData);
}
for(int i=0 ; i<data.size() ; i++){
String[] tabStr = data.get(i);
departementsList.add( new Departement( tabStr[2]+" - "+tabStr[3] ,"#drawable/"+tabStr[5] ));
}
}
}
you can implement item click listener like this
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView tvName;
public TextView tvHometown;
private Context context;
public ViewHolder(Context context, View itemView) {
super(itemView);
this.tvName = (TextView) itemView.findViewById(R.id.tvName);
this.tvHometown = (TextView) itemView.findViewById(R.id.tvHometown);
// Store the context
this.context = context;
// Attach a click listener to the entire row view
itemView.setOnClickListener(this);
}
// Handles the row being being clicked
#Override
public void onClick(View view) {
int position = getAdapterPosition(); // gets item position
// if (position != RecyclerView.NO_POSITION) { // Check if an item was deleted, but the user clicked it before the UI removed it
User user = users.get(position);
// We can access the data within the views
Toast.makeText(context, tvName.getText(), Toast.LENGTH_SHORT).show();
// }
}
}
Use onBindViewHolder to handle any interaction on your list items
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
Departement departement = list.get(position);
viewHolder.bind(departement, activity);
viewHolder.itemView.setOnClickListener(//your action//);
}
ItemView is the whole item; you can access your textviews or imageviews as you use it on your bind method,
You can use your bind method to apply listeners.
Handle on click of the item inside your ViewHolder constructor
like ,
public ViewHolder(View itemView) {
super(itemView);
textViewView = (TextView) itemView.findViewById(R.id.text);
imageView = (ImageView) itemView.findViewById(R.id.image);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position=getAdapterPosition();
Toast.makeText(context, list.get(position).getText(), Toast.LENGTH_SHORT).show();
}
});
}
Create your onClickListner interface as
interface RecylerViewItemClickListner
{
void onItemClick(Department item)
}
set the listner in Adapter class
private final RecylerViewItemClickListner mOnClickListener;
public Adapter(List<Departement> list, Activity activity) {
this.list = list;
this.activity = activity;
this.mOnClickListener = activity;
}
Now in ViewHolder class
public void bind(final Departement item, final mOnClickListener listener) {
itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
mOnClickListener.onItemClick(item);
}
});
}
and change onBindViewHolder as below
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), mOnClickListener);
}
Override onItemClick(Department item) in activity
#override
onItemClick(Department item)
{
//show toast here...
}
implement OnClickListener in your ViewHolder class
public class ViewHolder extends RecyclerView.ViewHolder implements
View.OnClickListener
{
#Override
public void onClick(View v)
{
//do action
}
}
Implement below method in your ViewHolder class.
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final User currentItem = users.get(getAdapterPosition());
Toast.makeText(mContext,currentItem.getText()+" is selected!",Toast.LENGTH_SHORT).show();
}
});