2017-01-09 15 views
0

私はSearchableSpinnerを自分のプロジェクトに実装しました。 Fragmentの中にあります。検索可能なスピナーとレルムとの組み合わせ

私はRealmをデータベースとして使用しています。私はこれを持って私のonCreateView方法で ...

@Override 
public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) { 
      View view = inflater.inflate(R.layout.ncrdocument, container, false); 
    realm = Realm.getDefaultInstance(); 
    documents = new ArrayList<>(); 
    documents = realm.where(MaterialDoc.class).findAll(); 
    ArrayAdapter<MaterialDoc> adapter = new ArrayAdapter<>(this.getContext(), android.R.layout.simple_list_item_1, documents); 
    matList.setAdapter(adapter); 
. 
. 
. 
return view; 

データをロード罰金、それはそれらを正しく示しているが、私はスピナーを検索しようとすると、私のアプリがクラッシュし、私はこのエラーを取得します。

An exception occured during performFiltering() 
java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created 
at io.realm.BaseRealm.checkIfValid(BaseRealm.java:383) 
at io.realm.MaterialDocRealmProxy.realmGet$document_number(MaterialDocRealmProxy.java:126) 
at com.my.application.test.Model.MaterialDoc.getDocumentNumber(MaterialDoc.java:29) 
at com.my.application.test.Model.MaterialDoc.toString(MaterialDoc.java:42) 
at android.widget.ArrayAdapter$ArrayFilter.performFiltering(ArrayAdapter.java:480) 
at android.widget.Filter$RequestHandler.handleMessage(Filter.java:234) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:145) 
at android.os.HandlerThread.run(HandlerThread.java:61) 
01-09 12:13:06.649 18606-18606/com.my.application.test D/AndroidRuntime: Shutting down VM 
01-09 12:13:06.669 18606-18606/com.my.application.test E/AndroidRuntime: FATAL EXCEPTION: main 
Process: com.my.application.test, PID: 18606 
java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference 
at android.widget.ArrayAdapter.getCount(ArrayAdapter.java:330) 
at android.widget.AdapterView.checkFocus(AdapterView.java:947) 
at android.widget.AdapterView$AdapterDataSetObserver.onInvalidated(AdapterView.java:1070) 
at android.widget.AbsListView$AdapterDataSetObserver.onInvalidated(AbsListView.java:8297) 
at android.database.DataSetObservable.notifyInvalidated(DataSetObservable.java:50) 
at android.widget.BaseAdapter.notifyDataSetInvalidated(BaseAdapter.java:59) 
at android.widget.ArrayAdapter$ArrayFilter.publishResults(ArrayAdapter.java:513) 
at android.widget.Filter$ResultsHandler.handleMessage(Filter.java:282) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:145) 
at android.app.ActivityThread.main(ActivityThread.java:6939) 
at java.lang.reflect.Method.invoke(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:372) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199) 
+0

http://stackoverflow.com/questions/40630322/how-to-implement-filterable-in-realmrecyclerviewadapter – EpicPandaForce

答えて

2

スレッド間で実数を扱うときは注意が必要です。レルムは、他のスレッドでは使用できないスレッドから発生します。したがって、結果セットのコピーを取得してください(これはアクティブなレルムインスタンスから切り離されています)。このコピーをアダプタに渡してください。 Realmにはこれを行う方法がcopyFromRealmあります。

、あなたが/レルムからの結果を切り離しコピーして、自動更新と遅延評価を失う、あなたがコピー・アウト@SarathKnによって受け入れ答えは全く効率的ではありません。この

Realm realm = Realm.getDefaultInstance(); 
    RealmResults<MaterialDoc> realmResults = realm.where(MaterialDoc.class).findAll(); 
    List<MaterialDoc> documents = realm.copyFromRealm(realmResults); 
    ArrayAdapter<MaterialDoc> adapter = new ArrayAdapter<>(this.getContext(), android.R.layout.simple_list_item_1, documents); 
    matList.setAdapter(adapter); 
+0

非常に効率的です!ありがとうございました! – FiN

+1

間違っています、 'copyFromRealm()'を使って不正なスレッドアクセスを "修正"するのが悪い – EpicPandaForce

3

のようにコードを変更します結果全体がすぐに設定されます。

results.copyFromRealm()の利用は除き時に顔をしかめているときに、実際に必要あなたはGSONてオブジェクトを送信する必要がある、またはあなたが取り消し可能な方法でオブジェクトを変更する必要がある場合(セーブ場合にのみ、通常、非管理対象、 /変更をキャンセルする)。

