国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android ViewPager中顯示圖片與播放視頻的填坑記錄

2019-10-22 18:12:05
字體:
來源:轉載
供稿:網友

ViewPager介紹

ViewPager的功能就是可以使視圖滑動,就像Lanucher左右滑動那樣。

ViewPager用于實現多頁面的切換效果,該類存在于Google的兼容包android-support-v4.jar里面.

ViewPager:

      1)ViewPager類直接繼承了ViewGroup類,所有它是一個容器類,可以在其中添加其他的view類。

      2)ViewPager類需要一個PagerAdapter適配器類給它提供數據。

      3)ViewPager經常和Fragment一起使用,并且提供了專門的FragmentPagerAdapter和FragmentStatePagerAdapter類供Fragment中 的ViewPager使用。

      4)在編寫ViewPager的應用的使用,還需要使用兩個組件類分別是PagerTitleStrip類和PagerTabStrip類,PagerTitleStrip類直接繼承 自ViewGroup類,而PagerTabStrip類繼承PagerTitleStrip類,所以這兩個類也是容器類。但是有一點需要注意,在定義XML的layout 的時候,這兩個類必須是ViewPager標簽的子標簽,不然會出錯。

本文將詳細介紹關于Android ViewPager中顯示圖片與播放視頻填坑的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。

一.需求來源與實現思路

1.最近項目需求中有用到需要在ViewPager中播放視頻和顯示圖片的功能,視頻是本地視頻,最開始的實現思路是ViewPager中根據當前item位置對應的是圖片還是視頻去初始化PhotoView和SurfaceView,同時銷毀時根據item的位置去判斷移除PhotoView和SurfaceView。

2.上面那種方式確實是可以實現的,但是存在2個問題,第一,MediaPlayer的生命周期不容易控制并且存在內存泄漏問題。第二,連續三個item都是視頻時,來回滑動的過程中發現會出現上個視頻的最后一幀畫面的bug。

3.未提升用戶體驗,視頻播放器初始化完成前上面會覆蓋有該視頻的第一幀圖片,但是發現存在第一幀圖片與視頻第一幀信息不符的情況,后面會通過代碼給出解決方案。

4.圖片和視頻尺寸如何適配以保證不變形。

二.需要填的坑

1.對于MediaPlayer的生命周期不容易控制的本質原因是這種實現思路上我的播放器只有1個,頻繁的初始化和銷毀造成了問題,所以后面我更改了實現方式,一個item的視頻對應一個播放器。

2.對于滑動過程中發現會出現上個視頻的最后一幀畫面的bug,發現是surfaceView這個控件造成的,后面通過將播放的載體更換為TextureView完美解決該問題。

3.SurfaceView與TextureView的本質異同

第一:兩者都能在獨立的線程中繪制和渲染,在專用的GPU線程中大大提高渲染的性能。

第二:SurfaceView專門提供了嵌入視圖層級的繪制界面,開發者可以控制該界面像Size等的形式,能保證界面在屏幕上的正確位置。但也有局限:
1.由于是獨立的一層View,更像是獨立的一個Window,不能加上動畫、平移、縮放;
2.兩個SurfaceView不能相互覆蓋。

第三:Texture更像是一般的View,像TextView那樣能被縮放、平移,也能加上動畫。TextureView只能在開啟了硬件加速的Window中使用,并且消費的內存要比SurfaceView多,并伴隨著1-3幀的延遲。

第四:屏幕鎖屏時SurfaceView會銷毀重建,TextureView不會!

三.具體實現核心代碼

1.ViewPager的初始化

mAdapter = ImageBrowseFragmentPagerAdapter(supportFragmentManager, this, imgs) imgs_viewpager.offscreenPageLimit = 1 imgs_viewpager.adapter = mAdapter imgs_viewpager.currentItem = mPosition //為了處理首次點擊時視頻播放的問題 val message = Message.obtain() message.what = START_PLAY_VIDEO mHandler.sendMessageDelayed(message, 200)

