Android 使用视图模型和实时数据实施搜索
Android implement search with view model and live data
我正在 android 进行一个大胆课程的项目 我目前正在尝试实现搜索功能,同时遵守 android 架构组件并使用 firestore 和 room 我正在对所有这些概念都比较陌生,所以请指出任何看起来不对的地方。
所以我创建了一个数据库存储库来保持我的 firestore 和房间数据库同步并传送数据。然后我使用 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 那样做?但我似乎无法理解它是如何工作的任何人都可以帮助我
目前我正在尝试 return 异步任务的结果列表,它曾经是 return 实时数据,但我将其更改为 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进行搜索操作。
在视图模型中创建具有最新搜索字符串的 MutableLiveData。
内部视图模型使用:
LiveData<Data> data =
LiveDataTransformations.switchMap(searchStringLiveData, string ->
repo.loadData(string)))
- Return 上面的实时数据到 activity 所以它可以观察和更新视图。
我遇到了同样的问题,通过@Rohit 的回答解决了,谢谢!我稍微简化了我的解决方案以更好地说明它。有 Categories
个,每个类别有很多 Items
个。 LiveData 应该只有来自一个类别的 return 项。用户可以更改类别,然后调用函数 search(id: Int)
,这会更改名为 currentCategory
的 MutableLiveData 的 value
。然后这会触发 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)
}
}
}
我使用以下方法实现条码搜索产品。
每次 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
}}
在activity
posViewModel.product.observe(this, Observer {
if (it == null) {
// not found
} else {
productList.add(it)
rvProductList.adapter!!.notifyDataSetChanged()
}
})
用于搜索
posViewModel.search(barcode) //search param or barcode
我遇到了同样的问题,我设法使用
修复了它
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();
}
});
}
}
在片段Activity中
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);
}
}
}
这是 KOTLIN 中的一个工作示例
在片段中
binding.search.addTextChangedListener { text ->
viewModel.searchNameChanged(text.toString())
}
viewModel.customers.observe(this, Observer {
adapter.submitList(it)
binding.swipe.isRefreshing=false
})
- 搜索 -> 是我的编辑文本
- customers -> 是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
}
我正在 android 进行一个大胆课程的项目 我目前正在尝试实现搜索功能,同时遵守 android 架构组件并使用 firestore 和 room 我正在对所有这些概念都比较陌生,所以请指出任何看起来不对的地方。
所以我创建了一个数据库存储库来保持我的 firestore 和房间数据库同步并传送数据。然后我使用 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 那样做?但我似乎无法理解它是如何工作的任何人都可以帮助我
目前我正在尝试 return 异步任务的结果列表,它曾经是 return 实时数据,但我将其更改为 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进行搜索操作。
在视图模型中创建具有最新搜索字符串的 MutableLiveData。
内部视图模型使用:
LiveData<Data> data =
LiveDataTransformations.switchMap(searchStringLiveData, string ->
repo.loadData(string)))
- Return 上面的实时数据到 activity 所以它可以观察和更新视图。
我遇到了同样的问题,通过@Rohit 的回答解决了,谢谢!我稍微简化了我的解决方案以更好地说明它。有 Categories
个,每个类别有很多 Items
个。 LiveData 应该只有来自一个类别的 return 项。用户可以更改类别,然后调用函数 search(id: Int)
,这会更改名为 currentCategory
的 MutableLiveData 的 value
。然后这会触发 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)
}
}
}
我使用以下方法实现条码搜索产品。
每次 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
}}
在activity
posViewModel.product.observe(this, Observer {
if (it == null) {
// not found
} else {
productList.add(it)
rvProductList.adapter!!.notifyDataSetChanged()
}
})
用于搜索
posViewModel.search(barcode) //search param or barcode
我遇到了同样的问题,我设法使用
修复了它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();
}
});
}
}
在片段Activity中
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);
}
}
}
这是 KOTLIN 中的一个工作示例
在片段中
binding.search.addTextChangedListener { text ->
viewModel.searchNameChanged(text.toString())
}
viewModel.customers.observe(this, Observer {
adapter.submitList(it)
binding.swipe.isRefreshing=false
})
- 搜索 -> 是我的编辑文本
- customers -> 是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
}