realm.copyFromRealm()を「無効なスレッドアクセスを避ける」に使用すると、が間違っています答えです。


代わりにハックフィックスとの実際の問題は、あなたが実際にUIスレッド上RealmResultsをフィルタリングする必要がある一方で、あなたが(理由は、バックグラウンドスレッドでフィルタリングを実行ArrayAdapterのデフォルトFilter実装を使用していることですUIスレッドで表示している結果セットです)。

あなたはArrayAdapterのFilterable実装のソースコードをチェックすると、それは次のようだ:

@Override 
public @NonNull Filter getFilter() { 
    if (mFilter == null) { 
     mFilter = new ArrayFilter(); 
    } 
    return mFilter; 
} 

/** 
* <p>An array filter constrains the content of the array adapter with 
* a prefix. Each item that does not start with the supplied prefix 
* is removed from the list.</p> 
*/ 
private class ArrayFilter extends Filter { 
    @Override 
    protected FilterResults performFiltering(CharSequence prefix) { 
     final FilterResults results = new FilterResults(); 

     if (mOriginalValues == null) { 
      synchronized (mLock) { 
       mOriginalValues = new ArrayList<>(mObjects); 
      } 
     } 

     if (prefix == null || prefix.length() == 0) { 
      final ArrayList<T> list; 
      synchronized (mLock) { 
       list = new ArrayList<>(mOriginalValues); 
      } 
      results.values = list; 
      results.count = list.size(); 
     } else { 
      final String prefixString = prefix.toString().toLowerCase(); 

      final ArrayList<T> values; 
      synchronized (mLock) { 
       values = new ArrayList<>(mOriginalValues); 
      } 

      final int count = values.size(); 
      final ArrayList<T> newValues = new ArrayList<>(); 

      for (int i = 0; i < count; i++) { 
       final T value = values.get(i); 
       final String valueText = value.toString().toLowerCase(); 

       // First match against the whole, non-splitted value 
       if (valueText.startsWith(prefixString)) { 
        newValues.add(value); 
       } else { 
        final String[] words = valueText.split(" "); 
        for (String word : words) { 
         if (word.startsWith(prefixString)) { 
          newValues.add(value); 
          break; 
         } 
        } 
       } 
      } 

      results.values = newValues; 
      results.count = newValues.size(); 
     } 

     return results; 
    } 

    @Override 
    protected void publishResults(CharSequence constraint, FilterResults results) { 
     //noinspection unchecked 
     mObjects = (List<T>) results.values; 
     if (results.count > 0) { 
      notifyDataSetChanged(); 
     } else { 
      notifyDataSetInvalidated(); 
     } 
    } 
} 

しかしperformFilteringバックグラウンドスレッドで実行され、publishResultsは、UIスレッドで実行されます。

実際には、このフィルタ実装をUIスレッドで実行されるレルムフィルタに置き換える必要があります。

@Override 
public @NonNull Filter getFilter() { 
    if (mFilter == null) { 
     mFilter = new RealmFilter(); 
    } 
    return mFilter; 
} 

/** 
* <p>An array filter constrains the content of the array adapter with 
* a prefix. Each item that does not start with the supplied prefix 
* is removed from the list.</p> 
*/ 
private class RealmFilter extends Filter { 
    @Override 
    protected FilterResults performFiltering(CharSequence prefix) { 
     return new FilterResults(); 
    } 

    @Override 
    protected void publishResults(CharSequence constraint, FilterResults results) { 
     String prefix = constraint.toString().trim(); 
     //noinspection unchecked 
     RealmQuery<MaterialDoc> query = realm.where(MaterialDoc.class); 
     if(prefix == null || "".equals(prefix)) { 
      /* do nothing */ 
     } else { 
      if(prefix.contains(" ")) { 
       String[] words = prefix.split("\\s"); 
       boolean isFirst = true; 
       for(String word : words) { 
        if(!"".equals(word)) { 
         if(isFirst) { 
          isFirst = false; 
         } else { 
          query = query.or(); 
         } 
         query = query.beginsWith(/*enter query field name*/, word, Case.INSENSITIVE); 
        } 
       } 
      } else { 
       query = query.beginsWith(/* enter query field name*/, prefix, Case.INSENSITIVE); 
      } 
     } 
     mObjects = query.findAll(); 
     notifyDataSetChanged(); 
    } 
} 

しかし、私はレルムの利用の観点からthis Filter solution that isn't conceptually wrongにリンクすることにより、前にこのhere(およびRecyclerView hereのため)に答えました。

関連する問題