This page is being accessed at exactly: 2023-03-31T02:32:58+00:0003/31/2023 TextureView: How to create a full-screen video background in Android applications - [RaHa] Programming
[RaHa] programming
  • Home
  • Services
  • About Us
  • Contact

tutorials.

TextureView: How to create a full-screen video background in Android applications

February 18, 2021

Video Cropping

In this part of Android SurfaceView story we are going to create application which will do the following:

  • display video from assets folder using TextureView.
  • display a full screen background video without distorting the video size

This tutorial is derived from the creation of our latest app, TrackBack - available on the play store soon. Some XML names and items have been changed slightly to suite this tutorial. We used royalty free video and a basic video editor to adjust the video size and combine clips.

Final Results:

Step 1 - Preparing

Create Android project and target Android version 4.0. Make sure you have following lines in your AndroidManifest.xml file.

<uses-sdk
    android:minSdkVersion="14"
    android:targetSdkVersion="14"/>

Step 2 - XML

Copy a video file to your assets folder at res/raw. If raw doesn't exist, make it.

In your values folder create dimen.xml file and add following lines.

<!-- common settings -->
<dimen name="padding_left_right">23dp</dimen>
<dimen name="margin_left_right">23dp</dimen>
<dimen name="margin_left_right_large">50dp</dimen>
<dimen name="margin_top_bottom">30dp</dimen>
<dimen name="margin_top_bottom_lg">90dp</dimen>
<dimen name="margin_btn_lg">50dp</dimen>
<dimen name="text_btn_lg">21dp</dimen>
<dimen name="margin_top_bottom_alt">45dp</dimen>
<dimen name="heading_lg">35sp</dimen>
<dimen name="heading_md">21sp</dimen>

In your values folder create string.xml file and add following lines - adjusting appropriately.

<!-- app required strings -->
<string name="app_name">TrackBack</string>
<!-- Strings related to login -->
<string name="welcome_login">Do you have an account? Sign in</string>
<string name="welcome_msg">Keep track of everyone and everything you love... in real time!</string>

In your layout folder create activity_video_crop.xml file or any appropriate name for this in your app.. and add following lines:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/welcome_constraint"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Activity_Welcome">

    <FrameLayout
        android:id="@+id/welcome_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <TextureView
        android:id="@+id/videoview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"

        >
    </TextureView>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimarySeeThrough"
        >
    <ImageView
        android:layout_width="55dp"
        android:layout_height="55dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="@dimen/margin_top_bottom_lg"
        android:id="@+id/welcome_logo"
        android:src="@drawable/logo_white"
        />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/margin_left_right"
            android:id="@+id/welcome_text"
          app:layout_constraintTop_toBottomOf="@+id/welcome_logo"
            android:text="@string/app_name"
            android:textSize="@dimen/heading_lg"
            android:textColor="@color/white"
            android:textStyle="bold"
            android:textAlignment="center"
            />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="90dp"
            android:layout_marginRight="@dimen/margin_left_right"
            android:layout_marginLeft="@dimen/margin_left_right"
            android:id="@+id/welcome_note_2"
          app:layout_constraintBottom_toBottomOf="@+id/btn_start"
            android:text="@string/welcome_msg"
            android:textSize="@dimen/heading_md"
            android:textColor="@color/white"
            android:backgroundTint="@color/white"
            android:textAlignment="center"
            />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
           android:background="@drawable/rounded_top_bottom_blue"
            android:text=" Continue "
            android:textSize="@dimen/text_btn_lg"
            android:textAlignment="center"
            android:gravity="center"
            android:foregroundGravity="center"
            android:textColor="@color/slight_white"
     app:layout_constraintBottom_toBottomOf="@id/welcome_sign_in"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
       android:layout_marginBottom="@dimen/margin_top_bottom_alt"
            android:layout_marginLeft="@dimen/margin_btn_lg"
            android:layout_marginRight="@dimen/margin_btn_lg"
            android:id="@+id/btn_start"
            />
        <TextView
            android:id="@+id/welcome_sign_in"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            android:textSize="20dp"
            android:textColor="@color/slight_white"
            android:text="@string/welcome_login"
        android:layout_marginBottom="@dimen/margin_top_bottom_lg"
            />
    </androidx.constraintlayout.widget.ConstraintLayout>
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Note: All that's required here is the FrameLayout and the TextureView. I prefer using ConstraintLayout as the root to easily position items.

