2016-01-11 33 views
8

Tôi muốn triển khai ứng dụng danh sách phát video. Tôi đã sử dụng một số RecyclerView để hiển thị mục danh sách của mình. Loại mục bao gồm 4 loại: Điều, Trạng thái, Ảnh và Video. Chúng ta chỉ nên tập trung vào loại Video. Đây là mã của tôi cho bộ chuyển đổi RecyclerView của:Danh sách phát video bằng Exoplayer

public class FollowedPostAdapter extends RecyclerView.Adapter implements OnFollowTagCallback, OnLikeCallback { 
    private Context context; 
    private List<PostItem> newsFeedList; 
    public RecyclerView recyclerView; 
    public LinearLayoutManager linearLayoutManager; 
    private HashMap<Integer, DemoPlayer> playerList; 

    private int visibleThreshold = 5; 
    // private int previousTotal = 0; 
    private int visibleItemCount, firstVisibleItem, totalItemCount; 
    private boolean loading; 
    private OnRecyclerViewLoadMoreListener loadMoreListener; 


    private final String text_comment; 
    private final String mReadMoreHtml; 
    private long userId; 


    public FollowedPostAdapter(Context context, RecyclerView recyclerView, List<PostItem> newsFeedList) { 
     this.context = context; 
     playerList = new HashMap<>(); 
     this.newsFeedList = newsFeedList; 
     this.recyclerView = recyclerView; 
     this.linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); 

    } 

    @Override 
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
     RecyclerView.ViewHolder viewHolder; 
     if (viewType == Constants.VIEWTYPE_ARTICLE) { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_article_item, parent, false); 
      viewHolder = new ArticleViewHolder(view); 
     } else if (viewType == Constants.VIEWTYPE_STATUS) { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_status_item, parent, false); 
      viewHolder = new StatusViewHolder(view); 
     } else if (viewType == Constants.VIEWTYPE_PHOTO) { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_photo_item, parent, false); 
      viewHolder = new PhotoViewHolder(view); 
     } else if (viewType == Constants.VIEWTYPE_VIDEO) { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_followed_video_item, parent, false); 
      viewHolder = new VideoViewHolder(view); 
     } else { 
      View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.progressbar_item, parent, false); 
      viewHolder = new ProgressBarViewHolder(view); 
     } 
     return viewHolder; 
    } 

    @Override 
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
     if (holder instanceof ArticleViewHolder) { 
      // code 

     } else if (holder instanceof StatusViewHolder) { 
     // code 

     } else if (holder instanceof PhotoViewHolder) { 
     // code 
     } else if (holder instanceof VideoViewHolder) { 
      PostItem item = newsFeedList.get(position); 
      VideoViewHolder mHolder = (VideoViewHolder) holder; 
      LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mHolder.videoView.getLayoutParams(); 
      lp.height = (int) (ScreenHelper.getScreenWidth((Activity) context) * Constants.POST_IMAGE_RATIO); 
      mHolder.videoView.setLayoutParams(lp); 
      mHolder.setup(item); 
      Picasso.with(context).load(item.imageCover).error(R.drawable.ic_user_avatar).placeholder(R.drawable.ic_user_avatar).into(mHolder.iv_avatar); 
      if (item.tags != null && item.tags.size() > 0) { 
       // get first tag as main tag 
       generateTagViews(mHolder.tag_flow_layout, item.tags.subList(0, 1), position); 
       mHolder.tag_flow_layout.setVisibility(View.VISIBLE); 
       mHolder.tag_flow_layout.setVisibility(View.VISIBLE); 
       // mHolder.indicator.setVisibility(View.VISIBLE); 
      } else { 
       mHolder.tag_flow_layout.setVisibility(View.GONE); 
       // mHolder.indicator.setVisibility(View.GONE); 
      } 
      if (item.time_created != null) { 
       mHolder.tv_time.setText(item.time_created); 
       //mHolder.indicator.setVisibility(View.VISIBLE); 
       mHolder.tv_time.setVisibility(View.VISIBLE); 
      } else { 
       //mHolder.indicator.setVisibility(View.GONE); 
       mHolder.tv_time.setVisibility(View.GONE); 
      } 
      if (item.description_short.isEmpty()) 
       mHolder.tv_description.setVisibility(View.GONE); 
      else { 
       mHolder.tv_description.setText(item.description_short); 
       mHolder.tv_description.setVisibility(View.VISIBLE); 
      } 

      mHolder.btn_comment.setText(String.valueOf(item.count_comment)); 
      mHolder.btn_like.setText(String.valueOf(item.count_like)); 
      mHolder.btn_unlike.setText(String.valueOf(item.count_unlike)); 
      mHolder.btn_share.setText(String.valueOf(item.count_share)); 
      if (item.tags.size() != 0) { 
       int tagId = item.tags.get(0).tag_id; 
       setFollowButtonActive(mHolder.btn_follow, TagHelper.isTagFollowed(tagId)); 
      } else 
       setFollowButtonActive(mHolder.btn_follow, false); 

     } 

    } 

    @Override 
    public void onViewRecycled(RecyclerView.ViewHolder holder) { 
     super.onViewRecycled(holder); 
     if (holder instanceof VideoViewHolder) { 
      DemoPlayer player = playerList.get(holder.getAdapterPosition()); 
      if (player != null) { 
       player.release(); 
       playerList.remove(holder.getAdapterPosition()); 
      } 
     } 
    } 


    public void pauseAllPlayers() { 
     for (int i = 0; i <= newsFeedList.size(); i++) { 
      DemoPlayer player = playerList.get(i); 
      if (player != null) { 
       if (player.getPlayerControl().isPlaying()) 
        player.getPlayerControl().pause(); 
       RecyclerView.ViewHolder holder = recyclerView.findViewHolderForLayoutPosition(i); 
       if (holder != null && holder instanceof VideoViewHolder) { 
        ((VideoViewHolder) holder).btn_play.setVisibility(View.VISIBLE); 
       } 

      } 
     } 
    } 





    public void refreshData() { 
     notifyDataSetChanged(); 
    } 

    public class ArticleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 
     // 
    } 

    public class StatusViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 

     // code 
    } 

    public class PhotoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 
     // code 
    } 

    public class VideoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, SurfaceHolder.Callback, AudioCapabilitiesReceiver.Listener, DemoPlayer.Listener, DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener { 
     @Bind(R.id.iv_avatar) 
     ImageView iv_avatar; 
     @Bind(R.id.tag_flow_layout) 
     FlowLayout tag_flow_layout; 
     @Bind(R.id.tv_time) 
     TextView tv_time; 
     @Bind(R.id.btn_follow) 
     FancyButton btn_follow; 
     @Bind(R.id.btn_comment) 
     FancyButton btn_comment; 
     @Bind(R.id.btn_like) 
     FancyButton btn_like; 
     @Bind(R.id.btn_unlike) 
     FancyButton btn_unlike; 
     @Bind(R.id.btn_share) 
     FancyButton btn_share; 
     @Bind(R.id.root) 
     FrameLayout videoView; 
     @Bind(R.id.btn_play) 
     ImageView btn_play; 
     @Bind(R.id.tv_description) 
     TextView tv_description; 

     // player's variable 
     private EventLogger eventLogger; 

     // private VideoControllerView mediaController; 
     private View shutterView; 
     private AspectRatioFrameLayout videoFrame; 
     private SurfaceView surfaceView; 
     private SubtitleLayout subtitleLayout; 

     private DemoPlayer player; 
     private boolean playerNeedsPrepare; 

     private long playerPosition = 0; 

     private Uri contentUri; 
     private int contentType; 
     private String contentId; 

     public static final int TYPE_DASH = 0; 
     public static final int TYPE_SS = 1; 
     public static final int TYPE_HLS = 2; 
     public static final int TYPE_OTHER = 3; 

     private static final String EXT_DASH = ".mpd"; 
     private static final String EXT_SS = ".ism"; 
     private static final String EXT_HLS = ".m3u8"; 

     private AudioCapabilitiesReceiver audioCapabilitiesReceiver; 

     public VideoViewHolder(View view) { 
      super(view); 
      ButterKnife.bind(this, view); 
      iv_avatar.setOnClickListener(this); 
      btn_follow.setOnClickListener(this); 
      btn_comment.setOnClickListener(this); 
      btn_like.setOnClickListener(this); 
      btn_unlike.setOnClickListener(this); 
      btn_share.setOnClickListener(this); 

      // player's setup 
      View root = view.findViewById(R.id.root); 
      root.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        doPlayResume(); 
       } 
      }); 
