【问题标题】:Android implement search with view model and live dataAndroid 使用视图模型和实时数据实现搜索
【发布时间】:2018-12-11 18:43:36
【问题描述】:

我正在为 udacity 课程在 android 中开发一个项目我目前正在尝试实现搜索功能,同时遵循 android 架构组件并使用 firestore 和 room 我对所有这些概念都很陌生,所以请指出任何看起来不对的地方。

所以我创建了一个数据库存储库,以保持我的 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 内容并没有改变任何想法?

【问题讨论】:

    标签: android search observable android-livedata


    【解决方案1】:

    您可以使用 Transformation.switchMap 进行搜索操作。

    1. 在视图模型中创建具有最新搜索字符串的 MutableLiveData。

    2. 内部视图模型使用:

        LiveData<Data> data = 
        LiveDataTransformations.switchMap(searchStringLiveData, string ->  
        repo.loadData(string)))
    
    1. 将上述实时数据返回给 Activity,以便它可以观察和更新视图。

    【讨论】:

    • 抱歉,我不太明白,请您澄清一下,我现在已经阅读了一些关于此的内容,但无法从您的回答中理解我应该将这段代码放在哪里,例如,因为这是一个搜索我正在尝试实现的函数是否还应该创建一个更新值的方法,例如在我的 viewmodel 类的顶部添加可变实时数据 private MutableLiveData name;添加更新值的方法 void setUserName(String userName) { this.name.setValue(userName); } 然后我会把开关图放在哪里
    • 没错,或者您可以使用直接更新数据的数据绑定
    • 根据我的尝试,我还没有让它工作,但我相信你是正确的,因此我接受了你的回答,并会在适当的时候提出一个新问题非常感谢
    【解决方案2】:

    我遇到了同样的问题,我设法用

    解决了它

    切换地图

    可变实时数据

    我们只需要使用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);
            }
    
        }
    }
    

    【讨论】:

    • 从这个解决方案中获得深刻的见解,谢谢!!!只是一个注释.. lambda 函数很棘手.. teamAllList = Transformations.switchMap(filterTextAll, input -> {
    【解决方案3】:

    这是 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
        }
    

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题,并通过@Rohit 的回答解决了它,谢谢!我稍微简化了我的解决方案以更好地说明它。有Categories,每个类别有很多Items。 LiveData 应该只返回一个类别中的项目。用户可以更改类别,然后调用有趣的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)
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        我使用以下方法实现条形码搜索产品。
        每次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
        

        【讨论】:

          猜你喜欢
          • 2022-01-17
          • 1970-01-01
          • 2021-06-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多