I'm writing a simple app which requires me to remember all checkbox states which are dynamically generated.
I'm trying to use SharedPreferences to save the states of these, I'm using an recyclerview, so I've defined my sharedpreferences and all in the mainactivity which extends Activity.
All good and well, now I'm trying to use this in my Adapter class by trying referencing MainActivity.data, without luck.
MainActivity relevant code:
public class MainActivity extends Activity {
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
public static String FILENAME = "AnimeConfig";
public SharedPreferences data = getSharedPreferences(FILENAME, 0);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new GetAnime().execute();
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
}
private class GetAnime extends AsyncTask<Void, Void, Void> {
private String animeUrl = "http://www.animeseason.com/anime-list";
public List<String> animes = new ArrayList<String>();
#Override
protected Void doInBackground(Void... params) {
try {
Document document = Jsoup.connect(animeUrl).get();
Elements elements = document.select("div.series_alpha span");
for (Element element : elements) {
animes.add(element.previousElementSibling().text());
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
mAdapter = new MyAdapter(animes);
mRecyclerView.setAdapter(mAdapter);
}
}
MyAdapter relevant code:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> mDataset;
public static class ViewHolder extends RecyclerView.ViewHolder {
public CheckBox mCheckbox;
public ViewHolder(CheckBox v) {
super(v);
mCheckbox = v;
}
}
public MyAdapter(List<String> animes) {
mDataset = animes;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
CheckBox v = (CheckBox) LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_item, viewGroup, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mCheckbox.setText(mDataset.get(position));
holder.mCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
String animeName = buttonView.getText().toString();
SharedPreferences.Editor editor = MainActivity.data.edit();
}
}
});
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
I don't understand why I can't use MainActivity.data.
you should declare SharedPreferences data as static so you can access it from other classes .
but i prefer to use helper class to do all this work for me
something like this :
public class GeneralHelper {
public static SharedPreferences getSharedPrefrence(Context contextversion) {
//do the code for getting SharedPreferences
}
}
Related
There is a holder
public class ClientTivarHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView texttovarname,texttovarprice;
public RoundedImageView imageTovar;
public CardView cardTovar;
public ItemClickListener itemClickListener;
private Dialog dialog;
private Context context;
public ClientTivarHolder(View itemView){
super(itemView);
texttovarname=itemView.findViewById(R.id.texttovrnme);
texttovarprice=itemView.findViewById(R.id.pricetexttovar);
imageTovar=itemView.findViewById(R.id.tovarImage);
cardTovar=itemView.findViewById(R.id.tovarcard);
}
public void setItemClickListner(ItemClickListener listner){this.itemClickListener=listner;}
#Override
public void onClick(View view){
itemClickListener.onClick(view,getAdapterPosition(),false);
}
}
There is an adapter
public class TovarsAdapter {
private String TovarName,TovarOpisanie,TovarPrice,TovarImage,ShopPhone,ShopUid;
public TovarsAdapter(){
}
public TovarsAdapter(String tovarName, String tovarOpisanie, String tovarPrice, String tovarImage, String shopPhone, String shopUid) {
this. TovarName = tovarName;
this. TovarOpisanie = tovarOpisanie;
this. TovarPrice = tovarPrice;
this. TovarImage = tovarImage;
this. ShopPhone = shopPhone;
this. ShopUid = shopUid;
}
public String getTovarName() {
return TovarName;
}
public void setTovarName(String tovarName) {
TovarName = tovarName;
}
public String getTovarOpisanie() {
return TovarOpisanie;
}
public void setTovarOpisanie(String tovarOpisanie) {
TovarOpisanie = tovarOpisanie;
}
public String getTovarPrice() {
return TovarPrice;
}
public void setTovarPrice(String tovarPrice) {
TovarPrice = tovarPrice;
}
public String getTovarImage() {
return TovarImage;
}
public void setTovarImage(String tovarImage) {
TovarImage = tovarImage;
}
public String getShopPhone() {
return ShopPhone;
}
public void setShopPhone(String shopPhone) {
ShopPhone = shopPhone;
}
public String getShopUid() {
return ShopUid;
}
public void setShopUid(String shopUid) {
ShopUid = shopUid;
}
}
And the interface class ItemClickListener
public interface ItemClickListener {
void onClick(View view, int position, boolean isLongClick);
}
Activate itself where I want to implement it
public class TovarActivity extends AppCompatActivity {
private DatabaseReference tovars;
private RecyclerView recyclerView;
private String uid,srav;
RecyclerView.LayoutManager layoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tovar);
layoutManager=new GridLayoutManager(this,2);
Log.d("Uid",getIntent().getExtras().get("ShopUid").toString());
recyclerView=(RecyclerView) findViewById(R.id.tovarrec);
tovars= FirebaseDatabase.getInstance().getReference().child("Tovars");
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
}
#Override
protected void onStart() {
super.onStart();
FirebaseRecyclerOptions<TovarsAdapter> options=new FirebaseRecyclerOptions.Builder<TovarsAdapter>()
.setQuery(tovars.orderByChild("ShopUid").equalTo(getIntent().getExtras().get("ShopUid").toString()),TovarsAdapter.class).build();
FirebaseRecyclerAdapter<TovarsAdapter, ClientTivarHolder> adapter=new FirebaseRecyclerAdapter<TovarsAdapter, ClientTivarHolder>(options) {
#Override
protected void onBindViewHolder( #androidx.annotation.NonNull ClientTivarHolder holder, int position, #androidx.annotation.NonNull TovarsAdapter model) {
holder.texttovarprice.setText(model.getTovarPrice());
holder.texttovarname.setText(model.getTovarOpisanie());
holder.texttovarname.setHint(model.getShopUid());
Transformation transformation=new RoundedTransformationBuilder().borderColor(Color.WHITE).borderWidthDp(3).cornerRadius(12).oval(false).build();
Picasso.get().load(model.getTovarImage()).transform(transformation).into(holder.imageTovar);
}
#Override
public ClientTivarHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tovars,parent,false);
ClientTivarHolder holder=new ClientTivarHolder(view);
return holder;
}
};
recyclerView.setAdapter(adapter);
adapter.startListening();
}
}
Problem: I don’t know how to make a listener on a separate item send a hint from it with a uid to receive certain information in the bottomsheetdialogfragmemt. I tried to make a fragment manager in the holder but it didn't work out. How can I solve my problem?
tried to make fragment manager inside holder but didn't work
I am trying to set the data for recycler view to display from an AsyncTask. I am calling the method setdataEntries from the postExecute of inner class AsyncTask. But the android studio is showing me error could not find the method.
Adapter class
public class EntryAdapter extends RecyclerView.Adapter<EntryAdapter.ViewHolder> {
List<UserTuple> entries;
final private itemClickListener mOnClickListener;
public interface itemClickListener{
void onItemClick(UserTuple utuple);
}
public EntryAdapter(itemClickListener clickhandler) {
mOnClickListener = clickhandler;
}
public void setdataEntries(List<UserTuple> Data) {
entries = Data;
notifyDataSetChanged();
}
#Override
public EntryAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.singleusertuple,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(EntryAdapter.ViewHolder holder, int position) {
holder.Username.setText(entries.get(position).getUsername());
holder.Password.setText(entries.get(position).getPassword());
}
#Override
public int getItemCount() {
return entries.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView Username;
private TextView Password;
private CardView card;
public ViewHolder(View itemView) {
super(itemView);
Username = itemView.findViewById(R.id.susername);
Password=itemView.findViewById(R.id.pass);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int adapterPosition = getAdapterPosition();
UserTuple ut=new UserTuple(entries.get(adapterPosition).getUsername(),entries.get(adapterPosition).getPassword());
mOnClickListener.onItemClick(ut);
}
}
}
Calling Activity
public class Usertuple extends AppCompatActivity implements EntryAdapter.itemClickListener {
private RecyclerView recyclerView ;
private RecyclerView.Adapter adapater;
private SnapHelper snapHelper;
private List<UserTuple> entries;
private ProgressBar mLoadingIndicator;
private Bundle extras;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logins);
extras = getIntent().getExtras();
//String site= extras.getString("sitename");
mLoadingIndicator = (ProgressBar) findViewById(R.id.pb_loading_indicator);
Log.i("Logins","Size of returned list "+entries.size());
recyclerView = findViewById(R.id.recycleview);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
adapater = new EntryAdapter(this);
recyclerView.setAdapter(adapater);
snapHelper= new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
dataView();
}
public void dataView() {
String site= extras.getString("sitename");
recyclerView.setVisibility(View.VISIBLE);
new FetchDataTask().execute(site);
}
#Override
public void onItemClick(UserTuple utuple) {
}
private String key(){
SharedPreferences sharedPref = getSharedPreferences(
"User", this.MODE_PRIVATE);
final String passphrase = sharedPref.getString("userid", "none");
return passphrase;
}
public void showerror(){
recyclerView.setVisibility(View.GONE);
Toast.makeText(this,"Error in retrieving",Toast.LENGTH_SHORT).show();
}
public setdata(List<UserTuple> data){
adapater.setdataEntries(data);
}
public class FetchDataTask extends AsyncTask<String, Void, List<UserTuple>> {
#Override
protected void onPreExecute() {
super.onPreExecute();
mLoadingIndicator.setVisibility(View.VISIBLE);
}
#Override
protected List<UserTuple> doInBackground(String... params) {
/* If there's no zip code, there's nothing to look up. */
if (params.length == 0) {
return null;
}
String site = params[0];
try {
AppDatabase db = Room.databaseBuilder(getApplicationContext(),AppDatabase.class, "production")
.build();
entries =db.entryDao().getSpecific(site);
for(UserTuple ut : entries){
Log.i("password",ut.getPassword());
String st = new Decryption().decrypt(ut.getPassword(),key());
Log.i("After decryption",st);
ut.setPassword(st);
}
return entries;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(List<UserTuple> Data) {
mLoadingIndicator.setVisibility(View.INVISIBLE);
if (Data != null) {
adapater.setdataEntries(Data);
} else {
showerror();
}
}
}
}
I want the database calls to be a background task. I don't want the activity to freeze waiting for database calls. Any ideas? Thanks
Declare adapter like
private EntryAdapter adapter;
instead of
private RecyclerView.Adapter adapater;
because RecyclerView.Adapter class does not have any method named setdataEntries but only EntryAdapter class has this method so only the object of type EntryAdapter can call setdataEntries method.
Or you can use down-casting as
((EntryAdapter)adapater).setdataEntries(data);
I am going through a tutorial which is using the Recycler View to display a list of weather for each day for a week.
There are two classes which I am confused in:
ForecastAdapter and MainActivity
Here is the code for the above two classes:
ForecastAdapter.java
public class ForecastAdapter extends RecyclerView.Adapter<ForecastAdapter.ForecastAdapterViewHolder> {
private String[] mWeatherData;
final private ForecastAdapterOnClickListener mClickHandler;
//Why do we need to create an interface here.
public interface ForecastAdapterOnClickListener {
void onClick(String weatherForDay);
}
public ForecastAdapter(ForecastAdapterOnClickListener forecastAdapterOnClickListener) {
mClickHandler = forecastAdapterOnClickListener;
}
public class ForecastAdapterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public final TextView mWeatherTextView;
public ForecastAdapterViewHolder(View view) {
super(view);
mWeatherTextView = (TextView) view.findViewById(R.id.tv_weather_data);
view.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int adapterPosition = getAdapterPosition();
String weatherForDay = mWeatherData[adapterPosition];
//Why are we calling onClick from mClickHandler here. Why can't we just display Toast here.
mClickHandler.onClick(weatherForDay);
/*Why can't we just display the Toast from here like this:
Toast.makeText(v.getContext(), weatherForDay, Toast.LENGTH_SHORT).show()
*/
}
}
#Override
public ForecastAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
int layoutIdForListItem = R.layout.forecast_list_item;
LayoutInflater inflater = LayoutInflater.from(context);
boolean shouldAttachToParentImmediately = false;
View view = inflater.inflate(layoutIdForListItem, viewGroup, shouldAttachToParentImmediately);
return new ForecastAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(ForecastAdapterViewHolder forecastAdapterViewHolder, int position) {
String weatherForThisDay = mWeatherData[position];
forecastAdapterViewHolder.mWeatherTextView.setText(weatherForThisDay);
}
#Override
public int getItemCount() {
if (null == mWeatherData) return 0;
return mWeatherData.length;
}
public void setWeatherData(String[] weatherData) {
mWeatherData = weatherData;
notifyDataSetChanged();
}
}
MainActivity.java
//Why are implementing ForecastAdapterOnClickListener here?
public class MainActivity extends AppCompatActivity implements ForecastAdapter.ForecastAdapterOnClickListener{
private RecyclerView mRecyclerView;
private ForecastAdapter mForecastAdapter;
private TextView mErrorMessageDisplay;
private ProgressBar mLoadingIndicator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forecast);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview_forecast);
mErrorMessageDisplay = (TextView) findViewById(R.id.tv_error_message_display);
LinearLayoutManager layoutManager
= new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mForecastAdapter = new ForecastAdapter(this);
mRecyclerView.setAdapter(mForecastAdapter);
mLoadingIndicator = (ProgressBar) findViewById(R.id.pb_loading_indicator);
loadWeatherData();
}
private void loadWeatherData() {
showWeatherDataView();
String location = SunshinePreferences.getPreferredWeatherLocation(this);
new FetchWeatherTask().execute(location);
}
#Override
public void onClick(String weatherForDay) {
Context context = this;
Toast.makeText(context, weatherForDay, Toast.LENGTH_SHORT)
.show();
}
private void showWeatherDataView() {
mErrorMessageDisplay.setVisibility(View.INVISIBLE);
mRecyclerView.setVisibility(View.VISIBLE);
}
private void showErrorMessage() {
mRecyclerView.setVisibility(View.INVISIBLE);
mErrorMessageDisplay.setVisibility(View.VISIBLE);
}
public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
#Override
protected void onPreExecute() {
super.onPreExecute();
mLoadingIndicator.setVisibility(View.VISIBLE);
}
#Override
protected String[] doInBackground(String... params) {
if (params.length == 0) {
return null;
}
String location = params[0];
URL weatherRequestUrl = NetworkUtils.buildUrl(location);
try {
String jsonWeatherResponse = NetworkUtils
.getResponseFromHttpUrl(weatherRequestUrl);
String[] simpleJsonWeatherData = OpenWeatherJsonUtils
.getSimpleWeatherStringsFromJson(MainActivity.this, jsonWeatherResponse);
return simpleJsonWeatherData;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(String[] weatherData) {
mLoadingIndicator.setVisibility(View.INVISIBLE);
if (weatherData != null) {
showWeatherDataView();
mForecastAdapter.setWeatherData(weatherData);
} else {
showErrorMessage();
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.forecast, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_refresh) {
mForecastAdapter.setWeatherData(null);
loadWeatherData();
return true;
}
return super.onOptionsItemSelected(item);
}
}
The adapter, view holder and recycler view is working as expected. We are now supposed to implement Click Handling on the rows of the recycler view. Whenever a particular row is clicked, we are supposed to display a toast.
As you can see, we are implementing OnClickListener in the ForecastAdapterViewHolder and in the onClick function we are calling the onClick of the interface "ForecastAdapterOnClickListener".
In the MainActivity.java, we are implementing this "ForecastAdapterOnClickListener" and then displaying the toast.
Why can't we just display the toast in the onClick that is defined for the "ForecastAdapterViewHolder" class. I have tried it and it works. What is the point of doing what is being done in the code?
Is there some advantage in setting the click listener like that?
Because you'll have to display information afterwards and isn't role of ViewHolder neither Adapter. Activity/fragment must do that.
It's to keep your code organized.
i am trying to update recyclerview with images from a API using ASYNC task but its not working properly . The problem is with the notifyDataSetChanged() method in the onPostExecute() of the ASYNC task. i have checked the json response and the result of the onPostExecute() the value are correct but the notifydatasetchanged() is not updating the adapter with the new data.
MainActivity :
public class MainActivity extends AppCompatActivity {
public static final String LOG_TAG = MainActivity.class.getName();
public MovieUtils utils = new MovieUtils();
private RecyclerView mRecyclerView;
private MovieAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
public ArrayList<String> movieData=new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mRecyclerView.setHasFixedSize(true);
//Using a grid layout manager to display the movies in a grid view.
mLayoutManager = new GridLayoutManager(getApplicationContext(), 2);
mRecyclerView.setLayoutManager(mLayoutManager);
//using the jsonParser to parse the String response and create a new data set.
// passing the data to the Adapter.
mAdapter = new MovieAdapter(movieData, getApplicationContext());
mRecyclerView.setAdapter(mAdapter);
DownloadMovieDetail downloadMovieDetail= new DownloadMovieDetail();
downloadMovieDetail.execute();
}
private class DownloadMovieDetail extends AsyncTask<String, Void, ArrayList<String>> {
#Override
protected ArrayList<String> doInBackground(String... params) {
ArrayList<String> movieDb = new ArrayList<String>();
try {
movieDb = utils.downloadMovie(responseString);
} catch (IOException e) {
e.printStackTrace();
}
return movieDb;
}
#Override
protected void onPostExecute(ArrayList<String> strings) {
movieData.addAll(strings);
Log.v(LOG_TAG,"the movie data"+movieData.toString());
mAdapter.notifyDataSetChanged();
}
}
}
Movie Adapter:
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.ViewHolder> {
public static final String LOG_TAG = MovieAdapter.class.getName();
public ArrayList<String> dummyData;
public Context context;
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView MovieImageView;
public ViewHolder(View itemView) {
super(itemView);
MovieImageView=(ImageView) itemView.findViewById(R.id.movie_image);
}
}
//Constructor for the adapter.
public MovieAdapter(ArrayList<String> data, Context c){
this.context=c;
dummyData=data;
}
#Override
public MovieAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.movie_view, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(MovieAdapter.ViewHolder holder, int position) {
Picasso.with(context).load(dummyData.toString()).into(holder.MovieImageView);
}
#Override
public int getItemCount()
{
return dummyData.size();
}
}
Define a setter for dummyData ArrayList in your adapter like this
public void setDummyData(ArrayList<String> data){
this.dummyData = data;
notifyDataSetChanged();
}
and then onPostExecute() replace
mAdapter.notifyDataSetChanged();
with
mAdapter.setDummyData(movieData);
You did't change the data on the adapter class. Create a setter for the ArrayList and then call notifyDataSetChanged()
public void setList(ArrayList<String> dummyData){
this.dummyData = dummyData;
notifyDataSetChanged();
}
And on onPostExecute just call
adapter.setList(string);
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.