【问题标题】:Android Studio - Help me to understand this codeAndroid Studio - 帮助我理解这段代码
【发布时间】:2018-11-09 22:39:06
【问题描述】:

我试图理解这段代码:

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

我的问题:

  • 为什么我必须创建一个名为ViewHolder 的private class 而不是 只是创建一个public method 来存储我的所有视图(txtName, txtSinger, playB, stopB)并在我的inflater 中使用它?
  • view.setTag(viewHolder) 是什么意思?
  • 在这种情况下,setTag 和 getTag 到底是什么?

如果有人能帮我分析一下,这对我对代码的理解很有帮助。

谢谢。

【问题讨论】:

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


    【解决方案1】:

    在这种情况下,setTag 和 getTag 究竟是什么?

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

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

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

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

    为什么我必须创建一个名为 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 模式”绕过第二个。您创建一个对象(视图持有者)来“持有”您查看后的视图。这就是你的代码:

    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 的 ViewHolder 实例,您可以使用它直接访问视图。当传入的view 参数为空时,您创建视图持有者,通过调用findViewById()填充它,然后保存它致电setTag()。当传入的view 参数不为空时,您可以通过调用getTag() 简单地检索视图持有者。

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

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

    代替这个较慢的代码:

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

    【讨论】:

    • 我对您回答的第三个问题有疑问“为什么我必须创建一个名为 ViewHolder [...] 的私有类”--> 为什么它必须是 class?是否可以将其设为public method ?
    • 为什么要它成为一种方法?但是,简短的回答,不。您只能将对象传递给setTag(),因此您必须为您的视图持有者定义一个类。
    • 我有一个较旧的项目,在那里我创建了一个名为 myViews() 的公共方法,我在 if-statement 的 onCreate 方法中使用了该方法。所以我只是想知道为什么我必须使用class 这次。
    • 我认为这里的主要区别在于,当你只有一个 Activity 时,你可以使用一种方法来组织你所有的 findViewById() 调用......但在这种情况下你有一个 每一行行的>不同视图持有者,因此您需要许多不同的findViewById()调用“组”。视图持有者对象/类是最好的方法。
    【解决方案2】:

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

    引用文档

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

    关于第二个和第三个问题,settag 和gettag 基本上是您的视图拥有“记忆”的一种方式,您可以参考here 获取更多信息和更详细的解释!

    希望这会有所帮助!

    【讨论】:

    • 感谢您的意见!我想我几乎明白了一切。我只需要验证最后一个问题,然后将其放入 Ben P. 答案中。
    猜你喜欢
    • 2011-02-21
    • 2010-11-12
    • 1970-01-01
    • 1970-01-01
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多