Step 3 - Basic code

Create a new activity class and call it ActivityCrop or something appropriate. Don’t forget to declare it inside AndroidManifest.xml file.

Imports:

package com.rahaprogramming.trackback;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.widget.FrameLayout;
import androidx.appcompat.app.AppCompatActivity;
public class Activity_Welcome extends AppCompatActivity implements
        TextureView.SurfaceTextureListener, MediaPlayer.OnCompletionListener {
    //declare class variables
    Context context = this;
    Uri video_uri;
    
    // MediaPlayer instance
    private MediaPlayer mMediaPlayer;
    //views
    private TextureView mTextureView;
    FrameLayout frameLayout;
    Surface surface;
    // Original video size - we created this video and knew the size.
    // for unknown size - use meta data
    private float mVideoWidth = 1080;
    private float mVideoHeight = 1440;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);
        //Toolbar toolbar = findViewById(R.id.toolbar);
        //setSupportActionBar(toolbar);
        video_uri = Uri.parse("android.resource://"+getPackageName()+"/"+R.raw.trackback);

        //set views
        mTextureView = findViewById(R.id.videoview);
        mTextureView.setClickable(false);
        frameLayout = findViewById(R.id.welcome_frame);

        //init MediaPlayer
        mMediaPlayer = new MediaPlayer();

        //set implemented listeners
        mMediaPlayer.setOnCompletionListener(this);
        mTextureView.setSurfaceTextureListener(this);
    }
    //plays the video
    private void playVideo() {
        //its a big file - use separate thread
        new Thread(new Runnable() {
            public void run() {
                try {
                    mMediaPlayer.setDataSource(context,video_uri);
                    mMediaPlayer.setLooping(true);
                    mMediaPlayer.prepareAsync();
                    // Play video when the media source is ready for playback.
                    mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                        @Override
                        public void onPrepared(MediaPlayer mediaPlayer) {
                            mediaPlayer.start();
                        }
                    });
                } catch (Exception e) { // I can split the exceptions to get which error i need.
                    Utils.log("Error: "+e.toString());
                    e.printStackTrace();
                }
            }
        }).start();
    }
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
        //set surface
        surface = new Surface(surfaceTexture);
        mMediaPlayer.setSurface(surface);
        //update viewable area
        updateTextureViewSize(width, height);
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
        //set surface
        surface = new Surface(surfaceTexture);
        mMediaPlayer.setSurface(surface);
        //update viewable area
        updateTextureViewSize(width, height);
    }

    //uses the view width to determine best crop to fit the screen
    //@param int viewWidth width of viewport
    //@param int viewHeight height of viewport
    private void updateTextureViewSize(int viewWidth, int viewHeight) {
        float scaleX = 1.0f;
        float scaleY = 1.0f;

        Utils.log(viewWidth+" "+viewHeight+" "+mVideoHeight+" "+mVideoWidth);
        if (mVideoWidth > viewWidth && mVideoHeight > viewHeight) {
            scaleX = mVideoWidth / viewWidth;
            scaleY = mVideoHeight / viewHeight;
        } else if (mVideoWidth < viewWidth && mVideoHeight < viewHeight) {
            scaleY = viewWidth / mVideoWidth;
            scaleX = viewHeight / mVideoHeight;
        } else if (viewWidth > mVideoWidth) {
            scaleY = (viewWidth / mVideoWidth) / (viewHeight / mVideoHeight);
        } else if (viewHeight > mVideoHeight) {
            scaleX = (viewHeight / mVideoHeight) / (viewWidth / mVideoWidth);
        }

        // Calculate pivot points, in our case crop from center
        int pivotPointX = viewWidth / 2;
        int pivotPointY = viewHeight / 2;

        Matrix matrix = new Matrix();
        matrix.setScale(scaleX, scaleY, pivotPointX, pivotPointY);
        //transform the video viewing size
        mTextureView.setTransform(matrix);
        //set the width and height of playing view
        mTextureView.setLayoutParams(new FrameLayout.LayoutParams(viewWidth, viewHeight));
        //finally, play the video
        playVideo();
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }

    // callback when the video is over
    public void onCompletion(MediaPlayer mp) {
        //if this happens.. never will.. just restart the video
        mp.stop();
        mp.release();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mMediaPlayer != null) {
            // Make sure we stop video and release resources when activity is destroyed.
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }
}

