使用游标适配器实现具有多个选择和过滤器的列表视图

发布于 2025-01-08 06:46:30 字数 6106 浏览 1 评论 0原文

这个问题在这个问题Android:过滤listview时检查了错误的项目。总结一下这个问题,当使用带有 CursorAdapter 和过滤器的列表视图时,过滤列表中选择的项目在过滤器被删除后会失去选择,而是选择未过滤列表中该位置的项目。

使用上面链接问题中的代码示例,我们应该在哪里放置代码来标记复选框。我相信它应该在 CustomCursorAdapter 的 getView() 方法中,但我不确定。另外,我们如何访问自定义适配器类中保存所有 selectedId 的 HashSet,因为它将在保存列表的主活动中进行初始化和修改。

我的活动实现了 ListView

@Override
public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.selectfriends);

      Log.v(TAG, "onCreate called") ;

      selectedIds = new ArrayList<String>() ;
      selectedLines = new ArrayList<Integer>()  ;

      mDbHelper = new FriendsDbAdapter(this);
      mDbHelper.open() ;

      Log.v(TAG, "database opened") ;

      Cursor c = mDbHelper.fetchAllFriends();
      startManagingCursor(c);

      Log.v(TAG, "fetchAllFriends Over") ;


      String[] from = new String[] {mDbHelper.KEY_NAME};
      int[] to = new int[] { R.id.text1 };

      final ListView listView = getListView();
      Log.d(TAG, "Got listView");

   // Now initialize the  adapter and set it to display using our row
       adapter =
           new FriendsSimpleCursorAdapter(this, R.layout.selectfriendsrow, c, from, to);

       Log.d(TAG, "we have got an adapter");
     // Initialize the filter-text box 
     //Code adapted from https://stackoverflow.com/questions/1737009/how-to-make-a-nice-looking-listview-filter-on-android

       filterText = (EditText) findViewById(R.id.filtertext) ;
       filterText.addTextChangedListener(filterTextWatcher) ;

     /* Set the FilterQueryProvider, to run queries for choices
     * that match the specified input.
     *  Code adapted from https://stackoverflow.com/questions/2002607/android-how-to-text-filter-a-listview-based-on-a-simplecursoradapter
     */

       adapter.setFilterQueryProvider(new FilterQueryProvider() {
            public Cursor runQuery(CharSequence constraint) {
                // Search for friends whose names begin with the specified letters.
                Log.v(TAG, "runQuery Constraint = " + constraint) ;
                String selection = mDbHelper.KEY_NAME + " LIKE '%"+constraint+"%'";

                mDbHelper.open(); 
                Cursor c = mDbHelper.fetchFriendsWithSelection(
                 (constraint != null ? constraint.toString() : null));
                return c;
     }
 });




       setListAdapter(adapter);

       Log.d(TAG, "setListAdapter worked") ;


       listView.setItemsCanFocus(false);
       listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

       // listView.setOnItemClickListener(mListener);

       Button btn;
       btn = (Button)findViewById(R.id.buttondone);

       mDbHelper.close();

  }


    @Override   
    protected void onListItemClick(ListView parent, View v, int position, long id) {

        String item = (String) getListAdapter().getItem(position);
        Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show();

        //gets the Bookmark ID of selected position
         Cursor cursor = (Cursor)parent.getItemAtPosition(position);
         String bookmarkID = cursor.getString(0);

         Log.d(TAG, "mListener -> bookmarkID = " + bookmarkID);

         Log.d(TAG, "mListener -> position = " + position);

 //        boolean currentlyChecked = checkedStates.get(position);
 //        checkedStates.set(position, !currentlyChecked);


         if (!selectedIds.contains(bookmarkID)) {

             selectedIds.add(bookmarkID);
             selectedLines.add(position);

         } else {

             selectedIds.remove(bookmarkID);
             selectedLines.remove(position);


             }

     }



  private TextWatcher filterTextWatcher = new TextWatcher() {

      public void afterTextChanged(Editable s)  {

      }

      public void beforeTextChanged(CharSequence s, int start, int count, int after)    {

      }

      public void onTextChanged(CharSequence s, int start, int before, int count)   {
          Log.v(TAG, "onTextChanged called. s = " + s);
          adapter.getFilter().filter(s);
      }
  };

  @Override
  protected void onDestroy()    {
        super.onDestroy();
        filterText.removeTextChangedListener(filterTextWatcher);
  }

我的自定义光标适配器:

