我有一个名为genreFragment的片段,它有一个recyclerview,显示设备中歌曲类型的列表。当我尝试为此recyclerview实现多选功能时,问题就出现了,每当我切换到此片段时,应用程序就会冻结并重新启动。
当我删除片段recyclerView.setAdapter(adapter);
中的行,它将recyclerview的适配器设置为recyclerview,或者此行genresList.add(genreModel);
,该应用程序不会冻结。
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import recyclerview_adapters.genres_adapter;
/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link genreFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link genreFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class genreFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
// true by default until content provider and recyclerview adapter is ready
private final boolean no_content = true;
private TextView nocontnet_text;
private RecyclerView recyclerView;
private List<GenreModel> genresList = new ArrayList<>();
private List<GenreModel> list = new ArrayList<>();
private OnFragmentInteractionListener mListener;
SharedPreferences sharedPreferences = null;
onGenreLongClickListener mOnGenreLongClickListener;
onClickWhileMultiSelectOn mOnClickWhileMultiSelectOn;
boolean isMultiSelectOn = false;
boolean selected = false;
int c = 0;
genres_adapter adapter;
/* IGNORE
public genreFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment genreFragment.
*/
// TODO: Rename and change types and number of parameters
public static genreFragment newInstance(String param1, String param2) {
genreFragment fragment = new genreFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
sharedPreferences = getActivity().getSharedPreferences("preferences", Context.MODE_PRIVATE);
}
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_genre, container, false);
nocontnet_text = (TextView) view.findViewById(R.id.nocontent_genres);
//initRecyclerView(view);
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerview_genres);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
recyclerView.setLayoutManager(layoutManager);
adapter = new genres_adapter(genresList);
// Here I set the clicklisteners and onlongclicklisteners for the recyclerview
adapter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(isMultiSelectOn){
int position = recyclerView.getChildAdapterPosition(view);
GenreModel genreModel = genresList.get(position);
selected = genreModel.isSelected();
if(selected){
genreModel.setSelected(false);
view.setBackgroundColor(Color.WHITE);
ImageView menu = view.findViewById(R.id.ac_more_playlists);
menu.setVisibility(View.VISIBLE);
mOnClickWhileMultiSelectOn.onClick(genreModel, position, true);
}
else{
genreModel.setSelected(true);
view.setBackgroundColor(Color.BLUE);
ImageView menu = view.findViewById(R.id.ac_more_playlists);
menu.setVisibility(View.GONE);
mOnClickWhileMultiSelectOn.onClick(genreModel, position, false);
}
}
else {
int position = recyclerView.getChildAdapterPosition(view);
GenreModel genreModel = genresList.get(position);
String genreName = genreModel.getName();
Intent intent = new Intent(getActivity(), genreActivity.class);
intent.putExtra("genre", genreName);
startActivity(intent);
}
}
});
adapter.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if(isMultiSelectOn){
int position = recyclerView.getChildAdapterPosition(view);
GenreModel genreModel = genresList.get(position);
selected = genreModel.isSelected();
if(selected){
genreModel.setSelected(false);
view.setBackgroundColor(Color.WHITE);
ImageView menu = view.findViewById(R.id.ac_more_playlists);
menu.setVisibility(View.VISIBLE);
mOnGenreLongClickListener.onLongClick(genreModel, position, true);
}
else{
genreModel.setSelected(true);
view.setBackgroundColor(Color.BLUE);
ImageView menu = view.findViewById(R.id.ac_more_playlists);
menu.setVisibility(View.GONE);
mOnGenreLongClickListener.onLongClick(genreModel, position, false);
}
}
else{
int position = recyclerView.getChildAdapterPosition(view);
GenreModel genreModel = genresList.get(position);
genreModel.setSelected(true);
view.setBackgroundColor(Color.BLUE);
ImageView menu = view.findViewById(R.id.ac_more_playlists);
menu.setVisibility(View.GONE);
isMultiSelectOn = true;
mOnGenreLongClickListener.onLongClick(genreModel, position, false);
}
/* An interface I use to commuincate between the main activity and the fragments ((MainActivity) getActivity()).setInterface(new clearSelectedRecyclerView() {
@Override
public void onTracksClear() {
//IGNORED
}
@Override
public void onAlbumsClear() {
//IGNORED
}
@Override
public void onArtistsClear() {
//IGNORED
}
@Override
public void onGenresClear() {
clearSelectedGenres(adapter, genresList);
isMultiSelectOn = false;
}
@Override
public void onPlaylistsClear() {
//IGNORED
}
});
*/
return true;
}
});
// The problem happens in this line
recyclerView.setAdapter(adapter);
/*IGNORE
int exPermissionCheck = sharedPreferences.getInt("exPermissionCheck", PackageManager.PERMISSION_DENIED);
if(exPermissionCheck == PackageManager.PERMISSION_GRANTED){
getGenresList();
if(genresList.size() > 0){
nocontnet_text.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
adapter.notifyDataSetChanged();
}
else{
nocontnet_text.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
}
}
else{
nocontnet_text.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
}
return view;
*/
}
public void getGenresList(){
ContentResolver resolver = getActivity().getContentResolver();
Uri musicUri = MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI;
Cursor cursor = resolver.query(musicUri, null, null, null, null);
if((cursor != null) && (cursor.moveToFirst())){
int genre_column = cursor.getColumnIndex(MediaStore.Audio.Genres.NAME);
int id_column = cursor.getColumnIndex(MediaStore.Audio.Genres._ID);
do{
String genreName = cursor.getString(genre_column);
long id = cursor.getLong(id_column);
GenreModel genreModel = new GenreModel();
genreModel.setName(genreName);
genreModel.setID(id);
// The app also doesn't freeze when I remove this line which ads a new element to the list attached to the adapter
genresList.add(genreModel);
}while(cursor.moveToNext());
//
}
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
mOnGenreLongClickListener = (onGenreLongClickListener) getActivity();
mOnClickWhileMultiSelectOn = (onClickWhileMultiSelectOn) getActivity();
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onDestroyView() {
super.onDestroyView();
genresList.clear();
}
interface onGenreLongClickListener{
void onLongClick(GenreModel genreModel, int position, boolean selected);
}
interface onClickWhileMultiSelectOn{
void onClick(GenreModel genreModel, int position, boolean selected);
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
private void clearSelectedGenres(genres_adapter adapter, List<GenreModel> genres){
GenreModel genreModel;
for(int i = 0; i < genres.size(); i++){
genreModel = genres.get(i);
if(genreModel.isSelected)
genreModel.setSelected(false);
}
adapter.notifyDataSetChanged();
}
}
这是recyclerview的适配器类
public class genres_adapter extends RecyclerView.Adapter<genres_adapter.MyViewHolder> {
private List<GenreModel> genresList;
String genreName;
private Context context;
boolean isSelected = false;
private View.OnClickListener mOnClickListener;
private View.OnLongClickListener mOnLongClickListener;
class MyViewHolder extends RecyclerView.ViewHolder{
TextView gn_tv;
ImageView menuButton;
public MyViewHolder(View itemView) {
super(itemView);
gn_tv = itemView.findViewById(R.id.playlist_name);
menuButton = itemView.findViewById(R.id.ac_more_playlists);
}
View getItemView(){
return itemView;
}
}
public genres_adapter(List<GenreModel> genresList){
this.genresList = genresList;
}
public void setOnClickListener(View.OnClickListener callback){
this.mOnClickListener = callback;
}
public void setOnLongClickListener(View.OnLongClickListener callback){
this.mOnLongClickListener = callback;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.rec_playlist_main, parent, false);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mOnClickListener.onClick(view);
}
});
itemView.setOnLongClickListener(mOnLongClickListener);
context = itemView.getContext();
MyViewHolder holder = new MyViewHolder(itemView);
return holder;
}
@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
View itemView;
GenreModel genreModel = genresList.get(position);
isSelected = genreModel.isSelected();
genreName = genreModel.getName();
holder.gn_tv.setText(genreName);
holder.menuButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
PopupMenu popupMenu = new PopupMenu(context, holder.menuButton);
popupMenu.inflate(R.menu.item_menu);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.play_audio:
break;
case R.id.play_next:
break;
case R.id.add_toqueue:
break;
case R.id.add_toplaylist:
break;
case R.id.delete_track:
break;
case R.id.edit_info:
break;
}
return false;
}
});
popupMenu.show();
}
});
if(isSelected){
holder.menuButton.setVisibility(View.GONE);
itemView = holder.getItemView();
itemView.setBackgroundColor(Color.BLUE);
}
else{
holder.menuButton.setVisibility(View.VISIBLE);
itemView = holder.getItemView();
itemView.setBackgroundColor(Color.WHITE);
}
}
@Override
public int getItemCount() {
return genresList.size();
}
}
recyclerview项目模型类
public class GenreModel {
String genreName;
long genreID;
boolean isSelected = false;
public String getName(){
return genreName;
}
public long getID(){
return genreID;
}
public boolean isSelected(){
return isSelected();
}
public void setName(String name){
this.genreName = name;
}
public void setID(long id){
this.genreID = id;
}
public void setSelected(boolean isSelected){
this.isSelected = isSelected;
}
}
这是我在logcat中得到的,显然存在内存泄漏,但我还没弄明白它可能在哪里。
02-12 19:36:00.612 16860-16860/com.sparidapps.musicplayer E/AndroidRuntime: Error reporting crash
java.lang.OutOfMemoryError: Failed to allocate a 48203392 byte allocation with 16749088 free bytes and 31MB until OOM
所以我的问题是导致此内存泄漏导致应用冻结的原因是什么?提前谢谢!