转载

java – 具有搜索实现的CursorTreeAdapter

我为 android

申请了一个应用程序,我将CursorTreeAdapter用作ExpandableListView.现在我想使用搜索框显示已过滤的ExpandableListView项目.喜欢这个:

这是我到目前为止的代码

MainActivity.java:

package com.example.cursortreeadaptersearch;

import java.util.HashMap;

import android.app.SearchManager;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.ContactsContract;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.widget.ExpandableListView;
import android.widget.SearchView;
import android.widget.SearchView.OnCloseListener;
import android.widget.SearchView.OnQueryTextListener;

import com.actionbarsherlock.app.SherlockFragmentActivity;

public class MainActivity extends SherlockFragmentActivity {

    private SearchView search;
    private MyListAdapter listAdapter;
    private ExpandableListView myList;

    private final String DEBUG_TAG = getClass().getSimpleName().toString();

    /**
     * The columns we are interested in from the database
     */
    static final String[] CONTACTS_PROJECTION = new String[] {
            ContactsContract.Contacts._ID,
            ContactsContract.Contacts.DISPLAY_NAME,
            ContactsContract.Contacts.PHOTO_ID,
            ContactsContract.CommonDataKinds.Email.DATA,
            ContactsContract.CommonDataKinds.Photo.CONTACT_ID };

    static final String[] GROUPS_SUMMARY_PROJECTION = new String[] {
            ContactsContract.Groups.TITLE, ContactsContract.Groups._ID,
            ContactsContract.Groups.SUMMARY_COUNT,
            ContactsContract.Groups.ACCOUNT_NAME,
            ContactsContract.Groups.ACCOUNT_TYPE,
            ContactsContract.Groups.DATA_SET };

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

        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        search = (SearchView) findViewById(R.id.search);
        search.setSearchableInfo(searchManager
                .getSearchableInfo(getComponentName()));
        search.setIconifiedByDefault(false);
        search.setOnQueryTextListener(new OnQueryTextListener() {

            @Override
            public boolean onQueryTextSubmit(String query) {
                listAdapter.filterList(query);
                expandAll();
                return false;
            }

            @Override
            public boolean onQueryTextChange(String query) {
                listAdapter.filterList(query);
                expandAll();
                return false;
            }
        });

        search.setOnCloseListener(new OnCloseListener() {

            @Override
            public boolean onClose() {
                listAdapter.filterList("");
                expandAll();
                return false;
            }
        });

        // get reference to the ExpandableListView
        myList = (ExpandableListView) findViewById(R.id.expandableList);
        // create the adapter
        listAdapter = new MyListAdapter(null, MainActivity.this);
        // attach the adapter to the list
        myList.setAdapter(listAdapter);