//   root.setOnTouchListener(new View.OnTouchListener() { 
//    @Override 
//    public boolean onTouch(View view, MotionEvent motionEvent) { 
//     if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { 
//      toggleControlsVisibility(); 
//     } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { 
//      view.performClick(); 
//     } 
//     return true; 
//    } 
//   }); 
//   root.setOnKeyListener(new View.OnKeyListener() { 
//    @Override 
//    public boolean onKey(View v, int keyCode, KeyEvent event) { 
//     if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE 
//       || keyCode == KeyEvent.KEYCODE_MENU) { 
//      return false; 
//     } 
//     return mediaController.dispatchKeyEvent(event); 
//    } 
//   }); 
      shutterView = view.findViewById(R.id.shutter); 
      videoFrame = (AspectRatioFrameLayout) view.findViewById(R.id.video_frame); 
      surfaceView = (SurfaceView) view.findViewById(R.id.surface_view); 
      surfaceView.getHolder().addCallback(this); 
      subtitleLayout = (SubtitleLayout) view.findViewById(R.id.subtitles); 
//   mediaController = new VideoControllerView(context); 
//   mediaController.setAnchorView((ViewGroup) root); 
      audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(context, this); 
      audioCapabilitiesReceiver.register(); 
     } 

     @Override 
     public void onClick(View v) { 
      switch (v.getId()) { 
       case R.id.iv_avatar: 
        viewTagDetail(getAdapterPosition()); 
        break; 
       case R.id.btn_follow: 
        followTag((FancyButton) v, getAdapterPosition()); 
        break; 
       case R.id.btn_comment: 
        commentPost(getAdapterPosition()); 
        break; 
       case R.id.btn_like: 
        likePost(getAdapterPosition(), btn_like, btn_unlike); 
        break; 
       case R.id.btn_unlike: 
        unlikePost(getAdapterPosition(), btn_like, btn_unlike); 
        break; 
       case R.id.btn_share: 
        sharePost(getAdapterPosition()); 
        break; 
       case R.id.btn_play: 
        doPlayResume(); 
        break; 
      } 
     } 

     public void setup(PostItem item) { 
      releasePlayer(); 
      player = playerList.get(getAdapterPosition()); 
      contentUri = Uri.parse(item.link); 
      contentType = TYPE_OTHER; 
      contentId = String.valueOf(item.post_id); 
      configureSubtitleView(); 
      if (player == null) { 
       preparePlayer(false); 
      } else { 
       player.setBackgrounded(false); 
      } 
     } 

     //  public void saveCurrentPosition() { 