public class FriendsSimpleCursorAdapter extends SimpleCursorAdapter implements Filterable {

private static final String TAG = "FriendsSimpleCursorAdapter";
private final Context context ;
private final String[] values ;
private final int layout ;
private final Cursor cursor ;

static class ViewHolder {
    public CheckedTextView checkedText ;
}

public FriendsSimpleCursorAdapter(Context context, int layout, Cursor c,
        String[] from, int[] to) {
    super(context, layout, c, from, to);
    this.context = context ;
    this.values = from ;
    this.layout = layout ;
    this.cursor = c ;
    Log.d(TAG, "At the end of the constructor") ;
}

@Override
public View getView(int position, View convertView, ViewGroup parent)   {
    Log.d(TAG, "At the start of rowView. position = " + position) ;
    View rowView = convertView ;
    if(rowView == null) {
        Log.d(TAG, "rowView = null");
        try {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        rowView = inflater.inflate(layout, parent, false);
        Log.d(TAG, "rowView inflated. rowView = " + rowView);
        ViewHolder viewHolder = new ViewHolder() ;
        viewHolder.checkedText = (CheckedTextView) rowView.findViewById(R.id.text1) ;
        rowView.setTag(viewHolder);
        }
        catch (Exception e) {
            Log.e(TAG, "exception = " + e);
        }
    }

    ViewHolder holder = (ViewHolder) rowView.getTag();

    int nameCol = cursor.getColumnIndex(FriendsDbAdapter.KEY_NAME) ;
    String name = cursor.getString(nameCol);
    holder.checkedText.setText(name);

    Log.d(TAG, "At the end of rowView");
    return rowView;

}

}

This problem is discussed in this question Android: Wrong item checked when filtering listview. To summarize the problem, when using a listview with a CursorAdapter and a filter, items selected on a filtered list lose their selection after the filter is removed and instead, items at that position in the unfiltered list get selected.

Using the code sample in the linked question above, where should we put the code to mark the checkboxes. I believe it should be in the getView() method of the CustomCursorAdapter, but I am not sure. Also, how do we access the HashSet holding all the selectedIds in the custom adapter class, because it will be initialized and modified in the main activity holding the list.

My activity implementing the ListView

@Override
public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.selectfriends);

      Log.v(TAG, "onCreate called") ;

      selectedIds = new ArrayList<String>() ;
      selectedLines = new ArrayList<Integer>()  ;

      mDbHelper = new FriendsDbAdapter(this);
      mDbHelper.open() ;

      Log.v(TAG, "database opened") ;

      Cursor c = mDbHelper.fetchAllFriends();
      startManagingCursor(c);

      Log.v(TAG, "fetchAllFriends Over") ;


      String[] from = new String[] {mDbHelper.KEY_NAME};
      int[] to = new int[] { R.id.text1 };

      final ListView listView = getListView();
      Log.d(TAG, "Got listView");

   // Now initialize the  adapter and set it to display using our row
       adapter =
           new FriendsSimpleCursorAdapter(this, R.layout.selectfriendsrow, c, from, to);

       Log.d(TAG, "we have got an adapter");
     // Initialize the filter-text box 
     //Code adapted from https://stackoverflow.com/questions/1737009/how-to-make-a-nice-looking-listview-filter-on-android

       filterText = (EditText) findViewById(R.id.filtertext) ;
       filterText.addTextChangedListener(filterTextWatcher) ;

     /* Set the FilterQueryProvider, to run queries for choices
     * that match the specified input.
     *  Code adapted from https://stackoverflow.com/questions/2002607/android-how-to-text-filter-a-listview-based-on-a-simplecursoradapter
     */

       adapter.setFilterQueryProvider(new FilterQueryProvider() {
            public Cursor runQuery(CharSequence constraint) {
                // Search for friends whose names begin with the specified letters.
                Log.v(TAG, "runQuery Constraint = " + constraint) ;
                String selection = mDbHelper.KEY_NAME + " LIKE '%"+constraint+"%'";

                mDbHelper.open(); 
                Cursor c = mDbHelper.fetchFriendsWithSelection(
                 (constraint != null ? constraint.toString() : null));
                return c;
     }
 });




       setListAdapter(adapter);

       Log.d(TAG, "setListAdapter worked") ;


       listView.setItemsCanFocus(false);
       listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

       // listView.setOnItemClickListener(mListener);

       Button btn;
       btn = (Button)findViewById(R.id.buttondone);

       mDbHelper.close();

  }


    @Override   
    protected void onListItemClick(ListView parent, View v, int position, long id) {

        String item = (String) getListAdapter().getItem(position);
        Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show();

        //gets the Bookmark ID of selected position
         Cursor cursor = (Cursor)parent.getItemAtPosition(position);
         String bookmarkID = cursor.getString(0);

         Log.d(TAG, "mListener -> bookmarkID = " + bookmarkID);

         Log.d(TAG, "mListener -> position = " + position);

 //        boolean currentlyChecked = checkedStates.get(position);
 //        checkedStates.set(position, !currentlyChecked);


         if (!selectedIds.contains(bookmarkID)) {

             selectedIds.add(bookmarkID);
             selectedLines.add(position);

         } else {

             selectedIds.remove(bookmarkID);
             selectedLines.remove(position);


             }

     }



  private TextWatcher filterTextWatcher = new TextWatcher() {

      public void afterTextChanged(Editable s)  {

      }

      public void beforeTextChanged(CharSequence s, int start, int count, int after)    {

      }

      public void onTextChanged(CharSequence s, int start, int before, int count)   {
          Log.v(TAG, "onTextChanged called. s = " + s);
          adapter.getFilter().filter(s);
      }
  };

  @Override
  protected void onDestroy()    {
        super.onDestroy();
        filterText.removeTextChangedListener(filterTextWatcher);
  }

My Custom Cursor Adapter:

public class FriendsSimpleCursorAdapter extends SimpleCursorAdapter implements Filterable {

private static final String TAG = "FriendsSimpleCursorAdapter";
private final Context context ;
private final String[] values ;
private final int layout ;
private final Cursor cursor ;

static class ViewHolder {
    public CheckedTextView checkedText ;
}

public FriendsSimpleCursorAdapter(Context context, int layout, Cursor c,
        String[] from, int[] to) {
    super(context, layout, c, from, to);
    this.context = context ;
    this.values = from ;
    this.layout = layout ;
    this.cursor = c ;
    Log.d(TAG, "At the end of the constructor") ;
}

@Override
public View getView(int position, View convertView, ViewGroup parent)   {
    Log.d(TAG, "At the start of rowView. position = " + position) ;
    View rowView = convertView ;
    if(rowView == null) {
        Log.d(TAG, "rowView = null");
        try {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        rowView = inflater.inflate(layout, parent, false);
        Log.d(TAG, "rowView inflated. rowView = " + rowView);
        ViewHolder viewHolder = new ViewHolder() ;
        viewHolder.checkedText = (CheckedTextView) rowView.findViewById(R.id.text1) ;
        rowView.setTag(viewHolder);
        }
        catch (Exception e) {
            Log.e(TAG, "exception = " + e);
        }
    }

    ViewHolder holder = (ViewHolder) rowView.getTag();

    int nameCol = cursor.getColumnIndex(FriendsDbAdapter.KEY_NAME) ;
    String name = cursor.getString(nameCol);
    holder.checkedText.setText(name);

    Log.d(TAG, "At the end of rowView");
    return rowView;

}

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半仙 2025-01-15 06:46:30

我所做的解决了这个问题:

cur=cursor.
c = simplecursoradapter 内部光标。

在我的 MainActivity 中:

(members)
static ArrayList<Boolean> checkedStates = new ArrayList<Boolean>();
static HashSet<String> selectedIds = new HashSet<String>();
static HashSet<Integer> selectedLines = new HashSet<Integer>();

在 listView 的 onItemClickListener 中:

if (!selectedIds.contains(bookmarkID)) {

    selectedIds.add(bookmarkID);
    selectedLines.add(position);


} else {

     selectedIds.remove(bookmarkID);
     selectedLines.remove(position);



 if (selectedIds.isEmpty()) {
    //clear everything
        selectedIds.clear();
        checkedStates.clear();      
        selectedLines.clear();

        //refill checkedStates to avoid force close bug - out of bounds
        if (cur.moveToFirst()) {
            while (!cur.isAfterLast()) {    
                MainActivity.checkedStates.add(false);

                cur.moveToNext();
            }
        }                       

 }

在我添加的 SimpleCursorAdapter 中(均在 getView 中):

// fill the checkedStates array with amount of bookmarks (prevent OutOfBounds Force close)
        if (c.moveToFirst()) {
            while (!c.isAfterLast()) {  
                MainActivity.checkedStates.add(false);
                c.moveToNext();
            }
        }

并且:

String bookmarkID = c.getString(0);
        CheckedTextView markedItem = (CheckedTextView) row.findViewById(R.id.btitle);
        if (MainActivity.selectedIds.contains(new String(bookmarkID))) {
            markedItem.setChecked(true);
            MainActivity.selectedLines.add(pos);

        } else {
            markedItem.setChecked(false);
            MainActivity.selectedLines.remove(pos);
        }

希望这会有所帮助...您当然需要根据您的需要进行调整。

编辑:

下载了FB SDK,无法通过FB登录。如果设备上安装了 FB 应用程序,您会遇到一个错误,即无法获得有效的 access_token。删除了 FB 应用并在 getFriends() 中获得了 FC。通过使用 runOnUiThread(new Runnable...) 包装其范围来解决此问题。

您得到的错误与错误的过滤、错误的复选框状态无关...您得到它是因为您在查询光标之前尝试访问它(已关闭)。
看起来您在适配器使用光标之前关闭了它。通过添加:

mDbHelper = new FriendsDbAdapter(context);

        mDbHelper.open() ;
        cursor = mDbHelper.fetchAllFriends();

到 SelectFriendsAdapter 中的 getView 范围来验证它。

添加此后,它就不会 FC 并且您可以开始处理您的过滤器。
确保光标未关闭,基本上如果您使用 startManagingCursor() 管理它,则无需手动关闭它。

希望你能从这里拿走它!

What I did that solved it:

cur = cursor.
c = the simplecursoradapter inner cursor.

in my MainActivity:

(members)
static ArrayList<Boolean> checkedStates = new ArrayList<Boolean>();
static HashSet<String> selectedIds = new HashSet<String>();
static HashSet<Integer> selectedLines = new HashSet<Integer>();

in the listView onItemClickListener:

if (!selectedIds.contains(bookmarkID)) {

    selectedIds.add(bookmarkID);
    selectedLines.add(position);


} else {

     selectedIds.remove(bookmarkID);
     selectedLines.remove(position);



 if (selectedIds.isEmpty()) {
    //clear everything
        selectedIds.clear();
        checkedStates.clear();      
        selectedLines.clear();

        //refill checkedStates to avoid force close bug - out of bounds
        if (cur.moveToFirst()) {
            while (!cur.isAfterLast()) {    
                MainActivity.checkedStates.add(false);

                cur.moveToNext();
            }
        }                       

 }

In the SimpleCursorAdapter I added (both in getView):

// fill the checkedStates array with amount of bookmarks (prevent OutOfBounds Force close)
        if (c.moveToFirst()) {
            while (!c.isAfterLast()) {  
                MainActivity.checkedStates.add(false);
                c.moveToNext();
            }
        }

and:

String bookmarkID = c.getString(0);
        CheckedTextView markedItem = (CheckedTextView) row.findViewById(R.id.btitle);
        if (MainActivity.selectedIds.contains(new String(bookmarkID))) {
            markedItem.setChecked(true);
            MainActivity.selectedLines.add(pos);

        } else {
            markedItem.setChecked(false);
            MainActivity.selectedLines.remove(pos);
        }

Hope this helps... you'll of course need to adjust it to your needs.

EDIT:

Downloaded FB SDK, couldn't get pass the FB login. you have a bug where you don't get a valid access_token if the FB app is installed on the device. Removed the FB app and got a FC in getFriends(). Solved it by wrapping its scope with runOnUiThread(new Runnable...).

The error you get has nothing to do with bad filtering, bad checkbox states... You get it because you're trying to access the cursor before querying it (already closed).
It seems like you're closing the cursor before the adapter is using it. Verified it by adding:

mDbHelper = new FriendsDbAdapter(context);

        mDbHelper.open() ;
        cursor = mDbHelper.fetchAllFriends();

to the getView scope in SelectFriendsAdapter.

After you add this, it won't FC and you can start taking care of your filter.
Make sure the cursor is not closed and basically if you're managing it with startManagingCursor(), there's not need to manually close it.

Hope you can take it from here!

挽梦忆笙歌 2025-01-15 06:46:30
adapter.setFilterQueryProvider(new FilterQueryProvider() {
            public Cursor runQuery(CharSequence constraint) {

                return db.fetchdatabyfilter(constraint.toString(), "name");

                }
        });

adapter.setFilterQueryProvider(new FilterQueryProvider() {
            public Cursor runQuery(CharSequence constraint) {

                return db.fetchdatabyfilter(constraint.toString(), "name");

                }
        });

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文