Search code examples
androidandroid-fragmentshorizontalscrollviewandroid-youtube-api

Multiple YouTubePlayerFragments inside a Horizontal ScrollView - all the players display the last loaded video


I want to show multiple YouTube videos in a Horizontal ScrollView. For that, I'm adding YouTubePlayerFragments dynamically. Here's the piece of my code:

 //set videos

    int count = 0;
    FragmentTransaction transaction = getChildFragmentManager().beginTransaction();

    for (final Video video : mVideos) {

        //create an instance of a video fragment
        final VideoFragment fragment = new VideoFragment();// VideoFragment extends YouTubePlayerFragment
        Bundle bundle = new Bundle();
        if (null != mVideos && 0 < mVideos.size()) {
            bundle.putString(VideoFragment.KEY_VIDEO_KEY, video.getKey());
        }
        fragment.setArguments(bundle);

        FrameLayout frameLayout = new FrameLayout(getActivity());
        frameLayout.setLayoutParams(new FrameLayout.LayoutParams(900, 500));
        frameLayout.setPadding(10, 0, 10, 0);
        frameLayout.setId(12345 + ++count);
        mLayoutTrailers.addView(frameLayout);//mLayoutTrailers is a LinearLayout inside my Horizontal ScrollView

        transaction.add(frameLayout.getId(), fragment, VideoFragment.TAG + count);
    }

    transaction.commit();

I'm initializing the youtube player inside OnResume of my VideoFragment. And below is how the videos gets loaded.

enter image description here

It shows the lastly loaded video for all the players. I debugged - It initializes all the players with the first video, then all the players with the second video & so on until the last one.

The issue is already discussed here. There seems to be a work around with initializing the player inside "setUserVisibleHint(boolean isVisibleToUser)" in my VideoFragment in stead of OnResume(), but I don't know how to use that with my HorizontalScrollView

Is there a way I can load multiple YouTube videos in a Horizontal ScrollView?

Any sort of help will be appreciated!


Solution

  • I dont think it is possible to create multiple instance of YouTube Player Fragment as it is Singleton.

    No matter how many instances you make they all gone be same and your horizontal scrollview will show thumbnail of last VideoFragment you added .

    Alternatively you can give an illusion of multiple YouTube Fragment by showing video thumb and on click of thumb play the video with the help of youtube player

          // get thumbnail image of that video by videoID
          String videoThumbUrl="http://img.youtube.com/vi/"+videoId+"/0.jpg";
    
          //Load thumb in viewpager/Hrizontal scrollview/Recycle View with the    
          //help of Picasso/Glide/UIL etc     
          Picasso.with(getActivity())
                        .load(videoThumbUrl) 
                        .into(videoThumbImageView);
    

    And then on click of thumb you can add youtube fragment in View container holding imageview and start playing video with the video id

    videoThumbImageView.setOnClickListenr(..)
    {...
    
     //Add Youtube fragment in container and start playing video
    }
    

    This is just a pseudo code but you get and idea.

    Edit

    Added code for how to play and stop videos on tap of thumbnails.

    I was offline so was unable to use YouTube fragment so I used Videoview to play random videos you can replace videoview with YouTube fragment and. Guranteed it still gone work great

    enter image description here enter image description here

    Array list to hold list of videos

        public static ArrayList<VideoModel> listOfVideos = new ArrayList<>();
    

    Variable to keep track of current playing video

          // Allows to remember the last item shown on screen
            private int lastPosition = 0;
    

    Recycler view item click listener

       videoGridAdapter.setOnCardClickListner(new VideoGridAdapter.OnCardClickListner() {
                @Override
                public void OnCardClicked(View view, int position) {
    
                    //Make Lats Tapped item playing false
                    listOfVideos.get(lastPosition).setPlaying(false);
    
                    //Make current Tapped item playing true
                    listOfVideos.get(position).setPlaying(true);
    
                    // update pointer of last tapped video to current tapped video
                    lastPosition = position;
    
                    //LET RCYCLERVIEW ADAPTER DO THE MAGIC
                    videoGridAdapter.notifyDataSetChanged();
                }
            });
    

    VideoModel class

            **
     * Created by Hitesh on 20-07-2016.
     */
    public class VideoModel {
    
        public int getImageURLTest() {
            return imageURLTest;
        }
    
        //Test Data
        private int imageURLTest;
    
        //Real Time Data
        private String videoURL;
        private String imageURL;
        private boolean isPlaying;
    
        public VideoModel(String videoURL, boolean isSelected, int imageURLTest) {
            this.videoURL = videoURL;
            this.isPlaying = isSelected;
            this.imageURLTest = imageURLTest;
        }
    
        public boolean isPlaying() {
            return isPlaying;
        }
    
        public void setPlaying(boolean playing) {
            isPlaying = playing;
        }
    
        /**
         * @return Youtube URl of Video
         */
        public String getVideoURL() {
            return videoURL;
        }
    
        /**
         * @return Thumbnail of Video using video ID
         */
        public String getImageURL() {
            //Derive Image URl from Video URl using Video ID
            return imageURL;
        }
    }
    

    Adpter itself

    import android.content.Context;
    import android.media.MediaPlayer;
    import android.net.Uri;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    
    import com.hiteshsahu.videoinraw.R;
    import com.hiteshsahu.videoinraw.ui.activity.VideoGridActivity;
    import com.squareup.picasso.Callback;
    import com.squareup.picasso.NetworkPolicy;
    import com.squareup.picasso.Picasso;
    
    public class VideoGridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
        private Context context;
    
        private OnCardClickListner onCardClickListner;
    
        public VideoGridAdapter() {
    
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                          int viewType) {
            RecyclerView.ViewHolder viewHolder;
    
            context = parent.getContext();
    
            // create a new view
            View v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.video_row, parent, false);
    
            // set the view's size, margins, paddings and layout parameters
            viewHolder = new VideoViewHolder(v);
    
            return viewHolder;
        }
    
        // Replace the contents of a view (invoked by the layout manager)
        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
            VideoViewHolder videoHolder = (VideoViewHolder) holder;
    
            if (VideoGridActivity.listOfVideos.get(position).isPlaying()) {
    
                //Playing Video :- Show VideoView and make play button GONE
                ((VideoViewHolder) holder).getVideoView().setVisibility(View.VISIBLE);
                ((VideoViewHolder) holder).getPlayButton().setVisibility(View.GONE);
    
                //Play Video in video View
                ((VideoViewHolder) holder).getVideoView().setVisibility(View.VISIBLE);
                ((VideoViewHolder) holder).getVideoView().setVideoURI(Uri.parse(VideoGridActivity.listOfVideos.get(position).getVideoURL()));
                // MediaControlandroid.widget.VideoView videoView = (VideoView) headerView.findViewById(R.id.vdo_banner);ler md = new MediaController(getActivity());
                ((VideoViewHolder) holder).getVideoView().setMediaController(null);
                ((VideoViewHolder) holder).getVideoView().requestFocus();
                // videoView.setZOrderOnTop(true);
                ((VideoViewHolder) holder).getVideoView().start();
                ((VideoViewHolder) holder).getVideoView().setOnTouchListener(new View.OnTouchListener() {
    
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        // TODO Auto-generated method stub
                        return true;
                    }
                });
    
                ((VideoViewHolder) holder).getVideoView().setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    
                    @Override
                    public void onPrepared(MediaPlayer mp) {
                        // TODO Auto-generated method stub
                        mp.setLooping(true);
                    }
                });
    
            } else {
    
                //Not playing make videoview gone and show play button
                ((VideoViewHolder) holder).getVideoView().setVisibility(View.GONE);
                ((VideoViewHolder) holder).getPlayButton().setVisibility(View.VISIBLE);
    
            }
    
            //set thumbnail image using video url
            Picasso.with(context)
                    .load(VideoGridActivity.listOfVideos.get(position).getImageURL()).fit().placeholder(VideoGridActivity.listOfVideos.get(position).getImageURLTest())
                    .networkPolicy(NetworkPolicy.OFFLINE).fit()
                    .centerCrop()
                    .into(((VideoViewHolder) holder).getVideoThumb(), new Callback() {
                        @Override
                        public void onSuccess() {
                        }
    
                        @Override
                        public void onError() {
                            // Try again online if cache failed
                            Picasso.with(context)
                                    .load(VideoGridActivity.listOfVideos.get(position).getImageURL()).fit().placeholder(VideoGridActivity.listOfVideos.get(position).getImageURLTest())
                                    .centerCrop()
                                    .into(((VideoViewHolder) holder).getVideoThumb());
                        }
                    });
    
    
            ((VideoViewHolder) holder).getContainer().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onCardClickListner.OnCardClicked(v, position);
    
    
                }
            });
    
        }
    
        @Override
        public int getItemCount() {
            return VideoGridActivity.listOfVideos.size();
        }
    
        public void setOnCardClickListner(OnCardClickListner onCardClickListner) {
            this.onCardClickListner = onCardClickListner;
        }
    
        public interface OnCardClickListner {
            void OnCardClicked(View view, int position);
        }
    
    }
    

    Layout activity main

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:context=".ui.activity.VideoGridActivity">
    
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">
    
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />
    
        </android.support.design.widget.AppBarLayout>
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/video_grid"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:foregroundGravity="center"
                app:layout_behavior="@string/appbar_scrolling_view_behavior"></android.support.v7.widget.RecyclerView>
        </RelativeLayout>
    
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            android:src="@android:drawable/ic_dialog_dialer" />
    
    </android.support.design.widget.CoordinatorLayout>
    

    Recyclerview item

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/root_container"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_margin="5dp">
    
    
        <ImageView
            android:id="@+id/video_thumb"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY" />
    
    
        <VideoView
            android:id="@+id/video_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:visibility="gone" />
    
        <!--Add a mask over video thumbnail with a play button icon-->
        <RelativeLayout
            android:id="@+id/play_button"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#68472986">
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:layout_gravity="center"
                android:src="@android:drawable/ic_media_play" />
        </RelativeLayout>
    
    </FrameLayout>