前言:ArtistAlbumBrowserActivity繼承自ExpandableListActivity,ExpandableListActivity是封裝了ExpandableListView的Activity,ExpandableListView是可以展開的ListView;在ArtistAlbumBrowserActivity中,一級界面的group是顯示歌手名字的ListView,點擊歌手名字展開之後,出現該歌手的專輯列表;
1.首先看下ExpandableListActivity的聲明:
public class ExpandableListActivity extends Activity implements
OnCreateContextMenuListener,
ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupCollapseListener,
ExpandableListView.OnGroupExpandListener {
................................
}
點評:在這個類的聲明中,我們看到ExpandableListActivity實現了4個監聽,分別是:
①OnCreateContextMenuListener
長按一個view,彈出view的上下文菜單
②ExpandableListView.OnChildClickListener
點擊二級界面listview item時的監聽
③ExpandableListView.OnGroupCollapseListener
點擊一級界面listview item收縮時的監聽
④ExpandableListView.OnGroupExpandListener
點擊一級界面listview item展開時的監聽
ArtistAlbumBrowserActivity重寫了onCreateContextMenu以及OnChildClick()方法;
2.開始分析ArtistAlbumBrowserActivity;
先從onCreate()方法開始:
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
MusicLogUtils.d(TAG, "onCreate");
//窗口自帶進度條,這個進度條是音樂播放進度條,與主界面nowplaying佈局的播放進度相對應
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
//窗口無title,因爲該activity是以view的形式存在viewpager中,有title很突兀
requestWindowFeature(Window.FEATURE_NO_TITLE);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
//上次退出音樂播放器時,音樂列表的展開狀態
if (icicle != null) {
mCurrentAlbumId = icicle.getString("selectedalbum");
mCurrentAlbumName = icicle.getString("selectedalbumname");
mCurrentArtistId = icicle.getString("selectedartist");
mCurrentArtistName = icicle.getString("selectedartistname");
} else {
Intent intent = getIntent();
mCurrentAlbumId = intent.getStringExtra("selectedalbum");
mCurrentArtistId = intent.getStringExtra("selectedartist");
}
//掃描媒體庫的廣播
IntentFilter f = new IntentFilter();
f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
f.addAction(Intent.ACTION_MEDIA_MOUNTED);
f.addDataScheme("file");
registerReceiver(mScanListener, f);
setContentView(R.layout.media_picker_activity_expanding);
ExpandableListView lv = getExpandableListView();
//長按一個view,會彈出View的上下文菜單
lv.setOnCreateContextMenuListener(this);
/*listview獲得焦點的時候,響應用戶在鍵盤輸入的匹配符,篩選出匹配的listview Items,這就跟搜索框的搜索功能相匹配了*/
lv.setTextFilterEnabled(true);
/*當設備的配置發生改變,比如橫豎屏的時候,它保存了當前activity的mAdapter的狀態,等到acitivty恢復的時候直接使用;
* 相比於onSaveInstanceState和onRestoreInstanceState只能存取Bundle中對象,
* onRetainNonConfigurationInstance與getLastNonConfigurationInstance的優點是可以存取任意對象,*/
mAdapter = (ArtistAlbumListAdapter) getLastNonConfigurationInstance();
if (mAdapter == null) {
MusicLogUtils.d(TAG, "starting query");
mAdapter = new ArtistAlbumListAdapter(
getApplication(),
this,
null, // cursor
R.layout.track_list_item_group,//group的佈局
new String[] {},
new int[] {},
R.layout.track_list_item_child,//child的佈局
new String[] {},
new int[] {});
//ExpandableList與數據進行綁定
setListAdapter(mAdapter);
setTitle(R.string.working_artists);
PDebug.End("ArtistAlbumBrowserActivity.setListAdapter()");
//異步查找數據,查找結束以後調用init(cursor)更新界面
getArtistCursor(mAdapter.getQueryHandler(), null);
} else {
mAdapter.setActivity(this);
mAdapter.reloadStringOnLocaleChanges();
setListAdapter(mAdapter);
mArtistCursor = mAdapter.getCursor();
if (mArtistCursor != null) {
/*前面已經調用了setListAdapter(mAdapter)進行了頁面的刷新,爲什麼還要init(mArtistCursor)-->mAdapter.changeCursor(c)再進行刷新一次呢?
*原因:分析過ListView源碼後知道,setListAdapter(mAdapter)的作用是給ListView的每個item設置view以及刷新ListView界面,
* 當數據源發生改變時調用該方法,並不會導致item界面的刷新,因爲item的數據是與cursor進行綁定的,要想item刷新,必須要通知mAdapter,
* 也就是調用mAdapter.notifyDataSetChanged(),這裏mAdapter.changeCursor(c)的本質也是mAdapter.notifyDataSetChanged()*/
init(mArtistCursor);
} else {
getArtistCursor(mAdapter.getQueryHandler(), null);
}
}
}
點評:onCreate()方法主要是ExpandableList與數據進行綁定,綁定數據需要適配器,我們看下這個適配器ArtistAlbumListAdapter;
ArtistAlbumListAdapter代碼比較多,我們只看下核心部分:
static class ArtistAlbumListAdapter extends SimpleCursorTreeAdapter implements SectionIndexer {
..............
........
class QueryHandler extends AsyncQueryHandler {
QueryHandler(ContentResolver res) {
super(res);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
/*當數據查找結束以後,根據cursor更新item,更新item的核心方法是mAdapter.changeCursor(c)*/
mActivity.init(cursor);
}
}
ArtistAlbumListAdapter(Context context, ArtistAlbumBrowserActivity currentactivity,
Cursor cursor, int glayout, String[] gfrom, int[] gto,
int clayout, String[] cfrom, int[] cto) {
super(context, cursor, glayout, gfrom, gto, clayout, cfrom, cto);
mActivity = currentactivity;
//用來異步查找數據庫
mQueryHandler = new QueryHandler(context.getContentResolver());
Resources r = context.getResources();
mDefaultAlbumIcon = (BitmapDrawable) r.getDrawable(R.drawable.albumart_mp_unknown_list);
mDefaultAlbumIcon.setFilterBitmap(false);
mDefaultAlbumIcon.setDither(false);
mContext = context;
//設置字母索引的Cursor
getColumnIndices(cursor);
mResources = context.getResources();
mAlbumSongSeparator = context.getString(R.string.albumsongseparator);
mUnknownAlbum = context.getString(R.string.unknown_album_name);
mUnknownArtist = context.getString(R.string.unknown_artist_name);
}
private void getColumnIndices(Cursor cursor) {
if (cursor != null) {
mGroupArtistIdIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID);
mGroupArtistIdx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST);
mGroupAlbumIdx =
cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS);
mGroupSongIdx =
cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS);
if (mIndexer != null) {
mIndexer.setCursor(cursor);
} else {
/*MusicAlphabetIndexer繼承自AlphabetIndexer,AlphabetIndexer是一個字母索引輔助類,具體怎麼索引就不分析了,下面就看這幾個參數的意思:
* cursor包含了數據的對象,mGroupArtistIdx是進行索引排序的列號,
* mResources.getString(R.string.fast_scroll_alphabet)是一個字母表,根據這個字母表進行排序;
* 這裏的意思是:在搜索框中輸入文字時,會出現一個menu,menu中的歌手列表以歌手的名字在字母表中的順序進行索引*/
mIndexer = new MusicAlphabetIndexer(cursor, mGroupArtistIdx,
mResources.getString(R.string.fast_scroll_alphabet));
}
}
}
@Override
public View newGroupView(Context context, Cursor cursor,
boolean isExpanded, ViewGroup parent) {
//創建一級ListView的佈局,也就是grouplist,佈局是track_list_item_group.xml;
View v = super.newGroupView(context, cursor, isExpanded, parent);
ImageView iv = (ImageView) v.findViewById(R.id.icon);
ViewGroup.LayoutParams p = iv.getLayoutParams();
p.width = ViewGroup.LayoutParams.WRAP_CONTENT;
p.height = ViewGroup.LayoutParams.WRAP_CONTENT;
ViewHolder vh = new ViewHolder();
//歌手名
vh.mLine1 = (TextView) v.findViewById(R.id.line1);
//該歌手幾張專輯
vh.mLine2 = (TextView) v.findViewById(R.id.line2);
//最右邊的播放圖標
vh.mPlayIndicator = (ImageView) v.findViewById(R.id.play_indicator);
//最左邊的icon
vh.mIcon = (ImageView) v.findViewById(R.id.icon);
vh.mIcon.setPadding(0, 0, 1, 0);
v.setTag(vh);
return v;
}
@Override
public View newChildView(Context context, Cursor cursor, boolean isLastChild,
ViewGroup parent) {
//創建二級ListView的佈局,也就是childlist,佈局是track_list_item_child.xml;
View v = super.newChildView(context, cursor, isLastChild, parent);
ViewHolder vh = new ViewHolder();
//專輯名
vh.mLine1 = (TextView) v.findViewById(R.id.line1);
//該專輯下幾首歌曲
vh.mLine2 = (TextView) v.findViewById(R.id.line2);
/最右邊的播放圖標
vh.mPlayIndicator = (ImageView) v.findViewById(R.id.play_indicator);
//最左邊的icon被設置爲默認圖標mDefaultAlbumIcon
vh.mIcon = (ImageView) v.findViewById(R.id.icon);
vh.mIcon.setBackgroundDrawable(mDefaultAlbumIcon);
vh.mIcon.setPadding(0, 0, 1, 0);
v.setTag(vh);
return v;
}
@Override
public void bindGroupView(View view, Context context, Cursor cursor, boolean isexpanded) {
//填充一級ListView的佈局,佈局是track_list_item_group.xml;
ViewHolder vh = (ViewHolder) view.getTag();
String artist = cursor.getString(mGroupArtistIdx);
String displayartist = artist;
boolean unknown = artist == null || artist.equals(MediaStore.UNKNOWN_STRING);
if (unknown) {
displayartist = mUnknownArtist;
}
//設置歌手名
vh.mLine1.setText(displayartist);
int numAlbums = cursor.getInt(mGroupAlbumIdx);
int numSongs = cursor.getInt(mGroupSongIdx);
String songsAlbums = MusicUtils.makeAlbumsLabel(context,
numAlbums, numSongs, unknown);
//該歌手有幾張專輯
vh.mLine2.setText(songsAlbums);
long currentartistid = MusicUtils.getCurrentArtistId();
long artistid = cursor.getLong(mGroupArtistIdIdx);
//當前播放的音樂如果是此歌手的,那麼,group的最右邊需要個播放圖標
if (currentartistid == artistid && !isexpanded) {
vh.mPlayIndicator.setVisibility(View.VISIBLE);
} else {
vh.mPlayIndicator.setVisibility(View.GONE);
}
}
@Override
public void bindChildView(View view, Context context, Cursor cursor, boolean islast) {
//綁定二級ListView的佈局,佈局是track_list_item_child.xml;
//這裏面的cursor是從getChildrenCursor()方法中查找來的,我們需要重寫getChildrenCursor()方法
ViewHolder vh = (ViewHolder) view.getTag();
String name = cursor.getString(
cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
String displayname = name;
boolean unknown = name == null || name.equals(MediaStore.UNKNOWN_STRING);
if (unknown) {
displayname = mUnknownAlbum;
}
//專輯名
vh.mLine1.setText(displayname);
int numsongs = cursor.getInt(
............
............
............
//專輯下幾首歌曲
vh.mLine2.setText(builder.toString());
ImageView iv = vh.mIcon;
String art = cursor.getString(cursor.getColumnIndexOrThrow(
MediaStore.Audio.Albums.ALBUM_ART));
//專輯圖標
if (unknown || art == null || art.length() == 0) {
iv.setBackgroundDrawable(mDefaultAlbumIcon);
iv.setImageDrawable(null);
} else {
long artIndex = cursor.getLong(0);
Drawable d = MusicUtils.getCachedArtwork(context, artIndex, mDefaultAlbumIcon);
iv.setImageDrawable(d);
}
long currentalbumid = MusicUtils.getCurrentAlbumId();
long aid = cursor.getLong(0);
//設置最右邊的播放圖標的可見性
iv = vh.mPlayIndicator;
if (currentalbumid == aid) {
iv.setVisibility(View.VISIBLE);
} else {
iv.setVisibility(View.GONE);
}
}
}
3.除了數據加載,還有幾個重要的邏輯;
①當某個歌手有多張專輯的時候,點擊專輯item,會跳轉到系統選擇界面;
public boolean onChildClick(ExpandableListView parent, View v,int groupPosition, int childPosition, long id) {
mCurrentAlbumId = Long.valueOf(id).toString();
//系統選擇界面
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
intent.putExtra("album", mCurrentAlbumId);
Cursor c = (Cursor) getExpandableListAdapter().getChild(groupPosition, childPosition);
String album = c.getString(c.getColumnIndex(MediaStore.Audio.Albums.ALBUM));
if (album == null || album.equals(MediaStore.UNKNOWN_STRING)) {
mArtistCursor.moveToPosition(groupPosition);
mCurrentArtistId = mArtistCursor.getString(
mArtistCursor.getColumnIndex(MediaStore.Audio.Artists._ID));
intent.putExtra("artist", mCurrentArtistId);
}
//跳轉到系統選擇界面
startActivity(intent);
return true;
}
②長按item,彈出上下文菜單
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
mSubMenu = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0, R.string.add_to_playlist);
MusicUtils.makePlaylistMenu(this, mSubMenu);
menu.add(0, DELETE_ITEM, 0, R.string.delete_item);
ExpandableListContextMenuInfo mi = (ExpandableListContextMenuInfo) menuInfoIn;
int itemtype = ExpandableListView.getPackedPositionType(mi.packedPosition);
int gpos = ExpandableListView.getPackedPositionGroup(mi.packedPosition);
int cpos = ExpandableListView.getPackedPositionChild(mi.packedPosition);
if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
//當長按group的時候,創建的menu列表
if (gpos == -1) {
// this shouldn't happen
MusicLogUtils.d(TAG, "no group");
return;
}
gpos = gpos - getExpandableListView().getHeaderViewsCount();
mArtistCursor.moveToPosition(gpos);
mCurrentArtistId = mArtistCursor.getString(
mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID));
mCurrentArtistName = mArtistCursor.getString(
mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
mCurrentAlbumId = null;
mIsUnknownArtist = mCurrentArtistName == null ||
mCurrentArtistName.equals(MediaStore.UNKNOWN_STRING);
mIsUnknownAlbum = true;
if (mIsUnknownArtist) {
menu.setHeaderTitle(getString(R.string.unknown_artist_name));
} else {
//菜單的title
menu.setHeaderTitle(mCurrentArtistName);
menu.add(0, SEARCH, 0, R.string.search_title);
}
return;
} else if (itemtype == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
//當長按child的時候,創建的item
if (cpos == -1) {
// this shouldn't happen
MusicLogUtils.d(TAG, "no child");
return;
}
Cursor c = (Cursor) getExpandableListAdapter().getChild(gpos, cpos);
c.moveToPosition(cpos);
mCurrentArtistId = null;
mCurrentAlbumId = Long.valueOf(mi.id).toString();
mCurrentAlbumName = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM));
gpos = gpos - getExpandableListView().getHeaderViewsCount();
mArtistCursor.moveToPosition(gpos);
mCurrentArtistNameForAlbum = mArtistCursor.getString(
mArtistCursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST));
mIsUnknownArtist = mCurrentArtistNameForAlbum == null ||
mCurrentArtistNameForAlbum.equals(MediaStore.UNKNOWN_STRING);
mIsUnknownAlbum = mCurrentAlbumName == null ||
mCurrentAlbumName.equals(MediaStore.UNKNOWN_STRING);
if (mIsUnknownAlbum) {
menu.setHeaderTitle(getString(R.string.unknown_album_name));
} else {
menu.setHeaderTitle(mCurrentAlbumName);
}
//除去未知專輯和未知歌手,其他的情況下添加搜索菜單
if (!mIsUnknownAlbum || !mIsUnknownArtist) {
menu.add(0, SEARCH, 0, R.string.search_title);
}
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
//"播放"item
case PLAY_SELECTION: {
long [] list =
mCurrentArtistId != null ?
MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
: MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
MusicUtils.playAll(this, list, 0);
return true;
}
case QUEUE: {
long [] list =
mCurrentArtistId != null ?
MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
: MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
MusicUtils.addToCurrentPlaylist(this, list);
return true;
}
case NEW_PLAYLIST: {
if (mCurrentArtistId != null) {
showCreateDialog("selectedartist_" + mCurrentArtistId,
MusicBrowserActivity.ARTIST_INDEX,
MusicUtils.NEW_PLAYLIST);
} else if (mCurrentAlbumId != null) {
showCreateDialog("selectedalbum_" + mCurrentAlbumId,
MusicBrowserActivity.ARTIST_INDEX,
MusicUtils.NEW_PLAYLIST);
}
return true;
}
//"添加到播放列表"item
case PLAYLIST_SELECTED: {
long [] list =
mCurrentArtistId != null ?
MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId))
: MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
long playlist = item.getIntent().getLongExtra("playlist", 0);
MusicUtils.addToPlaylist(this, list, playlist);
return true;
}
//"刪除"item
case DELETE_ITEM: {
long [] list;
Bundle b = new Bundle();
if (mCurrentArtistId != null) {
list = MusicUtils.getSongListForArtist(this, Long.parseLong(mCurrentArtistId));
b.putInt(MusicUtils.DELETE_DESC_STRING_ID, R.string.delete_artist_desc);
b.putString(MusicUtils.DELETE_DESC_TRACK_INFO, mCurrentArtistName);
} else {
list = MusicUtils.getSongListForAlbum(this, Long.parseLong(mCurrentAlbumId));
b.putInt(MusicUtils.DELETE_DESC_STRING_ID, R.string.delete_album_desc);
b.putString(MusicUtils.DELETE_DESC_TRACK_INFO, mCurrentAlbumName);
}
b.putLongArray("items", list);
Intent intent = new Intent();
intent.setClass(this, DeleteItems.class);
intent.putExtras(b);
startActivityForResult(intent, -1);
return true;
}
//"搜索"item
case SEARCH:
doSearch();
return true;
}
return super.onContextItemSelected(item);
}
總結:ArtistAlbumBrowserActivity沒什麼好說的,重點就是這些了