I am working on integrating some video into one of my Activities. It plays fine, but I have to click play for it to actually play. I searched the forums and the developer site and cannot find anything relating to autoplay or autostart. Any suggestions?
MainActivity.java
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
VideoView mVideoView = (VideoView) findViewById(R.id.videoView);
mVideoView.setVideoPath("file:///android_asset/video1.MP4");
mVideoView.setMediaController(new MediaController(this));
mVideoView.seekTo(1);
//mVideoView.requestFocus();
}
My contain_main.xml
<fragment 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:id="#+id/fragment"
android:name="imaker.MediaAutoPlay.MainActivityFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:layout="#layout/fragment_main">
<VideoView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/videoView"
android:layout_height="fill_parent"
android:layout_width="fill_parent" />
</fragment>
mVideoView.start() for start video
to start video
or you can also use
mVideoView.seekTo(1)
Auto Play Video like same as Facebook Youtube popular apps Popular Apps
Build.Gradle app module
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
implementation 'org.jsoup:jsoup:1.10.3'
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
Auto Play
public class ExoPlayerRecyclerView extends RecyclerView {
private static final String TAG = "ExoPlayerRecyclerView";
private static final String AppName = "Android ExoPlayer";
/**
* PlayerViewHolder UI component
* Watch PlayerViewHolder class
*/
private ImageView mediaCoverImage, volumeControl;
private ProgressBar progressBar;
private View viewHolderParent;
private FrameLayout mediaContainer;
private PlayerView videoSurfaceView;
private SimpleExoPlayer videoPlayer;
/**
* variable declaration
*/
// Media List
private ArrayList<MediaObject> mediaObjects = new ArrayList<>();
private int videoSurfaceDefaultHeight = 0;
private int screenDefaultHeight = 0;
private Context context;
private int playPosition = -1;
private boolean isVideoViewAdded;
private RequestManager requestManager;
// controlling volume state
private VolumeState volumeState;
private OnClickListener videoViewClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
toggleVolume();
}
};
public ExoPlayerRecyclerView(#NonNull Context context) {
super(context);
init(context);
}
public ExoPlayerRecyclerView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
this.context = context.getApplicationContext();
Display display = ((WindowManager) Objects.requireNonNull(
getContext().getSystemService(Context.WINDOW_SERVICE))).getDefaultDisplay();
Point point = new Point();
display.getSize(point);
videoSurfaceDefaultHeight = point.x;
screenDefaultHeight = point.y;
videoSurfaceView = new PlayerView(this.context);
videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
//Create the player using ExoPlayerFactory
videoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
// Disable Player Control
videoSurfaceView.setUseController(false);
// Bind the player to the view.
videoSurfaceView.setPlayer(videoPlayer);
// Turn on Volume
setVolumeControl(VolumeState.ON);
addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (mediaCoverImage != null) {
// show the old thumbnail
mediaCoverImage.setVisibility(VISIBLE);
}
// There's a special case when the end of the list has been reached.
// Need to handle that with this bit of logic
if (!recyclerView.canScrollVertically(1)) {
playVideo(true);
} else {
playVideo(false);
}
}
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
addOnChildAttachStateChangeListener(new OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(#NonNull View view) {
}
#Override
public void onChildViewDetachedFromWindow(#NonNull View view) {
if (viewHolderParent != null && viewHolderParent.equals(view)) {
resetVideoView();
}
}
});
videoPlayer.addListener(new Player.EventListener() {
#Override
public void onTimelineChanged(Timeline timeline, #Nullable Object manifest, int reason) {
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups,
TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
switch (playbackState) {
case Player.STATE_BUFFERING:
Log.e(TAG, "onPlayerStateChanged: Buffering video.");
if (progressBar != null) {
progressBar.setVisibility(VISIBLE);
}
break;
case Player.STATE_ENDED:
Log.d(TAG, "onPlayerStateChanged: Video ended.");
videoPlayer.seekTo(0);
break;
case Player.STATE_IDLE:
break;
case Player.STATE_READY:
Log.e(TAG, "onPlayerStateChanged: Ready to play.");
if (progressBar != null) {
progressBar.setVisibility(GONE);
}
if (!isVideoViewAdded) {
addVideoView();
}
break;
default:
break;
}
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
});
}
public void playVideo(boolean isEndOfList) {
int targetPosition;
if (!isEndOfList) {
int startPosition = ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
// if there is more than 2 list-items on the screen, set the difference to be 1
if (endPosition - startPosition > 1) {
endPosition = startPosition + 1;
}
// something is wrong. return.
if (startPosition < 0 || endPosition < 0) {
return;
}
// if there is more than 1 list-item on the screen
if (startPosition != endPosition) {
int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
targetPosition =
startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
} else {
targetPosition = startPosition;
}
} else {
targetPosition = mediaObjects.size() - 1;
}
Log.d(TAG, "playVideo: target position: " + targetPosition);
// video is already playing so return
if (targetPosition == playPosition) {
return;
}
// set the position of the list-item that is to be played
playPosition = targetPosition;
if (videoSurfaceView == null) {
return;
}
// remove any old surface views from previously playing videos
videoSurfaceView.setVisibility(INVISIBLE);
removeVideoView(videoSurfaceView);
int currentPosition =
targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
View child = getChildAt(currentPosition);
if (child == null) {
return;
}
PlayerViewHolder holder = (PlayerViewHolder) child.getTag();
if (holder == null) {
playPosition = -1;
return;
}
mediaCoverImage = holder.mediaCoverImage;
progressBar = holder.progressBar;
volumeControl = holder.volumeControl;
viewHolderParent = holder.itemView;
requestManager = holder.requestManager;
mediaContainer = holder.mediaContainer;
videoSurfaceView.setPlayer(videoPlayer);
viewHolderParent.setOnClickListener(videoViewClickListener);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(
context, Util.getUserAgent(context, AppName));
String mediaUrl = mediaObjects.get(targetPosition).getUrl();
if (mediaUrl != null) {
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(mediaUrl));
videoPlayer.prepare(videoSource);
videoPlayer.setPlayWhenReady(true);
}
}
/**
* Returns the visible region of the video surface on the screen.
* if some is cut off, it will return less than the #videoSurfaceDefaultHeight
*/
private int getVisibleVideoSurfaceHeight(int playPosition) {
int at = playPosition - ((LinearLayoutManager) Objects.requireNonNull(
getLayoutManager())).findFirstVisibleItemPosition();
Log.d(TAG, "getVisibleVideoSurfaceHeight: at: " + at);
View child = getChildAt(at);
if (child == null) {
return 0;
}
int[] location = new int[2];
child.getLocationInWindow(location);
if (location[1] < 0) {
return location[1] + videoSurfaceDefaultHeight;
} else {
return screenDefaultHeight - location[1];
}
}
// Remove the old player
private void removeVideoView(PlayerView videoView) {
ViewGroup parent = (ViewGroup) videoView.getParent();
if (parent == null) {
return;
}
int index = parent.indexOfChild(videoView);
if (index >= 0) {
parent.removeViewAt(index);
isVideoViewAdded = false;
viewHolderParent.setOnClickListener(null);
}
}
private void addVideoView() {
mediaContainer.addView(videoSurfaceView);
isVideoViewAdded = true;
videoSurfaceView.requestFocus();
videoSurfaceView.setVisibility(VISIBLE);
videoSurfaceView.setAlpha(1);
mediaCoverImage.setVisibility(GONE);
}
private void resetVideoView() {
if (isVideoViewAdded) {
removeVideoView(videoSurfaceView);
playPosition = -1;
videoSurfaceView.setVisibility(INVISIBLE);
mediaCoverImage.setVisibility(VISIBLE);
}
}
public void releasePlayer() {
if (videoPlayer != null) {
videoPlayer.release();
videoPlayer = null;
}
viewHolderParent = null;
}
public void onPausePlayer() {
if (videoPlayer != null) {
videoPlayer.stop(true);
}
}
private void toggleVolume() {
if (videoPlayer != null) {
if (volumeState == VolumeState.OFF) {
Log.d(TAG, "togglePlaybackState: enabling volume.");
setVolumeControl(VolumeState.ON);
} else if (volumeState == VolumeState.ON) {
Log.d(TAG, "togglePlaybackState: disabling volume.");
setVolumeControl(VolumeState.OFF);
}
}
}
//public void onRestartPlayer() {
// if (videoPlayer != null) {
// playVideo(true);
// }
//}
private void setVolumeControl(VolumeState state) {
volumeState = state;
if (state == VolumeState.OFF) {
videoPlayer.setVolume(0f);
animateVolumeControl();
} else if (state == VolumeState.ON) {
videoPlayer.setVolume(1f);
animateVolumeControl();
}
}
private void animateVolumeControl() {
if (volumeControl != null) {
volumeControl.bringToFront();
if (volumeState == VolumeState.OFF) {
requestManager.load(R.drawable.ic_volume_off)
.into(volumeControl);
} else if (volumeState == VolumeState.ON) {
requestManager.load(R.drawable.ic_volume_on)
.into(volumeControl);
}
volumeControl.animate().cancel();
volumeControl.setAlpha(1f);
volumeControl.animate()
.alpha(0f)
.setDuration(600).setStartDelay(1000);
}
}
public void setMediaObjects(ArrayList<MediaObject> mediaObjects) {
this.mediaObjects = mediaObjects;
}
/**
* Volume ENUM
*/
private enum VolumeState {
ON, OFF
}
}
Related
I am implementing a functionality in which I need to hit API again to fetch data while the list reaches to its end. I have used onScrollListener of RecyclerView to detect whether it reaches to end or not but code only runs first time when I opens the screen but it not work when it reaches to screens end.
How can I check when list reaches to its end.
Fragment Code:
#SuppressLint("RestrictedApi")
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_home, container, false);
initScrollListener();
}
private void initScrollListener() {
list.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
Toast.makeText(getActivity(), "Loading", Toast.LENGTH_SHORT).show();
if (!isLoading) {
if (linearLayoutManager != null && linearLayoutManager.findLastCompletelyVisibleItemPosition() == rowsArrayList.size() - 1) {
//bottom of list!
loadMore();
isLoading = true;
}
}
}
});
}
private void loadMore() {
rowsArrayList.add(null);
postListAdapter.notifyItemInserted(rowsArrayList.size() - 1);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
rowsArrayList.remove(rowsArrayList.size() - 1);
int scrollPosition = rowsArrayList.size();
postListAdapter.notifyItemRemoved(scrollPosition);
int currentSize = scrollPosition;
int nextLimit = currentSize + 10;
while (currentSize - 1 < nextLimit) {
rowsArrayList.add("Item " + currentSize);
currentSize++;
}
postListAdapter.notifyDataSetChanged();
isLoading = false;
}
}, 2000);
}
Adapter Code:
public class PostListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
Context context;
List<StoriesDatum> storiesDatumList;
StoriesDatum storiesDatum;
FragmentHome fragmentHome;
int qty;
View itemView;
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_LOADING = 1;
public PostListAdapter(Context context, List<StoriesDatum> storiesDatumList, FragmentHome fragmentHome) {
this.context = context;
this.storiesDatumList = storiesDatumList;
this.fragmentHome = fragmentHome;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_post, parent, false);
return new MyViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false);
return new LoadingViewHolder(view);
}
}
#Override
public void onBindViewHolder(#NonNull final RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof MyViewHolder) {
populateItemRows((MyViewHolder) holder, position);
} else if (holder instanceof LoadingViewHolder) {
showLoadingView((LoadingViewHolder) holder, position);
}
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return storiesDatumList.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
}
#Override
public int getItemCount() {
return storiesDatumList.size();
}
private class LoadingViewHolder extends RecyclerView.ViewHolder {
ProgressBar progressBar;
public LoadingViewHolder(#NonNull View itemView) {
super(itemView);
progressBar = itemView.findViewById(R.id.progressBar);
}
}
private void showLoadingView(LoadingViewHolder viewHolder, int position) {
//ProgressBar would be displayed
}
private void populateItemRows(MyViewHolder myViewHolder, final int position) {
String item = storiesDatumList.get(position).toString();
storiesDatum = storiesDatumList.get(position);
myViewHolder.txtCommunityName.setText(storiesDatum.getComTitle());
myViewHolder.txtPostTitle.setText(storiesDatum.getTitle());
String comm_photo = String.valueOf(storiesDatum.getComImage());
if (comm_photo == null || comm_photo.equals("null") || comm_photo.equals("")) {
myViewHolder.ivImage.setImageResource(R.mipmap.top_communities_circle);
} else {
if (comm_photo.startsWith("https://") || comm_photo.startsWith("http://")) {
long interval = 5000 * 1000;
Glide.with(context).load(comm_photo)
.into(myViewHolder.ivImage);
} else {
Glide.with(context).load(Constants.image_url + comm_photo)
.into(myViewHolder.ivImage);
}
}
myViewHolder.txtNamePosted.setText("Posted by " + storiesDatum.getUsername());
String created_date = storiesDatum.getCreatedAt();
//call date
String corecDate = fragmentHome.createDate(created_date);
myViewHolder.txtTime.setText(corecDate);
myViewHolder.txtCommentCount.setText(storiesDatum.getCommentsCount() + " Comments");
myViewHolder.txtLikeCount.setText(storiesDatum.getStoryemojiCount() + " Reactions");
/*change code for adapter*/
myViewHolder.tvPost.setText(storiesDatum.getSnapshots());
if (storiesDatum.getStoryBody() != null) {
myViewHolder.tvBody.setVisibility(View.VISIBLE);
myViewHolder.tvBody.setText(storiesDatum.getStoryBody());
} else {
myViewHolder.tvBody.setVisibility(View.GONE);
}
if (storiesDatum.getStoryType() == 2) {
myViewHolder.videoPost.setVisibility(View.GONE);
myViewHolder.youTubePlayerView.setVisibility(View.GONE);
if (storiesDatum.getStoryUpload() == null && (storiesDatum.getStoryUrl() != null)) {
myViewHolder.ivImageViaLink.setVisibility(View.VISIBLE);
myViewHolder.ivPostImage.setVisibility(View.GONE);
String story_url = storiesDatum.getStoryUrl().toString();
Log.e("TAG", "onResponse:story1 " + story_url);
if (story_url.startsWith("https://") || story_url.startsWith("http://")) {
Glide.with(context).load(story_url)
.into(myViewHolder.ivImageViaLink);
} else {
Glide.with(context).load(story_url)
// .placeholder(R.mipmap.top_communities_circle)
//.error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivImageViaLink);
}
} else if (storiesDatum.getStoryUrl() == null && (storiesDatum.getStoryUpload() != null)) {
myViewHolder.ivImageViaLink.setVisibility(View.GONE);
myViewHolder.ivPostImage.setVisibility(View.VISIBLE);
String story_url = storiesDatum.getStoryUpload().toString();
Log.e("TAG", "onResponse:story2 " + story_url);
if (story_url.startsWith("https://") || story_url.startsWith("http://")) {
Glide.with(context).load(story_url)
// .placeholder(R.mipmap.top_communities_circle)
//.error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivPostImage);
} else {
Glide.with(context).load(Constants.image_url + story_url)
// .placeholder(R.mipmap.top_communities_circle)
// .error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivPostImage);
}
} else {
myViewHolder.ivImageViaLink.setVisibility(View.VISIBLE);
myViewHolder.ivPostImage.setVisibility(View.VISIBLE);
String story_upload = storiesDatum.getStoryUpload().toString();
Log.e("TAG", "onResponse:story3 " + story_upload);
if (story_upload.startsWith("https://") || story_upload.startsWith("http://")) {
Glide.with(context).load(story_upload)
//.placeholder(R.mipmap.top_communities_circle)
//.error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivPostImage);
} else {
Glide.with(context).load(Constants.image_url + story_upload)
// .placeholder(R.mipmap.top_communities_circle)
// .error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivPostImage);
}
String story_url = storiesDatum.getStoryUrl().toString();
Log.e("TAG", "onResponse:story4 " + story_url);
if (story_url.startsWith("https://") || story_url.startsWith("http://")) {
Glide.with(context).load(story_url)
//.placeholder(R.mipmap.top_communities_circle)
// .error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivImageViaLink);
} else {
Glide.with(context).load(story_url)
// .placeholder(R.mipmap.top_communities_circle)
// .error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivImageViaLink);
}
}
} else if (storiesDatum.getStoryType() == 3) {
myViewHolder.videoPost.setVisibility(View.GONE);
myViewHolder.youTubePlayerView.setVisibility(View.GONE);
if (storiesDatum.getStoryUpload() == null && (storiesDatum.getStoryUrl() != null)) {
myViewHolder.ivImageViaLink.setVisibility(View.VISIBLE);
myViewHolder.btn_play.setVisibility(View.VISIBLE);
myViewHolder.ivPostImage.setVisibility(View.GONE);
String story_url1 = storiesDatum.getStoryUrl().toString();
String[] cutLink = story_url1.split("frameborder");
String[] cutLink1 = cutLink[0].split("embed/");
String link = cutLink1[1];
final String played_link = link.split("\"")[0];
String story_url = "https://img.youtube.com/vi/" + played_link + "/0.jpg";
Log.e("TAG", "onResponse:story1 " + story_url);
if (story_url.startsWith("https://") || story_url.startsWith("http://")) {
Glide.with(context).load(story_url)
// .placeholder(R.mipmap.top_communities_circle)
// .error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivImageViaLink);
} else {
Glide.with(context).load(story_url)
// .placeholder(R.mipmap.top_communities_circle)
// .error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivImageViaLink);
}
} else if (storiesDatum.getStoryUrl() == null && (storiesDatum.getStoryUpload() != null)) {
myViewHolder.ivImageViaLink.setVisibility(View.GONE);
myViewHolder.ivPostImage.setVisibility(View.VISIBLE);
myViewHolder.play.setVisibility(View.VISIBLE);
String story_url = storiesDatum.getStoryUpload().toString();
Log.e("TAG", "onResponse:story2 " + story_url);
} else {
myViewHolder.ivImageViaLink.setVisibility(View.VISIBLE);
myViewHolder.ivPostImage.setVisibility(View.VISIBLE);
myViewHolder.btn_play.setVisibility(View.VISIBLE);
myViewHolder.play.setVisibility(View.VISIBLE);
String story_upload = storiesDatum.getStoryUpload().toString();
Uri videoURI;
videoURI = Uri.parse(Constants.image_url + story_upload);
Log.e("TAG", "onResponse:story3 " + videoURI);
String story_url1 = storiesDatum.getStoryUrl().toString();
String[] cutLink = story_url1.split("frameborder");
String[] cutLink1 = cutLink[0].split("embed/");
String link = cutLink1[1];
final String played_link = link.split("\"")[0];
String story_url = "https://img.youtube.com/vi/" + played_link + "/0.jpg";
Log.e("TAG", "onResponse:story4 " + story_url);
if (story_url.startsWith("https://") || story_url.startsWith("http://")) {
Glide.with(context).load(story_url)
// .placeholder(R.mipmap.top_communities_circle)
// .error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivImageViaLink);
} else {
Glide.with(context).load(story_url)
// .placeholder(R.mipmap.top_communities_circle)
// .error(R.drawable.ic_launcher_background)
.into(myViewHolder.ivImageViaLink);
}
}
// viewHolder.tvItem.setText(item);
myViewHolder.txtCommunityName.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
storiesDatum = storiesDatumList.get(position);
fragmentHome.sendToCommunityPage(storiesDatum.getComTitle(), storiesDatum.getCommunityId(), storiesDatum.getComImage());
}
});
myViewHolder.txtNamePosted.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
storiesDatum = storiesDatumList.get(position);
fragmentHome.sendToUserProfile(storiesDatum.getUserId(), storiesDatum.getUsername(), storiesDatum.getProfile_pic());
}
});
myViewHolder.ivAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
storiesDatum = storiesDatumList.get(position);
fragmentHome.addCommunity(storiesDatum.getCommunityId());
}
});
Log.e("tag", "jpined_favourite" + storiesDatum.getJoined() + storiesDatum.getFav());
if (storiesDatum.getJoined() == 0) {
myViewHolder.ivAdd.setVisibility(View.VISIBLE);
} else {
myViewHolder.ivAdd.setVisibility(View.GONE);
}
if (storiesDatum.getFav() == 1) {
myViewHolder.ivFav.setImageResource(R.mipmap.mark_as_favorite_filled);
} else {
myViewHolder.ivFav.setImageResource(R.mipmap.mark_favorite);
}
myViewHolder.txtComment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fragmentHome.openCommentbox();
}
});
myViewHolder.ivFav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
storiesDatum = storiesDatumList.get(position);
if (storiesDatum.getFav() == 0) {
fragmentHome.addFavt(storiesDatum.getCommunityId().toString(), "markfav");
} else {
fragmentHome.addFavt(storiesDatum.getCommunityId().toString(), "unmarkfav");
}
}
});
myViewHolder.relativeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//fragmentHome.sendToSinglePost();
StoriesDatum storiesDatum1 = storiesDatumList.get(position);
fragmentHome.sendToSinglePost(storiesDatum1.getId().toString());
}
});
myViewHolder.relativeShare.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
StoriesDatum storiesDatum1 = storiesDatumList.get(position);
fragmentHome.sharepost(storiesDatum1.getSlugid(), storiesDatum1.getSlug());
}
});
}
}
public class MyViewHolder extends RecyclerView.ViewHolder {
}
Use this method
RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) //check for scroll down
{
if (!mNoMoreLoad) {
if ((mLayoutManager.getChildCount() +
mLayoutManager.findFirstVisibleItemPosition()) >= mLayoutManager.getItemCount()) {
mPage++;
mNoMoreLoad = true;
loadMore(mPage);
}
}
}
}
private void loadMore(int mPage) {
if (Utils.getInstance().isNetworkAvailable(getActivity())) {
hit api with page number
}
}
after getting response check the size of new data with your data limit
if (response.getData().size() < Constants.DATA_LIMIT) {
mNoMoreLoad = true;
} else {
mNoMoreLoad = false;
}
You can do this in the following ways:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged( RecyclerView recyclerView, int newState) {
if(!recyclerView.canScrollVertically(1))
Log.e(TAG,TAG+"<<end-333->>"); //Here is one way
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled( RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
Log.e(TAG,TAG+"<<end-->>"+linearLayoutManager.findLastVisibleItemPosition());
if(linearLayoutManager.findLastVisibleItemPosition() == linearLayoutManager.getItemCount()-1)
{
Log.e(TAG,TAG+"<<end-22->>"+linearLayoutManager.findLastVisibleItemPosition()); //Here is another way
}
}
});
}
I am currently working on an app, that finds all MP3s on a users phone and then puts them into a list. This works very fine and is very quick, even with many songs. Now I populate a new list with an object for each item of the list to then display it inside my recyclerview. The problem is, that I have 700+ songs on my phone and this blocks the UI thread quite some time.
Now, I want to use the recyclerview to not load all items from the list into the objects all at once but rather only when they are about to be displayed - but I have NO clue over how to do this. Right now, all objects are build and then displayed in a very long scrollview from the recyclerview after the UI thread has been blocked for a good 30 seconds. Can please anyone help me? Here is my code:
namespace Media_Player
{
[Activity(Label = "Media_Player", MainLauncher = true)]
public class MainActivity : Activity
{
static public MediaPlayer mediaPlayer;
List<MP3object> mp3;
MediaMetadataRetriever reader;
public static Button btn_StartOrPause, btn_Stop;
public static TextView txt_CurrentSong;
public static bool stopIsActive = false, firstStart = true;
public static Android.Net.Uri CurrentActiveSongUri;
RecyclerView mRecyclerView;
RecyclerView.LayoutManager mLayoutManager;
PhotoAlbumAdapter mAdapter;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.test);
reader = new MediaMetadataRetriever();
PopulateMP3List(ReturnPlayableMp3(true));
mediaPlayer = new MediaPlayer();
InitRecView();
}
private void InitRecView()
{
// Instantiate the adapter and pass in its data source:
mAdapter = new PhotoAlbumAdapter(mp3);
// Get our RecyclerView layout:
mRecyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
// Plug the adapter into the RecyclerView:
mRecyclerView.SetAdapter(mAdapter);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.SetLayoutManager(mLayoutManager);
}
private void PopulateMP3List(List<string> content)
{
mp3 = new List<MP3object>();
foreach (string obj in content)
{
WriteMetaDataToFileList(obj);
}
}
void WriteMetaDataToFileList(string obj)
{
reader.SetDataSource(obj);
//Write Mp3 as object to global list
MP3object ob = new MP3object();
{
if(reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle) != null)
{
ob.SongName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle);
}
else
{
ob.SongName = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist) != null)
{
ob.ArtistName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist);
}
else
{
ob.ArtistName = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyAlbum) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyAlbum) != null)
{
ob.AlbumName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyAlbum);
}
else
{
ob.AlbumName = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != null)
{
ob.Year = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear);
}
else
{
ob.Year = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != null)
{
ob.Year = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear);
}
else
{
ob.Year = Resources.GetString(Resource.String.Unknown);
}
ob.Mp3Uri = obj; // can never be unknown!
ob.DurationInSec = int.Parse(reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyDuration)) / 1000; // can never be unknown, div by 1000 to get sec not millis
}
mp3.Add(ob);
}
public List<string> ReturnPlayableMp3(bool sdCard)
{
List<string> res = new List<string>();
string phyle;
string path1 = null;
if(sdCard) // get mp3 from SD card
{
string baseFolderPath = "";
try
{
bool getSDPath = true;
Context context = Application.Context;
Java.IO.File[] dirs = context.GetExternalFilesDirs(null);
foreach (Java.IO.File folder in dirs)
{
bool IsRemovable = Android.OS.Environment.InvokeIsExternalStorageRemovable(folder);
bool IsEmulated = Android.OS.Environment.InvokeIsExternalStorageEmulated(folder);
if (getSDPath ? IsRemovable && !IsEmulated : !IsRemovable && IsEmulated)
baseFolderPath = folder.Path;
}
}
catch (Exception ex)
{
Console.WriteLine("GetBaseFolderPath caused the following exception: {0}", ex);
}
string xy = baseFolderPath.Remove(18); // This is result after this, but this hard coded solution could be a problem on different phones.: "/storage/05B6-2226/Android/data/Media_Player.Media_Player/files"
path1 = xy;
// path to SD card and MUSIC "/storage/05B6-2226/"
}
else // get Mp3 from internal storage
{
path1 = Android.OS.Environment.ExternalStorageDirectory.ToString();
}
var mp3Files = Directory.EnumerateFiles(path1, "*.mp3", SearchOption.AllDirectories);
foreach (string currentFile in mp3Files)
{
phyle = currentFile;
res.Add(phyle);
}
return res;
}
}
public class PhotoViewHolder : RecyclerView.ViewHolder
{
public ImageView Image { get; private set; }
public TextView Caption { get; private set; }
public PhotoViewHolder(View itemView) : base(itemView)
{
// Locate and cache view references:
Image = itemView.FindViewById<ImageView>(Resource.Id.imageView);
Caption = itemView.FindViewById<TextView>(Resource.Id.textView);
}
}
public class PhotoAlbumAdapter : RecyclerView.Adapter
{
public List<MP3object> mp3;
public PhotoAlbumAdapter(List<MP3object> mp3)
{
this.mp3 = mp3;
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.lay, parent, false);
PhotoViewHolder vh = new PhotoViewHolder(itemView);
return vh;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
PhotoViewHolder vh = holder as PhotoViewHolder;
vh.Caption.Text = mp3[position].SongName;
}
public override int ItemCount
{
get { return mp3.Count(); }
}
}
}
So getting the list of strings with the locations of the Mp3 works very quickly, but then "WriteMetaDataToFileList(obj)" kicks in, comming from "PopulateMP3List(List content)" and this is what takes so long. What I think I need is for the recyclerview to only build the first 20 objects, and when the user starts scrolling, builds the next 20 objects and attaches them to list for them to also be scrolled. Please help me out here :)
Here is an abstract class:
public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
private LinearLayoutManager linearLayoutManager;
protected PaginationScrollListener(LinearLayoutManager linearLayoutManager) {
this.linearLayoutManager = linearLayoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = linearLayoutManager.getChildCount();
int totalItemCount = linearLayoutManager.getItemCount();
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
if (!isLoading() && !isLastPage()) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount && firstVisibleItemPosition >= 0) {
loadMoreItems();
}
}
}
protected abstract void loadMoreItems();
public abstract boolean isLastPage();
public abstract boolean isLoading();
}
and In your adapter you must follow this pattern:
public class ConsultancyAdapter extends RecyclerView.Adapter<ConsultancyAdapter.ConsultancyVH> {
private static final int ITEM = 0;
private static final int LOADING = 1;
private boolean isLoadingAdded = false;
public ConsultancyAdapter(List<Consultancy> consultancies, ConsultancyAdapterListener listener) {
}
#NonNull
#Override
public ConsultancyVH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case ITEM:
viewHolder = getViewHolder(parent, layoutInflater);
break;
case LOADING:
View v2 = layoutInflater.inflate(R.layout.item_progress, parent, false);
viewHolder = new ConsultancyVH(v2);
break;
}
return (ConsultancyVH) viewHolder;
}
#NonNull
private RecyclerView.ViewHolder getViewHolder(ViewGroup parent, LayoutInflater inflater) {
RecyclerView.ViewHolder viewHolder;
View v1 = inflater.inflate(R.layout.item_consultancy, parent, false);
viewHolder = new ConsultancyVH(v1);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull ConsultancyVH holder, int position) {
Consultancy consultancy = consultancies.get(position);
switch (getItemViewType(position)) {
case ITEM:
ConsultancyVH mySingeCounseller = holder;
holder.title.setText(consultancy.getTitle()); // set cardTitle
holder.fieldArea.setText(consultancy.getField_filedoctorskills());
break;
case LOADING:
break;
}
}
#Override
public int getItemCount() {
return consultancies.size();
}
#Override
public int getItemViewType(int position) {
return (position == consultancies.size() - 1 && isLoadingAdded) ? LOADING : ITEM;
}
public void add(Consultancy mc) {
consultancies.add(mc);
notifyItemInserted(consultancies.size() - 1);
}
public void addAll(List<Consultancy> mcList) {
for (Consultancy mc : mcList) {
add(mc);
}
}
public void remove(Consultancy city) {
int position = consultancies.indexOf(city);
if (position > -1) {
consultancies.remove(position);
notifyItemRemoved(position);
}
}
public Consultancy getItem(int position) {
return consultancies.get(position);
}
public void clear() {
isLoadingAdded = false;
while (getItemCount() > 0) {
remove(getItem(0));
}
}
public boolean isEmpty() {
return getItemCount() == 0;
}
public void addLoadingFooter() {
isLoadingAdded = true;
add(new Consultancy());
}
public void removeLoadingFooter() {
isLoadingAdded = false;
int position = consultancies.size() - 1;
Consultancy item = getItem(position);
if (item != null) {
consultancies.remove(position);
notifyItemRemoved(position);
}
}
public interface ConsultancyAdapterListener {
void onCaseClicked(int position, String nid, String fieldArea, String title);
}
protected class ConsultancyVH extends RecyclerView.ViewHolder {
private TextView title, fieldArea;
private CircleImageView iconProfile;
private MaterialRippleLayout caseButtonRipple;
public ConsultancyVH(View itemView) {
super(itemView);
caseButtonRipple = itemView.findViewById(R.id.case_button_ripple);
this.title = itemView.findViewById(R.id.docName);
this.fieldArea = itemView.findViewById(R.id.fieldArea);
this.iconProfile = itemView.findViewById(R.id.icon_profile);
}
}
}
and in your activity:
private void setScrollListener() {
recyclerView.addOnScrollListener(new PaginationScrollListener(linearLayoutManager) {
#Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1;
loadNextPage();
}
#Override
public boolean isLastPage() {
return isLastPage;
}
#Override
public boolean isLoading() {
return isLoading;
}
});
loadFirstPage();
}
and in my loadFirstPage i talk to a API and you need some your code:
private void loadFirstPage() {
CallData().enqueue(new DefaultRetrofitCallback<List<Consultancy>>() {
#Override
protected void onFailure(Throwable t) {
super.onFailure(t);
}
#Override
protected void onSuccess(List<Consultancy> response) {
swipeRefreshLayout.setRefreshing(false);
dataList = response;
adapter.addAll(dataList);
recyclerView.setAdapter(adapter);
if (!checkLast(response)) adapter.addLoadingFooter();
else isLastPage = true;
}
#Override
protected void onOtherStatus(Response<List<Consultancy>> response) {
super.onOtherStatus(response);
}
#Override
protected void always() {
super.always();
}
});
}
and loadNextPage:
private void loadNextPage() {
CallData().enqueue(new DefaultRetrofitCallback<List<Consultancy>>() {
#Override
protected void onFailure(Throwable t) {
super.onFailure(t);
}
#Override
protected void onSuccess(List<Consultancy> response) {
swipeRefreshLayout.setRefreshing(false);
adapter.removeLoadingFooter();
isLoading = false;
swipeRefreshLayout.setRefreshing(false);
adapter.addAll(response);
if (!checkLast(response)) adapter.addLoadingFooter();
else isLastPage = true;
}
#Override
protected void onOtherStatus(Response<List<Consultancy>> response) {
super.onOtherStatus(response);
}
#Override
protected void always() {
super.always();
}
});
}
I have a button in every cell of a RecyclerView that launches a download network call. The cell displays differently according to whether it's downloading, downloaded or finished.
my simplified code :
#Override public void onBindViewHolder(final CatalogViewHolder holder, int position) {
final DownloadStatusCallback statusCallback = new DownloadStatusCallback() {
#Override public void started() {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
holder.itemView.setBackground(//color1
}
});
}
#Override public void finished() {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
holder.itemView.setBackground(//color 2
}
});
}
#Override public void error(Exception e) {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
holder.itemView.setBackground(//color 3
}
});
}
};
holder.button1.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
assyncCall(statusCallback);
}
});
}
The first time i clic on a cell, everything works fine. If I clic on the download button of another cell, both of them will update.
I understand that's due to recyclerview recycling cells, but I can't figure out how to do better.
Thanks !
my full adapter :
public class CatalogRecyclerAdapter extends RecyclerView.Adapter<CatalogViewHolder> {
public static final String TAG = "CatalogRecyclerAdapter";
private final LayoutInflater inflater;
private final DownloadCenter downloadCenter;
private final ListInterface.FlowController flowController;
private final ResourcesStringRepository resourcesStringRepository;
private final ImageManagerFactory imageManagerFactory;
private final Handler mainThreadHandler;
public CatalogRecyclerAdapter(LayoutInflater inflater, ListInterface.FlowController flowController,
DownloadCenter downloadCenter, ResourcesStringRepository resourcesStringRepository,
ImageManagerFactory imageManagerFactory, Handler mainThreadHandler) {
this.inflater = inflater;
this.flowController = flowController;
this.downloadCenter = downloadCenter;
this.resourcesStringRepository = resourcesStringRepository;
this.imageManagerFactory = imageManagerFactory;
this.mainThreadHandler = mainThreadHandler;
}
private static final int TITLE = 0;
private static final int USER = 2;
private static final int PROGRAM = 3;
private static final int COURSE = 4;
private static final int GROUP = 5;
private static final int MEDIA = 6;
private static final int ERROR = 7;
private static final int DEMO = 8;
//The list of all elements
private List<FilterableUser> users = new ArrayList<>();
private List<CatalogProgram> programs = new ArrayList<>();
private List<CatalogProgram> demos = new ArrayList<>();
private List<CatalogCourse> courses = new ArrayList<>();
private List<FilterableGroup> groups = new ArrayList<>();
private List<CatalogMedia> medias = new ArrayList<>();
//The list that will be displayed after filtering and research.
List<Object> displayedList = new ArrayList<>();
static final String TITLES[] = new String[10];
static {
Context ctx = M360Application.getContext();
TITLES[USER] = ctx.getString(R.string.users);
TITLES[PROGRAM] = ctx.getString(R.string.programs);
TITLES[COURSE] = ctx.getString(R.string.courses);
TITLES[GROUP] = ctx.getString(R.string.groups);
TITLES[MEDIA] = ctx.getString(R.string.documents);
TITLES[DEMO] = ctx.getString(R.string.programs_demo);
}
private String searchString;
#Override public int getItemViewType(int position) {
if (displayedList.get(position) instanceof String) {
return TITLE;
} else if (displayedList.get(position) instanceof FilterableUser) {
return USER;
} else if (displayedList.get(position) instanceof CatalogProgramDemo) {
return DEMO;
} else if (displayedList.get(position) instanceof CatalogProgram) {
return PROGRAM;
} else if (displayedList.get(position) instanceof CatalogCourse) {
return COURSE;
} else if (displayedList.get(position) instanceof FilterableGroup) {
return GROUP;
} else if (displayedList.get(position) instanceof CatalogMedia) {
return MEDIA;
} else if (displayedList.get(position) instanceof CatalogError) {
return ERROR;
} else {
throw new ClassCastException(
"this adapter's displayedList is corrupted" + displayedList.get(position).toString());
}
}
public void setData(List<Filterable> data, String searchedString) {
searchString = searchedString;
setData(data);
}
private void setData(List<Filterable> data) {
LogDev.i(TAG, "setting data size: " + data.size());
groups.clear();
users.clear();
programs.clear();
demos.clear();
courses.clear();
medias.clear();
for (Filterable element : data) {
if (element instanceof CatalogCourse) {
courses.add((CatalogCourse) element);
} else if (element instanceof FilterableUser) {
users.add((FilterableUser) element);
} else if (element instanceof CatalogProgramDemo) {
demos.add((CatalogProgramDemo) element);
} else if (element instanceof CatalogProgram) {
programs.add((CatalogProgram) element);
} else if (element instanceof FilterableGroup) {
groups.add((FilterableGroup) element);
} else if (element instanceof CatalogMedia) {
medias.add((CatalogMedia) element);
}
}
constructDataSet();
}
private void constructDataSet() {
displayedList.clear();
if (!demos.isEmpty()) {
displayedList.add(TITLES[DEMO]);
displayedList.addAll(demos);
}
if (!programs.isEmpty()) {
displayedList.add(TITLES[PROGRAM]);
displayedList.addAll(programs);
}
if (!courses.isEmpty()) {
displayedList.add(TITLES[COURSE]);
displayedList.addAll(courses);
}
if (!users.isEmpty()) {
displayedList.add(TITLES[USER]);
displayedList.addAll(users);
}
if (!groups.isEmpty()) {
displayedList.add(TITLES[GROUP]);
displayedList.addAll(groups);
}
if (!medias.isEmpty()) {
displayedList.add(TITLES[MEDIA]);
displayedList.addAll(medias);
}
if (displayedList.isEmpty()) {
displayedList.add(new CatalogError());
}
LogDev.w(TAG, "displayedList.size() : " + displayedList.size());
notifyDataSetChanged();
}
#Override public CatalogViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TITLE:
return new TitleViewHolder(inflater.inflate(R.layout.item_list_title_catalog, parent, false));
case USER:
return new UserViewHolder(inflater.inflate(R.layout.widget_user_small, parent, false));
case PROGRAM:
case DEMO:
return new ProgramViewHolder(inflater.inflate(R.layout.widget_program_small, parent, false));
case COURSE:
return new CourseViewHolder(inflater.inflate(R.layout.widget_course_small, parent, false));
case GROUP:
return new GroupViewHolder(inflater.inflate(R.layout.widget_group_small, parent, false));
case MEDIA:
return new MediaViewHolder(inflater.inflate(R.layout.widget_media_small, parent, false));
case ERROR:
return new CatalogErrorViewHolder(inflater.inflate(R.layout.widget_noresult_small, parent, false));
default:
LogDev.e(TAG, "view type not supported");
return null;
}
}
#Override public void onBindViewHolder(CatalogViewHolder holder, int position) {
Object displayedObject = displayedList.get(position);
//holder.bind(displayedObject, errorDisplayInterface);
if (holder instanceof TitleViewHolder && displayedObject instanceof String) {
((TitleViewHolder) holder).tv.setText((String) displayedObject);
} else if (holder instanceof ProgramViewHolder && displayedObject instanceof CatalogProgram) {
bindProgramViewHolder((ProgramViewHolder) holder, (CatalogProgram) displayedObject);
} else if (holder instanceof CourseViewHolder && displayedObject instanceof CatalogCourse) {
bindCourseViewHolder((CourseViewHolder) holder, (CatalogCourse) displayedObject);
} else if (holder instanceof GroupViewHolder && displayedObject instanceof FilterableGroup) {
bindGroupViewHolder((GroupViewHolder) holder, (FilterableGroup) displayedObject);
} else if (holder instanceof UserViewHolder && displayedObject instanceof FilterableUser) {
bindUserViewHolder((UserViewHolder) holder, (FilterableUser) displayedObject);
} else if (holder instanceof MediaViewHolder && displayedObject instanceof CatalogMedia) {
bindMediaViewHolder((MediaViewHolder) holder, (CatalogMedia) displayedObject);
} else if (holder instanceof CatalogErrorViewHolder) {
//No binding with any data
} else {
throw new ClassCastException(displayedObject.toString());
}
//Highlight
if (searchString != null && !searchString.isEmpty())
{
TextViewHighlighter.highlight(holder, searchString);
}
}
private void bindCourseViewHolder(final CourseViewHolder courseViewHolder, final CatalogCourse course) {
courseViewHolder.name_textView.setText(course.name);
courseViewHolder.viewNb_textView.setText(course.views != null ? course.views.toString() : "0");
if (course.elementCount == null) {
courseViewHolder.counterLinear.setVisibility(View.GONE);
} else {
courseViewHolder.counterLinear.setVisibility(View.VISIBLE);
courseViewHolder.questionNb_textView.setText(
course.elementCount.questions != null ? course.elementCount.questions.toString() : "0");
courseViewHolder.mediaNb_textView.setText(
course.elementCount.medias != null ? course.elementCount.medias.toString() : "0");
courseViewHolder.sheetNb_textView.setText(
course.elementCount.sheets != null ? course.elementCount.sheets.toString() : "0");
}
imageManagerFactory.course(course.id).thumbnail(courseViewHolder.pic_imageView);
//new CourseImageManager(course.id).load(courseViewHolder.pic_imageView);
View.OnClickListener clickListener = new View.OnClickListener() {
#Override public void onClick(View view) {
flowController.routeToCourse(course.id);
}
};
courseViewHolder.container.setOnClickListener(clickListener);
if (course.canBeOffline) {
courseViewHolder.downloadBlock.setVisibility(View.VISIBLE);
DownloadState state = downloadCenter.getCourseStatus(course.id);
LogDev.i(TAG, "can be offline " + state.name());
if (state == DownloadState.DOWNLOADING) {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADING);
}
if (state == DownloadState.TO_DOWNLOAD) {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADABLE);
}
if (state == DownloadState.DOWNLOADED || state == DownloadState.DOWNLOADED_WITH_SHARED_MODE) {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADED);
} else {
DownloadStatusCallback statusCallback = new DownloadStatusCallback() {
#Override public void started() {
LogDev.i(TAG, "started");
mainThreadHandler.post(new Runnable() {
#Override public void run() {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADING);
}
});
}
#Override public void finished() {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
updateDownloadBlock(courseViewHolder, DownloadableStatus.DOWNLOADED);
}
});
}
#Override public void error(Exception e) {
mainThreadHandler.post(new Runnable() {
#Override public void run() {
updateDownloadBlock(courseViewHolder, DownloadableStatus.ERROR);
}
});
}
};
downloadCenter.subscribe(course.id, statusCallback);
courseViewHolder.downloadBlock.setOnClickListener(new View.OnClickListener()
{
#Override public void onClick(View v) {
new Thread() {
#Override public void run() {
super.run();
try {
downloadCenter.downloadCourse(course.id, null);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
});
}
} else {
LogDev.i(TAG, "can't be offline");
courseViewHolder.downloadBlock.setVisibility(View.INVISIBLE);
}
}
private void updateDownloadBlock(CourseViewHolder courseViewHolder, DownloadableStatus status) {
if (status == null) return;
courseViewHolder.downloadBlock.setVisibility(
status.equals(DownloadableStatus.NOT_DOWNLOADABLE) ? View.GONE : View.VISIBLE);
courseViewHolder.downloadImage.setVisibility(
status.equals(DownloadableStatus.DOWNLOADABLE) ? View.VISIBLE : View.GONE);
courseViewHolder.downloadProgress.setVisibility(
status.equals(DownloadableStatus.DOWNLOADING) ? View.VISIBLE : View.GONE);
courseViewHolder.downloadedImage.setVisibility(
status.equals(DownloadableStatus.DOWNLOADED) ? View.VISIBLE : View.GONE);
courseViewHolder.downloadErrImage.setVisibility(
status.equals(DownloadableStatus.ERROR) ? View.VISIBLE : View.GONE);
}
private enum DownloadableStatus {
NOT_DOWNLOADABLE, DOWNLOADABLE, DOWNLOADING, DOWNLOADED, ERROR
}
private void bindProgramViewHolder(ProgramViewHolder programViewHolder, final CatalogProgram program) {
imageManagerFactory.program(program.id).thumbnail(programViewHolder.pic_imageView);
//new ProgramImageManager(program.id).load(programViewHolder.pic_imageView);
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override public void onClick(View view) {
flowController.routeToProgram(program.id);
}
};
programViewHolder.container.setOnClickListener(onClickListener);
programViewHolder.pic_imageView.setOnClickListener(onClickListener);
programViewHolder.title_textView.setText(program.name);
programViewHolder.viewCount_textView.setText(program.views != null ? program.views.toString() : "0");
}
private void bindUserViewHolder(UserViewHolder userViewHolder, final FilterableUser user) {
userViewHolder.name_textView.setText(user.name);
userViewHolder.job_textView.setText(user.description);
imageManagerFactory.user(user.id).thumbnail(userViewHolder.pic_imageView);
//new UserImageManager(user.id).loadProfilePic(userViewHolder.pic_imageView, NetworkUtils.isNetworkAvailable(),
// true);
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override public void onClick(View view) {
flowController.routeToUser(user.id);
}
};
userViewHolder.pic_imageView.setOnClickListener(onClickListener);
userViewHolder.container.setOnClickListener(onClickListener);
}
private void bindMediaViewHolder(MediaViewHolder mediaViewHolder, final CatalogMedia media) {
imageManagerFactory.media(media.id, media.type, media.extention).symbolOnThumbnail(mediaViewHolder.complex);
//new MediaImageManager(media).load(mediaViewHolder.pic_imageView, NetworkUtils.isNetworkAvailable(), false);
mediaViewHolder.title_textView.setText(media.title);
mediaViewHolder.authorName_textView.setText(media.authorName);
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override public void onClick(final View view) {
flowController.routeToDocument(media.id);
}
};
mediaViewHolder.complex.setOnClickListener(onClickListener);
mediaViewHolder.container.setOnClickListener(onClickListener);
}
private void bindGroupViewHolder(GroupViewHolder groupViewHolder, final FilterableGroup group) {
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override public void onClick(View view) {
flowController.routeToGrouop(group.id);
}
};
groupViewHolder.pic_imageView.setOnClickListener(onClickListener);
groupViewHolder.container.setOnClickListener(onClickListener);
groupViewHolder.name_textView.setText(group.name);
String str = resourcesStringRepository.getQuantityString(R.plurals.catalog_group_stat_program,
group.nbProgramsRunning, group.nbProgramsRunning);
str += " - " + resourcesStringRepository.getQuantityString(R.plurals.catalog_group_stat_user, group.nbUser,
group.nbUser);
groupViewHolder.stats_textView.setText(str);
imageManagerFactory.group(group.id).thumbnail(groupViewHolder.pic_imageView);
//new GroupImageManager(group.id).load(groupViewHolder.pic_imageView, NetworkUtils.isNetworkAvailable(), true);
}
#Override public int getItemCount() {
return displayedList.size();
}
}
It is recycling the views.So while clicking the button you have to store its position and change views accordingly.
Maintain a position storing variable globally like this
private int itemClicked=-1;
While clicking the view store the position into itemclicked
holder.button1.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
itemclicked=position;
assyncCall(statusCallback);
}
});
Then while updating views check if the position is same like this
if(position==itemclicked){
//show download for clicked view
}else{
//show download stopped for other views
}
Solution
As Surender and Trickcy Solution suggested, I updated the presented data and then tell the adapter to update the cell accordingly :
DownloadStatusCallback statusCallback = new DownloadStatusCallback() {
#Override public void started() {
LogDev.i(TAG, "started");
course.downloadState = DownloadState.DOWNLOADING;
final int position = courseViewHolder.getAdapterPosition();
mainThreadHandler.post(new Runnable() {
#Override public void run() {
notifyItemChanged(position);
}
});
}
#Override public void finished() {
course.downloadState = DownloadState.DOWNLOADED;
final int position = courseViewHolder.getAdapterPosition();
mainThreadHandler.post(new Runnable() {
#Override public void run() {
notifyItemChanged(position);
}
});
}
#Override public void error(Exception e) {
course.downloadState = DownloadState.ERROR_WHILE_DOWNLOADING;
final int position = courseViewHolder.getAdapterPosition();
mainThreadHandler.post(new Runnable() {
#Override public void run() {
notifyItemChanged(position);
}
});
}
};
I have a VideoLayout that displays a video using ExoPlayer. That VideoLayout is used twice in a Fragment (previewVideoLayout and fullVideoLayout in the code).
The Fragment shows by default previewVideoLayout; if the user presses a button in the Activity, it shows fullVideoLayout on top of previewVideoLayout with an animation. previewVideoLayout always plays OK, but fullVideoLayout doesn't display the video, though it plays its sound (on top of previewVideoLayout sound).
Is there a reason why, calling the same methods on both layouts, one plays but the second one doesn't? In case it's relevant; the Fragment is inside a ViewPager.
VideLayout:
public class VideoLayout {
public static final int MUTE_VOLUME = 0;
public static final int MAX_VOLUME = 100;
public static final int UPDATE_INTERVAL = 30;
protected static final String ARG_VIDEO_URL = "VIDEO_URL";
private final Context context;
private String videoUrl;
private HttpProxyCacheServer proxy;
private boolean visibleForUser;
private SimpleExoPlayer player;
private SimpleExoPlayerView simpleExoPlayerView;
private boolean pendingPlay;
private ProgressBar pbLoadIndicator;
private TimerTask updateProgressTask;
private Timer timer;
private ProgressBar videoProgress;
private ViewGroup layout;
private VideoFragment.VideoPreviewCallbacks videoPreviewCallbacks;
private String tag;
private boolean isFullVideo;
public VideoLayout(Context context, String videoUrl, boolean isFullVideo) {
this.context = context;
this.isFullVideo = isFullVideo;
this.videoUrl = "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4";
}
public void onAttach(Context context) {
if (context instanceof VideoFragment.VideoPreviewCallbacks) {
videoPreviewCallbacks = (VideoFragment.VideoPreviewCallbacks) context;
}
}
public void onCreate() {
this.videoUrl = "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4";
initPlayer();
}
public void onCreateView(ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layout = (ViewGroup) inflater.inflate(getLayoutRes(), null);
String tag = createTag();
if (parent.findViewWithTag(tag) == null) {
layout.setTag(tag);
parent.addView(layout, parent.getChildCount());
}
}
public void onViewCreated(View view) {
bindViews(view);
setupPlayerView();
}
public void onResume() {
if (pendingPlay && visibleForUser) {
pendingPlay = false;
startPlayer();
}
}
public void onPause() {
pausePlayer();
}
public void onDestroy() {
player.release();
}
public void setUserVisibleHint(boolean isVisibleToUser) {
visibleForUser = isVisibleToUser;
if (layout != null) {
layout.setVisibility(isVisibleToUser ? VISIBLE : GONE);
}
if (visibleForUser && simpleExoPlayerView != null) {
startPlayer();
} else if (!visibleForUser && simpleExoPlayerView != null) {
pausePlayer();
} else if (visibleForUser) {
pendingPlay = true;
}
}
private void bindViews(View view) {
simpleExoPlayerView = (SimpleExoPlayerView) view.findViewById(R.id.simpleExoPlayerView);
simpleExoPlayerView.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.white));
pbLoadIndicator = (ProgressBar) view.findViewById(R.id.pbLoadIndicator);
videoProgress = (ProgressBar) view.findViewById(R.id.progress_video);
}
private void setupPlayerView() {
simpleExoPlayerView.setUseController(false);
simpleExoPlayerView.setPlayer(player);
}
private void initPlayer() {
if (player == null) {
initProxy();
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
final LoopingMediaSource loopingSource = getVideoPlayerMediaSource(bandwidthMeter);
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
LoadControl loadControl = new DefaultLoadControl();
player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, loadControl);
player.addListener(new PlayerEventListener() {
#Override
public void onLoadingChanged(boolean isLoading) {
if (pbLoadIndicator != null)
pbLoadIndicator.setVisibility(isLoading ? View.VISIBLE : View.GONE);
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (pbLoadIndicator != null)
pbLoadIndicator.setVisibility(playWhenReady ? View.GONE : View.VISIBLE);
}
});
player.prepare(loopingSource);
player.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
}
}
private void initProxy() {
proxy = VideoCache.getProxy(getContext());
}
#NonNull
private LoopingMediaSource getVideoPlayerMediaSource(DefaultBandwidthMeter bandwidthMeter) {
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getContext(),
Util.getUserAgent(getContext(), "lifive.buy"), bandwidthMeter);
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
Uri url = Uri.parse(videoUrl);
MediaSource videoSource;
if (videoUrl.contains(".mp4")) {
url = Uri.parse(proxy.getProxyUrl(videoUrl));
videoSource = new ExtractorMediaSource(url,
dataSourceFactory, extractorsFactory, null, null);
} else {
videoSource = new HlsMediaSource(url, dataSourceFactory, null, null);
}
return new LoopingMediaSource(videoSource);
}
public void enableProgress(boolean enable) {
videoProgress.setVisibility(enable ? VISIBLE : View.GONE);
}
private void startPlayer() {
player.setPlayWhenReady(true);
createUpdateTimer();
}
/**
* creates the timer that updates the progress bar
*/
private void createUpdateTimer() {
cancelUpdateTimer();
updateProgressTask = new TimerTask() {
#Override
public void run() {
if (player != null) {
notifyUpdate();
}
}
};
timer = new Timer();
timer.scheduleAtFixedRate(updateProgressTask, 0, UPDATE_INTERVAL);
}
/**
* cancels the timer that updates the progress bar
*/
private void cancelUpdateTimer() {
if (timer != null) {
timer.cancel();
}
}
/**
* notifies that the UI should be updated. See createUpdateTimer
*/
private void notifyUpdate() {
new Handler(Looper.getMainLooper()).post(() -> {
updateProgressBar();
if (player.getDuration() > 0 && videoPreviewCallbacks != null) {
videoPreviewCallbacks.onProgress(player.getCurrentPosition(), player.getDuration());
}
});
}
private void updateProgressBar() {
videoProgress.setMax((int) player.getDuration());
videoProgress.setProgress((int) player.getCurrentPosition());
}
private void pausePlayer() {
pendingPlay = true;
player.setPlayWhenReady(false);
player.seekTo(1);
cancelUpdateTimer();
}
public int getVisibility() {
return layout == null ? GONE : layout.getVisibility();
}
public void muteSound(boolean on) {
player.setVolume(on ? MUTE_VOLUME : MAX_VOLUME);
}
private int getLayoutRes() {
return isFullVideo ? R.layout.layout_full_video_player : R.layout.layout_video_player;
}
private String createTag() {
if (this.tag == null)
this.tag = "VideoLayout" + hashCode();
return this.tag;
}
private Context getContext() {
return context.getApplicationContext();
}
Fragment:
public class VideoFragment extends Fragment {
protected static final String ARG_VIDEO_URL = "VIDEO_URL";
private static final String ARG_FULL_VIDEO_URL = "FULL_VIDEO_URL";
private VideoLayout previewVideoLayout;
private VideoLayout fullVideoLayout;
private String videoUrl;
private String fullVideoUrl;
private ViewGroup parent;
public VideoFragment() {
}
/**
* #param videoUrl video url
* #return A new instance of fragment VideoPreviewFragment.
*/
public static VideoFragment newInstance(String videoUrl, String fullVideoUrl) {
VideoFragment fragment = new VideoFragment();
Bundle args = new Bundle();
args.putString(ARG_VIDEO_URL, videoUrl);
args.putString(ARG_FULL_VIDEO_URL, fullVideoUrl);
fragment.setArguments(args);
return fragment;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof VideoPreviewCallbacks) {
if (getCurrentLayout() != null)
getCurrentLayout().onAttach(getContext());
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
videoUrl = getArguments().getString(ARG_VIDEO_URL);
fullVideoUrl = getArguments().getString(ARG_FULL_VIDEO_URL);
if (previewVideoLayout == null) {
previewVideoLayout = new VideoLayout(getContext(), videoUrl, false);
previewVideoLayout.setUserVisibleHint(getUserVisibleHint());
previewVideoLayout.onCreate();
}
if (fullVideoLayout == null && fullVideoUrl != null) {
}
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View inflate = inflater.inflate(R.layout.fragment_video_preview, container, false);
parent = (ViewGroup) inflate.findViewById(R.id.fragment_video_preview_root);
previewVideoLayout.onCreateView(parent);
return inflate;
}
#Override
public void onViewCreated(View view, Bundle savedInstance) {
super.onViewCreated(view, savedInstance);
previewVideoLayout.onViewCreated(view);
}
#Override
public void onResume() {
super.onResume();
if (previewVideoLayout != null)
previewVideoLayout.onResume();
if (fullVideoLayout != null)
fullVideoLayout.onResume();
}
#Override
public void onPause() {
super.onPause();
if (previewVideoLayout != null)
previewVideoLayout.onPause();
if (fullVideoLayout != null)
fullVideoLayout.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
if (previewVideoLayout != null)
previewVideoLayout.onDestroy();
if (fullVideoLayout != null)
fullVideoLayout.onDestroy();
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (previewVideoLayout != null)
previewVideoLayout.setUserVisibleHint(isVisibleToUser);
if (fullVideoLayout != null)
fullVideoLayout.setUserVisibleHint(isVisibleToUser);
}
public void enableProgress(boolean enable) {
if (previewVideoLayout != null)
previewVideoLayout.enableProgress(enable);
if (fullVideoLayout != null)
fullVideoLayout.enableProgress(enable);
}
private VideoLayout getCurrentLayout() {
if (fullVideoLayout != null)
return fullVideoLayout.getVisibility() == View.VISIBLE ? fullVideoLayout : previewVideoLayout;
return previewVideoLayout;
}
public void muteSound(boolean on) {
if(previewVideoLayout!=null)
previewVideoLayout.muteSound(on);
if(fullVideoLayout!=null)
fullVideoLayout.muteSound(on);
}
public void showFullVideo() {
fullVideoLayout = new VideoLayout(getContext(), fullVideoUrl, true);
fullVideoLayout.setUserVisibleHint(getUserVisibleHint());
fullVideoLayout.onCreate();
fullVideoLayout.onCreateView(parent);
fullVideoLayout.onViewCreated(getView());
fullVideoLayout.onResume();
}
public interface VideoPreviewCallbacks {
void onProgress(long currentMillis, long durationInMillis);
}
I'd like to implement streaming video list app. I used a RecyclerView to display my list item. Item type includes 4 type: Article, Status, Photo and Video. We should only focus on Video type. Here is my code for RecyclerView's adapter:
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);
}
}
When item type is video, I create an ExoPlayer instance to play this video and add this instance to a HashMap for later use (with key value is the item's position). If a video item is being recycled, I release the player and remove it from HashMap.
Everything looks good but a problem. I suppose the video item is at 0 position (now we can see the video preview in this item). I scroll down the RecyclerView just enough to hide the item 0. At this moment, the VideoViewHolder of item 0 is not recycled yet. Then, I scroll up to reveal item 0. The view is now blank, not show the video preview. I click item 0 to play the video, the player only plays audio (sound), not show video. After playing a few seconds, the video is now visible.
I debugged and found that after I scrolled down to hide video item, the SurfaceView is destroyed. When scroll up to reveal video item, the SurfaceView is created. I think this is the reason why VideoViewHolder shows blank view instead of the video preview.
Here is my question: How to display video preview after scroll back to video item?
Here is note from ExoPlayer documentation
SurfaceView rendering wasn’t properly synchronized with view animations until Android N. On earlier releases this could result in unwanted effects when a SurfaceView was placed into scrolling container, or when it was subjected to animation. Such effects included the SurfaceView’s contents appearing to lag slightly behind where it should be displayed, and the view turning black when subjected to animation.
To achieve smooth animation or scrolling of video prior to Android N, it’s therefore necessary to use TextureView rather than SurfaceView. If smooth animation or scrolling is not required then SurfaceView should be preferred
To enable TextureView surface_type you have to set it in your xml file like shown below
<com.google.android.exoplayer2.ui.SimpleExoPlayerView
android:id="#+id/playerView"
android:layout_width="match_parent"
android:layout_height="200dp"
app:surface_type="texture_view"/>