简体   繁体   English

如何根据微调器选择MVVM + LiveData更新recyclerview

[英]How to update recyclerview based on spinner selection MVVM + LiveData

I have an Android app (Java) that uses MVVM + LiveData to display a list of shows by season.我有一个 Android 应用程序(Java),它使用 MVVM + LiveData 按季节显示节目列表。 I want season 1 titles to display in a RecyclerView when the user first lands on the fragment.当用户第一次登陆片段时,我希望第 1 季的标题显示在 RecyclerView 中。 I then want the user to be able to select another season and then display those titles.然后我希望用户能够 select 另一个赛季然后显示这些标题。 I'm able to send the selected season to my ViewModel, but I don't know how to update my RecyclerView with the selected season's titles.我可以将所选季节发送到我的 ViewModel,但我不知道如何使用所选季节的标题更新我的 RecyclerView。 Any help is greatly appreciated!任何帮助是极大的赞赏!

Here is my fragment:这是我的片段:

    public class ShowFragment extends Fragment {
        private ShowTitlesAdapter showTitlesAdapter;
        private ArrayList<Titles> titlesList;
    
        private FragmentShowBinding binding;
        private TitlesViewModel titlesViewModel;
        private String ShowTag, ShowTitle, ShowImgUrl;
        private RecyclerView rvTitles;
        private Spinner popupSpinner;
    
        public ShowFragment() {
            // Required empty public constructor
        }
    
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            binding = FragmentShowBinding.inflate(inflater, container, false);
            View view = binding.getRoot();
            return view;
        }
    
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
    
            titlesList = new ArrayList<>();
    
            titlesViewModel = new ViewModelProvider(this).get(TitlesViewModel.class);
            initAdapter();
            init();
    
            ShowFragmentArgs args = ShowFragmentArgs.fromBundle(getArguments());
            ShowTag = args.getFranchiseTag();
            titlesViewModel.getAllTitles(ShowTag);
    
            ShowImgUrl = args.getShowImg();
            ShowTitle = args.getShowTitle();
    
            titlesViewModel.getAllTitles(ShowTitle);
            binding.showTitle.setText(ShowTitle);
            Drawable placeHolder = getResources().getDrawable(R.drawable.placeholder_1080x1080);
    
            Glide.with(getContext())
                    .load(ShowImgUrl) //image url
                    .placeholder(placeHolder)
                    .into(binding.showImage);
        }
    
        private void initAdapter() {
            binding.rvTitles.setLayoutManager(new LinearLayoutManager(getContext()));
            showTitlesAdapter = new ShowTitlesAdapter(getContext(), titlesList);
            binding.rvTitles.setAdapter(showTitlesAdapter);
        }
    
    
        private void init() {
            titlesList.clear();
    
            titlesViewModel.sortedSeasonListOne.observe(getViewLifecycleOwner(), new Observer<List<Titles>>() {
                @Override
                public void onChanged(List<Titles> titles) {
                    titlesList.addAll(titles);
                    showTitlesAdapter.setShowTitlesList(titlesList);
                }
            });
            titlesViewModel.getAllTitles(ShowTag);
    
            createDropDownList(titlesList);
        }
    
    
        public void createDropDownList(ArrayList<Titles> titles) {
            List<String> seasonListStr = new ArrayList<>();
    
            titlesViewModel.seasonsStrList.observe(getViewLifecycleOwner(), new Observer<List<String>>() {
                @Override
                public void onChanged(List<String> seasons) {
                    seasonListStr.addAll(seasons);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                        seasonListStr.replaceAll(s -> "Season " + s);
                    }
                    ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, seasonListStr);
                    adapter.setDropDownViewResource(android.R.layout.simple_list_item_1);
                    binding.popupSpinner.setAdapter(adapter);
                    binding.popupSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    
                        @Override
                        public void onItemSelected(AdapterView<?> parent, View view, int position, long l) {
                            titlesViewModel.populateTitlesInSeason(parent.getItemAtPosition(position).toString());
//                        showTitlesAdapter.setShowTitlesList(titlesList); --> I don't know how to update this list with another season of titles!
                        }
    
                        @Override
                        public void onNothingSelected(AdapterView<?> adapterView) {
                        }
                    });
                    binding.popupSpinner.setAdapter(adapter);
                }
            });
        }
    

        @Override
        public void onDestroyView() {
            super.onDestroyView();
            binding = null;
        }
    }

ViewModel:视图模型:

public class TitlesViewModel extends ViewModel {
    Repository repository;
    private final io.reactivex.rxjava3.disposables.CompositeDisposable disposables = new CompositeDisposable();

    public MutableLiveData<ArrayList<Titles>> titlesListLiveData;
    public List<String> seasonsList;
    public LiveData<List<Titles>> sortedList;
    public LiveData<List<Titles>> sortedSeasonListOne;
    public LiveData<List<Titles>> sortedSeasonListSeason;
    public LiveData<List<Integer>> seasons;
    public LiveData<List<String>> seasonsStrList;


