I'm trying to show pictures in a GridView. This Grid View is filled by an Adapter.
I always had the problem that the application is very laggy when it inflates a new card. After some searching in the internet i found Glide, which could maybe help to make the performance better.
Now I'm stuck because i can't imagine what context i should pass in at Glide.with(...):
Glide.with(**CONTEXT**).load(sights.get(i).mImageRessourceID).into(sightViewHolder.sightPhoto);
To build the Cards i use: MainActivity -> Fragment -> Adapter.
That's the code from the adapter:
public class SightCardAdapter extends RecyclerView.Adapter<SightCardAdapter.SightViewHolder> {
public static class SightViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView sightName;
ImageView sightPhoto;
SightViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
sightName = (TextView) itemView.findViewById(R.id.sight_name);
sightPhoto = (ImageView) itemView.findViewById(R.id.sight_photo);
}
}
List<Sight> sights;
SightCardAdapter(List<Sight> sights) {
this.sights = sights;
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public SightViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
SightViewHolder pvh = new SightViewHolder(v);
return pvh;
}
#Override
public void onBindViewHolder(SightViewHolder sightViewHolder, int i) {
sightViewHolder.sightName.setText(sights.get(i).mName);
//sightViewHolder.sightPhoto.setImageResource(sights.get(i).mImageRessourceID);
Glide.with().load(sights.get(i).mImageRessourceID).into(sightViewHolder.sightPhoto); // missing context
}
#Override
public int getItemCount() {
return sights.size();
}
}
Can anybody please give me some hint? I can't image what I should pass in.
PS: Additionally if anybody has some tipps how to make my code more efficient i would be very grateful :)
You can get Context from Any View Object.
Glide.with(sightViewHolder.sightPhoto.getContext()).load(sights.get(i).mImageRessourceID).into(sightViewHolder.sightPhoto);
While passing data to the Adapter also pass a parameter Context. If you are calling your Adapter from Fragment that is child of MainActivity, simply pass getApplicationContext() as context to your Adapter.
SightCardAdapter(Context context,List<Sight> sights) {
this.sights = sights;
this.context=context;
}
while calling from Fragment.
SightCardAdapter(getApplicationContext(),sights);
Pass Context when you create object for Adapter., Changed code below
public class SightCardAdapter extends RecyclerView.Adapter<SightCardAdapter.SightViewHolder> {
public static class SightViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView sightName;
ImageView sightPhoto;
SightViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
sightName = (TextView) itemView.findViewById(R.id.sight_name);
sightPhoto = (ImageView) itemView.findViewById(R.id.sight_photo);
}
}
Context context;
List<Sight> sights;
SightCardAdapter(List<Sight> sights,Context context) {
this.sights = sights;
this.context = context;
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public SightViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
SightViewHolder pvh = new SightViewHolder(v);
return pvh;
}
#Override
public void onBindViewHolder(SightViewHolder sightViewHolder, int i) {
sightViewHolder.sightName.setText(sights.get(i).mName);
//sightViewHolder.sightPhoto.setImageResource(sights.get(i).mImageRessourceID);
Glide.with(context).load(sights.get(i).mImageRessourceID).into(sightViewHolder.sightPhoto); // missing context
}
#Override
public int getItemCount() {
return sights.size();
}
}
Pass your context in adapter like this
Private Context mContext;
SightCardAdapter(List<Sight> sights,Context mContext) {
this.sights = sights;
this.mContext = mContext;
}
Glide.with(mContext).load("url").into(imageView);
for efficient use..
private RequestManager glideManager;
public MyAdater(Context context, Object activityOrFragment){
if (object instanceof Fragment) {
glideManager = Glide.with((Fragment) object);
} else if (object instanceof Activity) {
glideManager = Glide.with((Activity) object);
}
}
#Override
public void onBindViewHolder(ViewHolderGeneric viewHolder, int position) {
glideManager
.load(item.getResizedImageUrl())
.placeholder(R.drawable.emptystates_card_bg_none)
.fitCenter()
.into(ivGoods);
}
Adapter
MyRecyclerAdapter adapter = new MyRecyclerAdapter(mContext, this); //this -> Activity or Fragment
Try
Either pass context from activity while creating adapter object or get it from itemView.
Context context = itemView.getContext();
Pass this context to Glide.
Context context;//declare this globle
SightCardAdapter(Context context, List<Sight> sights) {
this.context=context;// add context in constructor of adapter class
this.sights = sights;
}
Now, use context in Glide as below:
Glide.with(context).load(sights.get(i).mImageRessourceID).into(sightViewHolder.sightPhoto);
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 am trying to send the context from my java class to my adapter of a RecyclerView, so I can click on the elements and be redirected to an intent. Apparently, the context isn't right. I've commented its line and the app works, but as soon as i try to prove it, it starts crashing again.
I assume its a context thing, but it would be pretty helpful if there's another way to set a click method to my elements.
The exception it throw its a null pointer exception:
java.lang.RuntimeException: Unable to start activity:
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.widget.Button.setOnClickListener(android.view.View$OnClickListener)'
on a null object reference
Here is the adapter code:
public class SubsidioAdapter extends RecyclerView.Adapter<SubsidioAdapter.SubsidioViewHolder> {
private Context context;
private List<Producto> productoList;
private LayoutInflater inflater;
public SubsidioAdapter(Context context, List<Producto> productoList) {
this.context = context;
this.productoList = productoList;
this.inflater = LayoutInflater.from(context);
}
#Override
public SubsidioViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = this.inflater.inflate(R.layout.producto_item, null);
return new SubsidioViewHolder(view);
}
#Override
public void onBindViewHolder(SubsidioViewHolder subsidioViewHolder, int position) {
subsidioViewHolder.imageView.setImageResource(this.productoList.get(position).getFoto());
subsidioViewHolder.textView1.setText(this.productoList.get(position).getNombre());
subsidioViewHolder.textView2.setText(this.productoList.get(position).getDescripcion());
subsidioViewHolder.textView3.setText(this.productoList.get(position).getPrecio());
subsidioViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
context.startActivity(new
Intent(context, ModificarProductoActivity.class));
}
});
}
#Override
public int getItemCount() {
return this.productoList.size();
}
public class SubsidioViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView1;
TextView textView2;
TextView textView3;
public SubsidioViewHolder(View itemView) {
super(itemView);
this.imageView = itemView.findViewById(R.id.fotoSub);
this.textView1 = itemView.findViewById(R.id.descSub);
this.textView2 = itemView.findViewById(R.id.nombreSub);
this.textView3 = itemView.findViewById(R.id.precioSub);
}
}
}
set an id to root element of adapter item layout, then find that view by id and use it instead of itemView ,
instead of :
subsidioViewHolder.itemView.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
...
}
});
use:
subsidioViewHolder.rootLayout.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
...
}
});
. and remember you can get context in recyclerView adapter inside onCreateViewHolder() without using constructor like this:
#Override
public SubsidioViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
context=viewGroup.getContext();
View view = this.inflater.inflate(R.layout.producto_item, null);
return new SubsidioViewHolder(view);
}
although it seems like that your problem is with
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 implement another activity which needs to be open when click on card but i getting Error:(56, 48) error: incompatible types: anonymous Callback JSONResponse cannot be converted to Context even if i convert to JSONResponse then there is another error. Problem is in that JSONResponse but i can't solve it.
DataAdapter.java
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
private ArrayList<AndroidVersion> android;
Context ctx;
public DataAdapter(ArrayList<AndroidVersion> android, Context ctx)
{
this.android = android;
this.ctx = ctx;
}
#Override
public DataAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_row, viewGroup, false);
return new ViewHolder(view,ctx,android);
}
#Override
public void onBindViewHolder(DataAdapter.ViewHolder viewHolder, int i) {
viewHolder.tv_name.setText(android.get(i).getName());
viewHolder.tv_version.setText(android.get(i).getVer());
viewHolder.tv_api_level.setText(android.get(i).getApi());
}
#Override
public int getItemCount() {
return android.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
private TextView tv_name,tv_version,tv_api_level;
ArrayList<AndroidVersion> android = new ArrayList<AndroidVersion>();
Context ctx;
public ViewHolder(View view, Context ctx, ArrayList<AndroidVersion> android)
{
super(view);
this.android = android;
this.ctx = ctx;
view.setOnClickListener(this);
tv_name = (TextView)view.findViewById(R.id.tv_name);
tv_version = (TextView)view.findViewById(R.id.tv_version);
tv_api_level = (TextView)view.findViewById(R.id.tv_api_level);
}
#Override
public void onClick(View v) {
int i = getAdapterPosition();
AndroidVersion android = this.android.get(i);
Intent intent = new Intent(this.ctx, ContactDetails.class);
intent.putExtra("name_id",android.getName());
intent.putExtra("version_id",android.getVer());
intent.putExtra("api_level_id",android.getApi());
((Context) this.ctx).startActivity(intent);}
}
}
JSONResponse.java
public class JSONResponse {
private AndroidVersion[] android;
public AndroidVersion[] getAndroid() {
return android;}
}
mainActivity when i write this getting error
main
First issue,
to this:
adapter = new DataAdapter(data,MainActivity.this);
is giving error because: if you put this inside a callback, you are refering to the onResponse scope, you cannot use this, because is outside scope, for that reason you have to you use MainActivity.this or getApplicationContext();
So im having this issue in my adapter to were when i go to set the Picasso method to convert my image url it will not allow me to pass the context no matter how i do it. i Have tried this and The class name .this neither seem to work. Not sure what or why this is happening. Here is the adapter class that im having issues with.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ContentViewHolder> {
public content[] mDataset;
public MyAdapter(content[] data) {
mDataset = data;
}
#Override
public ContentViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.test, parent, false);
ContentViewHolder viewHolder = new ContentViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(ContentViewHolder holder, int position) {
holder.bindContent(mDataset[position]);
}
#Override
public int getItemCount() {
return mDataset.length;
}
public class ContentViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView mUrl;
public TextView mTitle;
public TextView mDate;
public TextView mAuthor;
public ImageView mThumbnail;
public ContentViewHolder(View itemView) {
super(itemView);
mUrl= (TextView) itemView.findViewById(R.id.url);
mTitle = (TextView) itemView.findViewById(R.id.title);
mDate = (TextView) itemView.findViewById(R.id.date);
mAuthor = (TextView) itemView.findViewById(R.id.author);
mThumbnail =(ImageView)itemView.findViewById(R.id.thumbnail);
}
public void bindContent(content bloginfo) {
mUrl.setText(bloginfo.getUrl());
mTitle.setText(bloginfo.getTitle());
mDate.setText(bloginfo.getDate());
mAuthor.setText(bloginfo.getAuthor());
Picasso.with(context).load(bloginfo.getThumbnail()).into(mThumbnail);
}
#Override
public void onClick(View view) {
}
}
}
Ok so i got this to work here is the working adapter code below. Thank you all for your help.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ContentViewHolder> {
public content[] mDataset;
private Activity activityContext;
public MyAdapter(Activity context,content[] data) {
mDataset = data;
activityContext = context;
}
#Override
public ContentViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.test, parent, false);
ContentViewHolder viewHolder = new ContentViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(ContentViewHolder holder, int position) {
holder.bindContent(mDataset[position]);
}
#Override
public int getItemCount() {
return mDataset.length;
}
public class ContentViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView mUrl;
public TextView mTitle;
public TextView mDate;
public TextView mAuthor;
public ImageView mThumbnail;
public ContentViewHolder(View itemView) {
super(itemView);
mUrl= (TextView) itemView.findViewById(R.id.url);
mTitle = (TextView) itemView.findViewById(R.id.title);
mDate = (TextView) itemView.findViewById(R.id.date);
mAuthor = (TextView) itemView.findViewById(R.id.author);
mThumbnail =(ImageView)itemView.findViewById(R.id.thumbnail);
}
public void bindContent(content bloginfo) {
mUrl.setText(bloginfo.getUrl());
mTitle.setText(bloginfo.getTitle());
mDate.setText(bloginfo.getDate());
mAuthor.setText(bloginfo.getAuthor());
Picasso.with(activityContext).load(bloginfo.getThumbnail()).into(mThumbnail);
}
#Override
public void onClick(View view) {
}
}
}
This
Picasso.with(MyAdapter.this)
should be
Picasso.with(context)
Use Activity Context. This can be passed to the constructor of adapter class from activity.
http://developer.android.com/reference/android/content/Context.html
http://square.github.io/picasso/
Samples available on github
https://github.com/square/picasso/blob/master/picasso-sample/src/main/java/com/example/picasso/PicassoSampleAdapter.java
In your adapter you can make:
private Activity activityContext;
then in Adapter contructor:
public MyAdapter(Activity context, content[] data) {
mDataset = data;
activityContext = context;
}
Finally call:
Picasso.with(activityContext).load(bloginfo.getThumbnail()).into(mThumbnail);
If you create your Adapter in your Activity you need to create it using:
new myAdapter(this, YOURDATA), if you create it in Fragment, you need to use new myAdapter(getActivity(), YOURDATA).
Picasso.with(mThumbnail.getContext()).load(bloginfo.getThumbnail()).into(mThumbnail);