33

इसलिए मुझे MultiAutoCompleteTextView को विस्तारित करने में परेशानी हो रही है और इसे CursorLoader के साथ बैकिंग कर रहा है, साथ ही एक कस्टम Tokenizer का उपयोग करते हुए। यह मुद्दा विशेष रूप से mAdapter.setCursorToStringConverter(); कॉल के साथ बढ़ता है। convertToString() विधि जिसमें कर्सर को तर्क के रूप में रखा गया है, इस विधि के पहले कॉल पर वैध और अनजान कर्सर है। हालांकि बाद के कॉल का परिणाम नल कर्सर या बंद कर्सर होता है। मुझे लगता है कि LoaderManagerCursorLoader का प्रबंधन कैसे करता है इसके साथ कुछ करने के लिए कुछ है।CursorLoader द्वारा समर्थित AutoCompleteTextView

यदि मैं setCursorToStringConverter() विधि पर टिप्पणी करता हूं, तो मुझे इस दृश्य में दर्ज किए गए पाठ के आधार पर उपलब्ध विकल्पों की एक सूची दिखाई देती है। हालांकि, चूंकि convertToString() विधि लागू नहीं हुई है, तो कस्टम Tokenizer की terminateToken() विधि उस स्ट्रिंग को प्राप्त नहीं करती है जिसे मैं चाहता हूं, बल्कि कर्सर ऑब्जेक्ट की प्रतिनिधि स्ट्रिंग, क्योंकि कर्सर का उपयोग करने के लिए कर्सर का उपयोग नहीं किया गया है परिणामी क्वेरी में वांछित कॉलम का वर्तमान स्ट्रिंग मान।

क्या कोई भी तीन वर्गों (CursorLoader/LoaderManger, MultiAutoCompleteTextView, और Tokenizer) के संयोजन को लागू करने में सक्षम है?

क्या मैं इसके साथ सही दिशा में जा रहा हूं, या यह संभव नहीं है?

मैं एक कस्टम MultiAutoCompleteTextView एक कस्टम Tokenizer के साथ एक SimpleCursorAdapter द्वारा समर्थित लागू करने के लिए सक्षम है। मैं बस सोच रहा था कि CursorLoader का उपयोग करके इसे लागू करने के लिए संभव है, क्योंकि सख्त मोड MultiAutoCompleteTextView में कर्सर के बारे में शिकायत करता है स्पष्ट रूप से बंद नहीं किया जा रहा है।

किसी भी मदद की सराहना की जाएगी।

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

मैं अब setCursorToStringConverter() के बजाय setStringConversionColumn() विधि बोल रहा हूँ के रूप में @Olaf का सुझाव दिया। मैंने इसे onLoadFinished() में सेट किया है क्योंकि यह केवल Cursor उपलब्ध है क्योंकि यह 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 में चुने जाने की अनुमति नहीं दी जाएगी।

मुझे लगता है कि onTextChanged() विधि के साथ कुछ समस्या है क्योंकि यह restartLoader() पर कॉल करता है। यह इस दृश्य में पहली प्रविष्टि के लिए काम करता है लेकिन बाद की प्रविष्टियों के लिए नहीं। मुझे इस बिंदु पर भी यकीन नहीं है कि क्या गलत है।

अद्यतन 2

तो मैं इस मुद्दे की पहचान की है। समस्या TextWatcher की onTextChanged() विधि है। पहला टोकन समाप्त करने के लिए चयन करने के बाद (मान लीजिए कि टोकन "जो जॉनसन" था), फिर MultiAutoCompleteTextView (जैसे al) में अधिक वर्ण दर्ज करना s का मान जो onTextChanged() विधि में पारित हो गया है, अब न केवल अतिरिक्त जोड़े गए वर्ण, लेकिन टोकन के पात्र जिन्हें पहले ही समाप्त कर दिया गया था (इस बिंदु पर s का मान Joe Johnson al है)।अब mCursor का मान Joe Johnson al पर सेट हो जाता है जो बाद में onCreateLoader() में क्वेरी में पारित हो जाता है जो स्पष्ट रूप से कोई परिणाम नहीं देगा। क्या इस स्थिति के आसपास कोई रास्ता है? मैं किसी भी सुझाव के लिए खुला हूँ।

अद्यतन 3

जब मैं एक कस्टम Tokenizer मैं एक FilterQueryProvider इस तरह सेट के साथ एक कस्टम MultiAutoCompleteTextView एक SimpleCursorAdapter द्वारा समर्थित कार्यान्वित:

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() विधि कहा जाता हो जाता है के लिए

public void onTextChanged(CharSequence s, int start, int before, 
       int count) { 
    Log.d(DEBUG_TAG, "onTextChanged() : s " + s); 
    mAdapter.getFilterQueryProvider().runQuery(s); 
} 
: दो बार TextWatcher के onTextChanged() विधि से

तो मेरे पिछले उदाहरण में, constraint वेरिएबल जो runQuery() विधि में पहली बार Joe Johnson al में पारित हो जाता है। फिर दूसरी बार runQuery() विधि को constraint चर का मान कहा जाता है al है। मुझे नहीं पता कि runQuery() विधि दो बार क्यों चलती है जब इसे केवल onTextChanged() विधि में बुलाया जाता है।

+0

आपके मामले में, आप 'setCursorToStringConverter()' के बजाय 'setStringConversionColumn()' का उपयोग कर सकते हैं। –

+0

बस आप जो कर रहे हैं उसकी समझ प्राप्त करने के लिए, आप टेक्स्ट प्रति टेक्स्ट परिवर्तन की एक बड़ी मात्रा में पूछताछ कर रहे हैं? क्या यह आपके मामले में वास्तव में जरूरी है? – JanithaR

उत्तर

3

असल में, Androids स्वत: पूर्ण TextView बहुत शक्तिशाली नहीं है, मैं डेटा की बड़ी मात्रा में, मैं क्या है, मैं खोज के लिए पाठ संपादित करने के लिए एक पाठ परिवर्तन श्रोता रखने से निपटने के लिए जब, और फिर कुछ पर बदल जाता है जब भी संपादन पाठ, यह डेटाबेस से पूछताछ करता है।

यदि यह किसी की मदद कर सकते हैं, 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()); 
     } 
    }; 

पर एक edittext रखकर तो, अपने एडाप्टर में, आप एक getFilter() विधि बनाने की जरूरत ...

@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(); 
     } 
    } 
संबंधित मुद्दे