简体   繁体   中英

Confused about getTag()

I'm just starting to learn Java and programming altogether really. I followed a tutorial here: http://code.tutsplus.com/tutorials/create-a-music-player-on-android-song-playback--mobile-22778 But now I've been stuck for hours!!! Help please

It was working fine, and I also optimised the ListView by using the ViewHolder pattern as an extra challenge.

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

The only problem now is when I click on a song to play, it crashes. I think due to the setTag() messing with this part of my code. songPicked is a method created in the xml layout song.xml with the onClick tag.

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

This is the relevant code in 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();
}

I get this exception:

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)

I used to find myself wanting to use the tag attribute as a convenience method for views. I've since then decided that if you're using the tag attribute as a convenience method, you're not doing it wrong, but there's usually a more elegant workaround.

In your case I'd use a static array of views, and reference the index of the view in the array to access the index of that view. It's even easier if you are working with a ListView or its derivatives using Adapters , and almost laughably easy with an ArrayAdapter , as the views are stored for you, and one can get the index with yourArrayAdapter.getPosition(T item); .

Better to explicitly declare and know what you'll be receiving and take advantage of the type system java uses.

Thanks to krishnas comment I realised that I had deleted this from the getView method in the original tutorial:

songLay.setTag(position);
  return songLay;
}

Uggg so annoying that I spent 3 hours on that. But these forums are awesome with super quick responses!

In your adapter, there is a function getItem(position) that is currently returning null. Use that function to return your clicked list item details. For example your getItem function should be like this.

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

The tag concept is that if u have a button in your each item list and u add onclicklistener to that button, than u can do

yourButton.setTag(position);

And in your onClick on that button you can retrieve the position as

yourButton.getTag();

This returned position will be the position of the row in the list whose button was clicked.

See if it helps you!!!!

So setTag is a convenient method all views have for holding an arbitrary reference: you can in fact hold however many you want, provided you use the overloaded method with the extra parameter assigning an id to the tag.

That is mView.setTag(mFirstObject); uses an implicit tag id and mView.setTag(R.id.tag1, mSecondObject); uses the explicit R.id.tag1 tag id.

To retrieve mFirstObject then, use the statement Object first = mView.getTag(); and to retrieve mSecondObject then the call would be Object second = mView.getTag(R.id.tag1); (Note here I'm assuming these are both objects and hence no need to cast them).

The code is crashing because no where do you assign a tag using the implicit id. You'd have to do something like, for whichever view you assign android:onClick='songPicked' you have'd to do:

@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; } }

Where holder.viewWithOnClick=songPickedInXml is whichever view reference that has the corresponding onClick attribute. The View view argument in the void songPicked(View view) is the view for which onClick was defined. For instance, if it was a button, you could cast view to button without it crashing; if you tried to cast it to an imageview, it would crash.

However, I strongly you urge NOT to use onClick particularly for a listview item. Assuming the user can click the entire row to play the song, simply use ItemClickListener on the listview, and then on the LinearLayout songLay do setTag(currSong.getId()); and it should work. The reason not to use onClick--particularly in this case--is because it is entirely ambigious which view it'll reference at run time.

Doh--everyone got there before me, but just fyi you can shove whatever you want, not just the position, but the whole uri if you like!

As far as using setTag, use it all you want! It is a convenience method exactly for stuff like this. Just remember, the view will retain a reference to whatever you put into the tag bucket, so don't put anything there that will outlive the parent activity's lifecycle lest you leak memory!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM