-1

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.

tom gautot
  • 378
  • 2
  • 10

1 Answers1

0

Ok, well I just spent an hour and a half googling stuff and I finally found a solution :

Wrong fragment in ViewPager receives onContextItemSelected call

As I didn't fully understand what went wrong it took me really long to find it. So my Bad and sorry for all the trouble

tom gautot
  • 378
  • 2
  • 10