2016-01-11 18 views
8

Vorrei implementare l'applicazione di streaming video list. Ho usato uno RecyclerView per visualizzare la mia voce di elenco. Il tipo di oggetto include 4 tipi: articolo, stato, foto e video. Dovremmo concentrarci solo sul tipo di video. Ecco il mio codice per l'adattatore di RecyclerView:Streaming di un elenco di video utilizzando 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); 
    } 
} 
  • Quando tipo di elemento è video, creo un'istanza ExoPlayer per riprodurre il video e aggiungere questa istanza a un HashMap per un uso successivo (con valore di chiave è la voce del posizione). Se un elemento video viene riciclato, rilascia il lettore e lo rimuovo da HashMap.

  • Tutto sembra buono ma un problema. Suppongo che l'elemento video sia nella posizione 0 (ora possiamo vedere l'anteprima del video in questo articolo). Scorro lo RecyclerView quanto basta per nascondere l'elemento 0. In questo momento, VideoViewHolder dell'articolo 0 non è ancora stato riciclato. Quindi, scorro verso l'alto per rivelare l'elemento 0. La vista è ora vuota, non mostra l'anteprima del video. Faccio clic sulla voce 0 per riprodurre il video, il lettore riproduce solo audio (audio), non mostra video. Dopo aver riprodotto alcuni secondi, il video è ora visibile.

  • Ho eseguito il debug e ho scoperto che dopo aver fatto scorrere verso il basso per nascondere l'elemento video, lo SurfaceView è stato distrutto. Quando scorri verso l'alto per visualizzare l'elemento video, viene creato lo SurfaceView. Penso che questo sia il motivo per cui VideoViewHolder mostra una vista vuota invece dell'anteprima del video.

  • Ecco la mia domanda: come visualizzare l'anteprima del video dopo lo scorrimento all'elemento video?

+0

Hai trovato qualche soluzione?! Anch'io ho questo problema – Hani

risposta

0

Ecco nota ExoPlayer documentation

resa SurfaceView non è stato correttamente sincronizzato con vista animazioni, fino Android N. Su versioni precedenti questo potrebbe portare a effetti indesiderati quando un SurfaceView è stato posto in un contenitore di scorrimento o quando è stato sottoposto ad animazione. Tali effetti includevano il contenuto di SurfaceView che appariva leggermente indietro rispetto a dove dovrebbe essere visualizzato e la vista diventa nera quando viene sottoposta ad animazione.

Per ottenere un'animazione fluida o lo scorrimento di video prima di Android N, è quindi necessario utilizzare TextureView anziché SurfaceView. Se animazioni fluide o lo scorrimento non è richiesto quindi SurfaceView dovrebbe essere preferito

Per abilitare TextureView surface_type è necessario impostarla nel file XML come indicato qui sotto

<com.google.android.exoplayer2.ui.SimpleExoPlayerView 
    android:id="@+id/playerView" 
    android:layout_width="match_parent" 
    android:layout_height="200dp" 
    app:surface_type="texture_view"/>