207

I'm trying to use picasso library to be able to load url to imageView, but I'm not able to get the context to use the picasso library correctly.

public class FeedAdapter extends RecyclerView.Adapter<FeedAdapter.ViewHolder> {
    private List<Post> mDataset;



    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView txtHeader;
        public ImageView pub_image;
        public ViewHolder(View v) {
            super(v);
            txtHeader = (TextView) v.findViewById(R.id.firstline);
            pub_image = (ImageView) v.findViewById(R.id.imageView);


        }
    }




    // Provide a suitable constructor (depends on the kind of dataset)
    public FeedAdapter(List<Post> myDataset) {
        mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public FeedAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.feedholder, parent, false);
        // set the view's size, margins, paddings and layout parameters
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element

        holder.txtHeader.setText(mDataset.get(position).getPost_text());

        Picasso.with(this.context).load("http://i.imgur.com/DvpvklR.png").into(holder.pub_image);


    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.size();
    }

}
Ahmad Aghazadeh
  • 16,571
  • 12
  • 101
  • 98
Stranger B.
  • 9,004
  • 21
  • 71
  • 108

13 Answers13

404

You have a few options here:

  1. Pass Context as an argument to FeedAdapter and keep it as class field
  2. Use dependency injection to inject Context when you need it. I strongly suggest reading about it. There is a great tool for that -- Dagger by Square
  3. Get it from any View object. In your case this might work for you:

    holder.pub_image.getContext()

    As pub_image is a ImageView.

Ben Grant
  • 305
  • 6
  • 18
pelotasplus
  • 9,852
  • 1
  • 35
  • 37
  • 102
    I prefer the third option, getting the context from the view. It is the simplest and most straightforward solution. – Dick Lucas Jun 13 '16 at 16:08
  • or, far better, pass in the Picasso instance, rather than holding a Picasso instance on the class instance and lazy-loading it. – Eric Cochran Jul 13 '16 at 20:04
  • 10
    Each ViewHolder contains an instance of itemView that you pass in the constructor. Use that one like this: itemView.getContext() –  Oct 20 '16 at 10:03
  • 3
    You can't do #2, can you? I tried that, but this will end up being your `ApplicationContext` and not tied to the activity, leading to a runtime crash from within Glide. – boltup_im_coding Mar 05 '18 at 01:34
  • 2
    how to pass the component object to the adapter in case I want to use #2? – Ezio Mar 27 '18 at 11:00
  • In the third option, you can get context from itemView. Like this: `holder.itemView.getContext()` – Huy Nguyen May 19 '19 at 16:04
  • 3rd one is super simple. Just get the package name in the onBindViewHolder or wherever you like, using. "Context context = viewHolder.mAppIcon.getContext();" then pass it – user7418129 Mar 28 '20 at 14:54
  • Could 3rd result in a leak? – Minh Nghĩa Dec 15 '20 at 02:48
47

Edit As solidak said in the comments section, the original answer could lead to memory leak issues, and it's a bad practice to use this method, so it's better to use another method to access the context.

Original answer:

You can add a global variable:

private Context context;

then assign the context from here:

@Override
public FeedAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
    // create a new view
    View v=LayoutInflater.from(parent.getContext()).inflate(R.layout.feedholder, parent, false);
    // set the view's size, margins, paddings and layout parameters
    ViewHolder vh = new ViewHolder(v);
    // set the Context here 
    context = parent.getContext();
    return vh;
}

Happy Codding :)

Farhad Navayazdan
  • 1,091
  • 3
  • 12
  • 22
  • 33
    I strongly suggest AGAINST having global references to Context. This could lead to horrible memory leak issues!! – solidak Feb 21 '17 at 19:09
  • 1
    if you subclass viewholder, it can be private. – r3dm4n Jun 14 '17 at 16:16
  • If the adapter is attached to a RecyclerView then that context is guaranteed to be needed anyway. If you might unattach the adapter, then it's still fine unless for some reason you hold a strong reference to this adapter after it's been detached, in which case you should clear out the cached context, there is a method like `onDetachedFromRecyclerView` or something. – androidguy Dec 21 '17 at 22:18
39

Short answer:

Context context;

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);
    context = recyclerView.getContext();
}

Explanation why other answers are not great:

  1. Passing Context to the adapter is completely unnecessary, since RecyclerView you can access it from inside the class
  2. Obtaining Context at ViewHolder level means that you do it every time you bind or create a ViewHolder. You duplicate operations.
  3. I don't think you need to worry about any memory leak. If your adapter lingers outside your Activity lifespan (which would be weird) then you already have a leak.
Maciej Beimcik
  • 3,218
  • 1
  • 24
  • 26
20

You can use pub_image context (holder.pub_image.getContext()) :

@Override
public void onBindViewHolder(ViewHolder ViewHolder, int position) {

    holder.txtHeader.setText(mDataset.get(position).getPost_text());

    Picasso.with(holder.pub_image.getContext()).load("http://i.imgur.com/DvpvklR.png").into(holder.pub_image);


}
Ahmad Aghazadeh
  • 16,571
  • 12
  • 101
  • 98
10

you can use this:

itemView.getContext()
Jonathan.Brink
  • 23,757
  • 20
  • 73
  • 115
张家宝
  • 101
  • 1
  • 2
8

You can use like this view.getContext()

Example

holder.tv_room_name.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Toast.makeText(v.getContext(), "", Toast.LENGTH_SHORT).show();


        }
    });
Arpit Patel
  • 7,212
  • 5
  • 56
  • 67
6

You can define:

Context ctx; 

And on onCreate initialise ctx to:

ctx=parent.getContext(); 

Note: Parent is a ViewGroup.

5

Create a constructor of FeedAdapter :

Context context; //global
public FeedAdapter(Context context)
{
   this.context = context;  
}

and in Activity

FeedAdapter obj = new FeedAdapter(this);
Karan
  • 2,120
  • 15
  • 27
5

First globally declare

Context mContext;

pass context with the constructor, by modifying it.

public FeedAdapter(List<Post> myDataset, Context context) {
    mDataset = myDataset;
    this.mContext = context;
}

then use the mContext whereever you need it

Blue_Alien
  • 2,148
  • 2
  • 25
  • 29
4
View mView;
mView.getContext();
double-beep
  • 5,031
  • 17
  • 33
  • 41
  • While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. [From Review](/review/low-quality-posts/25721105) – double-beep Mar 29 '20 at 16:27
3

SOLUTION:

When using viewbinding use

holder.binding.productImg.context

Otherwise use

holder.productImg.context

Chill Pill :)

Ali Akram
  • 4,803
  • 3
  • 29
  • 38
2

First add a global variable

Context mContext;

Then change your constructor to this

public FeedAdapter(Context context, List<Post> myDataset) {
    mContext = context;
    mDataset = myDataset;
}

The pass your context when creating the adapter.

FeedAdapter myAdapter = new FeedAdapter(this,myDataset);
AKT
  • 176
  • 2
  • 10
1

If you are using Databinding on layout you can get the context from holder. An exemple below.

@Override
public void onBindViewHolder(@NonNull GenericViewHolder holder, int position) {
    View currentView = holder.binding.getRoot().findViewById(R.id.cycle_count_manage_location_line_layout);// id of your root layout
    currentView.setBackgroundColor(ContextCompat.getColor(holder.binding.getRoot().getContext(), R.color.light_green));
}
Amir Dora.
  • 2,831
  • 4
  • 40
  • 61