2.Handler處理消息

private val START_PLAY_VIDEO = 0private var DELETE_VIDEO = 1private var DELETE_VIDEO_START_PLAY = 2private var mHandler = Handler(Handler.Callback { msg ->  when (msg.what) {   //開始播放視頻   START_PLAY_VIDEO -> NotifyDispatch.dispatch(PreviewPlayVideoEvent(mPosition))   //刪除視頻時刷新ui   DELETE_VIDEO -> {    mAdapter?.setImgs(imgs)   }   //解決刪除視頻時之后跳轉到另一個item,當它是視頻時不繼續播放的問題   DELETE_VIDEO_START_PLAY -> NotifyDispatch.dispatch(PreviewPlayVideoEvent(mDeletePosition))  }  true })

3.刪除視頻或圖片的處理邏輯

 private fun deletePhotos(position: Int) {  if (imgs!!.isEmpty()) {   return  }  ThreadDispatch.right_now.execute({   var file: File?   file = File(imgs.get(position))   if (file != null && file?.exists()!!) {    val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)    val uri = Uri.fromFile(file)    intent.data = uri    sendBroadcast(intent)    file?.delete()    imgs.removeAt(position)   }   if (position == imgs.size) {    mDeletePosition = position - 1   } else {    mDeletePosition = position   }   val message = Message.obtain()   message.what = DELETE_VIDEO   mHandler.sendMessage(message)   NotifyDispatch.dispatch(DeletePreviewPhotoEvent(imgs))   val message1 = Message.obtain()   message1.what = DELETE_VIDEO_START_PLAY   mHandler.sendMessageDelayed(message1, 200)   if (imgs.isEmpty()) {    finish()   }  })// } }

4.ViewPager對應的Adapter

package com.immomo.camerax.gui.view.adapter;import android.content.Context;import android.os.Bundle;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentStatePagerAdapter;import android.support.v4.app.FragmentTransaction;import android.view.View;import android.view.ViewGroup;import com.immomo.camerax.gui.fragment.PreviewImgFragment;import com.immomo.camerax.gui.fragment.PreviewVideoFragment;import java.util.ArrayList;import java.util.List;/** * Created by liuxu on 2018/3/26. */public class ImageBrowseFragmentPagerAdapter extends FragmentStatePagerAdapter { private Context mContext; private List<String> datas; private int mCurrentSelectedPosition = -1; private FragmentManager mFragmentManager; private FragmentTransaction mFragmentTransaction; private ArrayList<Fragment> mFragments = new ArrayList<>(); public ImageBrowseFragmentPagerAdapter(FragmentManager fm, Context context, List<String> datas) {  super(fm);  mFragmentManager = fm;  mContext = context;  this.datas = datas; } public void removeContext(){  mContext = null; } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) {  mCurrentSelectedPosition = position; } @Override public void startUpdate(ViewGroup container) {  super.startUpdate(container); } public void setImgs(List<String> imgs) {  this.datas = imgs;  notifyDataSetChanged(); } //處理更新無效----刪除條目 @Override public int getItemPosition(Object object) {  return POSITION_NONE; } public int getPrimaryItemPosition() {  return mCurrentSelectedPosition; } public ImageBrowseFragmentPagerAdapter(FragmentManager fm) {  super(fm); } @Override public boolean isViewFromObject(View view, Object object) {  return view == ((Fragment) object).getView(); } @Override public Fragment getItem(int position) {  Bundle bundle = new Bundle();  bundle.putString("url", datas.get(position));  bundle.putInt("position", position);  if (datas.get(position).endsWith(".jpg")) {   PreviewImgFragment previewImgFragment = new PreviewImgFragment();   previewImgFragment.setArguments(bundle);   return previewImgFragment;  } else {   PreviewVideoFragment previewVideoFragment = new PreviewVideoFragment();   previewVideoFragment.setArguments(bundle);   return previewVideoFragment;  } } @Override public int getCount() {  return datas == null ? 0 : datas.size(); } @Override public Object instantiateItem(ViewGroup container, int position) {  return super.instantiateItem(container,position); } @Override public void destroyItem(ViewGroup container, int position, Object object) {  super.destroyItem(container,position,object); }}

5顯示圖片對應的Fragment

package com.immomo.camerax.gui.fragment;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import com.bumptech.glide.Glide;import com.immomo.camerax.R;import com.immomo.camerax.foundation.util.StatusBarUtils;import com.immomo.camerax.gui.view.ResizablePhotoView;/** * Created by liuxu on 2018/3/27. */public class PreviewImgFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {  View view = inflater.inflate(R.layout.fragment_preview_photo, null);  ResizablePhotoView resizablePhotoView = view.findViewById(R.id.customPhotoView);  String url = getArguments().getString("url");  Glide.with(getContext()).load(url).into(resizablePhotoView);  resizablePhotoView.setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View view) {    getActivity().finish();   }  });  return view; } @Override public void onPause() {  super.onPause(); } @Override public void onResume() {  super.onResume(); } @Override public void onDetach() {  super.onDetach(); } @Override public void onDestroyView() {  super.onDestroyView(); } @Override public void onStart() {  super.onStart(); } @Override public void onDestroy() {  super.onDestroy(); } @Override public void setUserVisibleHint(boolean isVisibleToUser) {  super.setUserVisibleHint(isVisibleToUser); }}

