web-dev-qa-db-ja.com

Androidビューモデルとライブデータを使用して検索を実装する

Android udacityコースのプロジェクトに取り組んでいます。現在、Androidアーキテクチャコンポーネントを順守し、firestoreを使用しながら検索機能を実装しようとしています。そして部屋私はこれらすべての概念にかなり慣れていないので、間違っていると思われることは何でも指摘してください。

そこで、firestoreデータベースとroomデータベースの同期を維持し、データを配信するためのデータベースリポジトリを作成しました。次に、viewmodelとオブザーバーパターン(私は思う)を使用しているので、オブザーバーはデータを取得し、変更を探してアダプター(refreshMyList(List))に渡し、次のようにrecyclerviewを設定します:

_ contactViewModel = ViewModelProviders.of(this).get(ContactsViewModel.class);
 contactViewModel.getAllContacts().observe(this, new 
 Observer<List<DatabaseContacts>>() {
        @Override
        public void onChanged(@Nullable List<DatabaseContacts> 
        databaseContacts) {
            ArrayList<DatabaseContacts> tempList = new ArrayList<>();
            tempList.addAll(databaseContacts);
            contactsAdapter.refreshMyList(tempList);
            if (tempList.size() < 1) {
                results.setVisibility(View.VISIBLE);
            } else {
                results.setVisibility(View.GONE);
            }
        }
    });
_

データの検索を実行したいのですが、部屋のクエリをすべて正常に設定し、データリポジトリに検索文字列に基づいて連絡先を取得するメソッドがありますが、リストを更新できないようです。 Transformations.switchMapのようにそれを行う方法はありますか?しかし、私はそれがどのように機能するかについて頭を包むことができないようです誰かが私を助けることができます

現在、非同期タスクから結果のリストを返そうとしています。以前はライブデータを返していましたが、getValue()は常にnullであったため、変更しました。これが正しいかどうかはわかりません。非同期:

_private static class searchContactByName extends AsyncTask<String, Void, 
ArrayList<DatabaseContacts>> {

    private LiveDatabaseContactsDao mDao;

    searchContactByName(LiveDatabaseContactsDao dao){
        this.mDao = dao;
    }

    @Override
    protected ArrayList<DatabaseContacts> doInBackground(String... params) {
        ArrayList<DatabaseContacts> contactsArrayList = new ArrayList<>();
        mDao.findByName("%" + params[0] + "%");
        return contactsArrayList;
    }
}
_

私はこれを連絡先リポジトリから独自の種類のラッパーで呼び出します:

_public List<DatabaseContacts> getContactByName(String name) throws 
ExecutionException, InterruptedException {
    //return databaseContactsDao.findByName(name);
    return new searchContactByName(databaseContactsDao).execute(name).get();
}
_

これは私のビューモデルから次のように呼び出されます:

_public List<DatabaseContacts> getContactByName(String name) throws 
ExecutionException, InterruptedException {
    return  contactRepository.getContactByName(name);
}
_

次に、これをフラグメントから呼び出します。

_private void searchDatabase(String searchString) throws ExecutionException, 
InterruptedException {
    List<DatabaseContacts> searchedContacts = 
    contactViewModel.getContactByName("%" + searchString + "%");
    ArrayList<DatabaseContacts> contactsArrayList = new ArrayList<>();
    if (searchedContacts !=  null){
        contactsArrayList.addAll(searchedContacts);
        contactsAdapter.refreshMyList(contactsArrayList);
    }
}
_

これは、私のonCreateOptionsMenuの検索クエリテキスト変更メソッドから呼び出されます。

_        @Override
        public boolean onQueryTextChange(String newText) {
            try {
                searchDatabase(newText);
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return false;
        }
_

しかし、それは私の元のrecyclerviewコンテンツがアイデアを変えることはありませんか?

5
martinseal1987

transformation.switchMapを使用して検索操作を行うことができます。

  1. Viewmodelで、最新の検索文字列を持つMutableLiveDataを作成します。

  2. ビューモデルの内部使用:

    LiveData<Data> data = 
    LiveDataTransformations.switchMap(searchStringLiveData, string ->  
    repo.loadData(string)))
  1. 上記のライブデータをアクティビティに戻し、ビューを監視および更新できるようにします。
