I'm building a list using a RecyclerView. When the user clicks the '+' button in the toolbar an AlertDialog is launched that prompts the users to add an item in the list. I've built the AlertDialog in a separate class that extends DialogFragment. How can I make so that the item is added to the List<Task>?
What I've done so far in my adapter class is make List<Task> mTaskList a public static variable and I also created a static method that will return this list. I call this static method in the DialogFragment class so I can retrieve the list. But I don't think this is good practice. Is there a better way I can be adding an item to my list?
TaskListFragment.java
public class TaskListFragment extends Fragment {
private RecyclerView mRecyclerView;
private TaskAdapter mAdapter;
private List<Task> mTaskList;
private static final String ADD_DIALOG = "add_dialog";
public TaskListFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_task_list, container, false);
mRecyclerView = view.findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
//mTaskList = createTasks();
//mAdapter = new TaskAdapter(mTaskList);
mRecyclerView.setAdapter(updateUI());
return view;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_list_item, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.add_new_task:
FragmentManager manager = getFragmentManager();
AddTaskFragment dialog = new AddTaskFragment();
dialog.show(manager, ADD_DIALOG);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/*
public List<Task> createTasks() {
List<Task> tasks = new ArrayList<>();
for(int i = 1; i <= 100; i++) {
Task task = new Task();
task.setTitle("Task #" + i);
task.setSolved(i % 2 == 0);
tasks.add(task);
}
return tasks;
}
*/
public TaskAdapter updateUI(){
if(mAdapter == null) {
mTaskList = new ArrayList<>();
mAdapter = new TaskAdapter(mTaskList);
}else {
mAdapter.notifyDataSetChanged();
}
return mAdapter;
}
}
TaskAdapter.java
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.ViewHolder> {
public static List<Task> mTaskList;
public TaskAdapter(List<Task> taskList) {
mTaskList = taskList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Task currentTask = mTaskList.get(position);
holder.bindData(currentTask);
}
#Override
public int getItemCount() {
return mTaskList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
private TextView mTitle;
private CheckBox mSolved;
private ViewHolder(View itemView) {
super(itemView);
mTitle = itemView.findViewById(R.id.task_title);
mSolved = itemView.findViewById(R.id.task_solved);
}
private void bindData(Task task) {
mTitle.setText(task.getTitle());
mSolved.setChecked(task.isSolved());
}
}
public static List<Task> getTaskList(){
return mTaskList;
}
}
AddTaskFragment.java
public class AddTaskFragment extends DialogFragment {
private EditText mTaskTitle;
private List<Task> mTaskList;
private void addTask() {
if(!mTaskTitle.getText().toString().equals("")) {
Task task = new Task();
task.setTitle(mTaskTitle.getText().toString());
mTaskList = TaskAdapter.getTaskList();
mTaskList.add(task);
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
//return super.onCreateDialog(savedInstanceState);
View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_task_dialog,null);
mTaskTitle = view.findViewById(R.id.task_title);
return new AlertDialog.Builder(getContext())
.setView(view)
.setTitle("Add Task")
.setPositiveButton("Add", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
addTask();
}
})
.create();
}
}
You could call startActivityForResult when starting addTask(), which would put the Task to add inside an Intent which you would receive in your activity's onActivityResult(int requestCode, int resultCode, Intent data).
You can look into using the ViewModel pattern that Google introduced last IO.
The section talking about sharing would be especially beneficial in your situation.
Related
**I want to know how to click the items in childRecyclerview show different fragment, I think we should try to modify childAdapter, I am searching for a long time on net. But no use. Please help or try to give some ideas how to achieve this.
hear is my code
**
//workflow fragment
public class WorkflowFragment extends Fragment {
private Context mContext;
private String[] workflowHeading;
private String[] workflowContent;
private int[] imageResourceId;
private RecyclerView recyclerview;
private List<Workflow> workflowList;
private WorkflowAdapter workflowAdapter;
private View view;
public static WorkflowFragment newInstance(String param1, String param2) {
WorkflowFragment fragment = new WorkflowFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
mContext = getActivity();
setHasOptionsMenu(true);
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
if(view==null) {
view = inflater.inflate(R.layout.fragment_workflow, container, false);
}
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
dataInitialize();
recyclerview = view.findViewById(R.id.workflow_recyclerview);
recyclerview.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerview.setHasFixedSize(true);
workflowAdapter =new WorkflowAdapter(getContext(),workflowList);
recyclerview.setAdapter(workflowAdapter);
workflowAdapter.notifyDataSetChanged();
}
private void dataInitialize() {
//list1
List<String> nestedList1 = new ArrayList<>();
nestedList1.add("A");
nestedList1.add("B");
nestedList1.add("C");
nestedList1.add("D");
nestedList1.add("E");
nestedList1.add("F");
nestedList1.add("G");
//list2
List<String>nestedList2 = new ArrayList<>();
nestedList2.add("H");
nestedList2.add("I");
nestedList2.add("G");
nestedList2.add("K");
//list3
List<String>nestedList3 = new ArrayList<>();
nestedList3.add("L");
nestedList3.add("M");
nestedList3.add("N");
nestedList3.add("P");
nestedList3.add("P");
nestedList3.add("Q");
nestedList3.add("R");
nestedList3.add("S");
//list4
List<String>nestedList4 = new ArrayList<>();
nestedList4.add("T");
nestedList4.add("U");
nestedList4.add("V");
nestedList4.add("W");
//list5
List<String>nestedList5 = new ArrayList<>();
nestedList5.add("X");
//list6
List<String>nestedList6 = new ArrayList<>();
nestedList6.add("Y");
nestedList6.add("Z");
workflowList = new ArrayList<>();
workflowList.add(new Workflow("1",nestedList1));
workflowList.add(new Workflow("2",nestedList2));
workflowList.add(new Workflow("3",nestedList3));
workflowList.add(new Workflow("4",nestedList4));
workflowList.add(new Workflow("5",nestedList5));
workflowList.add(new Workflow("6",nestedList6));
}
#Override
public void onCreateOptionsMenu(#NonNull Menu menu, #NonNull MenuInflater inflater) {
inflater.inflate(R.menu.menu_item,menu);
MenuItem searchItem = menu.findItem(R.id.search_action);
if(searchItem!=null){
SearchView searchView = (SearchView) searchItem.getActionView(); //here stop working
searchView.setMaxWidth(Integer.MAX_VALUE);
searchView.setQueryHint("Search Here!");
searchView.setIconified(false);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
workflowAdapter.getFilter().filter(query);
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
notifyItemChanged()
workflowAdapter.getFilter().filter(newText);
return false;
}
});
}
super.onCreateOptionsMenu(menu, inflater);
}
}
//ParentAdapter
public class WorkflowAdapter extends RecyclerView.Adapter<WorkflowAdapter.WorkflowViewHolder> implements Filterable{
Context context;
List<Workflow> worflowArrayList;
List<Workflow> worflowArrayListFull;
private List<String>nestedlist = new ArrayList<>();
public WorkflowAdapter(Context context, List<Workflow> worflowArrayList) {
this.context = context;
this.worflowArrayListFull = worflowArrayList;
this.worflowArrayList = new ArrayList<>(worflowArrayListFull);
}
#NonNull
#Override
public WorkflowViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.general_list_item,parent,false);
return new WorkflowViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull WorkflowViewHolder holder, int parentPosition) {
//-get element from your dataset at this position
//-replace the contents of the view with that element
Workflow workflow = worflowArrayList.get(parentPosition);
holder.tvHeading.setText(workflow.getItemText());
//holder.titleImage.setImageResource(model.titleImage);
boolean isExpandable = workflow.isExpandable();
holder.expandableLayout.setVisibility(isExpandable ? View.VISIBLE:View.GONE);
if(isExpandable){
holder.mArrowImage.setImageResource(R.drawable.ic_baseline_arrow_upward_24);
}else{
holder.mArrowImage.setImageResource(R.drawable.ic_baseline_arrow_downward_24);
}
NestedAdapter childAdapter = new NestedAdapter(nestedlist);
holder.nestedRecyclerView.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext()));
holder.nestedRecyclerView.setHasFixedSize(true);
holder.nestedRecyclerView.setAdapter(childAdapter);
holder.linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
switch(holder.getAdapterPosition()){
case 1:
Toast.makeText(holder.itemView.getContext(), "Item 11 clicked. ", Toast.LENGTH_SHORT).show();
break;
}
workflow.setExpandable(!workflow.isExpandable());
nestedlist = workflow.getNestedList();
notifyItemChanged(holder.getAdapterPosition());
}
});
}
#Override
public int getItemCount() {
return worflowArrayList.size();
}
#Override
public Filter getFilter() {
return workflowFilter;
}
private final Filter workflowFilter = new Filter(){
#Override
protected FilterResults performFiltering(CharSequence constraint) {
ArrayList<Workflow> filteredWorkflowList = new ArrayList<>();
if (constraint == null || constraint.length() == 0){
filteredWorkflowList.addAll(worflowArrayListFull);
}else{
String filterPattern = constraint.toString().toLowerCase().trim();
for (Workflow workflow: worflowArrayListFull){
if(workflow.itemText.toLowerCase().contains(filterPattern))
filteredWorkflowList.add(workflow);
}
}
FilterResults results = new FilterResults();
results.values = filteredWorkflowList;
results.count = filteredWorkflowList.size();
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
worflowArrayList.clear();
worflowArrayList.addAll((ArrayList)results.values);
notifyDataSetChanged();
}
};
public static class WorkflowViewHolder extends RecyclerView.ViewHolder{
private TextView tvHeading;
private LinearLayout linearLayout;
private RelativeLayout expandableLayout;
private ImageView mArrowImage;
private RecyclerView nestedRecyclerView;
public WorkflowViewHolder(#NonNull View itemView) {
super(itemView);
linearLayout=itemView.findViewById(R.id.linear_layout);
expandableLayout=itemView.findViewById(R.id.expandable_layout);
tvHeading = itemView.findViewById(R.id.tvHeading);
mArrowImage=itemView.findViewById(R.id.arro_imageview);
nestedRecyclerView = itemView.findViewById(R.id.child_rv);
}
}
}
//childAdapter
public class NestedAdapter extends RecyclerView.Adapter<com.larntech.myassistant_java.workflow.NestedAdapter.NestedViewHolder> {
private List<String> childItemList; /
public NestedAdapter(List<String> childItemList){
this.childItemList = childItemList;
}
#NonNull
#Override
public com.larntech.myassistant_java.workflow.NestedAdapter.NestedViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.nested_item,parent,false);
return new com.larntech.myassistant_java.workflow.NestedAdapter.NestedViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull com.larntech.myassistant_java.workflow.NestedAdapter.NestedViewHolder holder, int childPosition) {
holder.mTv.setText(childItemList.get(childPosition));
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
switch(holder.getAdapterPosition()){
case 0:
DemoFragment demoFragment =new DemoFragment();
AppCompatActivity activity=(AppCompatActivity) view.getContext();
activity.getSupportFragmentManager().beginTransaction().replace(R.id.fragment_workflow,demoFragment).addToBackStack(null).commit();
break;
case 1:
FragmentTwo fragmentTwo =new FragmentTwo();
AppCompatActivity activity2=(AppCompatActivity) view.getContext();
activity2.getSupportFragmentManager().beginTransaction().replace(R.id.fragment_workflow,fragmentTwo).addToBackStack(null).commit();
break;
case 2:
Toast.makeText(holder.itemView.getContext(), "Item clicked. ", Toast.LENGTH_SHORT).show();
break;
}
}
});
}
#Override
public int getItemCount() {
return childItemList.size();
}
public class NestedViewHolder extends RecyclerView.ViewHolder{
private TextView mTv;
public NestedViewHolder(#NonNull View itemView) {
super(itemView);
mTv=itemView.findViewById(R.id.nestedItemTv);
}
}
}
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've added swipe to my app, to delete specified note. For couple of days, I've facing problem with displaying data after swipe. For clarify:
Let's say, we have two items in recycler view. Whenever we swipe one of them, the second one should be visible, but it's not until I'll re-run the app. How I may solve it?
Main Activity
public class MainActivity extends AppCompatActivity implements MemoAdapter.OnNoteListener {
private static final String TAG = "MainActivity";
//Vars
private ArrayList<Note> mNotes = new ArrayList<>();
private MemoRepository mRepository;
private MemoAdapter mMemoAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRepository = new MemoRepository(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.memoToolbar);
setSupportActionBar(toolbar);
setTitle("My memos");
initRecyclerView();
}
//This method would be called after getting result from memo_content such as new memo, or edited existing memo.
#Override
protected void onResume() {
super.onResume();
getMemos();
}
private void getMemos(){
mRepository.getAllMemos().observe(this, new Observer<List<Note>>() {
#Override
public void onChanged(List<Note> notes) {
if (mNotes.size() > 0){
notes.clear();
}
if (notes != null){
mNotes.addAll(notes);
mMemoAdapter.watchMemoChanges((ArrayList<Note>) notes);
}
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.icon_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.addNewNote:
Toast.makeText(this, "Click!", Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, memo_content.class));
break;
case R.id.deleteAllNotes:
Toast.makeText(this, "Delete!", Toast.LENGTH_SHORT).show();
mRepository.deleteAllMemos();
break;
}
return super.onOptionsItemSelected(item);
}
private void initRecyclerView(){
//UI
RecyclerView mRecyclerView = findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
mMemoAdapter = new MemoAdapter(mNotes, this);
new ItemTouchHelper(itemTouch).attachToRecyclerView(mRecyclerView);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mMemoAdapter);
}
#Override
public void onMemoClick(int position) {
Intent intent = new Intent(this, memo_content.class);
intent.putExtra("memo_content", mNotes.get(position));
startActivity(intent);
}
private ItemTouchHelper.SimpleCallback itemTouch = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.START | ItemTouchHelper.LEFT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
mRepository.deleteMemo(mNotes.get(viewHolder.getAdapterPosition()));
Log.d(TAG, "onSwiped: "+mNotes.get(viewHolder.getAdapterPosition()));
}
};
}
Adapter
public class MemoAdapter extends RecyclerView.Adapter<MemoAdapter.MemoViewHolder> {
private ArrayList<Note> mNotes;
private OnNoteListener mListener;
public class MemoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView title, timestamp;
private MemoViewHolder(#NonNull final View itemView) {
super(itemView);
this.title = (TextView) itemView.findViewById(R.id.title);
this.timestamp = (TextView) itemView.findViewById(R.id.timestamp);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mListener.onMemoClick(getAdapterPosition());
}
}
public MemoAdapter(ArrayList<Note> notes, OnNoteListener listener) {
this.mNotes = notes;
this.mListener = listener;
}
#NonNull
#Override
public MemoViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_memo, parent, false);
return new MemoViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MemoViewHolder holder, int position) {
holder.title.setText(mNotes.get(position).getTitle());
holder.timestamp.setText(mNotes.get(position).getTimestamp());
}
public void watchMemoChanges(ArrayList<Note> notes){
this.mNotes = notes;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return mNotes.size();
}
public interface OnNoteListener{
void onMemoClick(int position);
}
Short answer:
You need to remove the statement notes.clear() when you receive a change in the LiveData list from the database via the observer.
Detailed answer
When you first run your app, it will show all right list because the condition if (mNotes.size() > 0) is not met, and so the received list won't be cleared via notes.clear(), so the RecyclerView will display the right data.
But when you delete a note, then when the observer is triggered again with the new list, the condition if (mNotes.size() > 0) will be met, so you will clear the list that is coming from the database before feeding the adapter with it, so the RecyclerView will be free of data.
So to solve this please replace notes.clear(); with mNotes.clear();
So the right code will be:
private void getMemos(){
mRepository.getAllMemos().observe(this, new Observer<List<Note>>() {
#Override
public void onChanged(List<Note> notes) {
if (mNotes.size() > 0){
mNotes.clear();
}
if (notes != null){
mNotes.addAll(notes);
mMemoAdapter.watchMemoChanges((ArrayList<Note>) notes);
}
}
});
}
Wish that help you out.
I didn't examine the rest of code, please let me know if there is another issue to help more
My MainActivity has a RecyclerView adapter, and data to this RecyclerView is added through a AlertDialog which passes the entered text to the MainActivity. The recycler view gets refreshed somehow when the positive button in the dialog is pressed even though I never call notifyItemInserted() or notifyDatasetChange() after passing the new input. I want to know how this happens, my guess is the recyclerview is somehow refreshed after the positive button is pressed in the dialog box
Custom AlertDialog Code:
public class CustomDialog extends AppCompatDialogFragment {
OnNoteAddedListener onNoteAddedListener;
public interface OnNoteAddedListener {
public void onClick(String note);
}
public CustomDialog(OnNoteAddedListener onNoteAddedListener) {
this.onNoteAddedListener = onNoteAddedListener;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
final LayoutInflater inflater = getActivity().getLayoutInflater();
final View dialogLayout = inflater.inflate(R.layout.dialog_box, null);
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(dialogLayout).setPositiveButton("Ok", new DialogInterface.OnClickListener() {#Override
public void onClick(DialogInterface dialog, int id) {
EditText addNote = dialogLayout.findViewById(R.id.note_text);
String note = addNote.getText().toString();
onNoteAddedListener.onClick(note);
}
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
CustomDialog.this.getDialog().cancel();
}
});
return builder.create();
}
}
Adapter code:
class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>
{
private static final String TAG = "RecyclerViewAdapter";
private List<String> notesList;
private Context mContext;
private SendPositionConnector sendPositionConnector;
public interface SendPositionConnector
{
public void sendPosition(int position);
}
public RecyclerViewAdapter(List<String> notesList, Context mContext)
{
this.notesList = notesList;
this.mContext = mContext;
this.sendPositionConnector = (MainActivity)mContext;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_listitem, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, final int position)
{
Log.d(TAG, "onBindViewHandler: called");
viewHolder.noteContent.setText(notesList.get(position));
viewHolder.parentLayout.setOnLongClickListener(new View.OnLongClickListener(){
#Override
public boolean onLongClick(View view)
{
Log.d(TAG, "onLongClick: long clicked on");
sendPositionConnector.sendPosition(position);
return false;
}
});
}
#Override
public int getItemCount()
{
return notesList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder
{
TextView noteContent;
RelativeLayout parentLayout;
ImageView bullet;
public ViewHolder(#NonNull View itemView)
{
super(itemView);
bullet = itemView.findViewById(R.id.bullet);
noteContent = itemView.findViewById(R.id.text_content);
parentLayout = itemView.findViewById(R.id.parent_layout);
}
}
}
Activity Code:
public class MainActivity extends AppCompatActivity implements RecyclerViewAdapter.SendPositionConnector
{
private static final String TAG = "MainActivity";
private List<String> notesList = new ArrayList<>();
private RecyclerView recyclerView;
private RecyclerViewAdapter adapter;
private int position;
public AgentAsyncTask agentAsyncTask;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.my_recycler_view);
registerForContextMenu(recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
agentAsyncTask = new AgentAsyncTask(notesList, getApplicationContext(), true, new AgentAsyncTask.OnRead(){
#Override
public void onRead(List<String> notesList)
{
if(!notesList.isEmpty())
MainActivity.this.notesList = notesList;
adapter = new RecyclerViewAdapter(notesList, MainActivity.this);
recyclerView.setAdapter(adapter);
}
});
agentAsyncTask.execute();
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.add_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle item selection
switch (item.getItemId())
{
case R.id.add_note:
showDialogBox(item);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
protected void onStop()
{
super.onStop();
new AgentAsyncTask(notesList, getApplicationContext(), false, new AgentAsyncTask.OnRead(){
#Override
public void onRead(List<String> notesList)
{
if(!notesList.isEmpty())
MainActivity.this.notesList = notesList;
}
}).execute();
}
#Override
protected void onDestroy()
{
super.onDestroy();
}
private boolean showDialogBox(MenuItem menuItem)
{
AppCompatDialogFragment dialogFragment = new CustomDialog(new CustomDialog.OnNoteAddedListener(){
#Override
public void onClick(String note)
{
Log.d(TAG, "onClick: "+ note);
notesList.add(note);
}
});
dialogFragment.show(getSupportFragmentManager(),"Adding");
return true;
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
{
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
}
#Override
public boolean onContextItemSelected(MenuItem menuItem)
{
switch(menuItem.getItemId())
{
case R.id.delete:
notesList.remove(position);
adapter.notifyItemRemoved(position);
adapter.notifyItemRangeChanged(position, notesList.size());
return true;
default:
return false;
}
}
#Override
public void sendPosition(int position)
{
this.position = position;
}
private static class AgentAsyncTask extends AsyncTask<Void, Void, List<String>>
{
private List<String> notesList;
private boolean flag;
OnRead onRead;
Context context;
AppDataBase dataBase;
private static final String TAG = "AgentAsyncTask";
public interface OnRead
{
public void onRead(List<String> notesList);
}
private AgentAsyncTask(List<String> notesList,Context context,boolean flag, OnRead onRead)
{
this.notesList = notesList;
this.onRead = onRead;
this.flag = flag;
this.context = context;
}
#Override
protected List<String> doInBackground(Void... params)
{
dataBase = Room.databaseBuilder(context, AppDataBase.class, "database-name").build();
if(!flag)
{
Gson gson = new Gson();
Type type = new TypeToken<List<String>>() {}.getType();
String json = gson.toJson(notesList, type);
Log.d(TAG, "doInBackground: "+json);
Notes notes = new Notes();
notes.setNoteContent(json);
notes.setUid(1);
dataBase.notesDao().insertNotes(notes);
return notesList;
}
else
{
Gson gson = new Gson();
String notesListContent = dataBase.notesDao().getNotes();
if(dataBase.notesDao().getCount() != 0)
{
notesList = gson.fromJson(notesListContent, new TypeToken<List<String>>()
{
}.getType());
}
else
{
return notesList;
}
return notesList;
}
}
#Override
protected void onPostExecute(List<String> notesList)
{
super.onPostExecute(notesList);
if(flag)
onRead.onRead(notesList);
}
}
}
What's probably happening is that when the dialog returns, it causes a re-layout of the RecyclerView, which rebinds the views. This is prone to bugs though, since it may not have updated the recycler about stuff like the list length or item view types, etc, so the appropriate notify method should always be used.
When you get the text from the dialog to main activity after pressing the positive button.
Append your list with that new text that you are passing to adapter and call method
adapter.notifyDataSetChanged();
I'm making a to do list app using a RecyclerView. When a task is clicked a dialog will appear and the user will be able to edit the name of the task. Once the user exists the dialog I want to reflect the changes of the task in the RecyclerView.
I have an activity called ListActivity that hosts a fragment which contains the RecyclerView. Likewise, I create my AlertDialog in a class called EditTaskFragment. Lastly, I store all the task objects in an arraylist in a Singleton class called TaskLab.
To retrieve the String after the user changes the name of a task item in the RecyclerView I am calling onActivityResult() in the ListFragment class. But I'm not sure how to update the task object that is stored in my Singleton and then how to get the ViewHolder to changes the Title field of a task item.
My ListFragment class
public class ListFragment extends Fragment {
private static final int REQUEST_TITLE = 0;
private RecyclerView mRecyclerView;
private TaskAdapter mAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_list, container, false);
mRecyclerView = view.findViewById(R.id.the_task_list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
updateUI();
return view;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == Activity.RESULT_OK && requestCode == ListFragment.REQUEST_TITLE) {
// Do something.
}
}
#Override
public void onResume() {
super.onResume();
updateUI();
}
public void updateUI() {
List<Task> taskList = TaskLab.get(getActivity()).getTaskList();
if(mAdapter == null) {
mAdapter = new TaskAdapter(taskList);
mRecyclerView.setAdapter(mAdapter);
}else {
mAdapter.notifyDataSetChanged();
}
}
private class TaskAdapter extends RecyclerView.Adapter<TaskHolder>{
private List<Task> mTaskList;
public TaskAdapter(List<Task> taskList) {
mTaskList = taskList;
}
#Override
public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(getActivity()).inflate(R.layout.task_list_layout, parent, false);
return new TaskHolder(view);
}
#Override
public void onBindViewHolder(TaskHolder holder, int position) {
Task currentTask = mTaskList.get(position);
holder.bindData(currentTask);
}
#Override
public int getItemCount() {
return mTaskList.size();
}
}
private class TaskHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private Task mTask;
private TextView mTaskTitle;
private CheckBox mSolved;
private static final String DIALOG = "edit_dialog";
public TaskHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
mTaskTitle = itemView.findViewById(R.id.task_title);
mSolved = itemView.findViewById(R.id.task_solved);
}
public void bindData(Task task) {
mTask = task;
mTaskTitle.setText(mTask.getTitle());
mSolved.setChecked(mTask.isSolved());
}
#Override
public void onClick(View view) {
FragmentManager manager = getFragmentManager();
EditTaskFragment dialog = EditTaskFragment.newInstance(mTask.getTitle());
dialog.setTargetFragment(ListFragment.this, REQUEST_TITLE);
dialog.show(manager, DIALOG);
}
}
}
My EditTaskFragment class
public class EditTaskFragment extends DialogFragment {
private EditText mTaskTitle;
private String mTaskName;
private static final String ARG_TASK_TITLE = "task_title";
public static final String EXTRA_TASK_TITLE = "list_app_task_title";
private void sendResult(int resultCode, String taskName) {
if(getTargetFragment() == null) {
return;
}
Intent intent = new Intent();
intent.putExtra(EXTRA_TASK_TITLE,taskName);
getTargetFragment().onActivityResult(getTargetRequestCode(),resultCode, intent);
}
public static EditTaskFragment newInstance(String taskName) {
Bundle args = new Bundle();
args.putString(ARG_TASK_TITLE, taskName);
EditTaskFragment fragment = new EditTaskFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTaskName = getArguments().getString(ARG_TASK_TITLE);
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_edit_task_dialog, null);
mTaskTitle = view.findViewById(R.id.edit_task_title);
mTaskTitle.setText(mTaskName);
return new AlertDialog.Builder(getActivity())
.setView(view)
.setTitle(R.string.edit_task_dialog_title)
.setPositiveButton(R.string.edit_task_dialog_positive, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
sendResult(Activity.RESULT_OK, mTaskTitle.getText().toString());
}
})
.setNegativeButton(R.string.edit_task_dialog_negative, null)
.create();
}
}
My Singleton class - TaskLab
public class TaskLab {
private static TaskLab sTaskLab;
private List<Task> mTaskList;
private TaskLab(Context context){
mTaskList = new ArrayList<>();
for(int i = 0; i < 10; i++) {
Task task = new Task();
task.setTitle("Task #" + i);
task.setSolved(i%2 == 0);
mTaskList.add(task);
}
}
public static TaskLab get(Context context) {
if(sTaskLab == null) {
sTaskLab = new TaskLab(context);
}
return sTaskLab;
}
public List<Task> getTaskList(){
return mTaskList;
}
public Task getTask(int id){
for(Task task : mTaskList) {
if(task.getId().equals(id)){
return task;
}
}
return null;
}
}
In EditTaskFragment.sendResult() you are calling onActivityResult() of the target fragment. This is wrong. onActivityResult() is meant to be called by the framework, not the programmer.
What you have to do is use the enclosing activity of the fragments in order to pass data between the fragments.
Define an interface TaskChangedListener
Make your activity implement it, as well as ListFragment
In EditTaskFragment.sendResult(), use ((TaskChangedListener) getActivity()).onTaskChanged(taskName)
In onTaskChanged() of the activity, you will need to find the ListFragment (you can find it by tag from the fragment manager) and then call its onTaskChanged() from there.
In onTaskChanged() of ListFragment do:
List taskList = TaskLab.get(getActivity()).getTaskList();
mAdapter.clear();
mAdapter.addAll(taskList);
mAdapter.notifyDataSetChanged();
You will need to add the clear() and addAll() methods to your TaskAdapter class:
void clear() {
mTaskList.clear();
}
void addAll(List<Task> taskList) {
mTaskList.addAll(taskList);
}
As a next step