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.
Related
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();
}
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 am building an AsyncTask and I need to pass multiple images to multiple ImageView containers.
I am trying to build the individual findViewById as "img" + position (an int that changes for each image)but my getResources() does not work.
I have tried passing a context to my AsyncTask when I instantiate it but it gives the same error.
Main Activity
while(i<20) {
for(String imgURL : imagePaths) {
new ImageDownloader(new WeakReference<AppCompatActivity>(this),i,getApplicationContext()).execute(imgURL,fpath);
i++;
}
}
AsyncTask
public class ImageDownloader extends AsyncTask<String, Integer, Bitmap> {
int position;
private WeakReference<AppCompatActivity> caller;
private final Context contextRef;
public ImageDownloader(WeakReference<AppCompatActivity> caller, Integer position, final Context context) {
this.caller = caller;
this.position = position;
this.contextRef = context;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
AppCompatActivity activity = caller.get();
Context context = contextRef;
if (context != null) {
int place = context.getResources().getIdentifier("img" + position, "id", context.getPackageName());
ImageView imageView = activity.findViewById(place);
imageView.setImageBitmap(bitmap);
}
I keep getting compilation failed, error:
cannot find symbol static getResources
I'm trying to show the list of background running apps in ListView. what I have tried is creating AppAdapter extending ArrayAdapter and used Async task to call the method which returns the list of running services' package names. I posted the code below.
AppAdapter:
public class AppAdapter extends ArrayAdapter<String> {
private List<ActivityManager.RunningServiceInfo> applist =null;
private Context context;
private PackageManager packageManager;
private List<String> applist1 = null;
public AppAdapter(#NonNull Context context, int resource, #NonNull List<String> objects) {
super(context, resource, objects);
this.context = context;
this.applist1 = objects;
packageManager = context.getPackageManager();
}
#Override
public int getCount() {
return ((applist1 != null) ? applist1.size() : 0);
}
#Override
public String getItem(int position) {
return ((applist1 != null) ? applist1.get(position) : null);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = layoutInflater.inflate(R.layout.list_item, null);
}
ActivityManager.RunningServiceInfo data = applist.get(position);
Log.d("Test", "data is : " + data);
if (data != null){
Log.d("Test", "data is not null : " + data);
TextView appName = view.findViewById(R.id.applabel);
TextView packageName = view.findViewById(R.id.pname);
ImageView iconView = view.findViewById(R.id.appicon);
ComponentName mComponentName = data.service;
appName.setText(mComponentName.getClassName());
packageName.setText(mComponentName.getPackageName());
} else {
Log.d("Test", "data is null : " + data);
}
return view;
}
}
Code in Main Activity:
private class LoadApplications extends AsyncTask<Void, Void, Void>{
private ProgressDialog progressDialog = null;
#Override
protected Void doInBackground(Void... voids) {
applist1 = runningServices();
listadapter = new AppAdapter(Taskmanager.this, R.layout.list_item, applist1);
Log.d("Test", "applist1 is : " + applist1);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
listView.setAdapter(listadapter);
progressDialog.dismiss();
super.onPostExecute(aVoid);
}
#Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(Taskmanager.this, null, "Loading..");
super.onPreExecute();
}
}
and the runningServices() method correctly returns the list of package names (if you want to see that let me know).
The problem is the after app launch it shows nothing (no list). I didn't forget to call the async task in the onCreate method of the activity.
Can someone please figure out what might be going wrong. I posted only limited code if you want more then ask me in the comments, please.
Do the necessary work after getting a list from the background task. Return the List value from the doInBackgroind method and then assign it to adapter in the onPostExecute method which runs on the UI thread.
public class LoadApplications extends AsyncTask<Void, Void, List<ActivityManager.RunningServiceInfo>> {
#Override
protected List<ActivityManager.RunningServiceInfo> doInBackground(Void... arg0) {
return runningServices();
}
#Override
protected void onPostExecute(List<ActivityManager.RunningServiceInfo> applist) {
// call super
listadapter = new AppAdapter(Taskmanager.this,
R.layout.list_item, applist);
listView.setAdapter(listadapter);
progressDialog.dismiss();
//other works
}
}
I have a Task which extends Androids AsyncTask in order to get data from my Database in the cloud. I am doing this as to avoid NetworkingOnMainThread exception (I know you can set up StrictMode but in my opionion it's more of a hack than a solution).
I get no errors whatsoever. I have checked values in debugging and everything gets fetched correctly from my database. I have a Venue where I set my name, description, image etc... and the ArrayList<Venue> which I use for my adapter gets instantiated properly.
The problem is - the ListView doesn't render.
Here is my custom adapter code
public class EventListArrayAdapter extends ArrayAdapter<String> {
Context context;
ArrayList<Venue> values;
public EventListArrayAdapter(Context context, ArrayList<Venue> values) {
super(context, R.layout.single_event_list_item);
this.context = context;
this.values = values;
}
public View getView(int position, View convertView, ViewGroup parent){
View v = convertView;
if(v==null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.single_event_list_item, null);
}
Venue currEvent = values.get(position);
if(currEvent!=null){
TextView singleItemTitle = (TextView) v.findViewById(R.id.singleItemTitle);
TextView singleItemLocation = (TextView) v.findViewById(R.id.singleItemLocation);
TextView singleItemDate = (TextView) v.findViewById(R.id.singleItemDate);
ImageView singleItemImage = (ImageView) v.findViewById(R.id.singleItemImage);
singleItemTitle.setText(currEvent.getName());
singleItemDate.setText(currEvent.getDate());
singleItemLocation.setText(currEvent.getLocation());
singleItemImage.setImageBitmap(currEvent.getVenueImage());
}
return v;
}
}
And here is my AsyncTask code
private class FillContentTask extends AsyncTask<String, Void, Void> {
ArrayList<Venue> venues;
Context activity;
ProgressDialog dialog;
public FillContentTask(Context context) {
this.activity = context;
}
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(activity);
dialog.setTitle("Loading...");
dialog.show();
}
#Override
protected Void doInBackground(String... params) {
this.venues = Venue.getAll();
for (Venue currentVenue : this.venues) {
try {
InputStream in = new java.net.URL(currentVenue.getImageURL()).openStream();
currentVenue.setVenueImage(BitmapFactory.decodeStream(in));
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPostExecute(Void result) {
ListView list = (ListView) findViewById(R.id.eventList);
EventListArrayAdapter adapter = new EventListArrayAdapter(activity, this.venues);
list.setAdapter(adapter);
dialog.hide();
}
}
And as I already said. To the best of my knowledge, the AsnycTask does it's job properly since all ArrayList<Venue> values get filled properly
Override the getCount method in your EventListArrayAdapter
#Override
public int getCount()
{
return values.size();
}
OR
change the super call in EventListArrayAdapter constructor from
super(context, R.layout.single_event_list_item);
to
super(context, R.layout.single_event_list_item,values);
and also you will need to change extends ArrayAdapter<String> to extends ArrayAdapter<Venue>