I am getting the following error in hundreds of my users. I used it in my code because it asked me to call notifyDataSetChanged() but it didn't work. I have shared my adapter file below. I've done a lot of research for this but can't pinpoint the problem. I need your help.
Regards.
Fatal Exception: java.lang.IllegalStateException
The content of the adapter has changed but ListView did not receive a notification.
Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131296786, class android.widget.ListView)
My Adapter Code:
public class TracksAdapter extends BaseAdapter {
private DatabaseReference mDatabaseReference;
Context context;
private static TracksAdapter mInstance = null;
private Activity activity;
private InterstitialAd interstitialAds;
private int pos = -1;
private LoadingDialog progress = null;
List<Track> trackItems;
public void showProgressDialog() {
progress = new LoadingDialog(context);
progress.setCircleColors(context.getResources().getColor(R.color.turu), context.getResources().getColor(R.color.turu), context.getResources().getColor(R.color.turu));
progress.show();
}
public void hideProgressDialog() {
if (progress != null) {
progress.dismiss();
progress = null;
}
}
protected void TracksAdapter(Activity activity) {
this.activity = activity;
}
public TracksAdapter(Context context, List<Track> rowItems) {
notifyDataSetChanged();
this.context = context;
this.trackItems = rowItems;
}
#Override
public int getCount() {
return trackItems.size();
}
#Override
public Object getItem(int position) {
pos = position;
if (trackItems != null && trackItems.size() > position) {
return trackItems.get(position); // Line 54.
}
return null;
}
#Override
public long getItemId(int position) {
return trackItems.indexOf(getItem(position));
}
/* private view holder class */
private class ViewHolder {
String id, names, phones, lastseens, dates;
TextView name;
TextView phone;
TextView lastseen;
Integer membership;
ImageView delete, file;
}
public static TracksAdapter getInstance() {
return mInstance;
}
#RequiresApi(api = Build.VERSION_CODES.O)
#SuppressLint("WrongViewCast")
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
}
}
}
Here is what I think the problem is, let us first see your code. This portion right here
public TracksAdapter(Context context, List<Track> rowItems) {
notifyDataSetChanged();
this.context = context;
this.trackItems = rowItems;
}
As you said you are calling notifyDataSetChanged(), but the thing is where is it being called.
You are calling it right now before any changes to data set has been made, you should call it after you have assigned the rowItems. You call it before and at that point nothing has changed.
So the correct code, will be as follows:
public TracksAdapter(Context context, List<Track> rowItems) {
this.context = context;
this.trackItems = rowItems;
notifyDataSetChanged();
}
Related
I have a boolean in my onBindViewHolder which decides whether a button is clickable or not.
In the constructor I set this value to false, later when the user unlocks something I want to set it to true with a setter.
Here is my RecyclerViewAdapter.java:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private ArrayList<Items> items;
Context mContext;
private boolean unlocked;
public RecyclerViewAdapter(Context context, ArrayList<Items> items, boolean unlocked) {
mContext = context;
this.items = items;
this.unlocked = tabUnlocked;
}
public void setUnlocked(boolean unlocked) {
this.unlocked = unlocked;
this.notifyDataSetChanged();
}
#NonNull
#Override
public RecyclerViewAdapter.iewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.item, parent, false);
RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
v.setLayoutParams(lp);
return new RecyclerViewAdapter.ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull final RecyclerViewAdapter.ViewHolder holder, int position) {
final Items currentItem = items.get(position);
final String name = currentItem.getName();
holder.itemTextView.setText(name);
holder.itemTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (unlocked) {
//do something
}
}
});
}
#Override
public int getItemCount() {
return item.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView itemTextView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
itemTextView = itemView.findViewById(R.id.textViewItem);
}
}
}
And here is how I call the Setter to change it:
I make the RecyclerView public:
public RecyclerViewAdapter adapter;
Then I create an instance in my onCreate:
recyclerView = rootView.findViewById(R.id.recyclerView);
adapter = new RecyclerViewAdapter(items);
recyclerView.setAdapter(adapter);
And when the user unlocks a certain thing I call this method of the current Class:
public void unlockTab() {
adapter.setUnlocked(true);
}
When I log the boolean inside of that setter it tells me it got changed.
But when I log the boolean inside the onBindViewHolder it still remains false.
Why is the setter method not setting the boolean to true in the whole RecyclerViewAdapter.java class?
Hope someone can help me!
EDIT
I already tried adding "this.notifyDataSetChanged();" to the Setter Method (Thanks to #a_local_nobody)
Adapters are responsible for changes to recyclerviews. but if you don't tell them something changed, they won't know to do so.
call notifyDataSetChanged();
public void unlockTab() {
adapter.setUnlocked(true);
adapter.notifyDataSetChanged();
}
what does it do ?
It tells your recyclerview to bind all the data again, so then your changes will apply
I'm loading ads in onClick of RecyclerView's adapter.
In fragment it is perfectly working in this code:
interstitialAd.loadAd(GDPR.getAdRequest(getActivity()));
But in adapter it is not working in these method I tried:
mInterstitialAd.loadAd(GDPR.getAdRequest(context));
I also tried this but it is not working:
mInterstitialAd.loadAd(GDPR.getAdRequest(this));
GDPR.getAdRequest's code is:
public static AdRequest getAdRequest(Activity activity) {
return new AdRequest.Builder()
.addNetworkExtrasBundle(AdMobAdapter.class, GDPR.getBundleAd(activity))
.build();
}
this is adapter's code:
public class VideosAdapter extends RecyclerView.Adapter<VideosAdapter.VideosViewHolder> {
private Context context;
private Video[] data;
private int layout;
private InterstitialAd mInterstitialAd;
public VideosAdapter(Context context, Video[] data, int layout) {
this.context = context;
this.data = data;
this.layout = layout;
}
#NonNull
#Override
public VideosViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(layout,null);
return new VideosViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull VideosViewHolder holder, final int position) {
final Video selectedItem = data[position];
holder.videoTitle.setText(selectedItem.getVideoTitle());
holder.videoCategoryTitle.setText(selectedItem.getCategoryName());
if (selectedItem.getVideoType().equals("0")){
String thumbImg = Constants.SERVER_IMG_FIRST + selectedItem.getVideoThumb();
Glide.with(context).load(Uri.parse(thumbImg)).into(holder.videoThumb);
}else {
// Get V and display img
Uri uri = Uri.parse(selectedItem.getVideoUrl());
String videoId = uri.getQueryParameter("v");
String thumbYtImg = Constants.YOUTUBE_IMG_FIRST + videoId + Constants.YOUTUBE_IMG_BACK;
Glide.with(context).load(Uri.parse(thumbYtImg)).into(holder.videoThumb);
}
String thumbCat = Constants.SERVER_IMG_FIRST + selectedItem.getCategoryThumb();
Glide.with(context).load(Uri.parse(thumbCat)).into(holder.videoCategoryThumb);
holder.videoInfoBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (Constants.SETTINGS.getAdmob().equals("1")){
Constants.ADS_COUNT++;
if (Constants.ADS_COUNT>=Integer.valueOf(Constants.SETTINGS.getAdmobIntrestitialAdDisplayAfter())){
Constants.ADS_COUNT = 0;
mInterstitialAd = new InterstitialAd(context);
mInterstitialAd.setAdUnitId(Constants.SETTINGS.getAdmobIntrestitialId());
mInterstitialAd.loadAd(GDPR.getAdRequest(context));
mInterstitialAd.setAdListener(new AdListener() {
#Override
public void onAdLoaded() {
super.onAdLoaded();
if(mInterstitialAd.isLoaded()){
mInterstitialAd.show();
}
}
#Override
public void onAdFailedToLoad(int errorCode) {
// Code to be executed when an ad request fails.
toDoOnInfoBtnClicked(position,data);
}
#Override
public void onAdClosed() {
// Code to be executed when the interstitial ad is closed.
toDoOnInfoBtnClicked(position,data);
}
});
}else{
toDoOnInfoBtnClicked(position,data);
}
}else{
toDoOnInfoBtnClicked(position,data);
}
}
});
}
#Override
public int getItemCount() {
return data.length;
}
public class VideosViewHolder extends RecyclerView.ViewHolder {
TextView videoTitle,videoCategoryTitle;
public VideosViewHolder(#NonNull View itemView) {
super(itemView);
videoTitle = itemView.findViewById(R.id.video_title);
videoCategoryTitle = itemView.findViewById(R.id.video_category_title);
}
}
}
It's just because you are sending Context object to the method getAdRequest(Activity activity) which needs Activity object. So you can fix it in 2 ways
Change adapter to take Activity not Context. like below
public class VideosAdapter extends RecyclerView.Adapter<VideosAdapter.VideosViewHolder> {
private Activity activity;
..
public VideosAdapter(Activity activity, Video[] data, int layout) {
this.activity = activity;
...
}
The second way to fix this is, change getAdRequest(Activity activity), like below
public static AdRequest getAdRequest(Context context) {
return new AdRequest.Builder()
.addNetworkExtrasBundle(AdMobAdapter.class, GDPR.getBundleAd(context))
.build();
}
But in the second way, I am not sure if GDPR.getBundleAd() can take context. So better to go with 1st way
3rd way is to typecast context as Activity as you commented. Like
mInterstitialAd.loadAd(GDPR.getAdRequest((Activity) context));
But there is one problem in this approach. As the constructor of your adapter take Context parameter, any Activity which using this adapter can create the object like new VideosAdapter(activity, ...) BUT can create the object like new VideosAdapter(applicationContext, ...) or new VideosAdapter(objectOfcontext, ...). So in the last 2 cases, you will get a typecast exception.
You should pass an Activity object to your adapter. otherwise you'll face with that error.
use like this :
adapter = new VideosAdapter(YourActivity.this,yourData,R.layout.some_layout);
I have made a note app and everything works fine. Just in RecyclerView List when I choose an item or items and in menu I click delete button I the note is not deleted. Although delete method works in another place.
Here's the method of delete button in menu :
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.delete_selected) {
for (NotePad notePad : notes) {
if (selectedIds.contains(notePad.getId())) {
database = new Database(this);
database.deleteNote(notePad.getId());
}
}
adapter.notifyDataSetChanged();
return true;
}
return false;
}
And method of delete note in my database class:
void deleteNote(long id) {
SQLiteDatabase database = this.getWritableDatabase();
database.delete(DATABASE_TABLE, ID + "=?", new String[] {String.valueOf(id)});
database.close();
}
I don't know how to relate id of selected note to my method cause deletNote method receives long value but my selectedId is List.
Thanks in advance.
UPDATED
I realized my code works find but it doesn't refresh the list and also goes back from ActionMode to Toolbar. I tried adapter.notifyDataSetChanged();
Use this adapter. I have used interface.
public class NotePadAdapter extends RecyclerView.Adapter < NotePadAdapter.ViewHolder > {
private Context context;
private LayoutInflater inflater;
private List < NotePad > notes;
private List < Long > selectedIds = new ArrayList < > ();
private NoteClickListener listener;
NotePadAdapter(Context context, NoteClickListener listener, List < NotePad > notes) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.notes = notes;
this.listener = listener;
}
public interface NoteClickListener {
public void onNoteClick(NotePad notePad);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.notes_list, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
String title = notes.get(position).getTitle();
String date = notes.get(position).getDate();
String time = notes.get(position).getTime();
holder.setOnClickListener() {
listener.onNoteClick(notes.get(position))
}
holder.noteTitle.setText(title);
holder.noteTitle.setText(title);
holder.noteDate.setText(date);
holder.noteTime.setText(time);
long id = notes.get(position).getId();
if (selectedIds.contains(id)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
holder.rooView.setForeground(new ColorDrawable(ContextCompat.getColor(context, R.color.colorControlActivated)));
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
holder.rooView.setForeground(new ColorDrawable(ContextCompat.getColor(context, android.R.color.transparent)));
}
}
}
#Override
public int getItemCount() {
return notes.size();
}
public NotePad getItem(int position) {
return notes.get(position);
}
public void setSelectedIds(List < Long > selectedIds) {
this.selectedIds = selectedIds;
notifyDataSetChanged();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView noteTitle, noteDate, noteTime;
ConstraintLayout rooView;
ViewHolder(#NonNull View itemView) {
super(itemView);
noteTitle = itemView.findViewById(R.id.note_title);
noteDate = itemView.findViewById(R.id.note_date);
noteTime = itemView.findViewById(R.id.note_time);
rooView = itemView.findViewById(R.id.root_view);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(v.getContext(), ContentActivity.class);
i.putExtra("ID", notes.get(getAdapterPosition()).getId());
v.getContext().startActivity(i);
}
});
}
}
}
In your activity/fragment where you are initiating this adapter pass this for the listener and implement the interface and whenever you click any item on the recyclerview then you will your whole NotePad object inside this function.
I've have an arraylist that is not displaying in RecyclerView. The arraylist has data but my RecyclerView Adapter shows no error, nor is my fragment activity showing no errors. I am at a complete loss where the programming error is. The getItemCount seems correct, the holder seems correct and the Fragment seems to be correct but I know there is a mistake somewhere. Here is my code:
Fragment:
public class TestFragment extends Fragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
List<PlanetData> items = new ArrayList<>();
RecyclerView mRecyclerView;
PlanetRecyclerViewAdapter adapter;
private OnFragmentInteractionListener mListener;
public TestFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
planetList();
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_test, container, false);
mRecyclerView = (RecyclerView)view.findViewById(R.id.planet_recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.addItemDecoration(new DividerItemDecoration(mRecyclerView.getContext(),DividerItemDecoration.VERTICAL));
adapter = new PlanetRecyclerViewAdapter(items, mRecyclerView.getContext());
mRecyclerView.setAdapter(adapter);
return view;
}
// 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");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
private List<PlanetData> planetList() {
List<PlanetData> planetvalues = new ArrayList<>();
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData("12"));
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData(Double.toString(Mercury.getMercuryRA())));
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData(Double.toString(Venus.getVenusRA())));
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData(Double.toString(Moon.getMoonRA())));
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData(Double.toString(Mars.getMarsRA())));
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData(Double.toString(Jupiter.getJupiterRA())));
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData(Double.toString(Saturn.getSaturnRA())));
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData(Double.toString(Uranus.getUranusRA())));
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData(Double.toString(Neptune.getNeptuneRA())));
planetvalues.add(new com.ksburneytwo.planetmathtest.PlanetData(Double.toString(Pluto.getPlutoRA())));
System.out.println("This is Arraylist:" + planetvalues);
return planetvalues;
}
}
Here is the PlanetData class:
public class PlanetData {
private String PlanetRA;
public PlanetData(String PlanetRA) {
this.PlanetRA = PlanetRA;
}
#Override
public String toString() {
return PlanetRA;
}
public String getPlanetRA (){
return PlanetRA;
}
public void setPlanetRA(String PlanetRA){
this.PlanetRA = PlanetRA;
}
}
Here is my RecyclerView Adapter:
public class PlanetRecyclerViewAdapter extends RecyclerView.Adapter<PlanetRecyclerViewAdapter.ViewHolder> {
private List<PlanetData> mPlanetDataList;
Context mContext;
public static class ViewHolder extends RecyclerView.ViewHolder{
public TextView currentRA;
public ViewHolder(View itemView) {
super(itemView);
currentRA = (TextView) itemView.findViewById(R.id.planet_location);
}
}
public PlanetRecyclerViewAdapter(List<PlanetData> mPlanetDataList, Context mContext){
this.mPlanetDataList = mPlanetDataList;
this.mContext = mContext;
}
#Override
public PlanetRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.planet_recycler_item,parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder( PlanetRecyclerViewAdapter.ViewHolder holder, int position) {
holder.currentRA.setText(mPlanetDataList.get(position).getPlanetRA());
}
#Override
public int getItemCount() {
return mPlanetDataList.size();
}
}
I don't see you ever actually adding anything to the items list.
You call planetList() in onCreateView(), but you aren't using the result of it, and planetList() doesn't affect items in any way: it makes its own ArrayList and returns that.
Either remove planetValues from the planetList() method and reference items directly:
private void planetList() { //changed signature to "void"
items.add(...);
items.add(...);
//etc
}
Or set the result of planetList() to items when you call it:
items.addAll(planetList());
You haven't populated items.
items = planetList();
I am getting a java.lang.ClassCastException when trying to use an Interface and an AsyncTask from inside my adapter. I don't know if this is 'allowed', but I can't see why it wouldn't be as long as everything is kept in context.
The code starts in my Fragment when I try and set my GridView with a custom Adapter.
mSceneGridAdapter = new SceneGridAdapter(getActivity(), mScenesList);
Which leads to my SceneGridAdapter class being called
public class SceneGridAdapter extends BaseAdapter implements HandleProductThumbnail {
private Context mContext;
private ArrayList<Scene> mSceneList = null;
private LayoutInflater mInflater;
private Drawable mThumbnail;
public SceneGridAdapter() {}
public SceneGridAdapter(Context context, ArrayList<Scene> sceneList) {
super();
mContext = context;
if (mSceneList != null) {
clearData();
} else {
mSceneList = sceneList;
}
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.scene_cell, null);
}
ImageView sceneView = (ImageView) convertView.findViewById(R.id.sceneImage);
Scene scene = mSceneList.get(position);
String specifier = scene.getSpecifier() + "/" + scene.getName();
LoadScenes scenes = new LoadScenes(mContext, specifier);
scenes.execute();
sceneView.setBackground(mThumbnail);
return convertView;
}
#Override
public int getCount() {
return mSceneList.size();
}
#Override
public Object getItem(int position) {
return mSceneList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public void clearData() {
mSceneList.clear();
}
#Override
public void handleProductThumbnail(Drawable thumb) {
mThumbnail = thumb;
}
}
Which implements my Interface HandleProductThumbnails
public interface HandleProductThumbnail {
void handleProductThumbnail(Drawable thumb);
}
and then calls my AsyncTask loader class called LoadScenes, which loads the thumbnails and returns them to the adapter.
public class LoadScenes extends AsyncTask<Void, Void, Drawable> {
private static final String LOADING = "Loading...";
private static final String PNG = "_ORIGINAL.PNG";
private Context mContext;
private HandleProductThumbnail mHandleThumbnail = null;
private ProgressDialog mProgressDialog;
private String mSpecifier;
public LoadScenes () {}
public LoadScenes (Context context, String specifier) {
mContext = context;
mSpecifier = specifier;
}
#Override
public void onPreExecute() {
mProgressDialog = new ProgressDialog(mContext);
mProgressDialog.setMessage(LOADING);
mProgressDialog.show();
}
public Drawable doInBackground(Void... v) {
Drawable productThumbnail = getDrawable(mSpecifier);
return productThumbnail;
}
public void onPostExecute(Drawable productThumbnail) {
mProgressDialog.dismiss();
mHandleThumbnail = (HandleProductThumbnail) mContext;
mHandleThumbnail.handleProductThumbnail(productThumbnail);
}
public Drawable getDrawable(String specifier) {
Bitmap bmp = null;
Drawable productThumbnail = null;
specifier = specifier.replace("\\", "/") + PNG;
try {
bmp = BitmapFactory.decodeStream(mContext.getAssets().open(specifier));
if (bmp != null) {
int width = 300;
int height = 240;
bmp = Bitmap.createScaledBitmap(bmp, width, height, true);
productThumbnail = new BitmapDrawable(mContext.getResources(), bmp);
}
} catch (IOException e) {
e.printStackTrace();
}
return productThumbnail;
}
}
and finally my log
09-30 10:34:35.725: E/AndroidRuntime(22442): java.lang.ClassCastException: com.example.xxx.visualizer.VisualizerActivity cannot be cast to com.example.xxx.interfaces.HandleProductThumbnail
09-30 10:34:35.725: E/AndroidRuntime(22442): at com.example.xxx.visualizer.LoadScenes.onPostExecute(LoadScenes.java:46)
The Fragment is attached to an Activity called VisualizerActivity, and all the context being passed comes from that Activity. While this problem is somewhat layered, the code is fairly boilerplate. I was wondering if I am just doing something that isn't allowed? From my understanding of Context this should work. Please let me know what I'm doing wrong, why, and if it can be fixed. Thank you
Looks like your activity is not implementing the HandleProductThumbnail, but your custom adapter. In your case, instead of casting, I would rather add a method to the AsyncTask subclass:
public void setHandleProductThumbnail(HandleProductThumbnail listener) {
mHandleThumbnail = listener;
}
and inside OnPostExecute
public void onPostExecute(Drawable productThumbnail) {
mProgressDialog.dismiss();
if (mHandleThumbnail != null)
mHandleThumbnail.handleProductThumbnail(productThumbnail);
}
Your problem is here :
mHandleThumbnail = (HandleProductThumbnail) mContext;
You're casting mContext to HandleProductThumbnail whereas mContext is a Context, hence not implementing your custom interface. If you need a reference to the HandleProductThumbnail, pass it as a constructor argument.
Last note : if you're trying to load thumbnails on every list item, I would suggest the use of Picasso, which would make all this much simpler for you.
It is clearly visible that it would create an class cast exception,
because mContext is your activity`s object which is not implementing the "HandleProductThumbnail" interface.
Though you rectify your code by right casting, yet it will produce error due to below lines of code in your adapter
LoadScenes scenes = new LoadScenes(mContext, specifier);
scenes.execute();
sceneView.setBackground(mThumbnail);
Because second line is an async call and mThumbnail would be null.You may apply another approach for rendering images from URL at run time.