我刚刚开始学习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)
答案 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而言,使用它你想要的一切!对于这样的东西来说,这是一种方便的方法。请记住,视图将保留对您放入标记桶的任何内容的引用,因此不要在那里放置任何比父活动生命周期更长的内容,以免泄漏内存!