//   if (player != null) 
//    videoItemList.get(getAdapterPosition()).position = player.getCurrentPosition(); 
//  } 
     public void doPlayResume() { 
      if (player == null) { 
       return; 
      } 

      if (player.getPlayerControl().isPlaying()) { 
       player.getPlayerControl().pause(); 
      } else { 
       player.getPlayerControl().start(); 
      } 
      showControls(); 
     } 

     @Override 
     public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) { 
      if (player == null) { 
       return; 
      } 
      boolean backgrounded = player.getBackgrounded(); 
      boolean playWhenReady = player.getPlayWhenReady(); 
      releasePlayer(); 
      preparePlayer(playWhenReady); 
      player.setBackgrounded(backgrounded); 
     } 

     private DemoPlayer.RendererBuilder getRendererBuilder() { 
      String userAgent = Util.getUserAgent(context, "ExoPlayerDemo"); 
      switch (contentType) { 
       case TYPE_SS: 
        return new SmoothStreamingRendererBuilder(context, userAgent, contentUri.toString(), 
          new SmoothStreamingTestMediaDrmCallback()); 
       case TYPE_DASH: 
        return new DashRendererBuilder(context, userAgent, contentUri.toString(), 
          new WidevineTestMediaDrmCallback(contentId)); 
       case TYPE_HLS: 
        return new HlsRendererBuilder(context, userAgent, contentUri.toString()); 
       case TYPE_OTHER: 
        return new ExtractorRendererBuilder(context, userAgent, contentUri); 
       default: 
        throw new IllegalStateException("Unsupported type: " + contentType); 
      } 
     } 

     private void preparePlayer(boolean playWhenReady) { 
      if (player == null) { 
       player = new DemoPlayer(getRendererBuilder()); 
       playerList.put(getAdapterPosition(), player); 
       player.addListener(this); 
       player.setCaptionListener(this); 
       player.setMetadataListener(this); 
       player.seekTo(playerPosition); 
       playerNeedsPrepare = true; 
//    mediaController.setMediaPlayer(player.getPlayerControl()); 
//    mediaController.setEnabled(true); 
       eventLogger = new EventLogger(); 
       eventLogger.startSession(); 
       player.addListener(eventLogger); 
       player.setInfoListener(eventLogger); 
       player.setInternalErrorListener(eventLogger); 
      } 
      if (playerNeedsPrepare) { 
       player.prepare(); 
       playerNeedsPrepare = false; 
      } 
      player.setSurface(surfaceView.getHolder().getSurface()); 
      player.setPlayWhenReady(playWhenReady); 
     } 

     private void releasePlayer() { 
      if (player != null) { 
       player.release(); 
       player = null; 
       eventLogger.endSession(); 
       eventLogger = null; 
       btn_play.setVisibility(View.VISIBLE); 
      } 
     } 

     @Override 
     public void onStateChanged(boolean playWhenReady, int playbackState) { 
      if (playbackState == ExoPlayer.STATE_ENDED) { 
       showControls(); 
      } 

     } 

     @Override 
     public void onError(Exception e) { 
      if (e instanceof UnsupportedDrmException) { 
       // Special case DRM failures. 
       UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e; 
       int stringId = Util.SDK_INT < 18 ? R.string.drm_error_not_supported 
         : unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME 
         ? R.string.drm_error_unsupported_scheme : R.string.drm_error_unknown; 
       Toast.makeText(context, stringId, Toast.LENGTH_LONG).show(); 
      } 
      playerNeedsPrepare = true; 
      showControls(); 
     } 

     @Override 
     public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { 
      shutterView.setVisibility(View.GONE); 
      videoFrame.setAspectRatio(
        height == 0 ? 1 : (width * pixelWidthHeightRatio)/height); 
     } 

     private boolean haveTracks(int type) { 
      return player != null && player.getTrackCount(type) > 0; 
     } 

