Android使用视图模型和实时数据实施搜索

时间:2018-07-03 12:34:13

标签: android search observable android-livedata

我正在为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?但我似乎无法全神贯注于它的工作原理,任何人都可以帮助我

当前,我正在尝试从异步任务中返回结果列表,它曾经返回实时数据,但是我将其更改为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中的on search查询文本更改方法调用的:

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

但是我的原始recyclerview内容不会改变任何想法吗?

5 个答案:

答案 0 :(得分:5)

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

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

  2. 内部视图模型使用:

q2 := 'SELECT ' || destColumns || ',' || destMeasures ||' FROM ' || tablename || '';
EXECUTE IMMEDIATE  q2 into Columnsfetched,MeasuresFetched;
  1. 将上述实时数据恢复为活动状态,以便可以观察和更新视图。

答案 1 :(得分:1)

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

我使用以下方法实现条形码搜索产品。
每当productBarCode的值更改时,都会在会议室db中搜索产品。

@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

答案 3 :(得分:0)

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

对其进行了修复
  

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);
        }

    }
}

答案 4 :(得分:0)

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