AutoCompleteTextView由CursorLoader支持 [英] AutoCompleteTextView backed by CursorLoader

查看:165
本文介绍了AutoCompleteTextView由CursorLoader支持的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,我有扩展 MultiAutoCompleteTextView 麻烦和支持它 CursorLoader ,同时使用自定义标记生成器。 通话;问题与 mAdapter.setCursorToStringConverter()专门上升。该 convertToString()方法,它有一个游标作为参数有权在此方法第一次调用一个有效的和未关闭的光标。然而随后的调用导致任何一个空指针或关闭的游标。我猜测这有什么做的 LoaderManager 如何管理 CursorLoader

So I am having trouble extending the MultiAutoCompleteTextView and backing it with a CursorLoader, while simultaneously using a custom Tokenizer. The issue rises specifically with the mAdapter.setCursorToStringConverter(); call. The convertToString() method which has a Cursor as an argument has a valid and unclosed cursor upon the first call to this method. However subsequent calls result in either a null cursor or a closed cursor. I am guessing this has something to do with how the LoaderManager manages the CursorLoader.

如果我评论了 setCursorToStringConverter()办法出来,然后我看到基于我进入到这个视图中的文本可用选项的列表。然而,由于还没有实现 convertToString()的方法,自定义的则 terminateToken()方法标记生成器没有收到,我想让它成为字符串,而是光标对象的再presentative字符串,因为光标还没有被用来获取当前字符串值在所得的查询所期望的柱。

If I comment the setCursorToStringConverter() method out, then I do see a list of available choices based on the text I entered into this view. However, since there is no convertToString() method implemented, then the terminateToken() method of the custom Tokenizer does not receive the string that I intend it to be, but rather a representative string of the cursor object, since the cursor has not been used to get the current string value of a desired column in the resulting query.

任何人都已经能够实现三个类的组合( CursorLoader / LoaderManger MultiAutoCompleteTextView 标记生成器)?

Has anyone been able to implement the combination of the three classes (CursorLoader/LoaderManger, MultiAutoCompleteTextView, and Tokenizer) ?

我是不是在正确的方向这一点,或者这根本不可能?

Am I going in the right direction with this, or is this simply not possible?

我已经能够实现自定义的 MultiAutoCompleteTextView SimpleCursorAdapter 还有一个自定义的<$ C $支持C>标记生成器。我只是想知道如果可能的使用来实现这样的 CursorLoader 代替,因为严格模式抱怨光标在 MultiAutoCompleteTextView 没有被显式关闭。

I have been able to implement a custom MultiAutoCompleteTextView backed by a SimpleCursorAdapter along with a custom Tokenizer. I was just wondering if its possible to implement this using a CursorLoader instead, since Strict Mode complains about the cursor in MultiAutoCompleteTextView not being explicitly closed.

任何帮助将是很大的AP preciated。

Any help would be greatly appreciated.