        Loader<Cursor> loader = getSupportLoaderManager().getLoader(-1);
        if (loader != null && !loader.isReset()) {
            runOnUiThread(new Runnable() {
                public void run() {
                    getSupportLoaderManager().restartLoader(-1, null,
                            mSpeakersLoaderCallback);
                }
            });
        } else {
            runOnUiThread(new Runnable() {
                public void run() {
                    getSupportLoaderManager().initLoader(-1, null,
                            mSpeakersLoaderCallback).forceLoad();
                    ;
                }
            });
        }

    }

    @Override
    public void onResume() {
        super.onResume();

        getApplicationContext().getContentResolver().registerContentObserver(
                ContactsContract.Data.CONTENT_URI, true,
                mSpeakerChangesObserver);
    }

    @Override
    public void onPause() {
        super.onPause();

        getApplicationContext().getContentResolver().unregisterContentObserver(
                mSpeakerChangesObserver);
    }

    // method to expand all groups
    private void expandAll() {
        int count = listAdapter.getGroupCount();
        for (int i = 0; i < count; i++) {
            myList.expandGroup(i);
        }
    }

    public LoaderManager.LoaderCallbacks<Cursor> mSpeakersLoaderCallback = new LoaderCallbacks<Cursor>() {

        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
            Log.d(DEBUG_TAG, "onCreateLoader for loader_id " + id);
            CursorLoader cl = null;

            HashMap<Integer, Integer> groupMap = listAdapter.getGroupMap();
            if (id != -1) {
                int groupPos = groupMap.get(id);
                if (groupPos == 0) { // E-mail group
                    String[] PROJECTION = new String[] {
                            ContactsContract.RawContacts._ID,
                            ContactsContract.CommonDataKinds.Email.DATA };
                    String sortOrder = "CASE WHEN "
                            + ContactsContract.Contacts.DISPLAY_NAME
                            + " NOT LIKE '%@%' THEN 1 ELSE 2 END, "
                            + ContactsContract.Contacts.DISPLAY_NAME + ", "
                            + ContactsContract.CommonDataKinds.Email.DATA
                            + " COLLATE NOCASE";
                    String selection = ContactsContract.CommonDataKinds.Email.DATA
                            + " NOT LIKE ''";
                    cl = new CursorLoader(getApplicationContext(),
                            ContactsContract.CommonDataKinds.Email.CONTENT_URI,
                            PROJECTION, selection, null, sortOrder);
                } else if (groupPos == 1) { // Name group
                    Uri contactsUri = ContactsContract.Data.CONTENT_URI;
                    String selection = "(("
                            + ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
                            + " NOTNULL) AND ("
                            + ContactsContract.CommonDataKinds.GroupMembership.HAS_PHONE_NUMBER
                            + "=1) AND ("
                            + ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
                            + " != '') AND ("
                            + ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID
                            + " = '1' ))"; // Row ID 1 == All contacts
                    String sortOrder = ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME
                            + " COLLATE LOCALIZED ASC";

                    cl = new CursorLoader(getApplicationContext(), contactsUri,
                            CONTACTS_PROJECTION, selection, null, sortOrder);
                }
            } else {
                // group cursor
                Uri groupsUri = ContactsContract.Groups.CONTENT_SUMMARY_URI;
                String selection = "((" + ContactsContract.Groups.TITLE
                        + " NOTNULL) AND (" + ContactsContract.Groups.TITLE
                        + " == 'Coworkers' ) OR ("
                        + ContactsContract.Groups.TITLE
                        + " == 'My Contacts' ))"; // Select only Coworkers
                                                 // (E-mail only) and My
                                                // Contacts (Name only)
                String sortOrder = ContactsContract.Groups.TITLE
                        + " COLLATE LOCALIZED ASC";
                cl = new CursorLoader(getApplicationContext(), groupsUri,
                        GROUPS_SUMMARY_PROJECTION, selection, null, sortOrder);
            }

            return cl;
        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
            // Swap the new cursor in.
            int id = loader.getId();
//          Log.d("Dump Cursor MainActivity",
//                  DatabaseUtils.dumpCursorToString(data));
            Log.d(DEBUG_TAG, "onLoadFinished() for loader_id " + id);
            if (id != -1) {
                // child cursor
                if (!data.isClosed()) {
                    Log.d(DEBUG_TAG, "data.getCount() " + data.getCount());

                    HashMap<Integer, Integer> groupMap = listAdapter
                            .getGroupMap();
                    try {
                        int groupPos = groupMap.get(id);
                        Log.d(DEBUG_TAG, "onLoadFinished() for groupPos "
                                + groupPos);
                        listAdapter.setChildrenCursor(groupPos, data);
                    } catch (NullPointerException e) {
                        Log.w("DEBUG",
                                "Adapter expired, try again on the next query: "
                                        + e.getMessage());
                    }
                }
            } else {
                listAdapter.setGroupCursor(data);
            }
        }

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
            // This is called when the last Cursor provided to onLoadFinished()
            // is about to be closed.
            int id = loader.getId();
            Log.d(DEBUG_TAG, "onLoaderReset() for loader_id " + id);
            if (id != 1) {
                // child cursor
                try {
                    listAdapter.setChildrenCursor(id, null);
                } catch (NullPointerException e) {
                    Log.w(DEBUG_TAG,
                            "Adapter expired, try again on the next query: "
                                    + e.getMessage());
                }
            } else {
                listAdapter.setGroupCursor(null);
            }
        }
    };

    private ContentObserver mSpeakerChangesObserver = new ContentObserver(
            new Handler()) {

        @Override
        public void onChange(boolean selfChange) {
            if (getApplicationContext() != null) {
                runOnUiThread(new Runnable() {
                    public void run() {
                        getSupportLoaderManager().restartLoader(-1, null,
                                mSpeakersLoaderCallback);
                    }
                });
            }
        }
    };
}

