I have 2 recycler view inside a fragment. 1st scroll vertically and 2nd scroll horizontal inside 1st one.(Just like instagram app). And recycler view display one item at a time and I am using PagerSnapHelper to scroll one item at time. In 2nd recycler view, I am displaying an image and playing a song from url. And I want to auto play song after scrolling to next/previous item(horizontal an vertical) both. When user click on image than song stop and if click again than song start again.
I wanted same logic for video player too after completing song playing work.
Here, I am facing issues in auto play song. Sometime song is playing for next item instead of current visible item. Some time song and image both working fine and than after next scroll suddenly song started playing from next item which not visible yet. And if user click on image than song stop and when click again than song is playing properly
I logged and check position number inside onBindViewHolder for 1st recycler view and it is logging 2 times. For current scrolled one and for next item too.
I tried many different ways to fix it like
setting recyclable to false holder.setIsRecyclable(false);
getting position from holder.getLayoutPosition()
using interfaces etc but count's figure it out.
Note: I am passing MediaPlayer from MainActivity to Fregment to 1st
recycler view to 2nd recycler view where song is playing.
This is my main codes structure
FragMent:
homeViewVerticalAdapter = new HomeViewVerticalAdapter(getActivity(), verticalDataModals, mp, ...)
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false);
infiniteScrollListener = new InfiniteScrollListener(linearLayoutManager, this);
infiniteScrollListener.setLoaded();
recyclerView.setLayoutManager(linearLayoutManager);
SnapHelper snapHelper = new PagerSnapHelper();
recyclerView.setOnFlingListener(null);
snapHelper.attachToRecyclerView(recyclerView);
homeViewVerticalAdapter.setHasStableIds(true);
// to disable caching so auto song play on all items
recyclerView.getRecycledViewPool().clear();
recyclerView.setItemViewCacheSize(0);
recyclerView.setAdapter(homeViewVerticalAdapter);
I logged inside recyclerView.addOnScrollListener and inside public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 2 logs are displaying.
1st recycler view(Vertical Scroll)
#Override
public void onBindViewHolder(#NonNull HomeViewVerticalAdapter.HomeViewHolder holder, int position) {
if (holder instanceof DataViewHolder) {
final VerticalDataModal verticalDataModal = verticalDataModalList.get(position);
HomeViewHorizontalAdapter homeViewHorizontalAdapter = null;
homeViewHorizontalAdapter = new HomeViewHorizontalAdapter(context, postViewItem, mp, ...)
holder.recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
SnapHelper snapHelper = new PagerSnapHelper();
holder.recyclerView.setOnFlingListener(null);
snapHelper.attachToRecyclerView(holder.recyclerView);
homeViewHorizontalAdapter.setHasStableIds(true);
holder.recyclerView.setAdapter(null);
holder.recyclerView.getRecycledViewPool().clear();
holder.recyclerView.setItemViewCacheSize(0);
holder.recyclerView.setAdapter(homeViewHorizontalAdapter);
homeViewHorizontalAdapter.notifyDataSetChanged();
......
....
}
}
2st recycler view(Horizontal Scroll)
#Override
public void onBindViewHolder(#NonNull HomeViewHorizontalAdapter.HomeViewHolder holder, int position) {
if (holder instanceof DataViewHolder) {
String musicPath = horizontalData1modal.getMusicUrl();
String imagePath = horizontalData1modal.getImageUrl();
Picasso.get()
.load(imagePath)
.placeholder(R.drawable.influencers)
.error(R.drawable.influencers)
.into(holder.imagePreviewLocal);
// playing fine when click on layout
holder.contentLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
musicPlayer(musicPath, "click", holder);
}
});
// not playing from auto play
musicPlayer(musicPath, "auto", holder);
......
....
}
}
and musing play method inside 2st recycler view(Horizontal Scroll):
Note: Here music playing, loader and hide a layout after some time logic
private void musicPlayer(String musicPath, String playType, #NonNull HomeViewHorizontalAdapter.HomeViewHolder holder) {
try {
Log.d(TAG, "music player detected: "+playType);
holder.contentDataLayout.setVisibility(View.VISIBLE);
if(timerMusicTrack != null){
timerMusicTrack.cancel();
}
if ((!mediaplayer.isPlaying() || playType.equals("auto")) && musicPath != null && !musicPath.equals("")) {
isPlaying = true;
mediaplayer.reset();
mediaplayer.stop();
mediaplayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
Uri musicUri = Uri.parse(musicPath);
mediaplayer.setDataSource(context, musicUri);
mediaplayer.prepareAsync();
progressBarLocal.setVisibility(View.VISIBLE);
mediaplayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mediaplayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END){
progressBarLocal.setVisibility(View.GONE);
return true;
} else if(what == MediaPlayer.MEDIA_INFO_BUFFERING_START){
progressBarLocal.setVisibility(View.VISIBLE);
}
return false;
}
});
progressBarLocal.setVisibility(View.GONE);
mediaplayer.start();
timerMusicTrack = new CountDownTimer(delayMillis, 100) {
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
timerUsed = true;
holder.contentDataLayout.setVisibility(View.GONE);
}
};
timerMusicTrack.start();
}
});
} else {
isPlaying = false;
mediaplayer.reset();
mediaplayer.stop();
progressBarLocal.setVisibility(View.GONE);
}
} catch (IOException e) {
e.printStackTrace();
progressBarLocal.setVisibility(View.GONE);
}
}
How can I auto play song for currently visible item ? Am I missing something ?
Related
I m using viewpager and its adapter i m displaying image and video as per condition.
I don't have fragment because i m using PagerAdapter.
Code:
pager.setOnPageChangeListener(pageChangeListener);
pager.post(new Runnable() {
#Override
public void run() {
pageChangeListener.onPageSelected(pager.getCurrentItem());
}
});
}
ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrollStateChanged(int arg0) {
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageSelected(int position) {
View view = mediaFullScreenImageAdapter.getActiveView(pager);
if (selectedPhotosByDate.get(position).getMessagetype().equalsIgnoreCase("VIDEO")) {
CustomVideoPlayer video_player = (CustomVideoPlayer) view.findViewById(R.id.video_player);
if (video_player.isPlaying()) {
Log.v("THISCALLEDD", "Y" + " : " + position);
video_player.pause();
}
}
}
};
#Nullable
public View getActiveView(final ViewPager viewPager) {
final PagerAdapter adapter = viewPager.getAdapter();
if (null == adapter || adapter.getCount() == 0 || viewPager.getChildCount() == 0) {
return null;
}
int position;
final int currentPosition = viewPager.getCurrentItem();
for (int i = 0; i < viewPager.getChildCount(); i++) {
final View child = viewPager.getChildAt(i);
final ViewPager.LayoutParams layoutParams = (ViewPager.LayoutParams) child.getLayoutParams();
if (layoutParams.isDecor) {
continue;
}
final Field positionField;
try {
positionField = ViewPager.LayoutParams.class.getDeclaredField("position");
positionField.setAccessible(true);
position = positionField.getInt(layoutParams);
} catch (NoSuchFieldException e) {
break;
} catch (IllegalAccessException e) {
break;
}
if (position == currentPosition) {
return child;
}
}
return null;
}
Update:
selectedPhotosByDate = appDatabase.chatMessagesDao().getMediaByGroupId(topicid, "Y");
if (selectedPhotosByDate.size() > 0 && selectedPhotosByDate != null) {
mediaFullScreenImageAdapter = new MediaFullScreenImageAdapter(MediaFullScreenSlideActivity.this, selectedPhotosByDate);
pager.setAdapter(mediaFullScreenImageAdapter);
for (int i = 0; i < selectedPhotosByDate.size(); i++) {
if (messageid == selectedPhotosByDate.get(i).getMessageId()) {
position = i;
break;
}
}
pager.setCurrentItem(position);
pager.setOffscreenPageLimit(selectedPhotosByDate.size());
}
I am able to get logcat value . But my video doesn't get pause.
If my video is playing and user swipe for other item playing video must be paused.
Advanced help would be appreciated!
Based on the documentation here, onPageSelected is called when a new page is selected. That means, the active view that you are trying to get using the currentItem from the ViewPager will return you the current selected item rather than the previously selected item. So your reference to video_player is also wrong and hence it is not working.
A simple way to do this is to have a variable currentPosition. Update it in onPageSelected but before updating, get its view and pause the video. This way whenever you access it, you will first get the previous index and then you will update it to the current index for the next time.
As per my understanding ,you want to pause previous video when user swipe in viewpager .
#Override
public void onPageSelected(int position) {
View view = mediaFullScreenImageAdapter.getActiveView(pager);
if (selectedPhotosByDate.get(position).getMessagetype().equalsIgnoreCase("VIDEO")) {
CustomVideoPlayer video_player = (CustomVideoPlayer) view.findViewById(R.id.video_player);
if (video_player.isPlaying()) {
Log.v("THISCALLEDD", "Y" + " : " + position);
video_player.pause();
}
}
}
you are getting view object of current active position ,but you want to pause video of (position - 1 ),you need to have view object of previous position .
Finally resolved the issue.
I moved this code
View view = mediaFullScreenImageAdapter.getActiveView(pager);
CustomVideoPlayer video_player = (CustomVideoPlayer) view.findViewById(R.id.video_player);
video_player.pause();
to onPageScrolled() method instead of onPageSelected() method.
This worked for me with a Youtube video playing as an embedded iFrame in a WebView:
viewPager.addOnPageChangeListener(object : SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
// NOTE: this workaround pauses any playing Youtube video in the WebView, if the video screen is being switched out.
// This works by getting the ViewPager#focusedChild, which will be the WebView if that screen is currently in focus,
// before the next selected ViewPager screen gets switched-to/loaded.
viewPager.focusedChild?.findViewById<WebView>(R.id.video_view)?.onPause()
// NOTE: we do a postDelay below, to ensure the WebView gets put back into its resume state, if it being switched to.
// This is delayed, so that this happens after the ViewPager screen with the WebView, has time to load it into place.
postDelayed({ viewPager.focusedChild?.findViewById<WebView>(R.id.video_view)?.onResume() }, 200)
}
})
The ViewHolder I have contains views that are needed for each item of my RecycleView, which are two TextViews, PlayPauseView, and AppCompatSeekBar. In the constructor I have initialized them to their ids of my layout, and is ready for my Adapter class to extend it. The problem is I have this PlayPauseView that needs to be toggled at the correct position for it to function right, because if I press the view at another position, the previous position is still toggled. Here's an image for better understanding.
The yellow means that it is currently playing, but two cannot play at the same time so I was hoping to get the position of the view inside the RecyclerView item.
This is from the Holder class
TextView mInstrumentalName, mProducer;
PlayPauseView mPlayButton; // Need to add pause/stop
AppCompatSeekBar mMusicSeekbar;
ItemClickListener itemClickListener;
public TrackHolder(View itemView) {
super(itemView);
mInstrumentalName = (TextView) itemView.findViewById(R.id.instrumental_name);
mProducer = (TextView) itemView.findViewById(R.id.producer);
mPlayButton = (PlayPauseView) itemView.findViewById(R.id.play_pause);
mMusicSeekbar = (AppCompatSeekBar) itemView.findViewById(R.id.music_seekbar);
itemView.setOnClickListener(this);
}
This is from the Adapter class
#Override
public void onBindViewHolder(#NonNull final TrackHolder holder, final int position) {
holder.mInstrumentalName.setText(tracks.get(position).getInstrumentalName());
holder.mProducer.setText(tracks.get(position).getProducer());
holder.mPlayButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.mPlayButton.toggle();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
if (mediaPlayer.isPlaying()) {
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(tracks.get(position).getMusicLink());
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
mediaPlayer.prepareAsync();
} else {
try {
mediaPlayer.setDataSource(tracks.get(position).getMusicLink());
previous_link = tracks.get(position).getMusicLink();
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
mediaPlayer.prepareAsync();
}
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
}
});
holder.setItemClickListener(new ItemClickListener() {
#Override
public void onItemClick(View v, int pos) {
Toast.makeText(context, Integer.toString(tracks.size()), Toast.LENGTH_SHORT).show();
}
});
}
If you want to get position in your view holder class then you can call getAdapterPosition() inside your class. this will return you the clicked position in view holder class
I finally found a solution. So what I did instead of focusing on the views inside of the ViewHolder, I refreshed/updated the whole item in the RecyclerView, meaning it will refresh the toggle state of the previous item. Here's a code snipped if anyone in the future has any trouble on the same question.
private int start_notifying = 0;
private int previous_pos = -1;
So start notifying is at 0 because you don't want to refresh the item until you toggled the play button at least once, and set the current position to become the previous position when you want to press a different list item. You then increment so you can start refreshing items on the next item pressed. Using notifyItemChanged(previous_pos) refreshed the item of the previous position that was pressed, making the play button go back to its normal state.
if (start_notifying > 0) {
notifyItemChanged(previous_pos);
previous_pos = position;
} else {
previous_pos = holder.getAdapterPosition();
Log.d("previous_pos", Integer.toString(holder.getAdapterPosition()));
start_notifying++;
}
I have a list view of songs with play and pause button in every row..
I can't have two pause Icon(two playing song) in my list view So I need to first reset all of them to play Icon then set the selected view to pause Icon..
How can I do this ? Or can you offer a better solution for this ?
This is my code :
In model class ( Product ) :
public int currentPosition= -1;
in Adapter:
public interface PlayPauseClick {
void playPauseOnClick(int position);
}
private PlayPauseClick callback;
public void setPlayPauseClickListener(PlayPauseClick listener) {
this.callback = listener;
}
.
.
.
holder.playPauseHive.setImageResource(product.getPlayPauseId());
holder.playPauseHive.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (callback != null) {
callback.playPauseOnClick(position);
if (position == product.currentPosition) {
product.setPlayPauseId(R.drawable.ic_pause);
//set the image to pause icon
}else{
//set the image to play icon
product.setPlayPauseId(R.drawable.ic_play);
}
notifyDataSetChanged();
}
}
});
my Callback inside my Activity:
#Override
public void playPauseOnClick(int position) {
final Product product = songList.get(position);
if(product.currentPosition == position){
product.currentPosition = -1; //pause the currently playing item
}else{
product.currentPosition = position; //play the item
}
this.adapter.notifyDataSetChanged();
}
For my case I use a variable to store the current playing item position. Let say
int x = -1; //-1 can indicate nothing was currently playing
So in the playPauseOnClick() you can do something like this
#Override
public void playPauseOnClick(int position) {
if(x == position){
x = -1; //pause the currently playing item
}else{
x = position; //play the item
}
this.adapter.notifyDataSetChanged();
}
The reason i remove product.setPlayPauseId() is because you really don't need them. You just need to set the play or pause icon based on your x varible which I created earlier. Do Something like this in your getView()
Product product = songList.get(position);
if (position == x) {
//set the image to pause icon
}else{
//set the image to play icon
}
So once you call adapter.notifyDataSetChanged() your adapter will do all the work for you. Whenever the x variable having the value -1 none of the icon will be display the pause icon, this can also make sure only one position will showing the pause icon as well.
Hope it help you.
I want the focus to move automatically to next row position in Recyclerview. My Recyclerview contains a videoview. On media completion, I want the next videoview to be played.Videoview is playing next video but only in first position videoview
private static int position= 0;
public static class ViewHolder extends RecyclerView.ViewHolder {
private VideoView videoView;
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
position= getAdapterPosition();
//recyclerView.smoothScrollToPosition(position);
Log.d("TAG_LIST", "Element " + position+ " clicked.");
}
});
}
holder.videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
//recyclerView.smoothScrollToPosition(position);
Video(holder);
}
});
private void Video(ViewHolder holder) {
if (++position>= items.size()) {
// Last song, just reset position
position= 0;
} else {
// Play next song
recyclerView.smoothScrollToPosition(position);
AsyncTask task = new AsyncTask (items.get(position).getUrl(), holder,position);
task.execute(items.get(position).getUrl());
}
}
You didnt post any code related to your problem but I can suggest you some methods for this.
Find first visible item position on RecyclerView and start video at your VideoView
Listen scroll changes on your RecyclerView and when first visible item index changed, stop your video, and start next RecyclerView item VideoView
Hi i have recyclerView row item consists of a play Button(Which change to stop and Play as toggle) and a play seek bar and TextView.
The each row plays different audios .
When i click on play button of respective row i am able to stop the other row audio ..
Addressing to the problem ..
When i am playing a audio of a respective row, the button of earlier row has to stow a play symbol (in the sense it has to change the state ) please help me how to update the other rows of recyclerView when the other item is clicked
#Override
public void onBindViewHolder(final ViewHolder holder, final int i) {
if (holder.viewType == ROW_TYPE){
// holder.play_button.setImageResource(R.drawable.play_button);
if(i == selected_item){
}
else {
// holder.play_button.setImageResource();
}
JSONObject obj;
String mp3_url = null, caption = null, thumbnail_url = null;
try {
obj = voicesArray.getJSONObject(i);
mp3_url = obj.getString("mp3_url");
caption = obj.getString("caption");
thumbnail_url = obj.getString("thumbnail_url");
} catch (JSONException e) {
e.printStackTrace();
}
holder.list_text.setText(caption);
imageLoader.DisplayImage(thumbnail_url, holder.list_image);
final String finalMp3_url = mp3_url;
holder.play_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
selected_item = i;
int ii = holder.getOldPosition();
// holder.play_button.setImageResource();
// updateItem(ii);
if(player != null){
player.reset();
holder.play_button.setImageResource(R.drawable.play_button);
myAudioPlay(holder, finalMp3_url);
}
else {
myAudioPlay(holder,finalMp3_url);
/*if (player == null) {
player = new MediaPlayer();
playAudiFile(finalMp3_url);
holder.play_button.setImageResource(R.drawable.pause_button);
} else if (player.isPlaying()) {
holder.play_button.setImageResource(R.drawable.play_button);
player.stop();
Log.d("player", "" + player);
player.reset();
} else {
holder.play_button.setImageResource(R.drawable.pause_button);
playAudiFile(finalMp3_url);
}*/
}
}
});
}
}
Apparently the purpose of a recyclerView is to recycle the views to minimize memory usage required for rendering view so setting an image in the view is not an apt solution,Their are many solutions for your problem the One i would pick(Pseudo code) is given below
Step 1. Create a Boolean value in your model class
bool isPlay=true;
Step 2. When you are playing an audio loop through your Model for the recycler adapter and set the value for isPlay accourdingly
for(object obj in listOfModels)
if(obj.Id!=idOfthePlayingView)
obj.isPlay=false;
Then notify your adapter
yourRecyclerAdapterObject.notifyDatasetChanged()