8
Rohit

私は同じ問題に直面し、私はそれを使用してそれを修正することができました

switchMap

そして

MutableLiveData

MutableLiveDataを使用してeditTextの現在の値を設定し、ユーザー検索時にsetValue(editText.getText())を呼び出す必要があります。

 public class FavoriteViewModel extends ViewModel {
            public LiveData<PagedList<TeamObject>> teamAllList;
        public MutableLiveData<String> filterTextAll = new MutableLiveData<>();

        public void initAllTeams(TeamDao teamDao) {
            this.teamDao = teamDao;
            PagedList.Config config = (new PagedList.Config.Builder())
                    .setPageSize(10)
                    .build();

            teamAllList = Transformations.switchMap(filterTextAll, input -> {
                if (input == null || input.equals("") || input.equals("%%")) {
//check if the current value is empty load all data else search
                    return new LivePagedListBuilder<>(
                            teamDao.loadAllTeam(), config)
                            .build();
                } else {
                    System.out.println("CURRENTINPUT: " + input);
                    return new LivePagedListBuilder<>(
                            teamDao.loadAllTeamByName(input), config)
                            .build();
                }

            });

            }

    }

フラグメントのアクティビティ

viewModel = ViewModelProviders.of(activity).get(FavoriteViewModel.class);
                        viewModel.initAllTeams(AppDatabase.getInstance(activity).teamDao());
                        FavoritePageListAdapter adapter = new FavoritePageListAdapter(activity);
                        viewModel.teamAllList.observe(
                                activity, pagedList -> {
                                    try {
                                        Log.e("Paging ", "PageAll" + pagedList.size());

                                        try {
                                            //to prevent animation recyclerview when change the list
                                            recycleFavourite.setItemAnimator(null);
                                            ((SimpleItemAnimator) Objects.requireNonNull(recycleFavourite.getItemAnimator())).setSupportsChangeAnimations(false);

                                        } catch (Exception e) {
                                        }

                                        adapter.submitList(pagedList);

                                    } catch (Exception e) {
                                    }
                                });
                        recycleFavourite.setAdapter(adapter);

//first time set an empty value to get all data
                        viewModel.filterTextAll.setValue("");



                edtSearchFavourite.addTextChangedListener(new TextWatcher() {
                    @Override
                    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                    }

                    @Override
                    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                    @Override
                    public void afterTextChanged(Editable editable) {
                      //just set the current value to search.
                        viewModel.filterTextAll.setValue("%" + editable.toString() + "%");


                    }
                });

ルームダオ

@Dao
public interface TeamDao {


        @Query("SELECT * FROM teams order by orders")
        DataSource.Factory<Integer, TeamObject> loadAllTeam();


        @Query("SELECT * FROM teams where team_name LIKE  :name or LOWER(team_name_en) like LOWER(:name) order by orders")
        DataSource.Factory<Integer, TeamObject> loadAllTeamByName(String name);


    }

PageListAdapter

public class FavoritePageListAdapter extends PagedListAdapter<TeamObject, FavoritePageListAdapter.OrderHolder> {
    private static DiffUtil.ItemCallback<TeamObject> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<TeamObject>() {
                // TeamObject details may have changed if reloaded from the database,
                // but ID is fixed.
                @Override
                public boolean areItemsTheSame(TeamObject oldTeamObject, TeamObject newTeamObject) {
                    System.out.println("GGGGGGGGGGGOTHERE1: " + (oldTeamObject.getTeam_id() == newTeamObject.getTeam_id()));
                    return oldTeamObject.getTeam_id() == newTeamObject.getTeam_id();
                }

                @Override
                public boolean areContentsTheSame(TeamObject oldTeamObject,
                                                  @NonNull TeamObject newTeamObject) {
                    System.out.println("GGGGGGGGGGGOTHERE2: " + (oldTeamObject.equals(newTeamObject)));
                    return oldTeamObject.equals(newTeamObject);
                }
            };

    private Activity activity;

