How to load Youtube thumbnails in a RecyclerView using Youtube API

18,954

Solution 1

You can try this maybe ? It doesnt use the API but its quick.

Load the image in the recycler view with Picasso from this URL:

https://img.youtube.com/vi/"here goes your video id"/default.jpg

-- Edit --

After some research and experiments:

To get the default full size thumbnail do this instead of default.jpg

https://img.youtube.com/vi/"here goes your video id"/0.jpg

Here is the link: http://www.reelseo.com/youtube-thumbnail-image/

Edit 2:

Just found someone in SO already gave an answer like mine with this quick and easy solution and has way more explanation and options you could choose from.

How do I get a YouTube video thumbnail from the YouTube API?


Final Edit:

This is working code. I went in and made an app recently with the api so I found out why you are getting the error. Its because you are not releasing the loader properly.

You can release the loader/loaders in two ways.

First

(Preferred, you will see why in a sec) You would want to release it after the image has been loaded into view and theres a listener for that and its called OnThumbNailLoadedListener. Thats where I released it (if you pay attention to the code below). This means you dont have to deal with this instance anymore. Once thumbnail is loaded youre done.

Second

Since getView() is called all the time there's new instances of YouTubeThumbnailLoader that you have to release. This means you have to store all of these in an ArrayList. You just do an advanced for loop and call release in all of them when activity is onStop();

Now you probably see why the first way is preferred. And I know you did the second option so just letting you know the first option will always be guaranteed to work (at least in my case). I used a YouTubeSupportFragment in an Activity and it worked fine. No issues whatsoever. You can definitely make the second option work but you would have to handle a lot of special cases I think.

final YouTubeThumbnailView youTubeThumbnailView = (YouTubeThumbnailView) convertView.findViewById(R.id.show_episode_thumbnail);
    youTubeThumbnailView.initialize(DeveloperKey.DEVELOPER_KEY, new YouTubeThumbnailView.OnInitializedListener() {
        @Override
        public void onInitializationSuccess(YouTubeThumbnailView youTubeThumbnailView, final YouTubeThumbnailLoader youTubeThumbnailLoader) {
            youTubeThumbnailLoader.setVideo(videoId);
            youTubeThumbnailLoader.setOnThumbnailLoadedListener(new YouTubeThumbnailLoader.OnThumbnailLoadedListener() {
                @Override
                public void onThumbnailLoaded(YouTubeThumbnailView youTubeThumbnailView, String s) {
                    youTubeThumbnailLoader.release();
                }

                @Override
                public void onThumbnailError(YouTubeThumbnailView youTubeThumbnailView, YouTubeThumbnailLoader.ErrorReason errorReason) {

                }
            });
        }

        @Override
        public void onInitializationFailure(YouTubeThumbnailView youTubeThumbnailView, YouTubeInitializationResult youTubeInitializationResult) {

        }
    });

Solution 2

In onBindViewHolder you are trying to initialize the same YoutubeThumbnailView again and again, Instead you can initialize it once when views are created in onCreateViewHolder. By setting video Id as tag to a YoutubeThumbnailView you can prevent mixing(or) wrong loading of thumbnails.


Adapter.

    private class ThumbnailAdapter extends RecyclerView.Adapter{

    private final int UNINITIALIZED = 1;
    private final int INITIALIZING = 2;
    private final int INITIALIZED = 3;
    private int blackColor = Color.parseColor("#FF000000");
    private int transparentColor = Color.parseColor("#00000000");

    public class VideoViewHolder extends RecyclerView.ViewHolder{

        public YouTubeThumbnailView ytThubnailView = null;
        public ImageView ivYtLogo = null;
        public TextView tvTitle = null;

        public VideoViewHolder(View itemView) {
            super(itemView);
            ytThubnailView = (YouTubeThumbnailView) itemView.findViewById(R.id.yt_thumbnail);
            ivYtLogo = (ImageView) itemView.findViewById(R.id.iv_yt_logo);
            tvTitle = (TextView) itemView.findViewById(R.id.tv_title);

            initialize();
        }

        public void initialize(){
            ivYtLogo.setBackgroundColor(blackColor);
            ytThubnailView.setTag(R.id.initialize, INITIALIZING);
            ytThubnailView.setTag(R.id.thumbnailloader, null);
            ytThubnailView.setTag(R.id.videoid, "");

            ytThubnailView.initialize(API_KEY, new YouTubeThumbnailView.OnInitializedListener() {
                @Override
                public void onInitializationSuccess(YouTubeThumbnailView youTubeThumbnailView, YouTubeThumbnailLoader youTubeThumbnailLoader) {
                    ytThubnailView.setTag(R.id.initialize, INITIALIZED);
                    ytThubnailView.setTag(R.id.thumbnailloader, youTubeThumbnailLoader);

                    youTubeThumbnailLoader.setOnThumbnailLoadedListener(new YouTubeThumbnailLoader.OnThumbnailLoadedListener() {
                        @Override
                        public void onThumbnailLoaded(YouTubeThumbnailView youTubeThumbnailView, String loadedVideoId) {
                            String currentVideoId = (String) ytThubnailView.getTag(R.id.videoid);
                            if(currentVideoId.equals(loadedVideoId)) {
                                ivYtLogo.setBackgroundColor(transparentColor);
                            }
                            else{
                                ivYtLogo.setBackgroundColor(blackColor);
                            }
                        }

                        @Override
                        public void onThumbnailError(YouTubeThumbnailView youTubeThumbnailView, YouTubeThumbnailLoader.ErrorReason errorReason) {
                            ivYtLogo.setBackgroundColor(blackColor);
                        }
                    });

                    String videoId = (String) ytThubnailView.getTag(R.id.videoid);
                    if(videoId != null && !videoId.isEmpty()){
                        youTubeThumbnailLoader.setVideo(videoId);
                    }
                }

                @Override
                public void onInitializationFailure(YouTubeThumbnailView youTubeThumbnailView, YouTubeInitializationResult youTubeInitializationResult) {
                    ytThubnailView.setTag(R.id.initialize, UNINITIALIZED);
                    ivYtLogo.setBackgroundColor(blackColor);
                }
            });
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = getLayoutInflater().inflate(R.layout.row_video_item, parent, false);
        VideoViewHolder videoViewHolder = new VideoViewHolder(view);
        return videoViewHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        final Entities e = entities.get(position);
        final VideoViewHolder videoViewHolder = (VideoViewHolder) holder;
        videoViewHolder.tvTitle.setText(e.name);
        videoViewHolder.ivYtLogo.setVisibility(View.VISIBLE);
        videoViewHolder.ytThubnailView.setTag(R.id.videoid, e.id);
        videoViewHolder.ivYtLogo.setBackgroundColor(blackColor);

        int state = (int) videoViewHolder.ytThubnailView.getTag(R.id.initialize);

        if(state == UNINITIALIZED){
            videoViewHolder.initialize();
        }
        else if(state == INITIALIZED){
            YouTubeThumbnailLoader loader = (YouTubeThumbnailLoader) videoViewHolder.ytThubnailView.getTag(R.id.thumbnailloader);
            loader.setVideo(e.id);
        }
    }

    @Override
    public int getItemCount() {
        return entities.size();
    }
}

The layout used for each row is.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="200dp">

    <com.google.android.youtube.player.YouTubeThumbnailView
        android:id="@+id/yt_thumbnail"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageView
        android:id="@+id/iv_yt_logo"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="center"
        android:src="@mipmap/youtube_play"
        android:background="#00000000"
        android:layout_centerInParent="true"/>

</RelativeLayout>

<TextView
    android:id="@+id/tv_title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textColor="#FF000000"
    android:textSize="16sp"
    android:text="Title"/>

<View
    android:id="@+id/seperator"
    android:layout_width="match_parent"
    android:layout_height="2dp"
    android:layout_marginTop="5dp"
    android:layout_marginBottom="5dp"
    android:background="#FF642108"/>

</LinearLayout>

tags.xml.

Location : src/main/res/values/tags.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="initialize" />
<item type="id" name="videoid"/>
<item type="id" name="thumbnailloader"/>
</resources>

enter image description here

Solution 3

public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoHolder> {

    private List<VideoPojo> listvideo;
    private VideoPojo videoPojo;
    private Context mContext;
    private boolean readyForLoadingYoutubeThumbnail = true;
    String KEY = "AIzaSyA5kyaLgS7MKxS19uHf2CUsIOmDkv92DGU";

    public VideoAdapter(Context context, List<VideoPojo> listvideo) {
        this.listvideo = listvideo;
        this.mContext = context;
        videoPojo = new VideoPojo();
    }

    @Override
    public VideoAdapter.VideoHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.video_layout, parent, false);
        return new VideoHolder(view);
    }

    @Override
    public void onBindViewHolder(final VideoAdapter.VideoHolder holder, final int position) {

        holder.murl.setText(listvideo.get(position).getVideoUrl());
        final String url = listvideo.get(position).getVideoUrl();
        Log.d(TAG, "readyForLoadingYoutubeThumbnail" + readyForLoadingYoutubeThumbnail);
        if (readyForLoadingYoutubeThumbnail) {
            Log.d(TAG, "initializing for youtube thumbnail view...");
            readyForLoadingYoutubeThumbnail = false;
            holder.youTubeThumbnailView.initialize(KEY, new YouTubeThumbnailView.OnInitializedListener() {
                @Override
                public void onInitializationSuccess(final YouTubeThumbnailView youTubeThumbnailView, final YouTubeThumbnailLoader youTubeThumbnailLoader) {

                    youTubeThumbnailLoader.setVideo(url);
                    youTubeThumbnailLoader.setOnThumbnailLoadedListener(new YouTubeThumbnailLoader.OnThumbnailLoadedListener() {

                        @Override
                        public void onThumbnailLoaded(YouTubeThumbnailView childYouTubeThumbnailView, String s) {
                            holder.loding.setVisibility(View.GONE);
                            youTubeThumbnailLoader.release(); // spy ga memory lick
                        }

                        @Override
                        public void onThumbnailError(YouTubeThumbnailView youTubeThumbnailView, YouTubeThumbnailLoader.ErrorReason errorReason) {
                            youTubeThumbnailLoader.release(); // spy ga memory lick
                        }
                    });

                    readyForLoadingYoutubeThumbnail = true;
                }

                @Override
                public void onInitializationFailure(YouTubeThumbnailView youTubeThumbnailView, YouTubeInitializationResult youTubeInitializationResult) {
                    //do nohing.. ada error, tambahin method ini jalan, error-nya lupa...
                    readyForLoadingYoutubeThumbnail = true;
                }
            });
        }
        holder.mdelate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                deleteVideoAlertDialog(listvideo.get(holder.getAdapterPosition()).getId(), holder.getAdapterPosition());
            }
        });

    }

    @Override
    public int getItemCount() {
        // Log.v(VideoAdapter.class.getSimpleName(), "" + listvideo.size());
        return listvideo.size();
    }

    public class VideoHolder extends RecyclerView.ViewHolder {

        YouTubeThumbnailView youTubeThumbnailView;
        protected FrameLayout playButton;
        TextView murl, mdelate;
        ImageView loding;

        public VideoHolder(View itemView) {
            super(itemView);
            mdelate = itemView.findViewById(R.id.mdelate);
            murl = itemView.findViewById(R.id.murl);
            playButton = itemView.findViewById(R.id.btnYoutube_player);
            youTubeThumbnailView = itemView.findViewById(R.id.youtube_thumbnail);
            loding = itemView.findViewById(R.id.loding);

            playButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = getAdapterPosition();
                    String url = listvideo.get(position).getVideoUrl();
                    Toast.makeText(mContext, url, Toast.LENGTH_SHORT).show();
                    Intent intent = YouTubeStandalonePlayer.createVideoIntent((Activity) mContext,
                            KEY, url, 100, false, true);
                    mContext.startActivity(intent);
                }
            });
        }

    }

    private void deleteVideoAlertDialog(final int row_id, final int adapterPosition) {
        final AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

        // Setting Dialog Title
        alertDialog.setTitle("Delete");
        // Setting Dialog Message
        alertDialog.setMessage("Are you sure you want to delete this video");
        alertDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                if (SQLiteHelper.deleteUser(mContext, row_id)) {
                    listvideo.remove(adapterPosition);
                    notifyItemRemoved(adapterPosition);
                    notifyItemRangeChanged(adapterPosition, listvideo.size());
                } else {
                    Toast.makeText(mContext, "internal issue ", Toast.LENGTH_SHORT).show();
                }
            }
        });
        // Setting Negative "NO" Button
        alertDialog.setNegativeButton("No", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                // Write your code here to invoke NO event
                dialog.cancel();
            }
        });
        // Showing Alert Message
        alertDialog.show();

    }

    public boolean addNewVideo(String Url, Context context) {
        videoPojo.setVideoUrl(Url);
        SQLiteHelper sqLiteHelper = new SQLiteHelper(context);
        if (sqLiteHelper.addNewVideo(context, videoPojo)) {
            listvideo.add(videoPojo);
            notifyDataSetChanged();
            Toast.makeText(context, "video Saved", Toast.LENGTH_SHORT).show();
            return true;
        }
        return false;
    }
}
Share:
18,954

Related videos on Youtube

Stranger B.
Author by

Stranger B.

Currently I am a software engineer at Capgemini. I simply like coding and helping others is a fulfilling way to stay up to date and give something back to the community and the Web from which I learned so much.

Updated on June 27, 2022

Comments

  • Stranger B.
    Stranger B. about 2 years

    I'm trying to load Youtube video thumbnails in a RecyclerView. I'm facing some issues.

    Here is what I'm doing in my adapter:

    public static class ItemViewHolder extends RecyclerView.ViewHolder {
    
        private YouTubeThumbnailView thumb;
        public  Post                 post;
    
        public ItemViewHolder(View v) {
            thumb = (YouTubeThumbnailView) v.findViewById(R.id.youtube_thumbnail);
        }
    
        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
            if (holder instanceof ItemViewHolder) {
    
                ((ItemViewHolder) holder).thumb.initialize(YOUTUPEKEY, new YouTubeThumbnailView.OnInitializedListener() {
                    @Override
                    public void onInitializationSuccess(YouTubeThumbnailView youTubeThumbnailView, YouTubeThumbnailLoader youTubeThumbnailLoader) {
                        youTubeThumbnailLoader.setVideo(VIDEOID);
    
                    }
    
                    @Override
                    public void onInitializationFailure(YouTubeThumbnailView youTubeThumbnailView, YouTubeInitializationResult youTubeInitializationResult) {
    
                    }});
    }}}
    

    It works fine, but I don't I'm doing it right. When I'm using the same adapter in another activity I get this Error:

    Activity com.example.yasser.version6.Mespublications has leaked ServiceConnection com.google.android.youtube.player.internal.r$e@4252bcb8 that was originally bound here
    

    and it takes time to load thumbnails and sometimes it mix between them when swiping.

    I added a function to release all the Youtube Loaders:

    public void ReleaseLoaders() {
        for (YouTubeThumbnailLoader loader : loaders.values()) {
            loader.release();
        }
    }
    

    and I'm calling this function from the Activity Onstop() :

    @Override
    public void onStop() {
        super.onStop();
        mAdapter.ReleaseLoaders();
    }
    

    It worked fine for some time, but the crashes sometimes.

    • adjuremods
      adjuremods over 8 years
      have you checked this SO ticket? It seems to have the same issue you're encountering - the memory leak one, not the RecyclerView, seems you handled that already.
    • Stranger B.
      Stranger B. over 8 years
      The error comes from the Recylcerview adapter
    • random
      random over 8 years
      How do you initialise your adapter?
  • Stranger B.
    Stranger B. over 8 years
    Thanks for your response, But I have tried it and Thumbnails still mixing !
  • Harish_N
    Harish_N over 8 years
    I'am using it in my App. It's Working fine. Thumbnails will not get Mixed Up.
  • Arnold Balliu
    Arnold Balliu over 8 years
    Just made a good edit to my answer. I suggest you take a look.
  • Saurabh Singh
    Saurabh Singh over 6 years
    Thanks a lot dude..!!