Android Studio - 帮助我理解这段代码

Android Studio - Help me 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;
}

但不是全部代码! 令我困惑的部分是 ViewHolder 部分。

我的问题:

如果有人能为我分解它,那将对提高我对代码的理解非常有帮助。

谢谢。

What is exactly setTag and getTag in this context?

Android 视图支持 "tags",它们是您可以附加到它们的任意对象。标签没有真正的定义,因为它们是您想要的任何东西。所有这些都同样有效:

  • view.setTag(2)
  • view.setTag("Hello world")
  • view.setTag(new Object())

what does view.setTag(viewHolder) means?

您将 viewHolder 对象作为其标签附加到 view。这本身 不做 任何事情,但它允许您稍后通过调用 (ViewHolder) view.getTag().

检索 viewHolder

Why do i have to create a private class called ViewHolder [...]

当您使用 ListView 适配器时,有两件事会降低应用程序的性能:调用 inflate() 和调用 findViewById().

您可以通过使用传入的 view 参数(当它不为空时)绕过第一个问题,并且仅在传入的 view 参数为空时调用 inflate() 。这就是您的代码:

if (view == null) {
    ...
    LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    view = layoutInflater.inflate(layout, null);
    ...
} else {
    ...
}

您可以使用此 "ViewHolder pattern" 绕过第二个。你创建一个 object (view holder) 到 "hold" views 在你查找它们之后。这就是您的代码:

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();
}

完成该块后,您将拥有一个名为 viewHolderViewHolder 实例,您可以使用它直接访问视图。当传入的 view 参数为 null 时,您 创建 视图持有者,通过调用 findViewById() 填充 它,并通过调用 setTag()保存。当传入的 view 参数不为 null 时,您可以通过调用 getTag().

简单地 检索 视图持有者

将它们放在一起,这意味着您可以编写如下代码:

viewHolder.txtName.setText(song.getName());

而不是这个较慢的代码:

TextView txtName = view.findViewById(R.id.songName_text);
txtName.setText(song.getName());

viewholder 是 android 应用中使用的一种设计模式,目的是取代更昂贵的 findviewbyid 调用。

引用文档

您的代码可能会在 ListView 滚动期间频繁调用 findViewById(),这会降低性能。即使 Adapter returns 一个用于回收的膨胀视图,您仍然需要查找元素并更新它们。避免重复使用 findViewById() 的一种方法是使用 "view holder" 设计模式。

关于第二个和第三个问题,settaggettag 基本上是让您的观点具有 "memories" 的一种方式,您可以参考 here 以获取更多信息和更详细的解释!

希望对您有所帮助!