MyListAdapter.java:

package com.example.cursortreeadaptersearch;

import java.util.HashMap;

import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorTreeAdapter;
import android.widget.TextView;

public class MyListAdapter extends CursorTreeAdapter {

    public HashMap<String, View> childView = new HashMap<String, View>();

    /**
     * The columns we are interested in from the database
     */

    private final String DEBUG_TAG = getClass().getSimpleName().toString();

    protected final HashMap<Integer, Integer> mGroupMap;

    private MainActivity mActivity;
    private LayoutInflater mInflater;

    String mConstraint;

    public MyListAdapter(Cursor cursor, Context context) {

        super(cursor, context);
        mActivity = (MainActivity) context;
        mInflater = LayoutInflater.from(context);
        mGroupMap = new HashMap<Integer, Integer>();
    }

    @Override
    public View newGroupView(Context context, Cursor cursor,
            boolean isExpanded, ViewGroup parent) {

        final View view = mInflater.inflate(R.layout.list_group, parent, false);
        return view;
    }

    @Override
    public void bindGroupView(View view, Context context, Cursor cursor,
            boolean isExpanded) {

        TextView lblListHeader = (TextView) view
                .findViewById(R.id.lblListHeader);

        if (lblListHeader != null) {
            lblListHeader.setText(cursor.getString(cursor
                    .getColumnIndex(ContactsContract.Groups.TITLE)));
        }
    }

    @Override
    public View newChildView(Context context, Cursor cursor,
            boolean isLastChild, ViewGroup parent) {

        final View view = mInflater.inflate(R.layout.list_item, parent, false);

        return view;
    }

    @Override
    public void bindChildView(View view, Context context, Cursor cursor,
            boolean isLastChild) {

        TextView txtListChild = (TextView) view.findViewById(R.id.lblListItem);

        if (txtListChild != null) {
            txtListChild.setText(cursor.getString(1)); // Selects E-mail or
                                                        // Display Name
        }

    }

    protected Cursor getChildrenCursor(Cursor groupCursor) {
        // Given the group, we return a cursor for all the children within that
        // group
        int groupPos = groupCursor.getPosition();
        int groupId = groupCursor.getInt(groupCursor
                .getColumnIndex(ContactsContract.Groups._ID));

        Log.d(DEBUG_TAG, "getChildrenCursor() for groupPos " + groupPos);
        Log.d(DEBUG_TAG, "getChildrenCursor() for groupId " + groupId);

        mGroupMap.put(groupId, groupPos);

        Loader loader = mActivity.getSupportLoaderManager().getLoader(groupId);
        if (loader != null && !loader.isReset()) {
            mActivity.getSupportLoaderManager().restartLoader(groupId, null,
                    mActivity.mSpeakersLoaderCallback);
        } else {
            mActivity.getSupportLoaderManager().initLoader(groupId, null,
                    mActivity.mSpeakersLoaderCallback);
        }

        return null;
    }

    // Access method
    public HashMap<Integer, Integer> getGroupMap() {
        return mGroupMap;
    }

    public void filterList(CharSequence constraint) {
        // TODO Filter the data here
    }
}

我有非常大的简化和清理代码(所以你们不需要做).

