0

私は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(); 
} 

}

ノート!

ありがとうございます!

答えて

0

バッキングデータベースを交換した後に新しいカーソルにバインドするときも同じ問題がありました。私のアプリケーションは非常にモジュラーなので、私はListLayoutManagerを使用して、私がサポートしている他のレイアウトのいずれかを使用しているときにこれは起こらなかったことにも気づいた。

何とかこれは、ヌルカーソルに変更すると(アダプタのレポートが0のアイテムになり、それは他のポイントでも行われます)、その後dbを変更しながら戻ったときにのみ発生します。検索間にヌルカーソルを挿入しても同じ問題は発生しませんでした。不安定なIDを報告するようにアダプターを設定すると、どちらも役に立ちませんでした。これは、私のカーソルで何か奇妙なことが起こっていると信じさせる。 しかし、上のヒントでは、実行時に列数、レイアウト、およびビュータイプを変更するときに似たような回避策に遭遇しました。このような問題を引き起こすことがわかっている状況が発生した場合は、単にLayoutManagerを再作成します。

注:LeakCanaryを介して、古いLayoutManagerが予期されたとおりに収集され、他のすべてがまだ使用中であることを確認しました。これによりメモリリークは発生しません。ここでのLayoutManager

を作成して設定するだけ固有のオーバーヘッドは、私のアダプタからの(わかりやすくするために編集)の例である:

public void receiveCursor(Cursor newCursor) { 
    if(oldCursor == null && recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager){ 
     // there was a library switch before this, otherwise there would be 
     // a non-null, empty, cursor 
     setCursor(newCursor); 

     // this is the pertinent exerpt from my re-setup code which is also called 
     // from code that switches layouts etc. 
     StaggeredGridLayoutManager manager = 
      new StaggeredGridLayoutManager(numCols, StaggeredGridLayoutManager.VERTICAL); 
      manager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); 
      recyclerView.setLayoutManager(manager); 
      recyclerView.setHasFixedSize(false); 
    }else { 
     setCursor(newCursor); 
    } 
} 

私はバッキングDBを交換することはむしろ稀な作業であることを追加する必要があります私は単純な解決策のためにそのパフォーマンスのトレードオフを完全に喜んで受けています。あなたのユースケースは、ユーザーがインタラクションしている間に何回も起こる可能性のある検索であるため、レイアウトの変更を頻繁に行っている間、オブジェクトの作成、初期化、ガベージコレクションのオーバーハングにより、私はちょうど私がGoogleでこの質問を見つける他の人のためにこれを文書化すると思った。

+0

アダプターの問題ではないと思いますが、nums列を2から3に変更してイメージビュー項目の高さを固定すると注意してください。全て大丈夫。私は問題はレイアウトマネージャーから来ていると思うが、多くのオープンバグはまだStaggeredGridLayoutについてcode.google.comにある。とても悲しい –

+0

それはまさに私が考えているものです。私がアダプタについて語ったとき、私はエラーがStaggerdGridLayoutManagerに固有であるという点を立てようとしていました。これが原因で、このバグの原因となった状況が発生したときにlayoutmanagerを再作成するという回避策に至りました。私はあなたが上に投稿した同じスタックトレースを持っていました。これは私のために修正されました(イメージサイズの修正はあなたのために働いたと言いますが、私の場合はcolの数値はユーザーの好みです。 –

関連する問題