对 getTag() 感到困惑

Confused about getTag()

我才刚刚开始学习 Java 和编程。我在这里学习了一个教程:http://code.tutsplus.com/tutorials/create-a-music-player-on-android-song-playback--mobile-22778 但是现在我已经被困了几个小时!!!请帮助

它运行良好,我还通过使用 ViewHolder 模式优化了 ListView 作为额外的挑战。

public class SongAdapter extends BaseAdapter {

    private ArrayList<Song> songs;
    private LayoutInflater songInf;

    public SongAdapter(Context c, ArrayList<Song> theSongs) {
        songs = theSongs;
        songInf = LayoutInflater.from(c);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return songs.size();
    }

    @Override
    public Object getItem(int arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long getItemId(int arg0) {
        // TODO Auto-generated method stub
        return 0;
    }

    class ViewHolder{

        TextView songView;
        TextView artistView;

    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        LinearLayout songLay = (LinearLayout)convertView;

        if (songLay == null) {

            songLay = (LinearLayout) songInf.inflate
                    (R.layout.song, parent, false);
            //get view holder instance
            holder = new ViewHolder();

            //populate viewholder with artist and text views

            holder.songView = (TextView) songLay.findViewById(R.id.song_title);
            holder.artistView = (TextView) songLay.findViewById(R.id.song_artist);

            songLay.setTag(R.string.TAG1,holder);
        }
        else {
           holder = (ViewHolder) songLay.getTag(R.string.TAG1);
        }

        //get song using position
        Song currSong = songs.get(position);

        //get title and artist strings
        holder.songView.setText(currSong.getSongTitle());
        holder.artistView.setText(currSong.getArtist());

        return songLay;
    }
}

现在唯一的问题是当我点击要播放的歌曲时,它崩溃了。我认为是由于 setTag() 弄乱了我的这部分代码。 songPicked 是在 xml 布局 song.xml 中使用 onClick 标记创建的方法。

public void songPicked(View view){
    musicSrv.setSong(Integer.parseInt(view.getTag().toString()));
    musicSrv.playSong();
}

这是MusicService.java中的相关代码。

public void setSong(int songIndex){
    songPosn = songIndex;
}

public void playSong(){
    //play a song
    player.reset();
    //get song
    MainActivity.Song playSong = songs.get(songPosn);
    //get ID
    long currSong = playSong.getID();
    //set uri
    Uri trackUri = ContentUris.withAppendedId(
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,currSong);
    try{
        player.setDataSource(getApplicationContext(), trackUri);
    }
    catch(Exception e){
        Log.e("MUSIC SERVICE", "Error setting data source", e);
    }

    player.prepareAsync();
}

我得到这个异常:

03-15 01:49:21.065  14179-14179/com.example.mediaplayertut E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.IllegalStateException: Could not execute method of the activity
            at android.view.View.onClick(View.java:3838)
            at android.view.View.performClick(View.java:4475)
            at android.view.View$PerformClick.run(View.java:18786)
            at android.os.Handler.handleCallback(Handler.java:730)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:176)
            at android.app.ActivityThread.main(ActivityThread.java:5419)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.reflect.InvocationTargetException
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at android.view.View.onClick(View.java:3833)
            at android.view.View.performClick(View.java:4475)
            at android.view.View$PerformClick.run(View.java:18786)
            at android.os.Handler.handleCallback(Handler.java:730)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:176)
            at android.app.ActivityThread.main(ActivityThread.java:5419)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.NullPointerException
            at com.example.mediaplayertut.MainActivity.songPicked(MainActivity.java:152)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at android.view.View.onClick(View.java:3833)
            at android.view.View.performClick(View.java:4475)
            at android.view.View$PerformClick.run(View.java:18786)
            at android.os.Handler.handleCallback(Handler.java:730)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:176)
            at android.app.ActivityThread.main(ActivityThread.java:5419)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
            at dalvik.system.NativeStart.main(Native Method)

我曾经发现自己想使用标签属性作为视图的便捷方法。从那时起,我就决定,如果您将标记属性用作一种方便的方法,您并没有做错,但通常有更优雅的解决方法。

