I am trying to implement RecyclerView inside a Fragment. I have designed a CustomAdapter(StateAdapter) class to load the contents of RecyclerView and an interface StateAdapterOnClickHandler to handle onClicks of the items inside RecyclerView but clicking on item doesn't work.
Here's my HomeFragment.java which extends Fragment. I am unable to figure out how to initialize the StateAdapterOnClickHandler and where to call its method onItemClick()
public class HomeFragment extends Fragment implements StateAdapter.StateAdapterOnClickHandler {
private View homeFragment;
private RecyclerView recyclerView;
private StateAdapter stateAdapter;
private List<String> states;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public static HomeFragment newInstance() {
HomeFragment homeFragment = new HomeFragment();
return homeFragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
homeFragment = inflater.inflate(R.layout.fragment_home,container,false);
recyclerView = homeFragment.findViewById(R.id.recyclerview_states);
states = Arrays.asList(getResources().getStringArray(R.array.india_states));
LinearLayoutManager layoutManager = new LinearLayoutManager(container.getContext(),LinearLayoutManager.VERTICAL,false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
stateAdapter = new StateAdapter();
stateAdapter.setStateNames(states);
recyclerView.setAdapter(stateAdapter);
return homeFragment;
}
#Override
public void onItemClick(String state) {
Intent intent = new Intent(getActivity(),DetailActivity.class);
intent.putExtra("State",state);
startActivity(intent);
}
}
Here's my StateAdapter class
public class StateAdapter extends RecyclerView.Adapter<StateAdapter.StateViewHolder> {
private List<String> stateNames;
private final StateAdapterOnClickHandler stateAdapterOnClickHandler;
StateAdapter(StateAdapterOnClickHandler stateAdapterOnClickHandler){
this.stateAdapterOnClickHandler = stateAdapterOnClickHandler;
}
public interface StateAdapterOnClickHandler {
void onItemClick(String state);
}
#Override
public StateAdapter.StateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
int idForListItem = R.layout.state_layout;
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(idForListItem,parent,false);
return new StateViewHolder(view);
}
#Override
public void onBindViewHolder(StateAdapter.StateViewHolder holder, int position) {
String stateName = stateNames.get(position);
holder.mTextView.setText(stateName);
}
#Override
public int getItemCount() {
if(stateNames == null) return 0;
return stateNames.size();
}
public class StateViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public final TextView mTextView;
public StateViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.tv_state_name);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int clickedPostion = getAdapterPosition();
String state = stateNames.get(clickedPostion);
stateAdapterOnClickHandler.onItemClick(state);
}
}
public void setStateNames(List<String> states) {
stateNames = states;
notifyDataSetChanged();
}
}
Please, tell me what's wrong with the code while handling the clicks. I am new to Android Programming and this is first time I am working with Fragments. Thanks for helping.
Related
sorry for my bad english
My problem: when I rotate my application, all data is lost, I am using a ViewModel, my data loads when I push the button, this is my code for test .
https://github.com/elviss116/androidMvvm-master
This is the Fragment :
public class MovieListFragment extends Fragment {
private MovieListViewModel mViewModel;
private List<MovieModel.DataModel> movieList = new ArrayList<>();
private RecyclerView recyclerView;
private MoviesAdapter mAdapter;
private Button cargar;
public static MovieListFragment newInstance() {
return new MovieListFragment();
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View view= inflater.inflate(R.layout.movie_list_fragment, container, false);
recyclerView = (RecyclerView)view.findViewById(R.id.recycler_view);
cargar = view.findViewById(R.id.btnCargar);
mAdapter = new MoviesAdapter(movieList);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(mAdapter);
return view;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
cargar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mViewModel = ViewModelProviders.of(MovieListFragment.this).get(MovieListViewModel.class);
mViewModel.init();
mViewModel.getMovies().observe(MovieListFragment.this, new Observer<MovieModel>() {
#Override
public void onChanged(#Nullable MovieModel movieModels) {
movieList.addAll(movieModels.getData());
mAdapter.notifyDataSetChanged();
}
});
}
});
}
}
this is the ViewModel :
public class MovieModel {
#SerializedName("data")
List<DataModel> data;
public List<DataModel> getData() {
return data;
}
public void setData(List<DataModel> data) {
this.data = data;
}
public class DataModel {
#SerializedName("name")
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
this is the adapter :
public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MyViewHolder> {
private List<MovieModel.DataModel> moviesList;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public MyViewHolder(View view) {
super(view);
name = (TextView) view.findViewById(R.id.name);
}
}
public MoviesAdapter(List<MovieModel.DataModel> moviesList) {
this.moviesList = moviesList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.movie_list_row, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
MovieModel.DataModel movie = moviesList.get(position);
holder.name.setText(movie.getName());
}
#Override
public int getItemCount() {
return moviesList.size();
}
}
When you rotate your phone the activity gets destroyed and re-created, so that is why you're seeing your data dissapear.
What I would recommend is grabbing and observing the view model when the activity is created, outside of the click listener. This way the fragment will immediately start observing the data when it is rotated, but will only be initialized when the button is clicked.
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(MovieListFragment.this).get(MovieListViewModel.class);
mViewModel.getMovies().observe(MovieListFragment.this, new Observer<MovieModel>() {
#Override
public void onChanged(#Nullable MovieModel movieModels) {
movieList.addAll(movieModels.getData());
mAdapter.notifyDataSetChanged();
}
});
cargar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mViewModel.init();
}
});
}
Need help with android app (java)
My app starts with SplashActivity. During it I`m getting json via retrofit request and after successfull response I fill my DB
SplashActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
ISplashInteractor splashInteractor = new SplashInteractor();
moviePresenter = new MoviePresenter(this, splashInteractor);
moviePresenter.initDB();
}
after DB successfully loaded automatically starts MovieListActivity
public class MovieListActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movie_list);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
Fragment allMoviesFragment = new AllMoviesFragment();
String tag = "MovieList";
transaction.replace(R.id.main_container, allMoviesFragment, tag);
transaction.commit();
}
}
and at once list of movies loads inside inner fragment AllMoviesFragment(inside recyclerView). till this point everything is ok.
now I set onRecyclerViewItemClickListener - it must open another fragment inside MovieListActivity but instead of this - my app restarts from begining and I observe loading of SpalshActivity again(I put progressbar during request) and again my RecyclerView...
my RecyclerViewAdapter
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<MovieDetails> movieList;
private OnRecyclerViewItemClickListener onRecyclerViewItemClickListener;
public RecyclerViewAdapter(List<MovieDetails> movieList) {
this.movieList = movieList;
}
public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener onRecyclerViewItemClickListener){
this.onRecyclerViewItemClickListener = onRecyclerViewItemClickListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.simple_selectable_list_item, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
MovieDetails movieDetails = (movieList.get(position));
holder.name.setText(movieDetails.getTitle());
holder.year.setText(String.valueOf(movieDetails.getReleaseYear()));
}
#Override
public int getItemCount() {
if (movieList == null)
return 0;
return movieList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView name;
TextView year;
public ViewHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.movie_name);
year = itemView.findViewById(R.id.release_year);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onRecyclerViewItemClickListener != null){
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION){
onRecyclerViewItemClickListener.onRecyclerViewItemClick(position);
}
}
}
});
}
}
public interface OnRecyclerViewItemClickListener {
void onRecyclerViewItemClick(int position);
}
}
my AllMoviesFragment
public class AllMoviesFragment extends Fragment implements RecyclerViewAdapter.OnRecyclerViewItemClickListener {
private static final String CURR_MOVIE = "Current Movie";
private static final String TAG = "MSApp";
List<MovieDetails> movieList;
RecyclerView recyclerView;
public AllMoviesFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
movieList = App.getAppInstance().getDatabaseInstance().getMovieDao().getAllMovies();
movieList.sort(new Comparator<MovieDetails>() {
#Override
public int compare(MovieDetails o1, MovieDetails o2) {
return o2.getReleaseYear() - o1.getReleaseYear();
}
});
View resultView = inflater.inflate(R.layout.fragment_all_movies, container, false);
recyclerView = resultView.findViewById(R.id.recycler_view);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(movieList);
adapter.setOnRecyclerViewItemClickListener(AllMoviesFragment.this);
recyclerView.setAdapter(adapter);
return resultView;
}
#Override
public void onRecyclerViewItemClick(int position) {
MovieDetails choosenMovie = movieList.get(position);
FragmentManager fragmentManager = getFragmentManager();
Fragment movieDetailsFragment = new MovieDetailsFragment();
Bundle args = new Bundle();
args.putParcelable(CURR_MOVIE, choosenMovie);
movieDetailsFragment.setArguments(args);
String tag = "MovieDetails";
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.main_container, movieDetailsFragment, tag);
transaction.addToBackStack(tag);
transaction.commit();
}
}
sorry for long post. I dont know how to cut it
I use App class for db access if it is important
#AkakiKapanadze
you was almost right
I didnot see the error message because it disappeared less 1 second
and class MovieDetailsFragment
after I remove binder of ButterKnife all work... I dont know WHY - I always use Butterknife inside Fragments
I'm trying to make a tab layout with each page having a RecyclerView inside of it. I'm using fragments with RecyclerView in them, then i put them inside of ViewPager. The problem is that even though tabs are being shown it's content is always empty.
My Activity class
public class Spells_Act extends AppCompatActivity {
private ViewPager pager;
private Spells_Slide slider;
private TabLayout tabLayout;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spells);
tabLayout = findViewById(R.id.tab);
pager = findViewById(R.id.pager);
slider = new Spells_Slide(getSupportFragmentManager());
slider.AddFragment(new Fragment1(), "one");
slider.AddFragment(new Fragment1(), "two");
slider.AddFragment(new Fragment1(), "three");
pager.setAdapter(slider);
tabLayout.setupWithViewPager(pager);
}
}
My ViewPager adapter
public class Spells_Slide extends FragmentPagerAdapter {
private Context context;
LayoutInflater inflater;
private List<RecyclerView> pages;
private List<String> list = new LinkedList<>();
private final List<Fragment> stFragment = new ArrayList();
public Spells_Slide(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return stFragment.get(position);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return list.get(position);
}
#Override
public int getCount() {
return list.size();
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object
object) {
return false;
}
public void AddFragment(Fragment fragment, String title) {
list.add(title);
stFragment.add(fragment);
}
My Fragment class
public class Fragment1 extends Fragment {
View v;
private RecyclerView recycler;
Adapter_Rec adapter;
private List<Spells> Spells = new LinkedList<>();
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Spells.add(new Spells("Ghost Sound", "Figment sounds", 0));
Spells.add(new Spells("Disrupt Undead", " Deals 1d6 damage to one undead", 0));
Spells.add(new Spells("Resistance", "Subject gains +1 on saving throws", 0));
Spells.add(new Spells("Ray of Frost", "Ray deals 1d3 cold damage", 0));
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
v = inflater.inflate(R.layout.frag1, container, false);
recycler = v.findViewById(R.id.frag_recycler);
recycler.setLayoutManager(new LinearLayoutManager(getActivity()));
adapter = new Adapter_Rec(Spells, getContext());
recycler.setAdapter(adapter);
return v;
}
}
And finally my RecyclerViewAdapter
public class Adapter_Rec extends
RecyclerView.Adapter<Adapter_Rec.MyViewHolder> {
private List<Spells> list = new LinkedList<>();
Context context;
public Adapter_Rec(List<Spells> list, Context context)
{
this.context=context;
this.list=list;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
v = LayoutInflater.from(context).inflate
(R.layout.spells_recycle,parent,false);
MyViewHolder vholder = new MyViewHolder(v);
return vholder;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.check.setOnCheckedChangeListener(null);
holder.name.setText(list.get(position).getName());
holder.benefits.setText(list.get(position).getDescription());
}
#Override
public int getItemCount() {
return list.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder
{
TextView name;
TextView benefits;
CheckBox check ;
public MyViewHolder(View item)
{ super(item);
name = item.findViewById(R.id.namee);
benefits = item.findViewById(R.id.desc);
check =item.findViewById(R.id.check);
}
}
}
delete isViewFromObject method, or return super.isViewFromObject(view, object);
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object
object) {
//return false; // return false will never show this fragment
return super.isViewFromObject(view, object);
}
My adaptor class:
public class Adaptor extends RecyclerView.Adapter<Adaptor.Holder>{
private ArrayList<Winkel> winkels;
private LayoutInflater inflater;
private ImageView icon;
private ItemCLickCallback itemCLickCallback;
public interface ItemCLickCallback {
void onItemClick(int p);
void onSecItemClick(int p);
}
public void setItemCLickCallback(final ItemCLickCallback itemCLickCallback1){
;this.itemCLickCallback = itemCLickCallback;
}
public Adaptor (ArrayList<Winkel> winkels,Context c){
this.inflater = LayoutInflater.from(c);
this.winkels = winkels;
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.list_item,parent,false);
return new Holder(view);
}
#Override
public void onBindViewHolder(Holder holder, int position) {
Winkel winkel = winkels.get(position);
holder.title.setText(winkel.getNaam());
if (winkel.isFavourtite()){
holder.icon.setImageResource(R.drawable.ic_star_black_18dp);
}
else{
holder.icon.setImageResource(R.drawable.ic_star_border_black_18dp);
}
}
#Override
public int getItemCount() {
return winkels.size();
}
class Holder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TextView title;
private View container;
private ImageView icon;
public Holder(View itemView) {
super(itemView);
title = (TextView)itemView.findViewById(R.id.lbl_item_text);
container = itemView.findViewById(R.id.cont_item_root);
icon = (ImageView) itemView.findViewById(R.id.im_item_icon_secondary);
icon.setOnClickListener(this);
container.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(v.getId()==R.id.cont_item_root){
itemCLickCallback.onItemClick(getAdapterPosition());
}
else{
itemCLickCallback.onSecItemClick(getAdapterPosition());
}
}
}
}
#Override
public void onClick(View v) {
}
}
}
Code where it gets implemented (my issue is here at adaptor.setItemClickCallback(context)
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Adaptor adaptor = new Adaptor(winkels,context);
recyclerview.setLayoutManager(new LinearLayoutManager(context));
recyclerview.addItemDecoration(new VerticalSpace(30));
recyclerview.setAdapter(adaptor);
adaptor.setItemCLickCallback(context);
}
Called from fragment overview:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_overview, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycle);
ODKortrijkWebservice webs = new ODKortrijkWebservice(this.getActivity(),mRecyclerView);
webs.execute();
return view;
}
How do I set the context for ItemCLickCallback(this)? It should be context (at least I think so?) because that's what gets passed from the fragment but I can't seem to figure out how to get this to work, so some help would be appreciated. Basically I want to add onclicklisteners for every item in my recyclerview, and the recyclerview is created by using data from a webservice, hence why the adaptor class is called from the class where I get the data from the web service, which gets excecuted in my fragment. Thank you.
I recommend you to make ODKortrijkWebservice.Callback like following.
In your ODKortrijkWebservice:
public class ODKortrijkWebservice extends AsyncTask<...> {
private Callback mCallback;
public interface Callback {
void onSuccess(ArrayList<Winkel> winkels);
}
public ODKortrijkWebservice(Context context, Callback callback) {
...
mCallback = callback;
...
}
#Override
protected void onPostExecute(Void aVoid) {
...
mCallback.onSuccess(winkels);
}
}
In your Fragment:
public class YourFragment extends Fragment implements ODKortrijkWebservice.Callback, Adaptor.ItemCLickCallback {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_overview, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycle);
ODKortrijkWebservice webs = new ODKortrijkWebservice(this.getActivity(), this);
webs.execute();
return view;
}
#Override
public void onSuccess(ArrayList<Winkel> winkels) {
Adaptor adaptor = new Adaptor(winkels, getContext());
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerView.addItemDecoration(new VerticalSpace(30));
mRecyclerView.setAdapter(adaptor);
adaptor.setItemCLickCallback(this);
}
#Override
public void onItemClick(int p) {
}
#Override
public void onSecItemClick(int p) {
}
}
Here,
You need to create one interface at RecycleView.Adapter<>
private static OnItemClickListener listener;
// Define the listener interface
public interface OnItemClickListener {
void onItemClick(View itemView, int position);
}
// Define the method that allows the parent activity or fragment to define the listener
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
send require details with interface method
public class ViewHolder extends RecyclerView.ViewHolder{
TextView header_title;
public ViewHolder(final View itemView) {
super(itemView);
txt_country_name = (TextView)itemView.findViewById(R.id.header_title);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Triggers click upwards to the adapter on click
if (listener != null)
listener.onItemClick(itemView, getLayoutPosition());
}
});
}
}
call adapter listener
ListAdapter listAdapter;
listAdapter.setOnItemClickListener(new listAdapter.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
Toast.makeText(getActivity(), position+ " was clicked!", Toast.LENGTH_SHORT).show();
}
});
In my RecyclerView.Adapter I have defined this interface:
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
The Fragment which contains the RecyclerView implements this interface.
After 4 or 5 orientation changes LeakCanary reports a memory leak:
My Fragment looks like this:
public class ImagesFragment extends Fragment implements ImageAdapter.OnItemClickListener {
private static final String IMAGES_FRAGMENT_TAG = "ImagesFragment";
private int SPAN_COUNT = 2;
private String categoryName;
private String categoryURL;
private int ImageCount;
protected RecyclerView mRecyclerView;
protected RecyclerView.LayoutManager mLayoutManager;
protected static ImageAdapter mAdapter;
#Override
public void onItemClick(View view, int position) {
Toast.makeText(mContext, "Clicked:" + String.valueOf(position), Toast.LENGTH_SHORT).show();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
this.categoryName = args.getString("category");
this.categoryURL = args.getString("URL");
this.ImageCount = args.getInt("Count");
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.images_recycler_view, container, false);
rootView.setTag(IMAGES_FRAGMENT_TAG);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.imagesRecyclerView);
mRecyclerView.addItemDecoration(new CategoryItemDecoration(px, SPAN_COUNT, mCurrentLayoutType));
mLayoutManager = new GridLayoutManager(getActivity().getApplicationContext(), SPAN_COUNT);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new ImageAdapter(categoryURL, this.ImageCount, this);
mRecyclerView.setAdapter(mAdapter);
return rootView;
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(IMAGES_FRAGMENT_TAG, IMAGES_FRAGMENT_TAG);
super.onSaveInstanceState(outState);
}
#Override
public void onDestroyView() {
super.onDestroyView();
System.out.println("OnDestroyView");
if (mRecyclerView != null) {
mRecyclerView.setAdapter(null);
}
}
#Override
public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = MyApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
}
My Adapter:
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImagesViewHolder> {
private static final String IMAGES_FRAGMENT_TAG = "ImagesFragment";
private Context mContext;
private static OnItemClickListener onItemClickListener;
private Integer imageCount;
public ImageAdapter(String url, Integer imageCount, OnItemClickListener itemListener) {
this.ROOTURL = url;
this.imageCount = imageCount;
this.onItemClickListener = itemListener;
}
#Override
public int getItemCount() {
return imageCount;
}
#Override
public ImagesViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
this.mContext = parent.getContext();
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.images_viewitem, parent, false);
ImagesViewHolder imagesViewHolder = new ImagesViewHolder(v);
return imagesViewHolder;
}
#Override
public void onBindViewHolder(final ImagesViewHolder holder, int position) {
Glide.with(mContext)
.load("URL")
.asBitmap()
.centerCrop()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(new BitmapImageViewTarget(holder.image) {
#Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
super.onLoadFailed(e, errorDrawable);
Log.e(IMAGES_FRAGMENT_TAG, "on load failed");
}
#Override
public void onResourceReady(Bitmap bitmap, GlideAnimation<? super Bitmap> glideAnimation) {
super.onResourceReady(bitmap, glideAnimation);
holder.image.setImageBitmap(bitmap);
}
});
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
public static class ImagesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private static final int PALETTE_SIZE = 24;
CardView cv;
ImageView image;
RelativeLayout mImageViewWrapper;
ImagesViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.imagesCardView);
image = (ImageView) itemView.findViewById(R.id.images_ImageView);
mImageViewWrapper = (RelativeLayout) itemView.findViewById(R.id.Images_imageViewWrapper);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
onItemClickListener.onItemClick(v, this.getLayoutPosition());
}
}
}
Your problem is this line in your Fragment:
protected static ImageAdapter mAdapter;
And this line in your Adapter:
private static OnItemClickListener onItemClickListener;
NEVER use static for variables. Just don't do it unless you really know what you are doing. The static keyword causes your ImageAdapter and OnClickListener to stick around after the Fragment is garbage collected. It means that the variable mAdapter is not part of any instance of your Fragment but instead part of the class itself - which of course is an instant memory leak! Remove those and you should be fine.
And by the way you could have figured this out by yourself fairly quickly. Look again at the LeakCanary output:
It says that the static variable onItemClickListener in the ImageAdapter leaks an ImagesFragment instance - or in other words exactly what I am telling you in this answer.
Also you should read this answer to learn more about memory leaks.