Android Implement Search With View Model And Live Data
Solution 1:
you can use Transformation.switchMap to do search operations.
LiveData<Data> data =
LiveDataTransformations.switchMap(searchStringLiveData, string ->
repo.loadData(string)))
- Return the above live data to activity so it can observe and update view.
Solution 2:
I faced the same issue and I managed to fix it using
switchMap
and
MutableLiveData
We just need to use MutableLiveData to set the current value of editText, and when the user search we call setValue(editText.getText())
publicclassFavoriteViewModelextendsViewModel {
public LiveData<PagedList<TeamObject>> teamAllList;
public MutableLiveData<String> filterTextAll = new MutableLiveData<>();
publicvoidinitAllTeams(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 searchreturnnew LivePagedListBuilder<>(
teamDao.loadAllTeam(), config)
.build();
} else {
System.out.println("CURRENTINPUT: " + input);
returnnew LivePagedListBuilder<>(
teamDao.loadAllTeamByName(input), config)
.build();
}
});
}
}
in Activity of fragment
viewModel = ViewModelProviders.of(activity).get(FavoriteViewModel.class);
viewModel.initAllTeams(AppDatabase.getInstance(activity).teamDao());
FavoritePageListAdapteradapter=newFavoritePageListAdapter(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(newTextWatcher() {
@OverridepublicvoidbeforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@OverridepublicvoidonTextChanged(CharSequence charSequence, int i, int i1, int i2) {
@OverridepublicvoidafterTextChanged(Editable editable) {
//just set the current value to search.
viewModel.filterTextAll.setValue("%" + editable.toString() + "%");
}
});
Room Dao
@DaopublicinterfaceTeamDao {
@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
publicclassFavoritePageListAdapterextendsPagedListAdapter<TeamObject, FavoritePageListAdapter.OrderHolder> {
privatestatic DiffUtil.ItemCallback<TeamObject> DIFF_CALLBACK =
newDiffUtil.ItemCallback<TeamObject>() {
// TeamObject details may have changed if reloaded from the database,// but ID is fixed.@OverridepublicbooleanareItemsTheSame(TeamObject oldTeamObject, TeamObject newTeamObject) {
System.out.println("GGGGGGGGGGGOTHERE1: " + (oldTeamObject.getTeam_id() == newTeamObject.getTeam_id()));
return oldTeamObject.getTeam_id() == newTeamObject.getTeam_id();
}
@OverridepublicbooleanareContentsTheSame(TeamObject oldTeamObject,
@NonNull TeamObject newTeamObject) {
System.out.println("GGGGGGGGGGGOTHERE2: " + (oldTeamObject.equals(newTeamObject)));
return oldTeamObject.equals(newTeamObject);
}
};
private Activity activity;
publicFavoritePageListAdapter() {
super(DIFF_CALLBACK);
}
publicFavoritePageListAdapter(Activity ac) {
super(DIFF_CALLBACK);
this.activity = ac;
}
@NonNull@Overridepublic OrderHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Viewview= LayoutInflater.from(parent.getContext()).inflate(R.layout.row_favourite, parent, false);
returnnewFavoritePageListAdapter.OrderHolder(view);
}
@OverridepublicvoidonBindViewHolder(@NonNull OrderHolder holder,
int position) {
System.out.println("GGGGGGGGGGGOTHERE!!!");
if (position <= -1) {
return;
}
TeamObjectteamObject= getItem(position);
try {
holder.txvTeamRowFavourite.setText(teamObject.getTeam_name());
} catch (Exception e) {
e.printStackTrace();
}
}
publicclassOrderHolderextendsRecyclerView.ViewHolder {
private TextView txvTeamRowFavourite;
OrderHolder(View itemView) {
super(itemView);
txvTeamRowFavourite = itemView.findViewById(R.id.txv_team_row_favourite);
}
}
}
Solution 3:
Here is a working example in KOTLIN
in the Fragment
binding.search.addTextChangedListener { text ->
viewModel.searchNameChanged(text.toString())
}
viewModel.customers.observe(this, Observer {
adapter.submitList(it)
binding.swipe.isRefreshing=false
})
- search -> is my edit text
- customers -> is the data list in the viewModel
View Model
privateval _searchStringLiveData = MutableLiveData<String>()
val customers = Transformations.switchMap(_searchStringLiveData){string->
repository.getCustomerByName(string)
}
init {
refreshCustomers()
_searchStringLiveData.value=""
}
funsearchNameChanged(name:String){
_searchStringLiveData.value=name
}
Solution 4:
I faced the same issue and solved it with the answer of @Rohit, thanks! I simplified my solution a bit to illustrate it better. There are Categories
and each Category has many Items
. The LiveData should only return items from one Category. The user can change the Category and then the fun search(id: Int)
is called, which changes the value
of a MutableLiveData called currentCategory
. This then triggers the switchMap
and results in a new query for items of the category:
classYourViewModel: ViewModel() {
// stores the current Categoryval 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()
}
funsearch(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)
}
}
}
Solution 5:
I implement the bar code searching product using the following approach. Everytime the value of productBarCode changes, the product will be searched in the room db.
@AppScopedclassPosMainViewModel@Injectconstructor(
var localProductRepository: LocalProductRepository) : ViewModel() {
val productBarCode: MutableLiveData<String> = MutableLiveData()
val product: LiveData<LocalProduct> = Transformations.switchMap(productBarCode) { barcode ->
localProductRepository.getProductByBarCode(barcode)
}
init {
productBarCode.value = ""
}
funsearch(barcode: String) {
productBarCode.value = barcode
}}
In activity
posViewModel.product.observe(this, Observer {
if (it == null) {
// not found
} else {
productList.add(it)
rvProductList.adapter!!.notifyDataSetChanged()
}
})
for searching
posViewModel.search(barcode) //search param or barcode
Post a Comment for "Android Implement Search With View Model And Live Data"