AutoCompleteTextView wspierany przez CursorLoader

Więc mam problem z rozszerzeniemMultiAutoCompleteTextView i cofnij goCursorLoader, używając jednocześnie zwyczajuTokenizer. Problem pojawia się szczególnie w przypadkumAdapter.setCursorToStringConverter(); połączenie. TheconvertToString() metoda, która ma kursor jako argument, ma poprawny i nie zamknięty kursor przy pierwszym wywołaniu tej metody. Jednak kolejne wywołania powodują albo zerowy kursor, albo zamknięty kursor. Zgaduję, że ma to coś wspólnego z tym, jakLoaderManager zarządzaCursorLoader.

Jeśli komentujęsetCursorToStringConverter() metody, a następnie widzę listę dostępnych wyborów na podstawie tekstu, który wprowadziłem do tego widoku. Ponieważ jednak nie maconvertToString() zaimplementowana metoda, a następnieterminateToken() metoda zwyczajuTokenizer nie otrzymuje ciągu, który chcę, ale raczej reprezentatywny ciąg obiektu kursora, ponieważ kursor nie został użyty do uzyskania bieżącej wartości ciągu żądanej kolumny w wynikowym zapytaniu.

Czy ktoś był w stanie wdrożyć połączenie trzech klas (CursorLoader/LoaderManger, MultiAutoCompleteTextView, iTokenizer)?

Czy zmierzam we właściwym kierunku z tym, czy jest to po prostu niemożliwe?

Udało mi się wdrożyć zwyczajMultiAutoCompleteTextView wspierany przezSimpleCursorAdapter wraz ze zwyczajemTokenizer. Zastanawiałem się tylko, czy możliwe jest zaimplementowanie tego za pomocąCursorLoader zamiast tego, ponieważ tryb ścisły narzeka na kursorMultiAutoCompleteTextView nie jest wyraźnie zamknięty.

Każda pomoc byłaby bardzo mile widziana.

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;
            }
        }

    }

}

AKTUALIZACJA 1

Teraz dzwonię dosetStringConversionColumn() metoda zamiastsetCursorToStringConverter() jak sugerował @Olaf. Ustawiłem to wonLoadFinished() ponieważ jest to jedyny razCursor jest dostępny, ponieważ wdrażaLoaderManger.

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);
}

Działa to przy wybieraniu jednego elementu dlaMultiAutoCompleteTextView, ale nie pozwoli na wybór wielu elementów wMultiAutoCompleteTextView.

Zgaduję, że jest jakiś problem zonTextChanged() metoda, ponieważ wywołujerestartLoader(). Działa to dla pierwszego wpisu w tym widoku, ale nie dla kolejnych wpisów. Nie jestem w tym momencie pewien, co jest nie tak.

AKTUALIZACJA 2

Zidentyfikowałem więc problem. Problemem jest tekst TextWatcheraonTextChanged() metoda. Po dokonaniu wyboru, aby zakończyć pierwszy token (powiedzmy, że token to „Joe Johnson”), a następnie wprowadź do niego więcej znakówMultiAutoCompleteTextView ( Jak na przykładal ) wartość args który przechodzi doonTextChanged() metoda zawiera teraz nie tylko dodatkowo dodane znaki, ale także znaki z tokena, który został wcześniej zakończony (wartośćs w tym momencie jestJoe Johnson al ). Teraz wartośćmCursor ustawia się naJoe Johnson al który następnie przechodzi do zapytania wonCreateLoader() co oczywiście nie zwróci żadnych wyników. Czy są jakieś sposoby na obejście tej sytuacji? Jestem otwarty na wszelkie sugestie.

AKTUALIZACJA 3

Kiedy wprowadziłem zwyczajMultiAutoCompleteTextView wspierany przezSimpleCursorAdapter wraz ze zwyczajemTokenizer UstawiłemFilterQueryProvider lubię to:

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;
    }
});

I z jakiegoś powodurunQuery() Metoda jest wywoływana dwukrotnie z obiektu TextWatcheronTextChanged() metoda:

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

Tak więc w moim poprzednim przykładzieconstraint zmienna, która zostanie przekazana dorunQuery() metoda za pierwszym razemJoe Johnson al. Potem drugi razrunQuery() metoda nazywa się wartościąconstraint zmienna jestal. Nie wiem dlaczegorunQuery() metoda działa dwa razy, gdy jest wywoływana tylko raz wonTextChanged() metoda.

questionAnswers(1)

yourAnswerToTheQuestion