//  private void toggleControlsVisibility() { 
//   if (mediaController.isShowing()) { 
//    mediaController.hide(); 
//   } else { 
//    showControls(); 
//   } 
//  } 

     private void showControls() { 
//   mediaController.show(5000); 
      if (player.getPlayerControl().isPlaying()) 
       btn_play.setVisibility(View.GONE); 
      else 
       btn_play.setVisibility(View.VISIBLE); 

     } 

     @Override 
     public void onCues(List<Cue> cues) { 
      subtitleLayout.setCues(cues); 
     } 

     @Override 
     public void onId3Metadata(Map<String, Object> metadata) { 

     } 

     @Override 
     public void surfaceCreated(SurfaceHolder holder) { 
      if (player != null) { 
       player.setSurface(holder.getSurface()); 
      } 
     } 

     @Override 
     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 
     } 

     @Override 
     public void surfaceDestroyed(SurfaceHolder holder) { 
      if (player != null) { 
       holder.lockCanvas(); 
       player.blockingClearSurface(); 
      } 
     } 

     private void configureSubtitleView() { 
      CaptionStyleCompat style; 
      float fontScale; 
      if (Util.SDK_INT >= 19) { 
       style = getUserCaptionStyleV19(); 
       fontScale = getUserCaptionFontScaleV19(); 
      } else { 
       style = CaptionStyleCompat.DEFAULT; 
       fontScale = 1.0f; 
      } 
      subtitleLayout.setStyle(style); 
      subtitleLayout.setFractionalTextSize(SubtitleLayout.DEFAULT_TEXT_SIZE_FRACTION * fontScale); 
     } 

     @TargetApi(19) 
     private float getUserCaptionFontScaleV19() { 
      CaptioningManager captioningManager = 
        (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); 
      return captioningManager.getFontScale(); 
     } 

     @TargetApi(19) 
     private CaptionStyleCompat getUserCaptionStyleV19() { 
      CaptioningManager captioningManager = 
        (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); 
      return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle()); 
     } 

     private int inferContentType(Uri uri, String fileExtension) { 
      String lastPathSegment = !TextUtils.isEmpty(fileExtension) ? "." + fileExtension 
        : uri.getLastPathSegment(); 
      if (lastPathSegment == null) { 
       return TYPE_OTHER; 
      } else if (lastPathSegment.endsWith(EXT_DASH)) { 
       return TYPE_DASH; 
      } else if (lastPathSegment.endsWith(EXT_SS)) { 
       return TYPE_SS; 
      } else if (lastPathSegment.endsWith(EXT_HLS)) { 
       return TYPE_HLS; 
      } else { 
       return TYPE_OTHER; 
      } 
     } 
    } 

    public class ProgressBarViewHolder extends RecyclerView.ViewHolder { 
     public ProgressBarViewHolder(View view) { 
      super(view); 
     } 
    } 

    public void unregisterEventBus() { 
     EventBus.getDefault().unregister(this); 
    } 
} 
  • Khi loại mục là video, tôi có thể tạo một thể hiện ExoPlayer để chơi video này và thêm ví dụ này cho một người HashMap để sử dụng sau (với giá trị chính là mục của Chức vụ). Nếu một mục video đang được tái chế, tôi giải phóng trình phát và xóa nó khỏi HashMap.

  • Mọi thứ có vẻ tốt nhưng là một vấn đề. Tôi cho rằng mục video ở vị trí 0 (bây giờ chúng ta có thể xem bản xem trước video trong mục này). Tôi cuộn xuống RecyclerView vừa đủ để ẩn mục 0. Tại thời điểm này, VideoViewHolder của mục 0 chưa được tái chế. Sau đó, tôi cuộn lên để tiết lộ mục 0. Khung nhìn giờ đây trống, không hiển thị xem trước video. Tôi nhấp vào mục 0 để phát video, trình phát chỉ phát âm thanh (âm thanh), không hiển thị video. Sau khi phát một vài giây, video hiện có thể hiển thị.

  • Tôi đã gỡ rối và thấy rằng sau khi tôi cuộn xuống để ẩn mục video, số SurfaceView bị hủy. Khi cuộn lên để hiển thị mục video, số SurfaceView được tạo. Tôi nghĩ rằng đây là lý do tại sao VideoViewHolder hiển thị chế độ xem trống thay vì xem trước video.

  • Đây là câu hỏi của tôi: Cách hiển thị xem trước video sau khi cuộn lại mục video?