6.圖片根據寬度適配高度的自定義View

package com.immomo.camerax.gui.view;import android.content.Context;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import com.github.chrisbanes.photoview.PhotoView;/** * Created by liuxu on 2018/4/7. */public class ResizablePhotoView extends PhotoView { public ResizablePhotoView(Context context) {  super(context); } public ResizablePhotoView(Context context, AttributeSet attr) {  super(context, attr); } public ResizablePhotoView(Context context, AttributeSet attr, int defStyle) {  super(context, attr, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  Drawable d = getDrawable();  if (d != null){   int width = MeasureSpec.getSize(widthMeasureSpec);   //高度根據使得圖片的寬度充滿屏幕計算而得   int height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth());   setMeasuredDimension(width, height);  }else {   super.onMeasure(widthMeasureSpec, heightMeasureSpec);  } }}

7.播放視頻對應的Fragment

/** * Created by liuxu on 2018/3/27. */public class PreviewVideoFragment extends Fragment { private ImageView mPhotoView; private TextureView mTextureView; private String mUrl; private int mPosition; private AndroidMediaPlayer mIjkVodMediaPlayer; private boolean mIsSelected; private boolean mIsFirstPrepared; private PreviewPlayVideoSubscriber mPreviewPlayVideoSubscriber = new PreviewPlayVideoSubscriber() {  @Override  public void onEventMainThread(PreviewPlayVideoEvent event) {   super.onEventMainThread(event);   MDLog.e("liuxu",event.getPosition()+"");   if (event != null && event.getPosition() == mPosition) {    //說明是當前條目    if (mIjkVodMediaPlayer != null && !mIjkVodMediaPlayer.isPlaying()) {     if (mTextureView != null) {      mIjkVodMediaPlayer.setSurface(mSurface);      mIjkVodMediaPlayer.prepareAsync();      mPhotoView.setVisibility(View.VISIBLE);     }    }    mIsSelected = true;   } else {    if (mIjkVodMediaPlayer != null && mIjkVodMediaPlayer.isPlaying()) {     mIjkVodMediaPlayer.pause();     mIjkVodMediaPlayer.stop();    }    if (mPhotoView != null) {     mPhotoView.setVisibility(View.VISIBLE);    }    mIsSelected = false;   }  } }; private String mWidth; private String mHeight; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {  mPreviewPlayVideoSubscriber.register();  View view = inflater.inflate(R.layout.fragment_preview_video, null);  mPhotoView = view.findViewById(R.id.photoView);  mTextureView = view.findViewById(R.id.surfaceView);  mUrl = getArguments().getString("url");  mPosition = getArguments().getInt("position");  layoutPlayer();  loadVideoScreenshot(getContext(), mUrl, mPhotoView, 1);  view.setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View view) {    release();    getActivity().finish();   }  });  initTextureMedia();  return view; } private void initTextureMedia() {  mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); } private void play(String url) {  try {   mIjkVodMediaPlayer = new AndroidMediaPlayer();   mIjkVodMediaPlayer.reset();   mIjkVodMediaPlayer.setDataSource(url);   //讓MediaPlayer和TextureView進行視頻畫面的結合   mIjkVodMediaPlayer.setSurface(mSurface);   //設置監聽   mIjkVodMediaPlayer.setOnBufferingUpdateListener((mp, percent) -> {   });   mIjkVodMediaPlayer.setOnCompletionListener(mp -> {    mp.seekTo(0);    mp.start();   });   mIjkVodMediaPlayer.setOnInfoListener((mp1, what, extra) -> {      if (what == IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {       mPhotoView.setVisibility(View.GONE);       mIsFirstPrepared = true;      }      return false;     });   mIjkVodMediaPlayer.setOnErrorListener((mp, what, extra) -> false);   mIjkVodMediaPlayer.setOnPreparedListener(mp -> {    mp.start();    if (!mIsFirstPrepared){    }else {     mPhotoView.setVisibility(View.GONE);    }   });   mIjkVodMediaPlayer.setScreenOnWhilePlaying(true);//在視頻播放的時候保持屏幕的高亮   if (mIsSelected){    //異步準備    mIjkVodMediaPlayer.prepareAsync();   }  } catch (Exception e) {   e.printStackTrace();  } } private Surface mSurface; private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {  @Override  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {   mSurface = new Surface(surface);   play(mUrl);  }  @Override  public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {  }  @Override  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {   if (mSurface != null){    mSurface.release();    mSurface = null;   }   if (mTextureView != null){    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {     mTextureView.releasePointerCapture();    }   }   release();   return false;  }  @Override  public void onSurfaceTextureUpdated(SurfaceTexture surface) {  } }; @Override public void onStart() {  super.onStart(); } @Override public void onAttach(Context context) {  super.onAttach(context); } @Override public void onDetach() {  super.onDetach(); } @Override public void onPause() {  MDLog.e("liuxu", "onPause" + mPosition);  //處理鎖屏時播放器停止播放  if (mIjkVodMediaPlayer != null && mIjkVodMediaPlayer.isPlaying()){   mIjkVodMediaPlayer.pause();   mIjkVodMediaPlayer.stop();  }  super.onPause(); } //屏幕打開時重新播放 @Override public void onResume() {  MDLog.e("liuxu", "onResume" + mPosition);  super.onResume();  if (mIsSelected && mIjkVodMediaPlayer != null && !mIjkVodMediaPlayer.isPlaying()) {   mIjkVodMediaPlayer.prepareAsync();  } } @Override public void onDestroy() {  MDLog.e("liuxu", "onDestroy");  release();  if (mPreviewPlayVideoSubscriber.isRegister()) {   mPreviewPlayVideoSubscriber.unregister();  }  super.onDestroy(); } private void release() {  if (mIjkVodMediaPlayer == null) {   return;  }  if (mIjkVodMediaPlayer.isPlaying()) {   mIjkVodMediaPlayer.stop();  }  mIjkVodMediaPlayer.release();  mIjkVodMediaPlayer = null; } @Override public boolean getUserVisibleHint() {  return super.getUserVisibleHint(); } /**  * 動態設置視頻寬高信息  */ private void layoutPlayer() {  //獲取視頻寬高比  getPlayInfo(mUrl);  float ratio = Float.parseFloat(mHeight) / Float.parseFloat(mWidth);  MDLog.e("type", mPosition + "ratio" + ratio);  int type = 0;  //添加容錯值  if (ratio < MediaConstants.INSTANCE.getASPECT_RATIO_1_1().toFloat() + MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()    && ratio > MediaConstants.INSTANCE.getASPECT_RATIO_1_1().toFloat() - MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()) {   type = ScreenAdapterUtils.INSTANCE.getSCALE_TYPE_11();  } else if (ratio < MediaConstants.INSTANCE.getASPECT_RATIO_4_3().toFloat() + MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()    && ratio > MediaConstants.INSTANCE.getASPECT_RATIO_4_3().toFloat() - MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()) {   type = ScreenAdapterUtils.INSTANCE.getSCALE_TYPE_43();   MDLog.e("type", "43");  } else if (ratio < MediaConstants.INSTANCE.getASPECT_RATIO_16_9().toFloat() + MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()    && ratio > MediaConstants.INSTANCE.getASPECT_RATIO_16_9().toFloat() - MediaConstants.INSTANCE.getSCREEN_DEFAULT_VALUE()) {   type = ScreenAdapterUtils.INSTANCE.getSCALE_TYPE_169();   MDLog.e("type", "169");  }  FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mTextureView.getLayoutParams();  layoutParams.height = ScreenAdapterUtils.INSTANCE.getSurfaceHeight(type);  mTextureView.setLayoutParams(layoutParams);  FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mPhotoView.getLayoutParams();  params.height = ScreenAdapterUtils.INSTANCE.getSurfaceHeight(type);  mPhotoView.setLayoutParams(params);  MDLog.e("params.height", params.height + ""); } private void getPlayInfo(String mUri) {  android.media.MediaMetadataRetriever mmr = new android.media.MediaMetadataRetriever();  try {   if (mUri != null) {    mmr.setDataSource(mUri);   } else {    //mmr.setDataSource(mFD, mOffset, mLength);   }   //寬   mWidth = mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);   //高   mHeight = mmr.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);//   mBitmap = mmr.getFrameAtTime(1 );  } catch (Exception ex) {  } finally {   mmr.release();  } } public static void loadVideoScreenshot(final Context context, String uri, ImageView imageView, long frameTimeMicros) {  // 這里的時間是以微秒為單位  RequestOptions requestOptions = RequestOptions.frameOf(frameTimeMicros);  requestOptions.set(FRAME_OPTION, MediaMetadataRetriever.OPTION_CLOSEST);  requestOptions.transform(new BitmapTransformation() {   @Override   protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {    return toTransform;   }   @Override   public void updateDiskCacheKey(MessageDigest messageDigest) {    try {     messageDigest.update((context.getPackageName() + "RotateTransform").getBytes("utf-8"));    } catch (Exception e) {     e.printStackTrace();    }   }  });  Glide.with(context).load(uri).apply(requestOptions).into(imageView); }}

4.結語

筆者使用這種方式實現了項目需求,但是由于本人接觸音視頻的相關內容比較少,全是在不斷探索和學習中前進,如有不足之處請評論指正,謝謝。大家共同學習共同進步。

好了以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 交口县| 嘉鱼县| 蒙山县| 上蔡县| 咸阳市| 扎囊县| 新兴县| 营口市| 武山县| 东乡县| 同德县| 西乌| 滦平县| 姜堰市| 惠安县| 西华县| 穆棱市| 北票市| 甘德县| 庆云县| 涞源县| 永川市| 昌吉市| 陕西省| 盐池县| 辉县市| 临漳县| 瑞安市| 浏阳市| 侯马市| 秭归县| 正阳县| 灌南县| 上饶市| 榆社县| 合水县| 肥城市| 镇安县| 云和县| 顺昌县| 琼中|