Skip to content

Instantly share code, notes, and snippets.

@shubhubhosale
Last active June 18, 2022 12:05
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save shubhubhosale/9ef8f34a00ee41835d909c1a023b3f7a to your computer and use it in GitHub Desktop.
Save shubhubhosale/9ef8f34a00ee41835d909c1a023b3f7a to your computer and use it in GitHub Desktop.
Android AutoPlay Videos in RecyclerView like Instagram using SimpleVideoView and
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context="pune.university.engineering.gifutilstest.VideoListActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="368dp"
android:layout_height="495dp"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="8dp" />
</LinearLayout>
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "27.0.3"
defaultConfig {
applicationId "pune.university.engineering.gifutilstest"
minSdkVersion 17
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:recyclerview-v7:26.0.0-alpha1'
compile 'com.jakewharton:butterknife:5.1.1'
compile 'com.squareup.picasso:picasso:2.3.2'
compile 'com.klinkerapps:simple_videoview:1.2.4'
compile 'com.danikula:videocache:2.7.0'
testCompile 'junit:junit:4.12'
}
package pune.university.engineering.gifutilstest;
import android.content.Context;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.VideoView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.klinker.android.simple_videoview.SimpleVideoView;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* Created by Altair on 5/27/2018.
*/
public class MyVideoRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<VideoModel> arrayList = new ArrayList<>();
private Context context;
public MyVideoRecyclerViewAdapter(ArrayList<VideoModel> arrayList, Context context) {
this.arrayList = arrayList;
this.context = context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layout = 0;
RecyclerView.ViewHolder viewHolder;
layout = R.layout.row_layout_video;
View memeView = LayoutInflater
.from(parent.getContext())
.inflate(layout, parent, false);
viewHolder = new MyViewHolder(memeView);
return viewHolder;
}
private static final String TAG = "MyVideoRecyclerViewAdap";
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder: " + arrayList.get(position));
VideoModel videoModel = (VideoModel) arrayList.get(position);
Double ratio = videoModel.getVideo_width_to_height_ratio();
int width = (int) context.getResources().getDisplayMetrics().widthPixels;
int height = (int) (width / ratio);
// changing the height of the wrapper Layout of the video Surface keeping in mind the width of the
// device and also the aspect ratio of the video
if (height > width) {
((MyViewHolder) holder).videoContainerLayout.getLayoutParams().height
= width;
} else if (height < width) {
((MyViewHolder) holder).videoContainerLayout.getLayoutParams().height
= height;
}
}
@Override
public int getItemCount() {
return arrayList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.video_view)
SimpleVideoView video_view;
@InjectView(R.id.videoContainerLayout)
FrameLayout videoContainerLayout;
public MyViewHolder(View itemView) {
super(itemView);
ButterKnife.inject(this, itemView);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:background="#000"
android:orientation="vertical">
<!-- For now put the wrapper Layout height
as a number and not wrap_content-->
<FrameLayout
android:id="@+id/videoContainerLayout"
android:layout_width="match_parent"
android:layout_height="200dp">
<!-- The Video surfaces don't do so good when they are wrap content
and it is also not good idea to dynamically change the height of the
Video Surfaces
so we put the Video surface in a wrapper Layout in this case a FrameLayout
and make the surface match parent for height and width
and make the dynamic height changes to the wrapper Layout instead
-->
<com.klinker.android.simple_videoview.SimpleVideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:loop="true"
app:muted="false"
app:showSpinner="true"
app:stopSystemAudio="true" />
<!-- This imageView will be used as a cover thumbnail -->
<ImageView
android:id="@+id/iv_cover"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- android:background="@color/colorPrimary" -->
</FrameLayout>
</RelativeLayout>
package pune.university.engineering.gifutilstest;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.support.annotation.AnyRes;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ImageView;
import com.danikula.videocache.HttpProxyCacheServer;
import com.klinker.android.simple_videoview.SimpleVideoView;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import butterknife.ButterKnife;
import butterknife.InjectView;
public class VideoListActivity extends AppCompatActivity {
private static final String TAG = "VideoListActivity";
@InjectView(R.id.recyclerView)
RecyclerView mRecyclerView;
private LinearLayoutManager mLayoutManager;
private int mScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
private ArrayList<VideoModel> arrayList = new ArrayList<>();
MyVideoRecyclerViewAdapter videoRecyclerViewAdapter;
// This Method returns the URI in string of the drawable passed as argument
public static final String getUriToDrawable(@NonNull Context context,
@AnyRes int drawableId) {
Uri imageUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE +
"://" + context.getResources().getResourcePackageName(drawableId)
+ '/' + context.getResources().getResourceTypeName(drawableId)
+ '/' + context.getResources().getResourceEntryName(drawableId));
return imageUri.toString();
}
@Override
protected void onPause() {
super.onPause();
// We need to pause any playback when the application is minimised
try {
int firstCompletelyVisibleItemPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
SimpleVideoView video_view = (SimpleVideoView) mLayoutManager.findViewByPosition(firstCompletelyVisibleItemPosition).findViewById(R.id.video_view);
video_view.release();
} catch (NullPointerException e) {
// Sometimes you scroll so fast that the views are not attached so it gives a NullPointerException
} catch (ArrayIndexOutOfBoundsException e) {
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_list);
ButterKnife.inject(this);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
// String array of URL of some sample Videos
String[] video_urls = new String[]{"http://clips.vorwaerts-gmbh.de/VfE_html5.mp4",
"http://techslides.com/demos/sample-videos/small.webm",
"http://mirrors.standaloneinstaller.com/video-sample/TRA3106.flv",
"http://mirrors.standaloneinstaller.com/video-sample/lion-sample.3gp",
"http://mirrors.standaloneinstaller.com/video-sample/dolbycanyon.mp4"
};
// Double array of Width to Height Ratio of the above videos manually pre calculated by me
Double[] video_width_to_height_ratio = new Double[]{1.777,
1.75,
1.451612,
1.2222,
1.2222
};
// Will be used to change the height of the Row Layout according to the height of the Video
// we use ratio of width and height and not only height because different devices have different dimensions
// String[] of Uris of the Thumbnails of the videos manually extracted by me
final String[] thumbnails_string = new String[]{
getUriToDrawable(this, R.drawable.vfe_html5),
getUriToDrawable(this, R.drawable.small),
getUriToDrawable(this, R.drawable.tra3106),
getUriToDrawable(this, R.drawable.lion_sample),
getUriToDrawable(this, R.drawable.dolbycanyon),
};
// Now putting that data in an ArrayList
for (int i = 0; i < video_urls.length; i++) {
// Object from the Video caching library com.danikula:videocache
HttpProxyCacheServer httpProxyCacheServer = new HttpProxyCacheServer(this);
// Passing the url of videos from the string[] video_urls to com.danikula:videocache
String proxy_url = httpProxyCacheServer.getProxyUrl(video_urls[i]);
// com.danikula:videocache will cache the video from the Url and save it in phone memory
// the proxy_url will be the local path to the video and look something like this
// file:///data/data/<Your Package Name>/cache/video-cache/0228e681cad575e5352b203922b5229b.m4v
// Now put this proxy_url in your arrayList instead of the video _url
arrayList.add(new VideoModel(proxy_url, video_width_to_height_ratio[i], thumbnails_string[i]));
// and we are done with the Video caching
}
videoRecyclerViewAdapter = new MyVideoRecyclerViewAdapter(arrayList, this);
mRecyclerView.setAdapter(videoRecyclerViewAdapter);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
// Position of the row that is active
int activeAdapter = 0;
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// Get the index of the first Completely visible item
int firstCompletelyVisibleItemPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
Log.d(TAG, "onScrolled: firstCompletelyVisibleItemPosition : " + firstCompletelyVisibleItemPosition);
// Even if we scroll by a few millimeters the video will start playing from the beginning
// So we need to check if the new Active row layout position is equal to the current active row layout position
if (activeAdapter != firstCompletelyVisibleItemPosition) {
try {
VideoModel videoModel = (VideoModel) arrayList.get(firstCompletelyVisibleItemPosition);
String video_url = videoModel.getVideo_url();
SimpleVideoView video_view = (SimpleVideoView) mLayoutManager.findViewByPosition(firstCompletelyVisibleItemPosition).findViewById(R.id.video_view);
final ImageView iv_cover = (ImageView) mLayoutManager.findViewByPosition(firstCompletelyVisibleItemPosition).findViewById(R.id.iv_cover);
// Start playing the video in Active row layout
video_view.start(Uri.parse(video_url));
// assign this row layout position as active row Layout
activeAdapter = firstCompletelyVisibleItemPosition;
Log.d(TAG, "onScrolled: activeAdapter : " + activeAdapter);
// Hide the thumbnail ImageView with a delay of 300 millisecond or else there will be black
// screen before a video plays
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
iv_cover.setVisibility(View.INVISIBLE);
}
}, 300);
} catch (NullPointerException e) {
// Sometimes you scroll so fast that the views are not attached so it gives a NullPointerException
} catch (ArrayIndexOutOfBoundsException e) {
}
/* Get the Video Surface directly above the fully visible Row Layout so that you may stop the playback
when a new row Layout is fully visible
*/
if (firstCompletelyVisibleItemPosition >= 1) {
try {
SimpleVideoView video_view_above = (SimpleVideoView) mLayoutManager.findViewByPosition(firstCompletelyVisibleItemPosition - 1).findViewById(R.id.video_view);
video_view_above.release();
VideoModel videoModel = (VideoModel) arrayList.get(firstCompletelyVisibleItemPosition - 1);
String thumbnail_string = videoModel.getThumbnail();
// video_view_above.start(Uri.parse(thumbnail_string));
ImageView iv_cover_above = (ImageView) mLayoutManager.findViewByPosition(firstCompletelyVisibleItemPosition - 1).findViewById(R.id.iv_cover);
// Show the cover Thumbnail ImageView
iv_cover_above.setVisibility(View.VISIBLE);
Picasso.with(VideoListActivity.this)
.load((Uri.parse(thumbnail_string)))
.into(iv_cover_above);
// video_view_above.setBackground(Uri.parse(thumbnail_string));
} catch (NullPointerException e) {
} catch (ArrayIndexOutOfBoundsException e) {
}
}
/* Get the Video Surface directly Below the fully visible Row Layout so that you may stop the playback
when a new row Layout is fully visible
*/
if (firstCompletelyVisibleItemPosition + 1 < arrayList.size()) {
try {
SimpleVideoView video_view_below = (SimpleVideoView) mLayoutManager.findViewByPosition(firstCompletelyVisibleItemPosition + 1).findViewById(R.id.video_view);
video_view_below.release();
VideoModel videoModel = (VideoModel) arrayList.get(firstCompletelyVisibleItemPosition + 1);
String thumbnail_string = videoModel.getThumbnail();
// video_view_below.start(Uri.parse(thumbnail_string));
ImageView iv_cover_below = (ImageView) mLayoutManager.findViewByPosition(firstCompletelyVisibleItemPosition + 1).findViewById(R.id.iv_cover);
iv_cover_below.setVisibility(View.VISIBLE);
Picasso.with(VideoListActivity.this)
.load((Uri.parse(thumbnail_string)))
.into(iv_cover_below);
} catch (NullPointerException e) {
} catch (ArrayIndexOutOfBoundsException e) {
}
}
}
}
});
}
}
package pune.university.engineering.gifutilstest;
/**
* Created by Altair on 5/28/2018.
*/
public class VideoModel {
String video_url;
Double video_width_to_height_ratio;
String thumbnail;
public String getVideo_url() {
return video_url;
}
public Double getVideo_width_to_height_ratio() {
return video_width_to_height_ratio;
}
public String getThumbnail() {
return thumbnail;
}
public VideoModel(String video_url, Double video_width_to_height_ratio, String thumbnail) {
this.video_url = video_url;
this.video_width_to_height_ratio = video_width_to_height_ratio;
this.thumbnail = thumbnail;
}
}
@myaseen2030
Copy link

c

@myaseen2030
Copy link

is that code working properly/?

@shubhubhosale
Copy link
Author

shubhubhosale commented Sep 25, 2019 via email

@SangKarthi94
Copy link

Yes, but only if There are only videos in the list and not pictures and videos like Instagram Only one viewholder is used Video is not playing if using multiple viewholder I will send you the entire source code

On Tue, Sep 24, 2019, 10:21 AM myaseen2030 @.*** wrote: is that code working properly/? — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://gist.github.com/9ef8f34a00ee41835d909c1a023b3f7a?email_source=notifications&email_token=AI6M56SP22VSGULFIAFTCLDQLG5V5A5CNFSM4IZ3YG4KYY3PNVWWK3TUL52HS4DFVNDWS43UINXW23LFNZ2KUY3PNVWWK3TUL5UWJTQAFZJNC#gistcomment-3035857, or mute the thread https://github.com/notifications/unsubscribe-auth/AI6M56XGWLP7BHSLCCT5HRTQLG5V5ANCNFSM4IZ3YG4A .

Is this code working?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment