Intereting Posts
Где найти исходные изображения ресурсов Android Android-программирование: как рисовать многострочный текст в прямоугольнике? Как писать текст справа налево (арабский текст) на Android? Щелкните по границам / координатам OnItemClickListener с помощью ArrayAdapter для ListView Работает ли proguard для обфускации статических строковых констант? Разница между методами масштабирования растрового изображения Android Studio Локальный путь не существует (папка классов вместо apk) Тестирование нескольких устройств Android на одной машине NotifyDataSetChange не работает с пользовательским адаптером Есть ли способ запустить приложение сразу после установки с помощью некоторого «вспомогательного приложения»? Выбираем (выпадающий) стиль WebView / Phonegap Android TextWatcher.afterTextChanged vs TextWatcher.onTextChanged Android – быстрый способ получить скрытые изображения и папки Как отключить прокрутку AppBarLayout в CoordinatorLayout?

Воспроизведение видео с использованием текстуры в recycliewiew

Я пытаюсь реализовать список с видео, например, vine или Instagram. Где они воспроизводят видеоролики, когда элемент списка отображается или полностью отображается, а видео приостанавливается, когда элемент списка скрывается. Я использую textureview с медиа-плеером для воспроизведения видео с URL-адреса и добавил его в качестве элемента списка в recyclerview. Следующий мой код.

Видеокласс адаптера:

public class VideosAdapter extends RecyclerView.Adapter<VideosAdapter.ViewHolder> { Context context; private ArrayList<String> urls; public static class ViewHolder extends RecyclerView.ViewHolder { public LinearLayout layout; public TextView textView; public ViewHolder(View v) { super(v); layout = (LinearLayout) v.findViewById(R.id.linearLayout); textView = (TextView) v.findViewById(R.id.textView); } } public VideosAdapter(Context context, ArrayList<String> urls) { this.context = context; this.urls = urls; } // Create new views (invoked by the layout manager) @Override public VideosAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // create a new view View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_main, parent, false); ViewHolder viewHolder = new ViewHolder(v); return viewHolder; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder holder, int position) { String url = urls.get(position); holder.textView.setText(url); playVideo(holder, url); } @Override public int getItemCount() { return urls.size(); } private void playVideo(ViewHolder holder, String url) { final CustomVideoPlayer vid = new CustomVideoPlayer(String.valueOf(url), context); holder.layout.addView(vid); holder.layout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { vid.changePlayState(); } }); } } 

Класс CustomVideoPlayer:

 public class CustomVideoPlayer extends TextureView implements TextureView.SurfaceTextureListener { Context context; String url; MediaPlayer mp; Surface surface; SurfaceTexture s; public CustomVideoPlayer(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; } public CustomVideoPlayer(String ur, Context context) { super(context); this.setSurfaceTextureListener(this); this.url = ur; this.context = context; } @Override public void onSurfaceTextureAvailable(final SurfaceTexture surface, int arg1, int arg2) { this.s = surface; Log.d("url", this.url); startVideo(surface); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture arg0) { return true; } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1,int arg2) { } @Override public void onSurfaceTextureUpdated(SurfaceTexture arg0) { } public void setVideo(String url) { this.url = url; } public void startVideo(SurfaceTexture t) { this.surface = new Surface(t); this.mp = new MediaPlayer(); this.mp.setSurface(this.surface); try { Uri uri = Uri.parse(this.url); this.mp.setDataSource(url); this.mp.prepareAsync(); this.mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { public void onPrepared(MediaPlayer mp) { mp.setLooping(true); mp.start(); } }); } catch (IllegalArgumentException e1) { e1.printStackTrace(); } catch (SecurityException e1) { e1.printStackTrace(); } catch (IllegalStateException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } try { } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } try { } catch (IllegalStateException e) { e.printStackTrace(); } } public void changePlayState() { if(this.mp.isPlaying()) this.mp.pause(); else this.mp.start(); } } 

