【问题标题】:Where to call filter for ArrayAdapter?在哪里调用 ArrayAdapter 的过滤器?
【发布时间】:2014-01-22 16:02:47
【问题描述】:

我正在开发一个家庭作业计划器应用程序,并且我正在寻找一种方法来仅显示包含 Task 对象的 ArrayList 中的某些元素。用户从课程标题列表中单击课程后,应显示与该课程相关的任务列表。目前,它显示所有任务的列表,无论选择了哪个课程。每个 Task 对象都将其所属的课程存储在名为 mBelongsToCourse 的字段中。我在我的 TaskAdapter 中创建了一个过滤器来按课程名称进行过滤,但我不知道在哪里调用它才能使它工作。我已经在这里广泛搜索了这个问题的答案,但是在任何人过滤 ArrayAdapter 的情况下,过滤器似乎都在响应一些用户输入,例如搜索。在我的例子中,当用户选择一门课程时,用户输入只是课程名称的额外意图,它启动了显示我的 TaskListFragment 的意图,但应该只显示该课程的任务。

下面是我的TaskListFragment 类,其中包括TaskAdapter 和TaskFilter:

public class TaskListFragment extends ListFragment {

private ArrayList<Task> mTasks;
private  static String courseName;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
    courseName = getActivity().getIntent().getStringExtra("name");
    getActivity().setTitle(courseName);
    mTasks = TaskLab.get(getActivity()).getTasks();

    TaskAdapter adapter = new TaskAdapter(mTasks);
    setListAdapter(adapter);
}

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    //Get the Task from the adapter
    Task t = ((TaskAdapter)getListAdapter()).getItem(position);
    // Start TaskActivity with this task
    //Intent i = new Intent(getActivity(), TaskPagerActivity.class);
    Intent i = new Intent(getActivity(), TaskActivity.class);
    i.putExtra(TaskFragment.EXTRA_TASK_ID, t.getId());
    startActivity(i);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.fragment_task_list, menu);
} 

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.menu_item_new_task:
        Task task = new Task();
        task.setBelongsToCourse(courseName);
        Log.d("yoyo", "belongsToCourse was set as: " + task.getBelongsToCourse());
        TaskLab.get(getActivity()).addTask(task);
        Intent i = new Intent(getActivity(), TaskActivity.class);
        i.putExtra(TaskFragment.EXTRA_TASK_ID, task.getId());
        startActivityForResult(i, 0);
        return true;
    default:
        return super.onOptionsItemSelected(item);
    }
}

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
    getActivity().getMenuInflater().inflate(R.menu.task_list_item_context, menu);
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
        Bundle savedInstanceState) {
    View v = super.onCreateView(inflater, parent, savedInstanceState);
    ListView listView = (ListView)v.findViewById(android.R.id.list);
    listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);

    listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {

        public void onItemCheckedStateChanged(ActionMode mode, int position,
                long id, boolean checked) {
            // Required, but not used in this implementation
        }

        // ActionMode.Callback methods
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.task_list_item_context, menu);
            return true;
        }

        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
            // Required, but not used in this implementation
        }
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
            case R.id.menu_item_delete_task:
                TaskAdapter adapter = (TaskAdapter)getListAdapter();
                TaskLab taskLab = TaskLab.get(getActivity());
                for (int i = adapter.getCount() - 1; i >= 0; i--) {
                    if (getListView().isItemChecked(i)) {
                        taskLab.deleteTask(adapter.getItem(i));
                    }
                }
                mode.finish();
                adapter.notifyDataSetChanged();
                return true;
            default:
                return false;
            }
        }

        public void onDestroyActionMode(ActionMode mode) {
            // Required, but not used in this implementation
        }
    });
    return v;
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
    int position = info.position;
    TaskAdapter adapter = (TaskAdapter)getListAdapter();
    Task task = adapter.getItem(position);

    switch (item.getItemId()) {
    case R.id.menu_item_delete_task:
        TaskLab.get(getActivity()).deleteTask(task);
        adapter.notifyDataSetChanged();
        return true;
    }
    return super.onContextItemSelected(item);
}


@Override
public void onResume() {
    super.onResume();
    ((TaskAdapter)getListAdapter()).notifyDataSetChanged();
}

private class TaskAdapter extends ArrayAdapter<Task> implements Filterable {

    private ArrayList<Task> taskList;
    private Filter taskFilter;

    public TaskAdapter(ArrayList<Task> tasks) {
        super(getActivity(), 0, tasks);
        this.taskList = tasks;
        this.taskFilter = new TaskFilter();
        getFilter().filter(courseName);
        notifyDataSetChanged();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // If we weren't given a view, inflate one
        if (convertView == null) {
            convertView = getActivity().getLayoutInflater()
                    .inflate(R.layout.list_item_task, null);
        }

        // Configure the view for this Task
        Task t = getItem(position);

        TextView titleTextView =
                (TextView)convertView.findViewById(R.id.task_list_item_titleTextView);
        titleTextView.setText(t.getTitle());
        TextView dateTextView =
                (TextView)convertView.findViewById(R.id.task_list_item_dateTextView);
        dateTextView.setText(t.getDate().toString());
        CheckBox completedCheckBox =
                (CheckBox)convertView.findViewById(R.id.task_list_item_completedCheckBox);
        completedCheckBox.setChecked(t.isCompleted());
        return convertView;
    }

    @Override
    public Filter getFilter() {
        if (taskFilter == null)
            taskFilter = new TaskFilter();
        return taskFilter;
    }

    private class TaskFilter extends Filter {

        @Override
        protected FilterResults performFiltering (CharSequence constraint) {
            FilterResults results = new FilterResults();
            if (constraint == null | constraint.length() == 0) {
                results.values = taskList;
                results.count = taskList.size();
            } else {
                ArrayList<Task> newTaskList = new ArrayList<Task>();
                for (Task t : taskList) {
                    if (t.getBelongsToCourse().toUpperCase().equals(constraint.toString().toUpperCase())) {
                        newTaskList.add(t);
                    }
                }
                results.values = newTaskList;
                results.count = newTaskList.size();
            } return results;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint,
                FilterResults results) {

            // Now we have to inform the adapter about the new list filtered
            if (results.count == 0)
                notifyDataSetInvalidated();
            else {
                taskList = (ArrayList<Task>)results.values;
                notifyDataSetChanged();
            }
        }
    }
}
}

任何帮助将不胜感激!我已经尝试解决这个问题太久了,但我仍然完全迷失了。

编辑:我已经删除了与原始过滤器有关的所有内容,而是在我的 TaskAdapter 中使用此方法:

public ArrayList<Task> filterTasks() {
     ArrayList<Task> filteredTasks = new ArrayList<Task>();
     for (Task t: taskList) {
          if (t.getBelongsToCourse().equals(courseName)) {
               filteredTasks.add(t);
          }
     } return filteredTasks;
}

过滤任务。在TaskListFragment的onCreate方法中,我在下面添加了中间一行:

TaskAdapter adapter = new TaskAdapter(mTasks);
adapter.filterTasks();
setListAdapter(adapter);

我还让 LogCat 打印出 filtersTasks 中的任务列表(仍在 onCreate 方法中)。因此,当我单击课程,添加任务,然后返回 TaskListFragment 时,我会打印出属于该课程的任务。每门课程的列表都是分开的!在过去一周试图做到这一点并没有看到任何结果之后,到目前为止,这对我来说是一个很大的进步。尽管如此,我还是在每个课程页面上看到了所有任务,而不是过滤后的任务。这只是我的 listView 没有更新的问题吗?

【问题讨论】:

  • 开始显示我的 TaskListFragment 的意图, - 将目标课程发送到您的 TaskListFragment(通过它创建时的 Bundle)并将其传递给 TaskAdapter班级。在TaskAdapter 的构造函数中,只需过滤任务列表。如果您不想在使用适配器时更改适配器,则不需要实现Filter(这似乎不是您的情况,因为您想在首次显示片段时过滤(仅一次?) )。
  • 嗨 Luksprog,非常感谢您的回答!目前,我正在从我的 CourseListFragment 向 TaskListFragment 发送一个额外的意图,这是已单击的课程的名称。所以我也应该把它传递给我的TaskAdapter?目前,由于它们都在同一个类中,因此包含课程名称的字段可以通过 TaskAdapter 访问,并且我尝试直接在构造函数中过滤结果,但是无论如何,我都得到了完整的任务列表当然我点击了。
  • 有什么办法可以给我更详细的说明我应该做什么,因为我觉得我必须忽略一些我没有意识到的重要方面,因为我是新手安卓。
  • 如果您使用 BaseAdapter(以获得更大的灵活性)有一个过滤系统,您可以覆盖 getFilter() 并实现 Filterable gist.github.com/fjfish/3024308

标签: android android-listview android-arrayadapter android-listfragment android-filterable


【解决方案1】:

在 onCreate 方法中,您将前一个 Activity 的所有任务传递给您的 Adapter,然后将其设置为您的 ListView,为什么不先过滤您的任务,然后再将它们传递给适配器?繁重的工作应该在不同的线程中完成,但为了避免阻塞 UI 太久,在开始初始化列表时显示一个 progressDialog,当它被过滤时隐藏它。

我的意思是如果你给 ListView 充气,用包含所有任务的列表设置它的 Adapter,如果你想在初始化后修改 List,你应该在设置 Adapter 之前这样做,以避免在上面放置额外的项目。如果您仍想这样做,请将过滤器应用到任务列表,然后调用 notifyDataSetChanged() 让 Adapter 更改项目。恕我直言,我会在将任务列表传递给适配器之前更改它以避免这种情况。如果您有 UI 冻结,请使用其他线程。

【讨论】:

  • 好的,我已经取出了与我的过滤器有关的所有内容,并在 TaskAdapter 中创建了一个方法来按课程名称过滤任务。然后在 TaskListFragment 的 onCreate 方法中,我添加了下面的中间行:TaskAdapter adapter = new TaskAdapter(mTasks);适配器.filterTasks(); setListAdapter(适配器);这是你的意思吗?如果我要摆脱实际的过滤器,我不应该调用 notifyDataSetChanged(),对吧?
  • 你越来越近了。将此方法放入 Adapter 将阻塞 UI,如果您看到 UI 冻结,请将其放入另一个线程。我是一个非常完美主义的开发人员,我会将该方法放在另一个类中或作为 TaskListFragment 中的私有方法,我真的不认为 Adapter 类是它的位置......但是,是的,本质上这就是我的意思。如果它解决了你的问题,请将我的答案标记为正确。
  • 好的,我将 filterTasks() 方法移到了 TaskListFragment 中。在我的 onCreate() 中,为此进行了修改:ArrayList filteredTaskList = filterTasks(mTasks); TaskAdapter 适配器 = new TaskAdapter(filteredTaskList);但是现在,我的 TaskListFragment 没有显示任何任务。 LogCat 确认正在保存任务,但在任何课程下都没有显示任何内容。我以前尝试过这种方式,但被没有任务显示的事实所困扰。
  • 好吧,从我刚才所说的关于没有任务显示的内容开始。它运行良好,但直到我返回课程列表并再次选择课程。然后出现过滤的任务。有什么方法可以让它们在我第一次返回 TaskListFragment 时出现?非常感谢您的帮助,我真的很感激。
  • 您应该首先使用构造函数将 ArrayList 过滤任务列表创建为对象。当您过滤任务时,您不会修改所有任务列表,不是吗?该对象应该是最终的,因此它永远不会被修改。当您问是否可以在您第一次返回 TaskListFragment 时显示任务时,您是什么意思?按下后退按钮?点击课程列表项?
猜你喜欢
  • 2023-03-24
  • 2013-01-23
  • 2018-04-29
  • 2018-05-02
  • 2017-03-31
  • 1970-01-01
  • 1970-01-01
  • 2016-07-30
  • 1970-01-01
相关资源
最近更新 更多