Here I am trying to make a music streaming app which works using an API, and I have utilized the MusicPlayer class available in Android. Here what I am trying to do is to get the response from the API in a listview and then click on any item in the listview to stream that particular song. The songs are present on a server and I am getting the correct responses from the server and music streaming is also implemented correctly according to me. The music progress is shown with the help of a SeekBar and I am 100% sure it is also working as I wanted it to be.
The new thing which I added to it recently is that, whenever I click on a particular song item from list, the headphone icon present on left of that item changes to play icon as shown in the screenshot attached below
Screenshot of the app from my phone
Here, play icon on left of songs show that they are recently played, but I want play icon only on those songs which is currently playing, not on recently played songs too.
But I cannot figure out way to do this, I have tried some things like
- Storing the clicked item position somewhere in the class, to use it later.
- Searching on internet, a way to modify only those list item which are not clicked in listview.
- Modifying the image resource of the recently clicked item when stop icon is clicked.
But 3rd one didn't work, and haven't found a way to achieve the 1st and 2nd one..
Code snippet of ListView implementation
CustomSongList adapter = new CustomSongList(Songs.this, songdetails);
list = (ListView) findViewById(R.id.list);
list.setAdapter(adapter);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
url = songdetails.get(position).getUrl(); // your URl
pos = position;
ImageView listenImage = (ImageView) view.findViewById(R.id.musicicon);
listenImage.setImageResource(R.drawable.play); /* I am changing icon from headphone icon to play icon here*/
if (mediaPlayer != null) {
mediaPlayer.stop();
}
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mediaFileLengthInMilliseconds = mediaPlayer.getDuration();// gets the song length in milliseconds from URL
mediaPlayer.start();
imPlay.setVisibility(View.INVISIBLE);
imPause.setVisibility(View.VISIBLE);
primarySeekBarProgressUpdater();
}
});
Here is a code snippet of ListView Adapter class.
public View getView(int position, View view, ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View rowView = inflater.inflate(R.layout.songlist_item, null, true);
TextView Title = (TextView) rowView.findViewById(R.id.title);
TextView Singer = (TextView) rowView.findViewById(R.id.singer);
TextView Duration = (TextView) rowView.findViewById(R.id.duration);
Title.setText(songlist.get(position).getTitle());
Singer.setText("Singer : " + songlist.get(position).getSinger());
Duration.setText("Duration : " + songlist.get(position).getDuration()+" mins");
return rowView;
}
And this is the snippet of songs layout file
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/seekBar"
android:layout_below="@+id/songheading"
android:layout_margin="10dp"
android:background="@android:color/transparent"
android:cacheColorHint="@android:color/transparent"
android:divider="@drawable/transperent_color"
android:dividerHeight="5dip" />
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/buttonPlay" />
<ImageView
android:id="@+id/buttonPlay"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/play" />
<ImageView
android:id="@+id/buttonPause"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/pause" />
<ImageView
android:id="@+id/buttonStop"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:background="@drawable/stop" />
The headphone icon set for each item of listview is done by following code snippet
<ImageView
android:id="@+id/musicicon"
android:layout_width="50dp"
android:layout_height="50dp"
android:contentDescription="@string/app_name"
android:gravity="center"
android:layout_marginTop="10dp"
android:src="@drawable/listen_icon" />
Edit
Applied the solution given by @KalaBalik
thanks @KalaBalik for posting the steps to mark the song, but I have applied your steps step by step, and what I found is that my app now doesn't show the list itself, when loading is complete, here is the screenshot
And here are the edits performed by me on the code as you said
Code snippet of listview
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/seekBar"
android:layout_below="@+id/songheading"
android:layout_margin="10dp"
android:background="@android:color/transparent"
android:cacheColorHint="@android:color/transparent"
android:choiceMode="singleChoice" // -----here
android:divider="@drawable/transperent_color"
android:dividerHeight="5dip" />
CheckedTextView implementation
<CheckedTextView
android:id="@+id/tvChecked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="@drawable/playing" />
Compound drawable for checkedTextview,
playing.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/play" android:state_checked="true" />
<item android:drawable="@drawable/listen_icon" />
SpannableStringBuilder implementation
String finalString = title + "\n" + singer + "\n" + duration;
SpannableStringBuilder sb = new SpannableStringBuilder();
Typeface exoMedium = Typeface.createFromAsset(context.getAssets(), "fonts/Exo-Medium.ttf");
TypefaceSpan exoMediumSpan = new CustomTypeFaceSpan("", exoMedium);
sb.setSpan(exoMediumSpan, finalString.indexOf(title), title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.setSpan(new ForegroundColorSpan(Color.BLUE), finalString.indexOf(singer), singer.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.setSpan(new ForegroundColorSpan(Color.CYAN), finalString.indexOf(duration), duration.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
I have created a CustomTypeFaceSpan using this link
setting SpannableStringBuilder in checkedTextView holder.checkedTextView.setText(sb);
here I Used ViewHolder pattern
and setting listview checked true when item clicked
list.setAdapter(adapter);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
list.setItemChecked(position, true);
But result is in the screenshot above, tell me where I have done something wrong.