    @Inject
    public TitlesViewModel(Repository repository) {
        this.repository = repository;
        titlesListLiveData = repository.getTitlesLiveData();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            sortedList = Transformations.map(titlesListLiveData, titlesList -> titlesList.stream().sorted(Comparator.comparing(Titles::getSeasonNumber).thenComparing(Titles::getEpisodeNumber)).collect(Collectors.toList()));
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            seasons = Transformations.map(sortedList, list -> list.stream().map(Titles::getSeasonNumber).distinct().collect(Collectors.toList()));
            seasonsStrList = Transformations.map(seasons, strList -> strList.stream().map(Object::toString).collect(Collectors.toList()));
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            sortedSeasonListOne = Transformations.map(titlesListLiveData, srtList -> srtList.stream().sorted(Comparator.comparing(Titles::getSeasonNumber).thenComparing(Titles::getEpisodeNumber)).filter(list ->1 == list.getSeasonNumber()).collect(Collectors.toList()));
        }

    }

    public void populateTitlesInSeason(String season) {
        String seasonNo = season.substring(7);
        int seasonNum = 1;
        try{
            seasonNum = Integer.parseInt(seasonNo);

        }catch(NumberFormatException ex){

        }


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            int finalSeasonNum = seasonNum;
            sortedSeasonListSeason = Transformations.map(titlesListLiveData, seasonList -> seasonList.stream().sorted(Comparator.comparing(Titles::getSeasonNumber).thenComparing(Titles::getEpisodeNumber)).filter(list -> finalSeasonNum == list.getSeasonNumber()).collect(Collectors.toList()));
        }
    }

    /////////Titles
    public void getAllTitles(String tag) {
        repository.getAllTitles(tag);
    }


    @RequiresApi(api = Build.VERSION_CODES.N)
    public MutableLiveData<ArrayList<Titles>> getTitlesListLiveData() {
        return repository.getTitlesLiveData();
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        disposables.clear();
    }
}

Repository:存储库:

public class Repository {

    final ApiService apiService;

    private final MutableLiveData<ArrayList<Titles>> titlesList = new MutableLiveData<>();

    @Inject
    public Repository(ApiService service){this.apiService = service;}

    public void getAllTitles(String tag){
        apiService.getTitles(tag).enqueue(new Callback<ArrayList<Titles>>() {

            @Override
            public void onResponse(Call<ArrayList<Titles>> call, Response<ArrayList<Titles>> response) {
                if (response.body() != null) {
                    titlesList.postValue(response.body());
                }
            }

            @Override
            public void onFailure(Call<ArrayList<Titles>> call, Throwable t) {
                Log.d("Titles","onFailure"+Thread.currentThread().getName());
            }
        });
    }

    public MutableLiveData<ArrayList<Titles>> getTitlesLiveData(){
        return titlesList;
    }

}

RecyclerView Adapter: RecyclerView 适配器:

public class ShowTitlesAdapter extends RecyclerView.Adapter<ShowTitlesAdapter.ShowTitlesViewHolder> {
    private ItemShowTitleBinding binding;
    private ArrayList<Titles> showTitles;

    private Context context;
    int pos;

    public ShowTitlesAdapter(Context context, ArrayList<Titles> showTitles){
        this.context = context;
        this.showTitles = showTitles;
    }

    @NonNull
    @Override
    public ShowTitlesViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);
        binding = ItemShowTitleBinding.inflate(inflater, parent, false);
        return new ShowTitlesViewHolder(binding);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onBindViewHolder(@NonNull ShowTitlesViewHolder holder, int position) {
        holder.binding.showTitleLayout.setClipToOutline(true);
        holder.binding.episodeNo.setText(Integer.toString(showTitles.get(position).getEpisodeNumber()));
        holder.binding.episodeTitle.setText(showTitles.get(position).getTitle());
        pos = position;
        holder.binding.showTitleLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ShowFragmentDirections.ActionShowFragmentToMovieShowDetailFragment action =
                        ShowFragmentDirections.actionShowFragmentToMovieShowDetailFragment(showTitles.get(position).getId(), showTitles.get(position).getFranchiseTag());
                Navigation.findNavController(v).navigate((NavDirections) action);
            }
        });
    }

    @Override
    public int getItemCount() {
        if(showTitles != null){
            return showTitles.size();
        }
        return 0;
    }

    public class ShowTitlesViewHolder extends RecyclerView.ViewHolder {

        private ItemShowTitleBinding binding;

        public ShowTitlesViewHolder(ItemShowTitleBinding binding) {
            super(binding.getRoot());
            this.binding = binding;

        }
    }

    public void setShowTitlesList(ArrayList<Titles> showTitles){
        this.showTitles = showTitles;
        notifyDataSetChanged();
    }

}

In short:简而言之:

  1. In ShowFragment you need to observe sortedSeasonListSeason of your TitlesViewModelShowFragment中,您需要观察sortedSeasonListSeasonTitlesViewModel
  2. When you receive concrete season you should pass it to populateTitlesInSeason (you already do it)当您收到具体季节时,您应该将其传递给populateTitlesInSeason (您已经这样做了)
  3. Inside populateTitlesInSeason method you should pass your sortedSeasonListSeason list back to ShowFragment via MutableLiveDatapopulateTitlesInSeason方法中,您应该通过MutableLiveDatasortedSeasonListSeason列表传回ShowFragment
  4. So you need to create MutableLiveData for sortedSeasonListSeason list in TitlesViewModel to pass selected data, for example sortedSeasonListSeasonLiveData所以你需要在TitlesViewModel中为sortedSeasonListSeason列表创建MutableLiveData以传递选定的数据,例如sortedSeasonListSeasonLiveData
  5. So you need just sortedSeasonListSeasonLiveData.postValue(sortedSeasonListSeason)所以你只需要sortedSeasonListSeasonLiveData.postValue(sortedSeasonListSeason)
  6. Now back to ShowFragment and handle received list of concrete titles in observer from stage #1 of my instruction现在回到ShowFragment并从我的指令的第 1 阶段处理观察者中收到的具体标题列表

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM