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
コンテンツがアイデアを変えることはありませんか?
transformation.switchMapを使用して検索操作を行うことができます。
Viewmodelで、最新の検索文字列を持つMutableLiveDataを作成します。
ビューモデルの内部使用:
LiveData<Data> data =
LiveDataTransformations.switchMap(searchStringLiveData, string ->
repo.loadData(string)))
私は同じ問題に直面し、私はそれを使用してそれを修正することができました
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);
}
}
}
私は同じ問題に直面し、@ 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)
}
}
}
フラグメントで
binding.search.addTextChangedListener { text ->
viewModel.searchNameChanged(text.toString())
}
viewModel.customers.observe(this, Observer {
adapter.submitList(it)
binding.swipe.isRefreshing=false
})
モデルを見る
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
}
次の方法でバーコード検索製品を実装しています。
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