I'm currently trying to implement a context menu for my app, there is a list of tasks and when the user long-clicks on one, a context menu should appear giving the user some options depending on the item he selected.
Implementing the menu itself wasn't too hard and my implementation gives the result I expect :
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
if(v.getId() == R.id.list){ //check if the click comes from the listview
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
menuChoosenTask = taskAdapter.getItem(info.position); //retrive the choosen task
Log.e("CreateMenu", "Choose item with name " + menuChoosenTask.getTaskName());
menu.setHeaderTitle(menuChoosenTask.getTaskName());
int options = 3;
if(menuChoosenTask.getTypeOfTask() == WeeklyTask.TYPE_TASK_WORK) options = 4;
for(int i = 0; i < options; i++){
menu.add(Menu.NONE, i, i, menuOptions[i]);
}
}
}
But for handling user clicks, the trouble comes and weird things happen :
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
final int menuItemIndex = item.getItemId();
menuChoosenTask = taskAdapter.getItem(info.position);
int choosenTaskId = menuChoosenTask.getId();
String selectedOption = menuOptions[menuItemIndex];
if(selectedOption.equals(getString(R.string.cm_option_edit))){
//DO STUFF
}
if(selectedOption.equals(getString(R.string.cm_option_delete_for_everyday))){
//DO STUFF
}
if(selectedOption.equals(getString(R.string.cm_option_delete_for_this_day)){
//DO STUFF
}
return true;
}
The problems happens when I try to retrieve the menuChoosenTask
, for some reason the taskAdapter may reduce it's size, that is, from what i've seen in the debugger : When creating the menu taskAdapter
has size 4
and info.position
is 3
so no problem, but when I click, info.position
is still 3
but now taskAdapter
has size 2
.
So I first tried to work around this by setting menuChoosenTask
as a global variable and just set it's state when the menu is created and re-use it when the user selects an option, but for some reasons the variable loses it's state when an option is selected and becomes null
.
My question is : How can I retrieve the object and why do these weird things happen?
EDIT :
I managed to narrow down the problem a bit : The 2 functions you see are in a class called DayFragment and one of it's variable is set by the children class when called, so basically what happens is that the first method (menu creation) is called from a child (let's say the second, and from what I've seen: the one on my screen) but the second method (option selection) is called from another one , here is the full code of the class plus one of its subclasses :
DayFragment.java:
public abstract class DayFragment extends Fragment {
protected int DAY_ID = 0;
protected List<WeeklyTask> tasks;
protected TaskAdapter taskAdapter;
protected MainViewModel mainViewModel;
protected AppDatabase mDB;
protected String[] menuOptions = {"Edit", "Delete for this day", "Delete for everyday", "To-Do's"};
protected WeeklyTask menuChoosenTask;
public DayFragment(){}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.tasks_list, container, false);
setDayId();
tasks = new ArrayList<>();
taskAdapter = new TaskAdapter(getActivity(), tasks);
mDB = AppDatabase.getInstance(getContext());
ListView listView = rootView.findViewById(R.id.list);
listView.setAdapter(taskAdapter);
registerForContextMenu(listView);
mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
setupFAB( (FloatingActionButton) rootView.findViewById(R.id.fab));
setupViewModel();
return rootView;
}
public abstract void setupFAB(FloatingActionButton fab);
protected abstract void setDayId();
public void setupViewModel() {
mainViewModel.getTasks().removeObservers(this);
mainViewModel.getTasks().observe(this, new Observer<List<WeeklyTask>>() {
@Override
public void onChanged(@Nullable List<WeeklyTask> weeklyTasks) {
taskAdapter.clear();
int id = 0;
WeeklyTask task;
for(int i = 0; i < weeklyTasks.size(); i++){
task = weeklyTasks.get(i);
if(task.getDays()[DAY_ID]){ // IF should be shown this day
taskAdapter.insert(task, id);
id++;
}
}
taskAdapter.notifyDataSetChanged();
}
});
}
public boolean atLeastOneTrue(boolean[] arr){
boolean out = false;
for(int i = 0; i < arr.length; i++){
out = out | arr[i];
}
return out;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
if(v.getId() == R.id.list){ //check if the click comes from the listview
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
menuChoosenTask = taskAdapter.getItem(info.position);
Log.e("CreateMenu", "Choose item with name " + menuChoosenTask.getTaskName() + " for day " + DAY_ID);
menu.setHeaderTitle(menuChoosenTask.getTaskName());
int options = 3;
if(menuChoosenTask.getTypeOfTask() == WeeklyTask.TYPE_TASK_WORK) options = 4;
for(int i = 0; i < options; i++){
menu.add(Menu.NONE, i, i, menuOptions[i]);
}
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
Log.e("menuClicked", "menu for day " + DAY_ID + " clicked on item no " + info.position);
final int menuItemIndex = item.getItemId();
menuChoosenTask = taskAdapter.getItem(info.position);
int choosenTaskId = menuChoosenTask.getId();
String selectedOption = menuOptions[menuItemIndex];
if(selectedOption.equals(getString(R.string.cm_option_edit))){
//DOSTUFF
}
if(selectedOption.equals(getString(R.string.cm_option_delete_for_everyday))){
//DOSTUFF
}
if(selectedOption.equals(getString(R.string.cm_option_delete_for_this_day))){
//DOSTUFF
}
return true;
}
}
One of its children classes (there are 7 of them) :
public class MondayFragment extends DayFragment {
public static final int DAY_ID = 0;
public void setDayId(){
super.DAY_ID = DAY_ID;
}
}
All these fragments are shown in a PagerAdapter (coupled with a view pager) so I 'kinda' get why this happens but I have no idea how to prevent it.