+0

Bạn đã tìm thấy giải pháp nào?! Tôi có vấn đề này – Hani

Trả lời

0

Đây là lưu ý từ ExoPlayer documentation

SurfaceView vẽ không đúng cách đồng bộ với quan điểm hình ảnh động cho đến khi Android N. Mở phiên bản trước này có thể dẫn đến tác dụng không mong muốn khi một SurfaceView đã được đặt vào cuộn chứa hoặc khi nó bị ảnh hưởng. Các hiệu ứng như vậy bao gồm nội dung của SurfaceView xuất hiện để tụt hậu một chút so với vị trí hiển thị của nó và chế độ xem chuyển sang màu đen khi chịu ảnh động.

Để đạt được hoạt ảnh mượt mà hoặc cuộn video trước Android N, do đó, cần sử dụng TextureView thay vì SurfaceView. Nếu hình ảnh động trơn tru hoặc di chuyển không cần thiết sau đó SurfaceView nên được ưa thích

Để kích hoạt TextureView surface_type bạn phải thiết lập nó trong file xml của bạn như hình dưới đây

<com.google.android.exoplayer2.ui.SimpleExoPlayerView 
    android:id="@+id/playerView" 
    android:layout_width="match_parent" 
    android:layout_height="200dp" 
    app:surface_type="texture_view"/> 
Các vấn đề liên quan