Listview recycles and shows duplicated images - android

I have created a listview which consists of list of tracks and play and pause image.When clicked on play image, pause image becomes visible and clicking on pause ,play image will become visible.Issue is that when i click on play and scrolls down i see another list item showing pause image and when i scroll down more another list item showing pause image.It is because list recylces an shows duplicated images.On studying the issue i think i need to place if-else statement in the getView method but i failed to implement correct code to resolve this issue..plzz help me with code in tht..

Create a new boolean variable in your SoundCloud class by the name isPlaying. Create getter setters for it, like you have done for other variables.
Get the object from SoundCloudList like this:
SoundCloug soundCloud = (SoundCloud) getItem(position);
This object can be used everywhere you are using soundcloudList.get(position), so that makes it easy to because we don't have to fetch object everytime.
Then in your getView use isPlaying to show play/pause button on every position like below:
if(soundCloud.isPlaying()){
holder.img1.setVisibility(View.VISIBLE);
holder.img2.setVisibility(View.GONE);
}
else{
holder.img1.setVisibility(View.GONE);
holder.img2.setVisibility(View.VISIBLE);
}
and then in your onClickListeners, set values for isPlaying like this:
holder.img1.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(final View v) {
soundCloud.setPlaying(true);
try {
notifyDataSetChanged();
holder.img1.setVisibility(View.INVISIBLE);
holder.img2.setVisibility(View.VISIBLE);
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource("http://api.soundcloud.com/tracks/" + soundcloudList.get(position).getId() + "/stream?client_id=e13865f9debacb5f96375fdd96b7fa1b");
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener()
{
#Override
public void onPrepared(MediaPlayer mp)
{
mp.start();
}
});
mMediaPlayer.setOnCompletionListener(
new MediaPlayer.OnCompletionListener()
{
#Override
public void onCompletion(MediaPlayer mp)
{
mMediaPlayer.release();
mMediaPlayer = null;
holder.img1.setVisibility(View.VISIBLE);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
holder.img2.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(final View v)
{
soundCloud.setPlaying(false);
notifyDataSetChanged();
holder.img1.setVisibility(View.VISIBLE);
holder.img2.setVisibility(View.INVISIBLE);
if(mMediaPlayer!=null)
{
mMediaPlayer.release();
mMediaPlayer = null;
}
}
});
This code will not show the play/pause button duplicated on multiple rows, because now, we are checking the play/pause of audio for every row in getView and only then setting the buttons visibility.

Related

AutoPlay music and video in recyclerview adapter

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 ?

Is it possible to get the position of views inside ViewHolder for the RecyclerView?

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++;
}

Move automatically to next row in Recyclerview

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

android media player - setting up with song selection

My project has a button. Each time the button is clicked I want to assign a new song to the media player. I'm running into error: cannot resolve method 'setDatSource(java.io.File)'
final MediaPlayer mPlayer1 = MediaPlayer.create(MainActivity.this, R.raw.bass);
b.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
try {
mPlayer.setDataSource(Environment.getExternalStorageDirectory());
}catch(Exception e){
e.printStackTrace();
}
return false;
}
});

ListView play button

This might have been asked plenty of times already but I can't seem to get an actual answer to this. I have an adapter that takes data from an API and converts it into a listview. The row has its own Play button in it. When clicking on the play button, inside the row, it should change the button to a pause button. When I click on a different row's play button, the last button clicked should go back to a play button. I know it's setimageresource on the buttons but I can't seem to track my last button clicked. Here are my codes:
This is part of the adapter and
#Override
public View getView(final int pos, View ConvertView, ViewGroup parent) {
final MyViewHolder holder;
if (ConvertView == null){
ConvertView = layoutinflater.inflate(R.layout.row, parent, false);
holder = new MyViewHolder();
holder.plays = (ImageButton) ConvertView.findViewById(R.id.plays);
holder.plays.setFocusable(false);
play = playdata.get(pos);
holder.posturl= play.getposturl();
mStartPlaying = true;
player = new MediaPlayer();
// player.setOnCompletionListener(onCompletion);
// player.setOnErrorListener(onError);
holder.plays.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String url = holder.posturl;
if (!mStartPlaying){
holder.plays.setImageResource(R.drawable.ic_action_stop);
startPlaying(url);
mStartPlaying = true;
} else{
holder.plays.setImageResource(R.drawable.ic_action_play);
player.stop();
mStartPlaying = false;
}
}
private void startPlaying(String url) {
// TODO Auto-generated method stub
try {
player.reset();
player.setDataSource(url);
// mPlayer.setDataSource(mFileName);
player.prepareAsync();
player.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
player.start();
}
});
} catch (IOException e) {
Log.e("preparefailed", "prepare() failed");
}
}
});
if (!player.isPlaying()){
holder.plays.setImageResource(R.drawable.ic_action_play);
}
ConvertView.setTag(holder);
ConvertView.setOnClickListener(new OnClickListener() {
public void onClick(View v){
Log.d("hi", "hi from "+ pos);
}
});
}
else{
holder = (MyViewHolder)ConvertView.getTag();
}
play = playdata.get(pos);
holder.play = play;
return ConvertView;
}
This is my main activity:
mLastClickedPosition = -1;
this.PlayList.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick (AdapterView<?> parent, View view, int position, long id) {
if(mLastClickedPosition != -1){
// do something to pause the item in your list at this position
}
// next, update mLastClickedPosition
mLastClickedPosition = position;
if(position==0){
ImageView imageView = (ImageView) view.findViewById(R.id.plays);
imageView.setImageResource(R.drawable.ic_action_stop);
}
// play audio
}
});
the mediaplayer is initialized in the adapter, I'm not really sure how I'd go about initializing it in the main activity instead. Any help is appreciated! Thanks in advanced!
If each row in the ListView know what state it is in(butten), You could control each row in the ListView using listeners or LocalBroadcastManager. Try this. That is a ListView skeleton to start thinking in OOP where each row really becomes an object that could know what state it is in . A ListView abstraction example that makes it easier to understand/see the java code as objects.
From the example above, let say that every row(EventItem) that are created are a listener or register for LocalBroadcastManager Then it would be easy to address each row. Talk to each row and ask it to stop having the button in a pause state, or whatever
...I'm updating this answer since the link above doesn't work. Here's a new link to a gethub project
listview-with-multiple-layouts
You should follow what Erik suggested and delegate the handling to each row.
BTW, could the problem be because of two listeners you have? You are maintaining the state in main activity which may not even be called. If this is not happening, you may need to move the maintaining state to the list listener (saving the last position).
But, follow what Erik suggested and that should help you to easy debugging and maintenance.
Good Luck.

Categories

Resources