【发布时间】:2018-08-19 21:09:14
【问题描述】:
我目前正在尝试为我的应用程序实现一个上下文菜单,有一个任务列表,当用户长按一个任务时,应该会出现一个上下文菜单,根据他选择的项目为用户提供一些选项。
实现菜单本身并不太难,我的实现给出了我期望的结果:
@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]);
}
}
}
但是对于处理用户点击,麻烦来了,奇怪的事情发生了:
@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;
}
当我尝试检索menuChoosenTask 时会出现问题,由于某种原因,taskAdapter 可能会减小它的大小,也就是说,从我在调试器中看到的:创建菜单时taskAdapter 的大小为@987654325 @ 和 info.position 是 3 所以没问题,但是当我点击时,info.position 仍然是 3 但现在 taskAdapter 的大小为 2。
因此,我首先尝试通过将menuChoosenTask 设置为全局变量来解决此问题,并在创建菜单时设置它的状态,并在用户选择一个选项时重新使用它,但由于某些原因,当选项被选中并变为null。
我的问题是:如何检索对象以及为什么会发生这些奇怪的事情?
编辑:
我设法缩小了问题范围:您看到的 2 个函数在一个名为 DayFragment 的类中,其中一个变量在调用时由子类设置,所以基本上发生的是第一种方法(菜单创建) 是从一个孩子调用的(假设是第二个,从我所见:我屏幕上的那个)但是第二种方法(选项选择)是从另一个调用的,这里是类的完整代码加一个它的子类:
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;
}
}
它的一个子类(有 7 个):
public class MondayFragment extends DayFragment {
public static final int DAY_ID = 0;
public void setDayId(){
super.DAY_ID = DAY_ID;
}
}
所有这些片段都显示在 PagerAdapter(与视图寻呼机相结合)中,所以我“有点”明白为什么会发生这种情况,但我不知道如何防止它。
【问题讨论】:
-
在方法
onCreateContextMenu()内可能没有点击检查。 -
int menuItemIndex = item.getItemId();也将 id 与 index 混为一谈......而 ids 通常是从 1 开始的,而索引通常是从 0 开始的 - 这导致偏移量为 +1。 -
@MartinZeitler 好的,谢谢,我不知道,但在这种情况下,当我设置 id 时,这不是问题
-
@tomgautot 您使用 id 设置索引。这仅在这些 id 从 0 开始时才有效。
标签: android variables memory contextmenu object-lifetime