public class CustomMultiAutoCompleteTextView extends MultiAutoCompleteTextView
  implements LoaderManager.LoaderCallbacks<Cursor> {

    private final String DEBUG_TAG = getClass().getSimpleName().toString();
    private Messenger2 mContext;
    private RecipientsCursorAdapter mAdapter;
    private ContentResolver mContentResolver;
    private final char delimiter = ' ';
    private CustomMultiAutoCompleteTextView mView;

    // If non-null, this is the current filter the user has provided.
    private String mCurFilter;

    // These are the Contacts rows that we will retrieve.
    final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
        ContactsContract.Contacts._ID,
        ContactsContract.Contacts.DISPLAY_NAME };

    public CustomMultiAutoCompleteTextView(Context c) {
        super(c);
        init(c);
    }

    public CustomMultiAutoCompleteTextView(Context c, AttributeSet attrs) {
        super(c, attrs);
        init(c);
    }

    private void init(Context context) {
        mContext = (Messenger2) context;
        mContentResolver = mContext.getContentResolver();
        mView = this; 

        mAdapter = new RecipientsCursorAdapter(mContext, 0, null, new String[0], new int[0], mContext);

        mAdapter.setCursorToStringConverter(new CursorToStringConverter() {
            @Override
            public CharSequence convertToString(Cursor c) {
                String contactName = c.getString(c.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
                return contactName;
            }
        });

        addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                Log.d(DEBUG_TAG, "onTextChanged()");
                if (!s.equals(""))
                    mCurFilter = s.toString();
                else
                    mCurFilter = "";

                mContext.getLoaderManager().restartLoader(0, null, mView);

            }

            @Override
            public void afterTextChanged(Editable s) {
            }
        });

        setAdapter(mAdapter);
        setTokenizer(new SpaceTokenizer());

        mContext.getLoaderManager().initLoader(0, null, this);

    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // This is called when a new Loader needs to be created. This
        // sample only has one Loader, so we don't care about the ID.
        // First, pick the base URI to use depending on whether we are
        // currently filtering.
        Log.d(DEBUG_TAG, "onCreateLoader()");
        Uri baseUri;
        if (mCurFilter != null) {
            baseUri = Uri.withAppendedPath( ContactsContract.Contacts.CONTENT_FILTER_URI,Uri.encode(mCurFilter));
        } else {
            baseUri = ContactsContract.Contacts.CONTENT_URI;
        }

        // Now create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        String selection = "((" + ContactsContract.Contacts.DISPLAY_NAME
                + " NOTNULL) AND ("
                + ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1) AND ("
                + ContactsContract.Contacts.DISPLAY_NAME + " != '' ))";
        String sortOrder = ContactsContract.Contacts.DISPLAY_NAME
                + " COLLATE LOCALIZED ASC";

        return new CursorLoader(mContext, baseUri, CONTACTS_SUMMARY_PROJECTION,
                selection, null, sortOrder);
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // Swap the new cursor in. (The framework will take care of closing
        // the old cursor once we return.)
        Log.d(DEBUG_TAG, "onLoadFinished()");
        mAdapter.swapCursor(data);

    }

    public void onLoaderReset(Loader<Cursor> loader) {
        // This is called when the last Cursor provided to onLoadFinished()
        // above is about to be closed. We need to make sure we are no
        // longer using it.
        Log.d(DEBUG_TAG, "onLoaderReset()");
        mAdapter.swapCursor(null);
    }

    private class SpaceTokenizer implements Tokenizer {

        public int findTokenStart(CharSequence text, int cursor) {
            int i = cursor;

            while (i > 0 && text.charAt(i - 1) != delimiter) {
                i--;
            }
            while (i < cursor && text.charAt(i) == delimiter) {
                i++;
            }

            return i;
        }

        public int findTokenEnd(CharSequence text, int cursor) {
            int i = cursor;
            int len = text.length();

            while (i < len) {
                if (text.charAt(i) == delimiter) {
                    return i;
                } else {
                    i++;
                }
            }

            return len;
        }

        public CharSequence terminateToken(CharSequence text) {
            Log.d(DEBUG_TAG, "terminateToken()");
            int i = text.length();
            while (i > 0 && text.charAt(i - 1) == delimiter) {
                i--;
            }

            if (i > 0 && text.charAt(i - 1) == delimiter) {
                return text;
            } else {

                CharSequence contactName = createContactBubble(text);

                return contactName;
            }
        }

    }

}

更新1

我现在调用 setStringConversionColumn()方法,而不是 setCursorToStringConverter()作为@Olaf建议。我已将这在 onLoadFinished(),因为这是唯一一次光标可用,因为这是实现 LoaderManger

I am now calling the setStringConversionColumn() method instead of the setCursorToStringConverter() as @Olaf suggested. I have set this in the onLoadFinished() since this is the only time the Cursor is available as this is implementing a LoaderManger.

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // Swap the new cursor in. (The framework will take care of closing
    // the old cursor once we return.)
    Log.d(DEBUG_TAG, "onLoadFinished()");   
    mAdapter.setStringConversionColumn(data.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME)); 
    mAdapter.swapCursor(data);
}

这在选择一个项目为 MultiAutoCompleteTextView ,但不会允许多个项目在 MultiAutoCompleteTextView 。

This works in selecting one item for the MultiAutoCompleteTextView, but will not allow multiple items to be selected in the MultiAutoCompleteTextView.

我猜有一些问题,与 onTextChanged()的方法,因为它会调用 restartLoader()。这适用于在此视图中的第一项,但不用于后续条目。我也不太清楚这一点有什么不好。

I am guessing there is some issue with the onTextChanged() method since it calls restartLoader(). This works for the first entry in this view but not for subsequent entries. I'm not too sure at this point what is wrong.

更新2

所以我已经确定的问题。问题是TextWatcher的 onTextChanged()方法。使得终止的第一个标记选择后(假设该令牌是乔·约翰逊),然后输入更多的字符到这个 MultiAutoCompleteTextView (如)的arg的值取值获取传递到 onTextChanged()方法现在不仅包含了额外添加的字符也从有previously被终止(的取值在这一点上是乔 - 约翰逊人)。现在的 mCursor 被设置为乔 - 约翰逊人随后被传递到查询的 onCreateLoader()这显然会返回任何结果。是否有解决这种情况下什么办法?我愿意接受任何建议。

So I have identified the issue. The problem is the TextWatcher's onTextChanged() method. After making the selection to terminate the first token ( let's say the token was "Joe Johnson" ), then entering more characters into this MultiAutoCompleteTextView ( such as al ) the value of the arg s that gets passed into the onTextChanged() method now contains not only the additionally added characters but also the characters from the token which has previously been terminated ( the value of s at this point is Joe Johnson al ). Now the value of mCursor gets set to Joe Johnson al which subsequently gets passed into the query in onCreateLoader() which will obviously return no results. Are there any ways around this situation? I am open to any suggestions.

更新3

当我实现了一个自定义的 MultiAutoCompleteTextView SimpleCursorAdapter 还有一个自定义的标记生成器支持我设置了​​ FilterQueryProvider 是这样的:

When I implemented a custom MultiAutoCompleteTextView backed by a SimpleCursorAdapter along with a custom Tokenizer I set a FilterQueryProvider like this:

mAdapter.setFilterQueryProvider(new FilterQueryProvider() {
    @Override
    public Cursor runQuery(CharSequence constraint) {
    Log.d(DEBUG_TAG, "runQuery() : constraint " + constraint);
        Uri baseUri;
        if (constraint != null) {
            baseUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI,
                Uri.encode(constraint.toString()));
        } else {
            baseUri = ContactsContract.Contacts.CONTENT_URI;
            }

        String selection = "((" + ContactsContract.Contacts.DISPLAY_NAME
            + " NOTNULL) AND ("
            + ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1) AND ("
            + ContactsContract.Contacts.DISPLAY_NAME + " != '' ))";

        final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
            ContactsContract.Contacts._ID,
            ContactsContract.Contacts.DISPLAY_NAME};
        String sortOrder = ContactsContract.Contacts.DISPLAY_NAME
            + " COLLATE LOCALIZED ASC";

        Cursor c = mContentResolver.query(baseUri,
    CONTACTS_SUMMARY_PROJECTION, selection, null, sortOrder);
        return c;
    }
});

和出于某种原因, runQuery()方法从TextWatcher的 onTextChanged()方法叫了两声:

And for some reason the runQuery() method gets called twice from the TextWatcher's onTextChanged() method:

public void onTextChanged(CharSequence s, int start, int before,
                int count) {
    Log.d(DEBUG_TAG, "onTextChanged()  : s " + s);
    mAdapter.getFilterQueryProvider().runQuery(s);
}

所以在我的previous例如,被传递到 runQuery()方法约束变量第一次是乔 - 约翰逊人。然后第二次 runQuery()方法被称为约束变量人的价值。我不知道为什么 runQuery()方法运行两次时,它只是在 onTextChanged()方法调用一次。

So in my previous example, the constraint variable that gets passed into the runQuery() method the first time is Joe Johnson al. Then the second time runQuery() method is called the value of the constraint variable is al. I don't know why the runQuery() method runs twice when its only called once in the onTextChanged() method.

推荐答案

基本上,机器人自动完成TextView的不是很厉害,当我不得不处理更大的数据量,我做的是,我把一个文本的变化监听器编辑文本搜索,然后只要东西是在编辑文本改变了,它查询数据库。

Basically, androids autocomplete textview is not very powerful, when I have to deal with bigger amounts of data, what I do is, i keep a text change listener to the edit text for search, and then whenever something is changed on the edit text, it queries database.

如果这可能帮助别人,把一个EditText上的onCreate

If this might help someone, placing an edittext on onCreate

EditText etSearch = (EditText)findViewById(R.id.etSearchBox);
etSearch.addTextChangedListener(filterTextWatcher);

//The filterTextWatcher is 

private TextWatcher filterTextWatcher = new TextWatcher() {
     @Override
    public void afterTextChanged(Editable s) {
    }

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

    @Override
    public void onTextChanged(CharSequence s, int start, int before,int count) {
        adapter.getFilter().filter(s.toString());
        }
    };  

那么,在您的适配器,您需要创建一个用getFilter()方法...

So, in your adapter, you need to create a getFilter() method...

@Override
    public Filter getFilter() {
    if (nameFilter == null) {
        nameFilter = new NameFilter();
    }
    return nameFilter;
}

    private class NameFilter extends Filter {

    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
    FilterResults results = new FilterResults();
    Cursor cursor = null;
    // get your cursor by passing appropriate query here
    results.values = cursor;
    results.count = cursor.getCount();
    return results;
    }

    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
    notifyDataSetChanged();
        }
    }

这篇关于AutoCompleteTextView由CursorLoader支持的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