Я пытаюсь создать список фильмов с ListFragment (следующим образом this tutorial). В фрагменте у меня есть Loader и AsyncTaskLoader, который загружает информацию из веб-службы. AsyncTaskLoader корректно загружает всю информацию, и метод «onLoadFinished» выполняется правильно. В onLoadFinished Я вызываю функцию в списке для установки новых данных, а затем вызываю notifyDataSetChanged(). Я знаю, что данные в адаптере установлены правильно (оператор журнала будет печатать заголовок первого фильма из класса адаптера), но listview ничего не покажет. Однако, если я поставлю список фильмов для конструктора адаптера, он работает.Обновление списка в ListFragment
Код для ListFragment (не все код, только код, который устанавливает адаптер, погрузчик и т.д.):
public class MovieListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<MovieInformation>>{
public static final String TAG = "MovieListFragment";
private SimpleMovieListAdapter listAdapter;
public MovieListFragment() {
}
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
System.out.println("DataListFragment.onActivityCreated");
setEmptyText("No results");
listAdapter = new SimpleMovieListAdapter(getActivity(), R.layout.movie_list_row_layout);
setListAdapter(listAdapter);
setListShown(false);
getLoaderManager().initLoader(0, null, this);
}
Загрузчик:
public Loader<List<MovieInformation>> onCreateLoader(int arg0, Bundle arg1) {
Log.d(TAG, "onCreateLoader");
return new MovieListDownloader(getActivity());
}
@Override
public void onLoadFinished(Loader<List<MovieInformation>> arg0,
List<MovieInformation> data) {
listAdapter.setData(data);
Log.d(TAG, "onLoadFinished");
Log.d(TAG, ("length is: " + data.size()));
setListShown(true);
/*
if(data != null && !data.isEmpty()){
setListShown(true);
}else{
setListShownNoAnimation(true);
}
*/
}
@Override
public void onLoaderReset(Loader<List<MovieInformation>> arg0) {
Log.d(TAG, "onLoaderReset");
listAdapter.setData(null);
}
Адаптер :
public class SimpleMovieListAdapter extends ArrayAdapter<MovieInformation> {
private static final String TAG = "SimpleMovieListAdapter";
private Context context;
private List<MovieInformation> movies;
ImageLoader imageLoader;
public SimpleMovieListAdapter(Context context, int textViewResourceId,
List<MovieInformation> objects) {
super(context, textViewResourceId, objects);
this.context = context;
this.movies = objects;
imageLoader = ImageLoader.getInstance();
}
public SimpleMovieListAdapter(Context context, int textViewResourceId){
super(context, textViewResourceId);
this.context = context;
imageLoader = ImageLoader.getInstance();
}
public void setData(List<MovieInformation> movies){
this.movies = movies;
//This log statement will display the title of the first movie in the set (so the list of movies is updated correctly)
Log.d(TAG, this.movies.get(0).getTitle());
notifyDataSetChanged();
}
public View getView(int position, View convertView, ViewGroup parent){
Log.d(TAG, ("GetView position" + position));
View rowView;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(convertView == null){
rowView = inflater.inflate(R.layout.movie_list_row_layout, parent, false);
} else {
rowView = convertView;
}
TextView titleText = (TextView) rowView.findViewById(R.id.movie_list_title);
TextView yearText = (TextView) rowView.findViewById(R.id.movie_list_year);
TextView voteAverageText = (TextView) rowView.findViewById(R.id.movie_list_vote_average);
ImageView posterView = (ImageView) rowView.findViewById(R.id.movie_list_poster);
titleText.setText(movies.get(position).getTitle());
yearText.setText("(" + movies.get(position).getYear() + ")");
voteAverageText.setText(movies.get(position).getFormattedRatingAndVoteCount() + " " +
context.getString(R.string.movie_info_votes) + ")");
imageLoader.displayImage((MyApplication.POSTER_SOURCE_ADDRESS +
movies.get(position).getPosterPath()), posterView, MyApplication.options);
return rowView;
}
}
Редактировать 1 Я нашел решение сразу после размещения вопроса, но я не думаю, что это «хорошее» решение. В onLoadFinished, вместо передачи данных на adapter.setData() Я просто создал новый адаптер (передающий массив фильмов в конструктор), а затем снова вызвал setListAdapter(). Это работает, но похоже на уродливое решение (создание и установка новых адаптеров, вероятно, потребует больше ресурсов?).
Edit 2 Просто чтобы прояснить кое-что о УстановитьДанные в adapterclass: После добавления всех данных в массив (this.movies) цикл I через весь массив и напечатать название для каждого фильма с лог файле заявление. Таким образом, данные сохраняются в arraylist в классе адаптера после вызова setData, но список пока не заполнен данными.
public void setData(List<MovieInformation> movies){
this.movies = movies;
if(movies != null){
this.movies.addAll(movies);
}
for(MovieInformation m : this.movies){
Log.d(TAG, ("movie : " + this.movies.indexOf(m) + " " + this.movies.get(this.movies.indexOf(m)).getTitle()));
}
notifyDataSetChanged();
}
К сожалению, этот список будет еще не обновляет. Фильмы будут добавлены в ArrayList в классе Adapter, но getView никогда не будет выполнен (поэтому строки никогда не создаются) – Skrot
Что происходит с нулевой проверкой фильмов в одной строке после разыменования ее без нулевой проверки? –
есть место, где он устанавливает setData (null), он может получить NPE, если вы делаете this.movies.addAll (null) –