在您的情况下,我将使用静态视图数组,并在数组中引用视图的索引以访问该视图的索引。如果您正在使用 ListView 或其派生词使用 Adapters,它会更容易,而使用 ArrayAdapter 几乎可笑地容易,因为视图是为您存储的,并且可以获取索引yourArrayAdapter.getPosition(T item);.

最好明确声明并知道您将收到什么,并利用 java 使用的类型系统。

感谢 krishnas 的评论,我意识到我已经从原始教程的 getView 方法中删除了它:

songLay.setTag(position);
  return songLay;
}

Uggg 太烦人了,我花了 3 个小时。但是这些论坛非常棒,回复速度超快!

在您的适配器中,有一个函数 getItem(position) 当前 returning null。使用该功能 return 您点击的列表项的详细信息。例如你的 getItem 函数应该是这样的。

@Override
public Object getItem(int arg0) {
    // TODO Auto-generated method stub
    return songs.get(arg0);
}

标签的概念是,如果您在每个项目列表中都有一个按钮,并且您将 onclicklistener 添加到该按钮,那么您就可以做到

yourButton.setTag(position);

并且在您点击该按钮时,您可以将位置检索为

yourButton.getTag();

此 returned 位置将是列表中按钮被单击的行的位置。

看看对你有没有帮助!!!

所以 setTag 是一种方便的方法,所有视图都可以保存任意引用:实际上,您可以保存任意数量,前提是您使用重载方法,并为标签分配一个 id 的额外参数。

mView.setTag(mFirstObject); 使用隐式标签 ID,而 mView.setTag(R.id.tag1, mSecondObject); 使用显式 R.id.tag1 标签 ID。

要检索 mFirstObject 然后使用语句 Object first = mView.getTag(); 并检索 mSecondObject 然后调用将是 Object second = mView.getTag(R.id.tag1); (注意这里我假设它们都是对象,因此不需要投射它们)。

代码正在崩溃,因为您没有在哪里使用隐式 ID 分配标签。你必须做一些事情,比如,对于你分配的任何视图 android:onClick='songPicked' 你必须做的:

@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; LinearLayout songLay = (LinearLayout)convertView; if (songLay == null) { songLay = (LinearLayout) songInf.inflate(R.layout.song, parent, false); //get view holder instance holder = new ViewHolder(); //populate viewholder with artist and text views holder.songView = (TextView) songLay.findViewById(R.id.song_title); holder.artistView = (TextView) songLay.findViewById(R.id.song_artist); songLay.setTag(R.string.TAG1,holder); } else { holder = (ViewHolder) songLay.getTag(R.string.TAG1); } //get song using position Song currSong = songs.get(position); //get title and artist strings holder.songView.setText(currSong.getSongTitle()); holder.artistView.setText(currSong.getArtist()); /* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ holder.viewWithOnClick=songPickedInXml.setTag(currSong.getId()); /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ return songLay; } }

其中 holder.viewWithOnClick=songPickedInXml 是具有相应 onClick 属性的视图引用。 void songPicked(View view) 中的 View view 参数是为其定义了 onClick 的视图。例如,如果它是一个按钮,您可以将 view 转换为按钮而不会崩溃;如果你试图将它投射到图像视图,它会崩溃。

但是,我强烈建议您不要将 onClick 特别用于列表视图项目。假设用户可以单击整行来播放歌曲,只需在列表视图上使用 ItemClickListener,然后在 LinearLayout songLay 上执行 setTag(currSong.getId());它应该工作。不使用 onClick 的原因——特别是在这种情况下——是因为它在 运行 时间引用哪个视图是完全不明确的。

Doh--每个人都比我先到达那里,但仅供参考,你可以随心所欲地推,而不仅仅是位置,如果你愿意,还可以推整个 uri!

至于setTag,想怎么用就怎么用!对于这样的事情,这是一种方便的方法。请记住,该视图将保留对您放入标签桶中的任何内容的引用,因此不要在其中放置任何会超过父 activity 生命周期的内容,以免泄漏内存!