Im trying to understand this code:
private class ViewHolder {
TextView txtName, txtSinger;
ImageView playB, stopB;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
final ViewHolder viewHolder;
if (view == null) {
viewHolder = new ViewHolder();
// inflate (create) another copy of our custom layout
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = layoutInflater.inflate(layout, null);
viewHolder.txtName = (TextView) view.findViewById(R.id.songName_text);
viewHolder.txtSinger = (TextView) view.findViewById(R.id.singer_text);
viewHolder.playB = (ImageView) view.findViewById(R.id.play_png);
viewHolder.stopB = (ImageView) view.findViewById(R.id.stop_png);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
final Song song = arrayList.get(position);
// make changes to our custom layout and its subviews
viewHolder.txtName.setText(song.getName());
viewHolder.txtSinger.setText(song.getSinger());
// play music
viewHolder.playB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// MediaPlayer has not been initialized OR clicked song is not the currently loaded song
if (currentSong == null || song != currentSong) {
// Sets the currently loaded song
currentSong = song;
mediaPlayer = MediaPlayer.create(context, song.getSong());
Toast.makeText(context, "Playing: "+ song.getSinger() + " " + song.getName(), Toast.LENGTH_LONG).show();
}
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
viewHolder.playB.setImageResource(R.drawable.play);
} else {
mediaPlayer.start();
viewHolder.playB.setImageResource(R.drawable.pause);
}
}
});
// stop
viewHolder.stopB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// If currently loaded song is set the MediaPlayer must be initialized
if (currentSong != null) {
mediaPlayer.stop();
mediaPlayer.release();
currentSong = null; // Set back currently loaded song
}
viewHolder.playB.setImageResource(R.drawable.play);
}
});
return view;
}
But not the whole code!
The part that is confusing me is the ViewHolder
part.
My questions:
private class
called ViewHolder
instead
of just creating a public method
to store all my views (txtName,
txtSinger, playB, stopB
) and use that in my inflater
?view.setTag(viewHolder)
means?setTag
and getTag
in this context?If anyone can break this down for me it would be very helpful to progress my understanding of code.
Thank you.
What is exactly setTag and getTag in this context?
Android views support "tags", which are arbitrary objects you can attach to them. There's no real definition for tags, because they're whatever you want them to be. All of these are equally valid:
view.setTag(2)
view.setTag("Hello world")
view.setTag(new Object())
what does view.setTag(viewHolder) means?
You're attaching the viewHolder
object to view
as its tag. This doesn't do anything by itself, but it lets you retrieve the viewHolder
later on by calling (ViewHolder) view.getTag()
.
Why do i have to create a private class called ViewHolder [...]
When you're working with ListView
adapters, there are two things that can slow the performance of your app way down: calling inflate()
and calling findViewById()
.
You get around the first by using the passed-in view
argument when it's not null, and only calling inflate()
when the passed in view
argument is null. That's this bit of your code:
if (view == null) { ... LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = layoutInflater.inflate(layout, null); ... } else { ... }
You get around the second by using this "ViewHolder pattern". You create an object (the view holder) to "hold" the views after you look them up. That's this bit of your code:
final ViewHolder viewHolder; if (view == null) { viewHolder = new ViewHolder(); ... viewHolder.txtName = (TextView) view.findViewById(R.id.songName_text); viewHolder.txtSinger = (TextView) view.findViewById(R.id.singer_text); viewHolder.playB = (ImageView) view.findViewById(R.id.play_png); viewHolder.stopB = (ImageView) view.findViewById(R.id.stop_png); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); }
Once that block is done, you'll have a ViewHolder
instance named viewHolder
that you can use to access views directly. When the passed-in view
argument is null, you create the view holder, populate it by calling findViewById()
, and save it by calling setTag()
. When the passed-in view
argument is not null, you can simply retrieve the view holder by calling getTag()
.
Put that all together, and that means you can write code like this:
viewHolder.txtName.setText(song.getName());
Instead of this slower code:
TextView txtName = view.findViewById(R.id.songName_text);
txtName.setText(song.getName());