    public FavoritePageListAdapter() {
        super(DIFF_CALLBACK);
    }

    public FavoritePageListAdapter(Activity ac) {
        super(DIFF_CALLBACK);
        this.activity = ac;

    }

    @NonNull
    @Override
    public OrderHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_favourite, parent, false);
        return new FavoritePageListAdapter.OrderHolder(view);

    }

    @Override
    public void onBindViewHolder(@NonNull OrderHolder holder,
                                 int position) {
        System.out.println("GGGGGGGGGGGOTHERE!!!");

        if (position <= -1) {
            return;
        }
        TeamObject teamObject = getItem(position);


        try {
                holder.txvTeamRowFavourite.setText(teamObject.getTeam_name());


        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    public class OrderHolder extends RecyclerView.ViewHolder {

        private TextView txvTeamRowFavourite;


        OrderHolder(View itemView) {
            super(itemView);
            txvTeamRowFavourite = itemView.findViewById(R.id.txv_team_row_favourite);
        }

    }
}
3
Momen Zaqout

私は同じ問題に直面し、@ Rohitの答えでそれを解決しました、ありがとう!ソリューションを少し簡略化して、わかりやすく説明しました。 Categoriesがあり、各カテゴリには多くのItemsがあります。 LiveDataは、1つのカテゴリからのアイテムのみを返す必要があります。ユーザーはカテゴリを変更でき、次にfun search(id: Int)が呼び出されます。これにより、valueと呼ばれるMutableLiveDataのcurrentCategoryが変更されます。これにより、switchMapがトリガーされ、カテゴリのアイテムに対する新しいクエリが生成されます。

class YourViewModel: ViewModel() {

    // stores the current Category
    val currentCategory: MutableLiveData<Category> = MutableLiveData()

    // the magic happens here, every time the value of the currentCategory changes, getItemByCategoryID is called as well and returns a LiveData<Item>
    val items: LiveData<List<Item>> = Transformations.switchMap(currentCategory) { category ->
           // queries the database for a new list of items of the new category wrapped into a LiveData<Item>
           itemDao.getItemByCategoryID(category.id)
    }

    init {
        currentCategory.value = getStartCategoryFromSomewhere()
    }

    fun search(id: Int) { // is called by the fragment when you want to change the category. This can also be a search String...
        currentCategory.value?.let { current ->
            // sets a Category as the new value of the MutableLiveData
            current.value = getNewCategoryByIdFromSomeWhereElse(id)
        }
    }
}
2
Spipau

これがKOTLINの実例です

フラグメントで

binding.search.addTextChangedListener { text ->
            viewModel.searchNameChanged(text.toString())
        }


        viewModel.customers.observe(this, Observer {
            adapter.submitList(it)
            binding.swipe.isRefreshing=false
        })
  • 検索->は私の編集テキストです
  • 顧客->はviewModelのデータリストです

モデルを見る

     private val _searchStringLiveData = MutableLiveData<String>()

         val customers = Transformations.switchMap(_searchStringLiveData){string->
                repository.getCustomerByName(string)
            }

    init {
            refreshCustomers()
            _searchStringLiveData.value=""
        }


fun searchNameChanged(name:String){
        _searchStringLiveData.value=name
    }
0
Tarik Husin

次の方法でバーコード検索製品を実装しています。
productBarCodeの値が変更されるたびに、製品はルームデータベースで検索されます。

@AppScoped
class PosMainViewModel @Inject constructor(
var localProductRepository: LocalProductRepository) : ViewModel() {

val productBarCode: MutableLiveData<String> = MutableLiveData()

val product: LiveData<LocalProduct> = Transformations.switchMap(productBarCode) { barcode ->
    localProductRepository.getProductByBarCode(barcode)
}

init {
    productBarCode.value = ""
}

fun search(barcode: String) {
    productBarCode.value = barcode
}}

活動中

posViewModel.product.observe(this, Observer {
        if (it == null) {
           // not found
        } else {
            productList.add(it)
            rvProductList.adapter!!.notifyDataSetChanged()
        }
    })

検索用

posViewModel.search(barcode) //search param or barcode
0
Zulqarnain