Android Studio-帮助我理解此代码

时间:2018-11-09 22:39:07

标签: java android android-studio layout-inflater android-viewholder

我正在尝试理解以下代码:

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部分。

我的问题:

  • 为什么我必须创建一个名为private class的{​​{1}} 仅创建一个ViewHolder来存储我所有的视图(public method)并将其用于我的txtName, txtSinger, playB, stopB的想法?
  • inflater是什么意思?
  • 在这种情况下,view.setTag(viewHolder)setTag到底是什么?

如果有人可以帮我解决这个问题,那么对我对代码的理解将非常有帮助。

谢谢。

2 个答案:

答案 0 :(得分:1)

  

在这种情况下,setTag和getTag到底是什么?

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

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

view.setTag(viewHolder)是什么意思?

您正在将viewHolder对象作为其标签附加到view。这本身并不能执行任何操作,但是可以让您稍后调用viewHolder来检索(ViewHolder) view.getTag()

  

我为什么必须创建一个名为ViewHolder的私有类[...]

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

您可以通过使用传入的view参数(不为null)来绕过第一个参数,而仅当传入的inflate()参数为null时才调用view。这就是您的代码:

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

使用此“ ViewHolder模式”可以绕过第二个。创建对象(视图持有者)以在查找视图后“保留”视图。这就是您的代码:

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

完成该块后,您将拥有一个名为ViewHolder的{​​{1}}实例,可用于直接访问视图。当传入的viewHolder参数为空时,您创建视图持有者,通过调用view 填充它,并保存< / em>,方法是调用findViewById()。如果传入的setTag()参数不为null,则可以通过调用view检索视图持有者。

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

getTag()

代替此较慢的代码:

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

答案 1 :(得分:1)

viewholder是一种在Android应用中使用的设计模式,用于替换更昂贵的findviewbyid调用。

引用文档

在滚动ListView时,您的代码可能会频繁调用findViewById(),这可能会降低性能。即使适配器返回膨胀视图以进行回收,您仍然需要查找元素并进行更新。重复使用findViewById()的一种方法是使用“视图持有人”设计模式。

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

希望这会有所帮助!