私はRecaglerViewとダイアログフラグメントのStaggeredGridLayoutManagerを併用しています。それはSearchFragmentと呼ばれています。
私の問題は、データ(サーバーから他のデータを取得するための他のクエリを作成する)を変更するときです。アイテム0とアイテム1の間に大きな空きスペースがあります。スクロールすると新しいデータが表示されますが、それ以降はクラッシュします。
Logcat:
java.lang.NullPointerException: Attempt to read from field 'int android.support.v7.widget.StaggeredGridLayoutManager$Span.mIndex' on a null object reference
at android.support.v7.widget.StaggeredGridLayoutManager.hasGapsToFix(StaggeredGridLayoutManager.java:344)
at android.support.v7.widget.StaggeredGridLayoutManager.checkForGaps(StaggeredGridLayoutManager.java:272)
at android.support.v7.widget.StaggeredGridLayoutManager.onScrollStateChanged(StaggeredGridLayoutManager.java:307)
at android.support.v7.widget.RecyclerView.dispatchOnScrollStateChanged(RecyclerView.java:3977)
at android.support.v7.widget.RecyclerView.setScrollState(RecyclerView.java:1219)
at android.support.v7.widget.RecyclerView.access$3900(RecyclerView.java:147)
at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:4128)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:777)
at android.view.Choreographer.doCallbacks(Choreographer.java:590)
at android.view.Choreographer.doFrame(Choreographer.java:559)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:763)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6897)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
検索フラグメント:私はwrap_contentするheighすべての項目を変更
public class SearchFragment extends BaseFragment implements SearchView.OnQueryTextListener, MusicPlayerService.MusicPlayerCallback {
public static final String KEY_SONGS = "KEY_SONGS";
public static final String KEY_ALBUMS = "KEY_ALBUMS";
public static final String KEY_VIDEOS = "KEY_VIDEOS";
private RecyclerView rcResult;
private StaggeredGridLayoutManager layoutManager;
private SearchAdapter searchAdapter;
private boolean gotPlayingItem;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setDefaultTitle("");
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frm_search, container, false);
rcResult = (RecyclerView) view.findViewById(R.id.rc_search_result);
layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
rcResult.setLayoutManager(layoutManager);
return view;
}
@Override
public void onResume() {
super.onResume();
getMainActivity().setSearchQueryListener(this);
getThisApplication().getPlayerService().addMediaPlayerCallback(this);
}
@Override
public void onPause() {
getMainActivity().setSearchQueryListener(null);
getThisApplication().getPlayerService().removeMediaPlayerCallback(this);
super.onPause();
}
@Override
public boolean onQueryTextSubmit(String query) {
getMainActivity().clearSearchViewFocus();
fetchData(query);
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
return true;
}
private void fetchData(String query) {
ProgressHUD.show(mContext, null, false, false, null);
NetworkManager.searchKeyword(new Callback<SingerSearchResponse>() {
@Override
public void onResponse(Call<SingerSearchResponse> call, Response<SingerSearchResponse> response) {
// TODO: Handle search response data
KeyboardUtil.hideSoftKeyboard(getMainActivity());
SingerSearchResponse singerSearchResponse = response.body();
if (null == singerSearchResponse) {
} else {
SingerSearchResponse.SearchResponse searchResponse = singerSearchResponse.getSearchResponse();
Map<String, List<? extends BaseDBModel>> searchResult = new HashMap<>();
if (null != searchResponse) {
if (null == searchResponse.getSongs() || searchResponse.getSongs().size() == 0) {
} else {
searchResult.put(KEY_SONGS, searchResponse.getSongs());
}
if (null == searchResponse.getAlbums() || searchResponse.getAlbums().size() == 0) {
} else {
searchResult.put(KEY_ALBUMS, searchResponse.getAlbums());
}
if (null == searchResponse.getVideos() || searchResponse.getVideos().size() == 0) {
} else {
searchResult.put(KEY_VIDEOS, searchResponse.getVideos());
}
bindData(searchResult);
}
}
ProgressHUD.dismissHUD();
}
@Override
public void onFailure(Call<SingerSearchResponse> call, Throwable t) {
ProgressHUD.dismissHUD();
}
}, query);
}
private void bindData(Map<String, List<? extends BaseDBModel>> searchResult) {
searchAdapter = new SearchAdapter(getMainActivity(), searchResult);
layoutManager.invalidateSpanAssignments();
rcResult.setAdapter(searchAdapter);
}
@Override
public void onDestroy() {
getMainActivity().closeSearchView();
super.onDestroy();
}
@Override
public void onStartPlaying(BaseDBModel<? extends BaseDBModel> currentPlayingItem) {
mUIThread.post(new Runnable() {
@Override
public void run() {
gotPlayingItem = false;
searchAdapter.notifyDataSetChanged();
}
});
}
@Override
public void onPlaying(BaseDBModel<? extends BaseDBModel> currentPlayingItem, int currentPositionMils) {
if (!gotPlayingItem) {
mUIThread.post(new Runnable() {
@Override
public void run() {
gotPlayingItem = true;
if (null != searchAdapter) {
searchAdapter.notifyDataSetChanged();
}
}
});
}
}
@Override
public void onPlayerResume() {
}
@Override
public void onPlayerPause() {
}
@Override
public void onPlayingCompleted() {
mUIThread.post(new Runnable() {
@Override
public void run() {
gotPlayingItem = false;
searchAdapter.notifyDataSetChanged();
}
});
}
@Override
public void onError() {
}
@Override
public void onShuffle() {
}
SearchAdapter
public class SearchAdapter extends RecyclerView.Adapter implements OnAdapterUpdate{
private final int TYPE_TITLE = 0;
private final int TYPE_SONG = 1;
private final int TYPE_ALBUM = 2;
private final int TYPE_VIDEO = 3;
private int POS_SONG_TITLE;
private int POS_ALBUM_TITLE;
private int POS_VIDEO_TITLE;
private MainActivity mainActivity;
private LayoutInflater layoutInflater;
private Map<String, List<? extends BaseDBModel>> data;
private List<? extends BaseDBModel> listSongs;
private List<? extends BaseDBModel> listAlbums;
private List<? extends BaseDBModel> listVideos;
private int totalRecords;
public SearchAdapter(MainActivity mainActivity, Map<String, List<? extends BaseDBModel>> data) {
this.mainActivity = mainActivity;
layoutInflater = (LayoutInflater) mainActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.data = data;
preparingData();
}
private void preparingData() {
totalRecords = 0;
POS_SONG_TITLE = POS_ALBUM_TITLE = POS_VIDEO_TITLE = -1;
if (data.containsKey(SearchFragment.KEY_SONGS)) {
listSongs = data.get(SearchFragment.KEY_SONGS);
totalRecords += listSongs.size() + 1;
POS_SONG_TITLE = 0;
POS_ALBUM_TITLE = totalRecords;
}
if (data.containsKey(SearchFragment.KEY_ALBUMS)) {
listAlbums = data.get(SearchFragment.KEY_ALBUMS);
totalRecords += listAlbums.size() + 1;
POS_VIDEO_TITLE = totalRecords;
}
if (data.containsKey(SearchFragment.KEY_VIDEOS)) {
listVideos = data.get(SearchFragment.KEY_VIDEOS);
totalRecords += listVideos.size() + 1;
}
}
@Override
public int getItemViewType(int position) {
if ((position == POS_SONG_TITLE && null != listSongs) || (position == POS_ALBUM_TITLE && null != listAlbums) || (position == POS_VIDEO_TITLE && null != listVideos)) {
return TYPE_TITLE;
} else if (null != listSongs && position > POS_SONG_TITLE && position <= listSongs.size()) {
return TYPE_SONG;
} else if (null != listAlbums && position > POS_ALBUM_TITLE && position <= POS_ALBUM_TITLE + listAlbums.size()) {
return TYPE_ALBUM;
} else if (null != listVideos && position > POS_VIDEO_TITLE && position <= totalRecords) {
return TYPE_VIDEO;
} else {
return super.getItemViewType(position);
}
}
private ICloseMoreAction iCloseMoreAction = new ICloseMoreAction() {
@Override
public void closeAllMoreMenu() {
closeAllMenu();
}
};
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
View itemView = null;
switch (viewType) {
case TYPE_TITLE:
itemView = layoutInflater.inflate(R.layout.item_title, parent, false);
viewHolder = new TitleViewHolder(itemView);
break;
case TYPE_SONG:
itemView = layoutInflater.inflate(R.layout.item_media_songs, parent, false);
viewHolder = new SongViewHolder(itemView, mainActivity, iCloseMoreAction, this);
break;
case TYPE_ALBUM:
itemView = layoutInflater.inflate(R.layout.item_media_album, parent, false);
viewHolder = new AlbumViewHolder(itemView, mainActivity);
break;
case TYPE_VIDEO:
itemView = layoutInflater.inflate(R.layout.item_media_videos, parent, false);
viewHolder = new VideoViewHolder(itemView, mainActivity);
break;
}
return viewHolder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
if (null != listSongs && position > POS_SONG_TITLE && position < listSongs.size() + 1) {
MediaSongModel mediaSongModel = (MediaSongModel) listSongs.get(position - 1);
((SongViewHolder) holder).bindData(mediaSongModel, null, BaseDBModel.TYPE_ONLINE, false);
layoutParams.setFullSpan(true);
} else if (null != listAlbums && position > POS_ALBUM_TITLE && position < POS_ALBUM_TITLE + listAlbums.size() + 1) {
MediaAlbumModel mediaAlbumModel = (MediaAlbumModel) listAlbums.get(position - POS_ALBUM_TITLE - 1);
((AlbumViewHolder) holder).bindData(mediaAlbumModel, null);
} else if (null != listVideos && position > POS_VIDEO_TITLE && position < totalRecords) {
MediaVideoModel mediaVideoModel = (MediaVideoModel) listVideos.get(position - POS_VIDEO_TITLE - 1);
((VideoViewHolder) holder).bindData(mediaVideoModel, null);
} else {
if (POS_SONG_TITLE != -1 && position == POS_SONG_TITLE) {
((TitleViewHolder) holder).bindData("MP3");
} else if (POS_ALBUM_TITLE != -1 && position == POS_ALBUM_TITLE) {
((TitleViewHolder) holder).bindData("Albums");
} else if (POS_VIDEO_TITLE != -1 && position == POS_VIDEO_TITLE) {
((TitleViewHolder) holder).bindData("Videos");
}
}
}
@Override
public int getItemCount() {
return totalRecords;
}
private void closeAllMenu() {
int songListSize = listSongs.size();
for (int i = 0; i < songListSize; i++) {
if (((MediaSongModel) listSongs.get(i)).isExpanded()){
((MediaSongModel) listSongs.get(i)).setExpanded(false);
notifyDataSetChanged();
break;
}
}
}
@Override
public void updateAdapter() {
notifyDataSetChanged();
}
@Override
public void removePosition(int position) {
listSongs.remove(position);
notifyItemRemoved(position);
}
public interface ICloseMoreAction {
void closeAllMoreMenu();
}
}
ノート!
ありがとうございます!
アダプターの問題ではないと思いますが、nums列を2から3に変更してイメージビュー項目の高さを固定すると注意してください。全て大丈夫。私は問題はレイアウトマネージャーから来ていると思うが、多くのオープンバグはまだStaggeredGridLayoutについてcode.google.comにある。とても悲しい –
それはまさに私が考えているものです。私がアダプタについて語ったとき、私はエラーがStaggerdGridLayoutManagerに固有であるという点を立てようとしていました。これが原因で、このバグの原因となった状況が発生したときにlayoutmanagerを再作成するという回避策に至りました。私はあなたが上に投稿した同じスタックトレースを持っていました。これは私のために修正されました(イメージサイズの修正はあなたのために働いたと言いますが、私の場合はcolの数値はユーザーの好みです。 –