我正在使用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();
}
}
答案 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()
}