Когда я запускаю этот код, в нем есть несколько проблем.

1) Первые два пункта / видео буферов и играть отлично. Но когда я прокручиваю, он не загружает третье видео, и первое видео также удаляется из списка.

2) В прокрутке видео / элементы списка снова начинается буферизация для элемента, который уже был буферизирован.

3) В списке быстрых прокруток происходит слишком лагги, застревание и сбои.

Прикреплено изображение logcat, которое я получаю во время прокрутки списка и воспроизведения видео.

Введите описание изображения здесь

Может ли кто-нибудь вести меня через это? Каков правильный способ создания списка, например, приложения для винограда?

Solutions Collecting From Web of "Воспроизведение видео с использованием текстуры в recycliewiew"

Я смог добиться этого, сначала загрузив видео с url, а затем воспроизведя его с помощью пользовательского плеера. Вот как я это сделал, если кто-то еще нуждался в этом:

1) Получить все необходимые URL-адреса

2) Начните загрузку видео (в очереди) из URL-адресов в локальном хранилище и сохраните флаг в настройках (что видео уже загружено или нет)

3) Назначьте URL-адрес адаптера, в котором инициализируется объект контроллера видеоплеера, который обрабатывает воспроизведение видео

4) Установите addOnScrollListener, чтобы проверить, какая позиция / видео в данный момент видна, и проверьте, загружено ли видео или нет, если да, тогда воспроизведите его.

Ниже приведен полный код:

Основная деятельность

 public class MainActivity extends ActionBarActivity implements IVideoDownloadListener { private static String TAG = "MainActivity"; private Context context; private RecyclerView mRecyclerView; private ProgressBar progressBar; private VideosAdapter mAdapter; private RecyclerView.LayoutManager mLayoutManager; private ArrayList<Video> urls; VideosDownloader videosDownloader; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = MainActivity.this; mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); progressBar = (ProgressBar) findViewById(R.id.progressBar); urls = new ArrayList<Video>(); mRecyclerView.setHasFixedSize(true); mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); mAdapter = new VideosAdapter(MainActivity.this, urls); mRecyclerView.setAdapter(mAdapter); videosDownloader = new VideosDownloader(context); videosDownloader.setOnVideoDownloadListener(this); if(Utils.hasConnection(context)) { getVideoUrls(); mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE) { LinearLayoutManager layoutManager = ((LinearLayoutManager) recyclerView.getLayoutManager()); int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition(); int findFirstCompletelyVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition(); Video video; if (urls != null && urls.size() > 0) { if (findFirstCompletelyVisibleItemPosition >= 0) { video = urls.get(findFirstCompletelyVisibleItemPosition); mAdapter.videoPlayerController.setcurrentPositionOfItemToPlay(findFirstCompletelyVisibleItemPosition); mAdapter.videoPlayerController.handlePlayBack(video); } else { video = urls.get(firstVisiblePosition); mAdapter.videoPlayerController.setcurrentPositionOfItemToPlay(firstVisiblePosition); mAdapter.videoPlayerController.handlePlayBack(video); } } } } }); } else Toast.makeText(context, "No internet available", Toast.LENGTH_LONG).show(); } @Override public void onVideoDownloaded(Video video) { mAdapter.videoPlayerController.handlePlayBack(video); } private void getVideoUrls() { Video video1 = new Video("0", "1", "http://techslides.com/demos/sample-videos/small.mp4"); urls.add(video1); Video video2 = new Video("1", "2", "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"); urls.add(video2); Video video3 = new Video("2", "3", "http://sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4"); urls.add(video3); Video video4 = new Video("3", "4", "http://dev.exiv2.org/attachments/341/video-2012-07-05-02-29-27.mp4"); urls.add(video4); Video video5 = new Video("4", "5", "http://techslides.com/demos/sample-videos/small.mp4"); urls.add(video5); Video video6 = new Video("5", "6", "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"); urls.add(video6); Video video7 = new Video("6", "7", "http://sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4"); urls.add(video7); mAdapter.notifyDataSetChanged(); progressBar.setVisibility(View.GONE); videosDownloader.startVideosDownloading(urls); } } 

VideosAdapter

 public class VideosAdapter extends RecyclerView.Adapter<VideosAdapter.ViewHolder> { private static String TAG = "VideosAdapter"; Context context; private ArrayList<Video> urls; public VideoPlayerController videoPlayerController; public static class ViewHolder extends RecyclerView.ViewHolder { public TextView textView; public ProgressBar progressBar; public RelativeLayout layout; public ViewHolder(View v) { super(v); layout = (RelativeLayout) v.findViewById(R.id.layout); textView = (TextView) v.findViewById(R.id.textView); progressBar = (ProgressBar) v.findViewById(R.id.progressBar); } } public VideosAdapter(Context context, final ArrayList<Video> urls) { this.context = context; this.urls = urls; videoPlayerController = new VideoPlayerController(context); } // Create new views (invoked by the layout manager) @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // create a new view View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_main, parent, false); Configuration configuration = context.getResources().getConfiguration(); int screenWidthDp = configuration.screenWidthDp; //The current width of the available screen space, in dp units, corresponding to screen width resource qualifier. int smallestScreenWidthDp = configuration.smallestScreenWidthDp; //The smallest screen size an application will see in normal operation, corresponding to smallest screen width resource qualifier. ViewHolder viewHolder = new ViewHolder(v); int screenWidthPixels = Utils.convertDpToPixel(screenWidthDp, context); RelativeLayout.LayoutParams rel_btn = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, screenWidthPixels); viewHolder.layout.setLayoutParams(rel_btn); return viewHolder; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(final ViewHolder holder, int position) { Video video = urls.get(position); holder.textView.setText("Video " + video.getId()); final VideoPlayer videoPlayer = new VideoPlayer(context); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); videoPlayer.setLayoutParams(params); holder.layout.addView(videoPlayer); videoPlayerController.loadVideo(video, videoPlayer, holder.progressBar); videoPlayer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { videoPlayer.changePlayState(); } }); } @Override public void onViewRecycled(ViewHolder holder) { super.onViewRecycled(holder); Log.d(TAG, "onViewRecycledCalled"); holder.layout.removeAllViews(); } @Override public int getItemCount() { return urls.size(); } } 

VideosDownloader

 public class VideosDownloader { private static String TAG = "VideosDownloader"; Context context; FileCache fileCache; IVideoDownloadListener iVideoDownloadListener; public VideosDownloader(Context context) { this.context = context; fileCache = new FileCache(context); } ///////////////////////////////////////////////////////////////// // Start downloading all videos from given urls public void startVideosDownloading(final ArrayList<Video> videosList) { Thread thread = new Thread(new Runnable() { @Override public void run() { for(int i=0; i<videosList.size(); i++) { final Video video = videosList.get(i); String id = video.getId(); String url = video.getUrl(); String isVideoDownloaded = Utils.readPreferences(context, video.getUrl(), "false"); boolean isVideoAvailable = Boolean.valueOf(isVideoDownloaded); if(!isVideoAvailable) { //Download video from url String downloadedPath = downloadVideo(url); //Log.i(TAG, "Vides downloaded at: " + downloadedPath); Activity activity = (Activity) context; activity.runOnUiThread(new Runnable() { @Override public void run() { Utils.savePreferences(context, video.getUrl(), "true"); iVideoDownloadListener.onVideoDownloaded(video); } }); } } } }); thread.start(); } ///////////////////////////////////////////////////////////////// private String downloadVideo(String urlStr) { URL url = null; File file = null; try { file = fileCache.getFile(urlStr); url = new URL(urlStr); long startTime = System.currentTimeMillis(); URLConnection ucon = null; ucon = url.openConnection(); InputStream is = ucon.getInputStream(); BufferedInputStream inStream = new BufferedInputStream(is, 1024 * 5); FileOutputStream outStream = new FileOutputStream(file); byte[] buff = new byte[5 * 1024]; //Read bytes (and store them) until there is nothing more to read(-1) int len; while ((len = inStream.read(buff)) != -1) { outStream.write(buff, 0, len); } //clean up outStream.flush(); outStream.close(); inStream.close(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return file.getAbsolutePath(); } public void setOnVideoDownloadListener(IVideoDownloadListener iVideoDownloadListener) { this.iVideoDownloadListener = iVideoDownloadListener; } } 

VideoPlayerController

 public class VideoPlayerController { private static String TAG = "VideoPlayerController"; Context context; FileCache fileCache; int currentPositionOfItemToPlay = 0; Video currentPlayingVideo; private Map<String, VideoPlayer> videos = Collections.synchronizedMap(new WeakHashMap<String, VideoPlayer>()); private Map<String, ProgressBar> videosSpinner = Collections.synchronizedMap(new WeakHashMap<String, ProgressBar>()); public VideoPlayerController(Context context) { this.context = context; fileCache = new FileCache(context); } public void loadVideo(Video video, VideoPlayer videoPlayer, ProgressBar progressBar) { //Add video to map videos.put(video.getIndexPosition(), videoPlayer); videosSpinner.put(video.getIndexPosition(), progressBar); handlePlayBack(video); } //This method would check two things //First if video is downloaded or its local path exist //Second if the videoplayer of this video is currently showing in the list or visible public void handlePlayBack(Video video) { //Check if video is available if(isVideoDownloaded(video)) { // then check if it is currently at a visible or playable position in the listview if(isVideoVisible(video)) { //IF yes then playvideo playVideo(video); } } } private void playVideo(final Video video) { //Before playing it check if this video is already playing if(currentPlayingVideo != video) { //Start playing new url if(videos.containsKey(video.getIndexPosition())) { final VideoPlayer videoPlayer2 = videos.get(video.getIndexPosition()); String localPath = fileCache.getFile(video.getUrl()).getAbsolutePath(); if(!videoPlayer2.isLoaded) { videoPlayer2.loadVideo(localPath, video); videoPlayer2.setOnVideoPreparedListener(new IVideoPreparedListener() { @Override public void onVideoPrepared(Video mVideo) { //Pause current playing video if any if(video.getIndexPosition() == mVideo.getIndexPosition()) { if(currentPlayingVideo!=null) { VideoPlayer videoPlayer1 = videos.get(currentPlayingVideo.getIndexPosition()); videoPlayer1.pausePlay(); } videoPlayer2.mp.start(); currentPlayingVideo = mVideo; } } }); } else { //Pause current playing video if any if(currentPlayingVideo!=null) { VideoPlayer videoPlayer1 = videos.get(currentPlayingVideo.getIndexPosition()); videoPlayer1.pausePlay(); } boolean isStarted = videoPlayer2.startPlay(); { //Log.i(TAG, "Started playing Video Index: " + video.getIndexPosition()); //Log.i(TAG, "Started playing Video: " + video.getUrl()); } currentPlayingVideo = video; } } } else { //Log.i(TAG, "Already playing Video: " + video.getUrl()); } } private boolean isVideoVisible(Video video) { //To check if the video is visible in the listview or it is currently at a playable position //we need the position of this video in listview and current scroll position of the listview int positionOfVideo = Integer.valueOf(video.getIndexPosition()); if(currentPositionOfItemToPlay == positionOfVideo) return true; return false; } private boolean isVideoDownloaded(Video video) { String isVideoDownloaded = Utils.readPreferences(context, video.getUrl(), "false"); boolean isVideoAvailable = Boolean.valueOf(isVideoDownloaded); if(isVideoAvailable) { //If video is downloaded then hide its progress hideProgressSpinner(video); return true; } showProgressSpinner(video); return false; } private void showProgressSpinner(Video video) { ProgressBar progressBar = videosSpinner.get(video.getIndexPosition()); if(progressBar!=null) progressBar.setVisibility(View.VISIBLE); } private void hideProgressSpinner(Video video) { ProgressBar progressBar = videosSpinner.get(video.getIndexPosition()); if(progressBar!=null && progressBar.isShown()) { progressBar.setVisibility(View.GONE); Log.i(TAG, "ProgressSpinner Hided Index: " + video.getIndexPosition()); } } public void setcurrentPositionOfItemToPlay(int mCurrentPositionOfItemToPlay) { currentPositionOfItemToPlay = mCurrentPositionOfItemToPlay; } } 

Видео проигрыватель

 public class VideoPlayer extends TextureView implements TextureView.SurfaceTextureListener { private static String TAG = "VideoPlayer"; /**This flag determines that if current VideoPlayer object is first item of the list if it is first item of list*/ boolean isFirstListItem; boolean isLoaded; boolean isMpPrepared; IVideoPreparedListener iVideoPreparedListener; Video video; String url; MediaPlayer mp; Surface surface; SurfaceTexture s; public VideoPlayer(Context context) { super(context); } public VideoPlayer(Context context, AttributeSet attrs) { super(context, attrs); } public void loadVideo(String localPath, Video video) { this.url = localPath; this.video = video; isLoaded = true; if (this.isAvailable()) { prepareVideo(getSurfaceTexture()); } setSurfaceTextureListener(this); } @Override public void onSurfaceTextureAvailable(final SurfaceTexture surface, int width, int height) { isMpPrepared = false; prepareVideo(surface); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { if(mp!=null) { mp.stop(); mp.reset(); mp.release(); mp = null; } return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } public void prepareVideo(SurfaceTexture t) { this.surface = new Surface(t); mp = new MediaPlayer(); mp.setSurface(this.surface); try { mp.setDataSource(url); mp.prepareAsync(); mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { public void onPrepared(MediaPlayer mp) { isMpPrepared = true; mp.setLooping(true); iVideoPreparedListener.onVideoPrepared(video); } }); } catch (IllegalArgumentException e1) { e1.printStackTrace(); } catch (SecurityException e1) { e1.printStackTrace(); } catch (IllegalStateException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } try { } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } try { } catch (IllegalStateException e) { e.printStackTrace(); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); } @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); } public boolean startPlay() { if(mp!=null) if(!mp.isPlaying()) { mp.start(); return true; } return false; } public void pausePlay() { if(mp!=null) mp.pause(); } public void stopPlay() { if(mp!=null) mp.stop(); } public void changePlayState() { if(mp!=null) { if(mp.isPlaying()) mp.pause(); else mp.start(); } } public void setOnVideoPreparedListener(IVideoPreparedListener iVideoPreparedListener) { this.iVideoPreparedListener = iVideoPreparedListener; } } 

IVideoDownloadListener

 public interface IVideoDownloadListener { public void onVideoDownloaded(Video video); } 

IVideoPreparedListener

 public interface IVideoPreparedListener { public void onVideoPrepared(Video video); } 

Почему бы вам не добавить пользовательский вид видео в файл макета «view_main». Проверьте видимость просмотра видео и воспроизведите, только если вид виден.

 public static boolean isViewVisible(View subView, View parentView) { Rect scrollBounds = new Rect(); parentView.getHitRect(scrollBounds); if (subView.getLocalVisibleRect(scrollBounds)) { return true; } return false; } 

Код для проверки видимости. Вызовите это в состоянии прокрутки измененного прослушивателя, когда состояние прокрутки простаивает.

Также вам нужно будет использовать AsyncTask для загрузки видео, но загружать только одно видео за раз или вы можете выйти из памяти.

Вы должны хранить кеш видео на локальном компьютере, загружая их на бэкэнд и воспроизводя по одному видео за раз из локальной памяти, чтобы сохранить прокрутку списка.