正如你所看到的,我共有3个游标(组为1,孩子为2).数据来自 ContactsContract (这是用户的联系人).

来自子级1的光标表示所有联系人的所有电子邮件,来自子级别2的光标表示联系人的所有显示名称. (大多数装载机功能是从 here 开始).

现在唯一的办法是如何实现搜索?我应该通过内容提供者或数据库中的原始查询来做吗?我希望显示两个子表的结果.我认为,因为在打字时容易出错,这个tokenize = porter是我的例子中的一个选项.

我希望有人能指出我的方向很好.

编辑:

我已经在MyListAdapter.java中尝试过( FilterQueryProvider ,建议为 Kyle I. ):

public void filterList(CharSequence constraint) {
    final Cursor oldCursor = getCursor();
    setFilterQueryProvider(filterQueryProvider);
    getFilter().filter(constraint, new FilterListener() {
        public void onFilterComplete(int count) {
            // assuming your activity manages the Cursor 
            // (which is a recommended way)
            notifyDataSetChanged();
//          stopManagingCursor(oldCursor);
//          final Cursor newCursor = getCursor();
//          startManagingCursor(newCursor);
//          // safely close the oldCursor
            if (oldCursor != null && !oldCursor.isClosed()) {
                oldCursor.close();
            }
        }
    });
}

private FilterQueryProvider filterQueryProvider = new FilterQueryProvider() {
    public Cursor runQuery(CharSequence constraint) {
        // assuming you have your custom DBHelper instance 
        // ready to execute the DB request
        String s = '%' + constraint.toString() + '%';
        return mActivity.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
                MainActivity.CONTACTS_PROJECTION,
                ContactsContract.CommonDataKinds.GroupMembership.DISPLAY_NAME + " LIKE ?",
            new String[] { s },
            null);
    }
};

这在MainActivity.java中:

search.setOnQueryTextListener(new OnQueryTextListener() {

            @Override
            public boolean onQueryTextSubmit(String query) {
                listAdapter.filterList(query);
                expandAll();
                return false;
            }

            @Override
            public boolean onQueryTextChange(String query) {
                listAdapter.filterList(query);
                expandAll();
                return false;
            }
        });

        search.setOnCloseListener(new OnCloseListener() {

            @Override
            public boolean onClose() {
                listAdapter.filterList("");
                expandAll();
                return false;
            }
        });

但是当我尝试搜索时,我会收到这些错误:

12-20 13:20:19.449: E/CursorWindow(28747): Failed to read row 0, column -1 from a CursorWindow which has 96 rows, 4 columns.
12-20 13:20:19.449: D/AndroidRuntime(28747): Shutting down VM
12-20 13:20:19.449: W/dalvikvm(28747): threadid=1: thread exiting with uncaught exception (group=0x415c62a0)
12-20 13:20:19.499: E/AndroidRuntime(28747): FATAL EXCEPTION: main
12-20 13:20:19.499: E/AndroidRuntime(28747): java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow.  Make sure the Cursor is initialized correctly before accessing data from it.

我做错了什么?或者是因为在runQuery中只返回1个查询(显示名称)而不是2个(显示名称和电子邮件)?

编辑2:

首先,我将所有的数据库实现都更改为 ContactsContract .这变得更容易维护,以便您不必编写自己的数据库实现.

我现在尝试的是将我的约束保存在 FilterQueryProvider 的runQuery()中,然后在getChildrenCursor中针对该约束运行查询. (如 JRaymond 所示)

private String mConstraint;
protected Cursor getChildrenCursor(Cursor groupCursor) {
    // Given the group, we return a cursor for all the children within that
    // group
    int groupPos = groupCursor.getPosition();
    int groupId = groupCursor.getInt(groupCursor
            .getColumnIndex(ContactsContract.Groups._ID));

    Log.d(DEBUG_TAG, "getChildrenCursor() for groupPos " + groupPos);
    Log.d(DEBUG_TAG, "getChildrenCursor() for groupId " + groupId);

    mGroupMap.put(groupId, groupPos);

    Bundle b = new Bundle();
    b.putString("constraint", mConstraint);

    Loader loader = mActivity.getSupportLoaderManager().getLoader(groupId);
    if (loader != null && !loader.isReset()) {
        if (mConstraint == null || mConstraint.isEmpty()) {
            // Normal query
            mActivity.getSupportLoaderManager().restartLoader(groupId,
                    null, mActivity.mSpeakersLoaderCallback);
        } else {
            // Constrained query
            mActivity.getSupportLoaderManager().restartLoader(groupId, b,
                    mActivity.mSpeakersLoaderCallback);

        }
    } else {
        if (mConstraint == null || mConstraint.isEmpty()) {
            // Normal query
            mActivity.getSupportLoaderManager().initLoader(groupId, null,
                    mActivity.mSpeakersLoaderCallback);
        } else {
            // Constrained query
            mActivity.getSupportLoaderManager().initLoader(groupId, b,
                    mActivity.mSpeakersLoaderCallback);
        }
    }

    return null;
}

这里是 FilterQueryProvider

private FilterQueryProvider filterQueryProvider = new FilterQueryProvider() {
    public Cursor runQuery(CharSequence constraint) {
        // Load the group cursor here and assign mConstraint
        mConstraint = constraint.toString();
        Uri groupsUri = ContactsContract.Groups.CONTENT_SUMMARY_URI;
        String selection = "((" + ContactsContract.Groups.TITLE
                + " NOTNULL) AND (" + ContactsContract.Groups.TITLE
                + " == 'Coworkers' ) OR (" + ContactsContract.Groups.TITLE
                + " == 'My Contacts' ))"; // Select only Coworkers
                                            // (E-mail only) and My
                                            // Contacts (Name only)
        String sortOrder = ContactsContract.Groups.TITLE
                + " COLLATE LOCALIZED ASC";
        return mActivity.getContentResolver().query(groupsUri,
                MainActivity.GROUPS_SUMMARY_PROJECTION, selection, null,
                sortOrder);
    }
};

正如你所看到的,我加载了组的查询,以获得getChildrenCursor的工作.我应该从MainActivity中运行哪些查询?

该项目可以下载 here ,您可以在Eclipse中导入.

我已经研究了你的问题,不幸的是我没有时间复制你的设置.但是,在通用术语中,您应该能够保存约束,然后在“getChildrenCursor”中运行针对该约束的查询:

Cursor getChildrenCursor(Cursor groupCursor) {
  if (mConstraint == null || mConstraint.isEmpty()) {
    // Normal query
  } else {
    // Constrained query
  }

}

我不确定,但是当您在filterQueryProvider()中返回游标时,我很确定getChildrenCursor()将被调用以响应父游标的更改.然后,您只需管理约束的空/填充状态.

细节:

在你的filterList函数中,而不是做一个复杂的过程,只需调用runQueryOnBackgroundThread(constraint);这将自动将数据库工作卸载到后台.将您的约束保存在filterQueryProvider中:

String s = '%' + constraint.toString() + '%';
mConstraint = s;

对于查询,它只取决于您要从数据库中获取的内容 – 快速调整您发布的代码运行查询,如下所示:

String selection = ContactsContract.CommonDataKinds.Email.DATA
    + " NOT LIKE ''";
if (constraint != null) {
    selection += " AND " + ContactsContract.CommonDataKinds.Email.DATA + " LIKE ?";
}
cl = new CursorLoader(getApplicationContext(), 
    ContactsContract.CommonDataKinds.Email.CONTENT_URI,
    PROJECTION, selection, constraint, sortOrder);

我不太确定的一件事是你要进行的自动扩展,我的过滤器可以工作,但是您需要重新打开并重新打开列表才能看到更改.

http://stackoverflow.com/questions/20585273/cursortreeadapter-with-search-implementation

原文  https://codeday.me/bug/20181015/295282.html
正文到此结束
Loading...