对getTag()感到困惑

时间:2015-03-15 05:33:08

标签: java android listview

我刚刚开始学习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$1.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$1.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$1.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)

4 个答案:

答案 0 :(得分:0)

我曾经发现自己想要使用tag属性作为视图的便捷方法。从那时起我就决定如果你使用tag属性作为一种方便的方法,你就不会做错,但通常会有更优雅的解决方法。

在您的情况下,我将使用静态视图数组,并引用数组中视图的索引来访问该视图的索引。如果您使用ListView使用Adapters或其衍生物,并且使用ArrayAdapter几乎可以轻松地使用yourArrayAdapter.getPosition(T item);,这会更容易,因为视图是为您存储的,并且可以获得索引与{{1}}。

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

答案 1 :(得分:0)

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

songLay.setTag(position);
  return songLay;
}

Uggg太烦人了,我花了3个小时。但是这些论坛很棒,反应非常迅速!

答案 2 :(得分:0)

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

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

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

yourButton.setTag(position);

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

yourButton.getTag();

此返回的位置将是列表中按下单击按钮的行的位置。

看看它对你有帮助!!!!

答案 3 :(得分:0)

因此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属性的视图引用。 View view中的void songPicked(View view)参数是已定义onClick的视图。例如,如果它是一个按钮,你可以将view强制转换为按钮而不会崩溃;如果你试图把它投射到一个imageview,它会崩溃。

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

多哈 - 每个人都在我之前到达那里,但是你可以随心所欲地推,无论是你的位置,还有整个uri,如果你愿意的话!

就使用setTag而言,使用它你想要的一切!对于这样的东西来说,这是一种方便的方法。请记住,视图将保留对您放入标记桶的任何内容的引用,因此不要在那里放置任何比父活动生命周期更长的内容,以免泄漏内存!