Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

YouTubePlayerSupportFragment in ListView item is null

I'm trying to use a YouTubePlayerSupportFragment in a ListView item. The ListView is inside a Fragment that's in an Activity. Since there are nested fragments I'm using the getChildFragmentManager() method to try to find the fragment from the XML layout. Here is the code.

Java

convertView = inflater.inflate(R.layout.youtube_post_layout, null);
YouTubePlayerSupportFragment youTubePlayerFragment = (YouTubePlayerSupportFragment) getChildFragmentManager().findFragmentById(R.id.youtube_video);
if (youTubePlayerFragment == null) {
    Log.i("YouTube", "player is null");
} else {
    Log.i("YouTube", youTubePlayerFragment.toString());
}

XML

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

    <include layout="@layout/post_base_layout" />

    <fragment
        android:name="com.google.android.youtube.player.YouTubePlayerSupportFragment"
        android:id="@+id/youtube_video"
        android:layout_width="match_parent"
        android:layout_height="240dp"
        android:layout_marginLeft="62dp"
        android:layout_marginRight="32dp"
        android:background="@color/light_grey"
        android:cropToPadding="true"
        android:padding="1dp"
        android:scaleType="centerCrop" />

    <include layout="@layout/post_bottom_layout" />

</LinearLayout>

The problem is that when I try to create the fragment by doing findFragmentById() to get it from the XML, it returns null, and that's what is posted to the stack trace.

I'm trying to follow the YouTube API samples and make the changes needed for nested fragments but I can't find out what's causing the issue.

like image 870
Wenger Avatar asked Oct 04 '22 12:10

Wenger


2 Answers

There are problems with the whole approach you have taken.

First, inflating child fragments directly from an XML is not supported. When nesting fragments, you must add them dinamically using getChildFragmentManager in the parent fragment.

Second, even if it worked (and it might in some cases), you have inflated the view but not added it to the actual fragment's layout. That's what the second and third parameters in the inflate method are there for. The FragmentManager will look for fragment in its current layout, or event in the backstack, but can't find fragments inflated in a random XML and never put to use.

Finally, and most important, you should never add fragments as items in an ListView. Fragments expect to have a lifecycle tied to the Activity they are in, while a ListView, or any AdapterView in general, will create, destroy and recycle its children as needed.

Possible solutions:

  • How many videos do you need on your list? If there are not many of them, a ScrollView+LinearLayout might actually be the easiest solution.

  • If you really need View recycling, maybe you can do without live video in every row? Consider using a YouTubeThumbnailView in your rows, then redirecting your user to another activity / loading a the video in a single YoutubeFragment located outside of the list on your layout.

  • If you definitely want videos playing directly on every row in the list, you will need to use YoutubePlayerView instead of fragments on your row layout. In this case, read the documentation carefully, because you will need to handle the video initialization and view recycling yourself.

like image 85
ivagarz Avatar answered Oct 10 '22 01:10

ivagarz


your app will crash When you try to inflate view with same Fragment every ListView item (As duplication in fragment id like this quiz)

There Two solution (one of them from @ivagarz answer) :

1- You can display only one youtube video Using YouTubePlayerView above your ListView into your Activity layout But your Activity Must extends YouTubeBaseActivity Like YouTube Android Player API Doc (I Think this Solution will not help you to solve your problem)

2- you can use YouTubeThumbnailView to Display youtube Thumbnail Image of Video for Every Item into ListView with youtube player button over youtube Thumbnail Image Used to Display Video.

Like The next Picture :

enter image description here

When User click on youtube player button, You Have two ways:

First option using YouTubePlayerSupportFragment : you can replacing YouTubeThumbnailView with YouTubePlayerSupportFragment If you take This Option you must :

A- add YouTubePlayerSupportFragment programmatically on the Fly (to avoid duplication in Fragment Id).

B- Display Only one Video into your ListView As answer of this Question.

Ex:

list_item.xml layout for ListView item:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/parent_relativeLayout"
   android:layout_width="match_parent"
   android:layout_height="match_parent" >

    <com.google.android.youtube.player.YouTubeThumbnailView
            android:id="@+id/youtube_thumbnail"
            android:layout_width="fill_parent"
            android:layout_height="250dp"
            android:scaleType="centerCrop"
            android:visibility="gone"/>

    <RelativeLayout android:id="@+id/relativeLayout_over_youtube_thumbnail"
        android:layout_width="fill_parent"
        android:layout_height="250dp"
        android:background="@color/color_background_transparent"
        android:visibility="gone">

        <ImageView android:id="@+id/btnYoutube_player"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="center"
            android:src="@drawable/youtube_player"/>

    </RelativeLayout>

</RelativeLayout>

in getView mehtod of ListView adapter:

  public View getView(int position, View convertView, ViewGroup parent) {

    convertView = inflator.inflate(R.layout.list_item.xml, parent, false);
    final RelativeLayout relativeLayoutOverYouTubeThumbnailView = (RelativeLayout) convertView.findViewById(R.id.relativeLayout_over_youtube_thumbnail);
    final YouTubeThumbnailView youTubeThumbnailView = (YouTubeThumbnailView) convertView.findViewById(R.id.youtube_thumbnail);

    // get parent relative layout
    RelativeLayout parentRelativeLayout = (RelativeLayout) convertView.findViewById(R.id.parent_relativeLayout);
    // then create dynamic FrameLayout
    FrameLayout.LayoutParams dynamicFrameLayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
    final FrameLayout dynamicFrameLayout = new FrameLayout(ctx);
        dynamicFrameLayout.setId(Different_ID);
        dynamicFrameLayout.setLayoutParams(dynamicFrameLayoutParams);
    // then add dynamic FrameLayout as children in parent relative layout
    parentRelativeLayout.addView(dynamicFrameLayout);

    ImageView youtubePlayerButton = (ImageView) convertView.findViewById(R.id.btnYoutube_player);
    youtubePlayerButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {

            View parentView = (View) v.getParent().getParent();
            YouTubeThumbnailView youTubeThumbnailView = (YouTubeThumbnailView) parentView.findViewById(R.id.youtube_thumbnail);
            RelativeLayout relativeLayoutOverYouTubeThumbnailView = (RelativeLayout) parentView.findViewById(R.id.relativeLayout_over_youtube_thumbnail);
            youTubeThumbnailView.setVisibility(View.GONE);
            relativeLayoutOverYouTubeThumbnailView.setVisibility(View.GONE);
            YouTubeFragment youTubeFragment = new YouTubeFragment();
                youTubeFragment.youTubeFragmentInitialize(VideoID, youTubeFragment, parentView);

            getSupportFragmentManager()
               .beginTransaction()
                    .replace(dynamicFrameLayout.getId(), youTubeFragment)
                    .commit();

        }
    });

    final YouTubeThumbnailLoader.OnThumbnailLoadedListener  onThumbnailLoadedListener = new YouTubeThumbnailLoader.OnThumbnailLoadedListener(){
        @Override
        public void onThumbnailError(YouTubeThumbnailView youTubeThumbnailView, YouTubeThumbnailLoader.ErrorReason errorReason) {

        }

        @Override
        public void onThumbnailLoaded(YouTubeThumbnailView youTubeThumbnailView, String s) {
            loadingImage.setVisibility(View.GONE);
            youTubeThumbnailView.setVisibility(View.VISIBLE);
            relativeLayoutOverYouTubeThumbnailView.setVisibility(View.VISIBLE);
        }
    };

    youTubeThumbnailView.initialize(API_KEY, new YouTubeThumbnailView.OnInitializedListener() {
            @Override
            public void onInitializationSuccess(YouTubeThumbnailView youTubeThumbnailView, YouTubeThumbnailLoader youTubeThumbnailLoader) {

                youTubeThumbnailLoader.setVideo(VideoID);
                youTubeThumbnailLoader.setOnThumbnailLoadedListener(onThumbnailLoadedListener);
            }

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

            }
        });
}

I was created my own Fragment extends from YouTubePlayerSupportFragment :

public class YouTubeFragment  extends YouTubePlayerSupportFragment {

    public void youTubeFragmentInitialize(final String videoId, final YouTubeFragment fragment, final View parent) {

        fragment.initialize(apiKey, new YouTubePlayer.OnInitializedListener() {
            @Override
            public void onInitializationSuccess(YouTubePlayer.Provider provider, final YouTubePlayer youTubePlayer, boolean wasRestored) {

           youTubePlayer.setShowFullscreenButton(false);
                if (!wasRestored) {
                    youTubePlayer.loadVideo(videoId);
                }
            }

            @Override
            public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult youTubeInitializationResult) {

                Log.e("app", youTubeInitializationResult.toString());
            }
        });
    }
}

Second option using YouTubeStandalonePlayer or creating your Own Activity extends YouTubeBaseActivity : Just when User click on youtube player button, you can create an Intent to open new activity to display Video

Intent intent = YouTubeStandalonePlayer.createVideoIntent(context, YOUR_DEVELOPER_KEY, VIDEO_ID);
 startActivity(intent); 
like image 36
ahmed hamdy Avatar answered Oct 10 '22 02:10

ahmed hamdy