当notifyDataSetChanged与MVVM一起使用时,RecyclerView不会更新

时间:2019-04-14 06:28:04

标签: android android-layout android-recyclerview

我正在使用MVVM。主屏幕仅在调试期间显示电影的海报(而不是在常规运行期间显示)。

问题在于观察RecyclerView人口。 Observer中有MainActivity。我希望notifyDataSetChanged方法会导致     收到来自API的数据后,海报就会出现,但不会发生。

我与该问题相关的清除代码仅在https://github.com/RayaLevinson/Test中可用

我错过了与Observer相关的一些重点。请帮我!谢谢。

   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView = findViewById(R.id.recycler_view_movie);

        mMainActivityViewModal = ViewModelProviders.of(this).get(MainActivityViewModel.class);
        mMainActivityViewModal.init();
        mMainActivityViewModal.getMovies().observe(this, new Observer<List<Movie>>() {
            @Override
            public void onChanged(@Nullable List<Movie> movies) {
                mAdapter.notifyDataSetChanged();
            }
        });

        initRecyclerView();
    }

    private void initRecyclerView() {
        mAdapter = new RecyclerViewAdapter(this, mMainActivityViewModal.getMovies().getValue());
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, 2));
        mRecyclerView.setAdapter(mAdapter);
    }

MovieRepository.java

public class MovieRepository {

    private static final String TAG = "MovieRepository";
    private static String mSortBy = "popular";

    private static MovieRepository instance;
    private List<Movie> movies = new ArrayList<>();

    public static MovieRepository getInstance() {
        if (instance == null) {
            instance = new MovieRepository();
        }
        return instance;
    }

    public MutableLiveData<List<Movie>> getMovies() {
        setMovies();

        MutableLiveData<List<Movie>> data = new MutableLiveData<List<Movie>>();
        data.setValue(movies);
        return data;
    }

    private void setMovies() {
        Context context = GlobalApplication.getAppContext();

        if (NetworkUtils.isNetworkAvailable(context)) {
            movies.clear();
            new MovieRepository.FetchMoviesTask().execute(mSortBy);
        } else {
            alertUserAboutNetworkError();
        }
    }

    private void alertUserAboutNetworkError() {
        Context context = GlobalApplication.getAppContext();
     //   Toast.makeText(context, R.string.networkErr, Toast.LENGTH_LONG).show();
    }

    private class FetchMoviesTask extends AsyncTask<String, Void, List<Movie>> {

        @Override
        protected List<Movie> doInBackground(String... params) {

            if (params.length == 0) {
                return null;
            }

            String sortBy = params[0];

            Log.d(TAG, "In doInBackground " + sortBy);
            URL moviesRequestUrl = NetworkUtils.buildUrl(sortBy);

            try {
                String jsonWeatherResponse = NetworkUtils.getResponseFromHttpUrl(moviesRequestUrl);

                return MovieJsonUtils.getMoviesDataFromJson(jsonWeatherResponse);

            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override
        protected void onPostExecute(List<Movie> parsedMoviesData) {
            if (parsedMoviesData != null) {
                for (Movie movie : parsedMoviesData) {
                    movies.add(movie);
                    Log.d(TAG, "In onPostExecute " + " movie was added");
                }

            }
        }
    }
}


MainActivityViewModel.java

public class MainActivityViewModel extends ViewModel {
    private MutableLiveData<List<Movie>> mMovies;
    private MovieRepository mMoviewRepository;

    public void init() {
        if (mMovies != null) {
            return;
        }
        mMoviewRepository = MovieRepository.getInstance();
        mMovies = mMoviewRepository.getMovies();
    }

    public LiveData<List<Movie>> getMovies() {
        return mMovies;
    }
}

RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

    private static final String TAG = "RecyclerViewAdapter";
    private final Context mContext;
    private List<Movie> mMovies;

    public RecyclerViewAdapter(Context mContext, List<Movie> movies) {
        this.mMovies = movies;
        this.mContext   = mContext;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_list_item, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
        Log.d(TAG, "onBindViewHolder called");

        Picasso.get()
                .load(mMovies.get(holder.getAdapterPosition()).getPosterPath())
                .placeholder(R.mipmap.ic_launcher)
                .into(holder.image);


    }

    @Override
    public int getItemCount() {
        return mMovies.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        final ImageView image;
        final LinearLayout parentLayout;

        private ViewHolder(@NonNull View itemView) {
            super(itemView);

            image = itemView.findViewById(R.id.image);
            parentLayout = itemView.findViewById(R.id.parent_layout);
        }
    }

    public void update(List<Movie> movies) {
        mMovies.clear();
        mMovies.addAll(movies);
        notifyDataSetChanged();
    }
}

1 个答案:

答案 0 :(得分:0)

您的componentwillReceiveprops在AsyncTask完成之前执行class Test extends React.Component { state ={ show: false } show(){ this.setState({show: true}) } render() { return ( <div className="App"> <button onClick={this.show.bind(this)}>show the button</button> {this.state.show && <Notification show={true}/>} </div> ) } } const style = { marginBottom: '0.5rem', float: 'right', boxShadow: '0 4px 8px 0 rgba(0, 0, 0, 0.1)', marginTop: '-9px' }; class Notification extends React.Component { constructor(props){ super(props); this.state ={ open: true } } componentWillReceiveProps(props){ console.log('will recieve props') this.setState({open: props.show}) } handleClick(){ this.setState({open: false}) } render(){ if(!this.state.open){ return null } return ( <div className="test"> <div> <button onClick={()=>this.handleClick()}>Card</button> </div> </div> ) } }; ReactDOM.render( <Test name="World" />, document.getElementById('container') ); 。您可以在调试输出中看到这一点。

您要做的是在MovieRepository#getMovies()方法中调用Livedata.setValue()(因为您的on不在主线程上)。然后,您必须从postValue()方法中调用onPostExecute()

我也建议您稍微重构一下ViewModel。从mAdapter.update()方法中删除对存储库的调用,并创建一个仅从存储库中调用load函数的新方法。因此,如果您以后想支持无穷滚动等功能,将对您有很大帮助。

只是个见解,但是我喜欢在ViewModel中而不是在Repository中创建我的可观察对象,并将其作为参数传递。那就是它的样子:

活动

onChanged()

ViewModel

init()

存储库

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    viewModel = ViewModelProviders.of(this).get(YOUR_VIEW_MODEL.class);
    viewModel.init();
    viewModel.getItemsObservable().observe(this, new Observer<List<Item>>() {
        @Override
        public void onChanged(@Nullable List<Item> items) {
            // Add/replace your existing adapter 
            adapter.add/replaceItems(items);
            // For better performance when adding/updating elements you should call notifyItemRangeInserted()/notifyItemRangeChanged(). For replacing the whole dataset notifyDataSetChanged() is fine
            adapter.notifyDataSetChanged();

            // Normally i would put those calls inside the adapter and make appropriate methods but for demonstration.
        }
    });

    initRecyclerView();
    viewModel.loadItems()
}