android – VideoView onPrepared is not called if the VideoView is invisible-ThrowExceptions

Exception or error:

I am hiding the VideoView initially and when the video is loaded I am showing the VideoView. But onPrepared is never called if the VideoView is invisible initially. However onPrepared is called properly if VideoView is visible. Is there any way to hide the videoView until video is loaded. Any help would be appreciated. Thanks!

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >


<VideoView
        android:id="@+id/video"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:visibility="invisible" />

</RelativeLayout>

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        VideoView videoView = (VideoView) findViewById(R.id.video);
        Uri videoUri = Uri.parse(url);
        videoView.setVideoURI(videoUri);
        videoView.requestFocus();

        videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

            @Override
            public void onPrepared(MediaPlayer mp) {
                Toast.makeText(mActivity, "on prepared", Toast.LENGTH_SHORT).show();


                 videoView.setVisibility(View.VISIBLE);


            }
        });


}
How to solve:

You could try setting alpha channel of video view to 0 or close to 0.

###

Solved it making its layout invisible instead of videoView itself.
Thanks to @J.Kowalski .

Layout:

<FrameLayout
    android:id="@+id/layout_video_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <VideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="false"
        android:scrollbars="none"/>
</FrameLayout>

<ProgressBar
    android:id="@+id/progress"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:visibility="gone"
    />

Set OnPreparedListener:

videoView.setOnPreparedListener(this);

Show progress bar before ready:

@Override
public void showProgress() {
    progress.setVisibility(View.VISIBLE);
    layoutVideoView.setVisibility(View.INVISIBLE);
}

Load video URI:

@Override
public void loadVideo(Uri uri) {
    videoView.setVideoURI(uri);
}

When its ready, onPrepared is called:

@Override
public void onPrepared(MediaPlayer mp) {
    Log.d("debug", "onPrepared");
    iStepPreviewPresenter.onVideoViewReady();
}

Finally show layout and start video:

@Override
public void hideProgress() {
    progress.setVisibility(View.INVISIBLE);
    layoutVideoView.setVisibility(View.VISIBLE);
}

@Override
public void startVideo() {
    videoView.start();
}

Nailed it!

###

It can be achieved by one of these tricks

  1. You can set the height and width of the VideoView to 1px until the video is prepared and then switch to full screen by changing the properties to match parent

  2. As @FDIM said in his answer, we can set the alpha value of the view to 0 until video is prepared. But it works only if we use TextureView to load the video. It doesn’t work with normal VideoView. Here is a custom VideoView extending TextureView which mimics the default VideoVideo implementation. This class can be directly used in xml file and can set the alpha to 0 to hide the video.

    public class TextureVideoView extends TextureView  implements SurfaceTextureListener                {
    
    private MediaPlayer mMediaPlayer;
    
    public TextureVideoView(Context context) {
        super(context);
        init();
    }
    
    public TextureVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    
    private void init() {
        mMediaPlayer = new MediaPlayer();
        this.setSurfaceTextureListener(this);
    }
    
    public void seekTo(int msec) {
        mMediaPlayer.seekTo(msec);
    }
    
    public void setVideoPath(final String path) throws IOException {
        mMediaPlayer.reset();
        mMediaPlayer.setDataSource(path);
        mMediaPlayer.prepare();
    }
    
    public void setVideoPath(FileDescriptor fd, long offset, long length) throws IOException {
        mMediaPlayer.reset();
        mMediaPlayer.setDataSource(fd, offset, length);
        mMediaPlayer.prepare();
    }
    
    public void setVideoURI(Context context, Uri uri) throws IOException {
        mMediaPlayer.reset();
        mMediaPlayer.setDataSource(context, uri);
    }
    
    public void setOnPreparedListener(OnPreparedListener onPreparedListener) {
        mMediaPlayer.setOnPreparedListener(onPreparedListener);
    }
    
    public void setOnCompletionListener(OnCompletionListener onCompletionListener) {
        mMediaPlayer.setOnCompletionListener(onCompletionListener);
    }
    
    public void setOnErrorListener(OnErrorListener onErrorListener) {
        mMediaPlayer.setOnErrorListener(onErrorListener);
    }
    
    public void start() {
        mMediaPlayer.start();
    }
    
    public void pause() {
        mMediaPlayer.pause();
    }
    
    public void setVolume(float leftVolume, float rightVolume ) {
        mMediaPlayer.setVolume(leftVolume, rightVolume);
    }
    
    public boolean isPlaying() {
        return mMediaPlayer.isPlaying();
    }
    
    public void stopPlayback() {
        mMediaPlayer.stop();
    }
    
    public void reset() {
        mMediaPlayer.reset();
    }
    
    public void release() {
        mMediaPlayer.release();
    }
    
    
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
            int height) {
    
        mMediaPlayer.setSurface(new Surface(surface));
    }
    
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }
    
    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
            int height) {
    
    }
    
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    
    }
    

###

An easier workaround:

Just add a dummy parentview for the videoview and set the visiblity for that parentview instead of changing the visibility of the videoview itself.

Leave a Reply

Your email address will not be published. Required fields are marked *