Step 5 - Video cropping

The resizing is done by the updateTextureViewSize method. First we need to calculate scaleX and scaleY factor and set it to Matrix object using method setScale(..). Next pass this matrix to TextureView by setTransform(..) method and you are done.

//uses the view width to determine best crop to fit the screen
    //@param int viewWidth width of viewport
    //@param int viewHeight height of viewport
    private void updateTextureViewSize(int viewWidth, int viewHeight) {
        float scaleX = 1.0f;
        float scaleY = 1.0f;

        Utils.log(viewWidth+" "+viewHeight+" "+mVideoHeight+" "+mVideoWidth);
        if (mVideoWidth > viewWidth && mVideoHeight > viewHeight) {
            scaleX = mVideoWidth / viewWidth;
            scaleY = mVideoHeight / viewHeight;
        } else if (mVideoWidth < viewWidth && mVideoHeight < viewHeight) {
            scaleY = viewWidth / mVideoWidth;
            scaleX = viewHeight / mVideoHeight;
        } else if (viewWidth > mVideoWidth) {
            scaleY = (viewWidth / mVideoWidth) / (viewHeight / mVideoHeight);
        } else if (viewHeight > mVideoHeight) {
            scaleX = (viewHeight / mVideoHeight) / (viewWidth / mVideoWidth);
        }

        // Calculate pivot points, in our case crop from center
        int pivotPointX = viewWidth / 2;
        int pivotPointY = viewHeight / 2;

        Matrix matrix = new Matrix();
        matrix.setScale(scaleX, scaleY, pivotPointX, pivotPointY);
        //transform the video viewing size
        mTextureView.setTransform(matrix);
        //set the width and height of playing view
        mTextureView.setLayoutParams(new FrameLayout.LayoutParams(viewWidth, viewHeight));
        //finally, play the video
        playVideo();
    }

Step 6 - Launch

When you launch application, you should notice that video is cropped correctly and displayed properly now. Of course, when width to height ratio is too big, video looses it quality as is scaled too much - as in:
ImageView.setScaleType(ImageVIew.ScaleType.CENTER_CROP);

  • More News

    • How to Set Up a Signature in RoundCube Webmail
    • How to add custom email signatures on iPhone and iPad
    • Learning the blockchain by coding your own blockchain in GO!
    • How to build a cryptomining rig: Bitcoin mining
    • How to use CheckBox in Android
  • Comments

    1. more info says:
      September 30, 2021 at 9:52 pm

      Great blog you have got here..
      It’s difficult to find quality writing
      like yours nowadays. I really appreciate people like you!

      Take care!!

      Reply
    2. Francesca Russ says:
      October 5, 2021 at 1:23 am

      I have read a few of the articles on your blog site now, and I truly like your updating style.

      Reply
    3. Kara says:
      October 5, 2021 at 11:33 am

      What a fantastic written post. It’ s so useful …

      Reply
    4. One Nine Elms website says:
      October 5, 2021 at 4:57 pm

      excellent as well as fantastic blog. I actually wish to thank you, for giving us far better details.

      Reply
    5. Lucie says:
      October 6, 2021 at 12:04 am

      Having read this I believed it was very enlightening.
      I appreciate you finding the time and energy to put this
      short article together.
      I once again find myself spending a lot of time
      both reading and commenting.
      But so what, it was still worthwhile!

      Reply
    6. Magda says:
      October 7, 2021 at 4:48 am

      fantastic and outstanding blog. I actually intend to thanks, for offering us
      far better details.

      Reply
    7. Adriana says:
      January 26, 2022 at 3:28 am

      I like what you guys tend to be up too. Such clever work and coverage!
      Keep up the good works guys I’ve added you
      guys to my personal blogroll.

      Reply
    8. Ina Arteaga says:
      March 5, 2022 at 12:54 am

      It’s really a nice and useful piece of information. I am happy that you shared this useful
      info with us. Please keep us informed like this.
      Thank you for sharing.

      Reply

    Leave a Reply Cancel reply

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

    Setting the standard

    Community

    • Contact Us
    • Stackoverflow
    • Salvation Repair

    About Us

    • Our Story
    • Services
    • FAQs

    Company

    • About Us
    • Contact

    Have a Questions?

    • 228-304-9202
    • admin@rahaprogramming.com

    Copyright © All rights reserved