前言:最近在开发android8.1的原生音乐播放器,代码路径是vendor\\mediatek\\proprietary\\packages\\apps\\Music;音乐播放器的逻辑比较清晰明了,分析起来也比较简单;
本节分析是Music的主界面
废话不多说,直接上源码:
MusicBrowserActivity是音乐播放器的主界面,我们来分析一下它的流程:
1.先从onCreate方法开始,进行初始化:
代码1:
public void onCreate(Bundle savedInstanceState) {
//保存savedInstanceState的原因是:当权限请求成功之后,继续进行初始化工作时需要savedInstanceState
mSavedInstanceState = savedInstanceState;
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//请求权限,权限请求成功之后才会进行初始化工作,权限请求的结果在onRequestPermissionsResult方法中
if (getApplicationContext()
.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
requestMusicPermissions();
mPermissionReqProcessed = false;
} else {
mPermissionReqProcessed = true;
onCreateContinue(mSavedInstanceState);
}
//创建通知管理,发送通知需要指定一个管道channel
NotificationManager mgr = (NotificationManager)this.
getSystemService(Context.NOTIFICATION_SERVICE);
mgr.createNotificationChannel(new NotificationChannel(
MUSIC_NOTIFICATION_CHANNEL, \"MUSIC\", NotificationManager.IMPORTANCE_LOW));
}
点评:onCreate的主要工作是请求权限,权限的请求的结果在onRequestPermissionsResult方法中,看这个方法:
代码2:
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
if (requestCode == REQUEST_EXTERNAL_STORAGE) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mPermissionReqProcessed = true;
//权限请求成功之后,使用mSavedInstanceState继续执行初始化工作
onCreateContinue(mSavedInstanceState);
onResumeContinue();
} else {
//权限请求失败,便直接finish,并toast权限被拒绝
finish();
Toast.makeText(this, R.string.music_storage_permission_deny
, Toast.LENGTH_SHORT).show();
}
}
}
点评:当权限请求成功之后,继续执行初始化操作onCreateContinue();
2.继续执行初始化
代码3:
public void onCreateContinue(Bundle savedInstanceState) {
//设置音频流类型
setVolumeControlStream(AudioManager.STREAM_MUSIC);
//绑定MediaPlaybackService服务,返回一个ServiceToken对象,Activity通过该ServiceToken与Service进行通信
mToken = MusicUtils.bindToService(this, this);
/*判断手机是否存在物理的menu键;
在前面我们看到标签栏里面有5个标签,其中最后一个标签是模拟出来的虚拟的menu键,如果手机存在物理menu键的话,这个虚拟标签就是多于的,需要删掉*/
mHasMenukey = ViewConfiguration.get(this).hasPermanentMenuKey();
//LocalActivityManager是把activity转换成view对象的一个api,通过startActivity().getDecorView()方法获取Activity当前Window窗口下的view对象
mActivityManager = new LocalActivityManager(this, false);
mActivityManager.dispatchCreate(savedInstanceState);
mTabHost = getTabHost();
//初始化标签栏
initTab();
//上次退出音乐播放器时的当前标签
mCurrentTab = MusicUtils.getIntPref(this, SAVE_TAB, ARTIST_INDEX);
if ((mCurrentTab < 0) || (mCurrentTab >= mTabCount)) {
mCurrentTab = ARTIST_INDEX;
}
//给mTabHost设置默认标签
if (mCurrentTab == ARTIST_INDEX) {
mTabHost.setCurrentTab(ALBUM_INDEX);
}
//设置标签栏中标签变化时的监听,activity重写了onTabChanged方法
mTabHost.setOnTabChangedListener(this);
//将音乐,专辑,歌曲,播放列表这四个view添加进mPagers集合
initPager();
mViewPager = (ViewPager) findViewById(R.id.viewpage);
//将mPagers中view与mViewPager进行绑定
mViewPager.setAdapter(new MusicPagerAdapter());
//设置mViewPager页面变化的监听,activity重写了onPageSelected方法
mViewPager.setOnPageChangeListener(this);
//mViewPager.setOffscreenPageLimit(VIEW_PAGER_OFFSCREEN_PAGE_NUM);
//监听SD卡状态变化的广播,音乐播放器中的音频来源主要是SD卡
IntentFilter f = new IntentFilter();
f.addAction(MusicUtils.SDCARD_STATUS_UPDATE);
registerReceiver(mSdcardstatustListener, f);
//当不存在物理menu键时,需要创建假的menu键,本机就需要一个假的menu键
createFakeMenu();
//初始化搜索按钮并设置了搜索按钮的点击事件,该搜索按钮是存在于nowplaying布局当中的
initSearchButton();
}
点评:onCreateContinue()方法主要进行了这些操作:初始化标签栏,初始化ViewPager,初始化虚拟menu键,初始化搜索按钮,接下来详细分析这些初始化操作:
①初始化标签栏,也就是initTab()方法:
代码4:
private void initTab() {
//mPermissionReqProcessed表示权限申请成功
if (mPermissionReqProcessed == true) {
/**标签栏的布局;注意:buttonbar中有6个view,也就是说标签栏中本应该有6个标签,
* 但是由于项目需要,将最后一个标签即虚拟标签给gone掉了,
* 所以我们看到的最多只有5个;它们分别是:音乐,专辑,歌曲,播放列表,正在播放*/
final TabWidget tabWidget =
(TabWidget) getLayoutInflater().inflate(R.layout.buttonbar, null);
//当前屏幕方向
mOrientaiton = getResources().getConfiguration().orientation;
//标签个数,默认是6个
mTabCount = tabWidget.getChildCount();
View tabView;
/**如果存在menu键的话,就将假的menu键去掉;
* 例如本机就没有menu键,它就需要一个虚拟标签,所以标签栏中应有6个标签,前面说了,由于最后一个虚拟标签不可见,其实也就5个*/
if (mHasMenukey) {
mTabCount--;
}
//将标签转换成view,依次将每个view添加进TabHost当中
for (int i = 0; i < mTabCount; i++) {
tabView = tabWidget.getChildAt(0);
if (tabView != null) {
tabWidget.removeView(tabView);
}
//将view添加进TabHost当中
mTabHost.addTab(mTabHost.newTabSpec
(getStringId(i)).setIndicator(tabView).setContent(android.R.id.tabcontent));
}
/**如果当前是竖屏的话,由于屏幕限制,需要将第5个标签以及第5个以后的标签都不可见,即竖屏下只能看到4个标签,
* 横屏下可以看到4个以上,看不到的标签就是模拟虚拟按键的那个标签*/
if (mOrientaiton == Configuration.ORIENTATION_PORTRAIT) {
TabWidget tabWidgetTemp = mTabHost.getTabWidget();
for (int i = PLAYBACK_INDEX; i < mTabCount; i++) {
tabView = tabWidgetTemp.getChildTabViewAt(i);
if (tabView != null) {
tabView.setVisibility(View.GONE);
}
}
}
}
点评:在初始化标签栏的时候,要注意竖屏时屏幕宽度的限制以及手机是否有物理menu菜单;
②初始化ViewPager:
初始化ViewPager需要分两步,第一步是收集页面,initPager();
代码5:
private void initPager() {
mPagers.clear();
View view = null;
/*getView(i)方法就是前面所说的利用mActivityManager获取Activity当前Window窗口下的view对象,然后将这些view对象添加进mPagers集合;
* 这里是将音乐,专辑,歌曲,播放列表这四个view添加进了集合,正在播放和虚拟菜单的view并没有加进去*/
for (int i = 0; i <= PLAYLIST_INDEX; i++) {
view = (i == mCurrentTab) ? getView(i) : null;
mPagers.add(view);
}
}
我们来看下这个getView(i) :
代码6:
private View getView(int index) {
View view = null;
Intent intent = new Intent(Intent.ACTION_PICK);
switch (index) {
case ARTIST_INDEX:
intent.setDataAndType(Uri.EMPTY, \"vnd.android.cursor.dir/artistalbum\");
break;
case ALBUM_INDEX:
intent.setDataAndType(Uri.EMPTY, \"vnd.android.cursor.dir/album\");
break;
case SONG_INDEX:
intent.setDataAndType(Uri.EMPTY, \"vnd.android.cursor.dir/track\");
break;
case PLAYLIST_INDEX:
intent.setDataAndType(Uri.EMPTY, MediaStore.Audio.Playlists.CONTENT_TYPE);
break;
default:
MusicLogUtils.v(TAG, \"default\");
return null;
}
intent.putExtra(\"withtabs\", true);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//获取当前Activity的view
view = mActivityManager.startActivity(getStringId(index), intent).getDecorView();
return view;
}
点评:页面view收集结束了,就需要进行绑定了,也就是调用mViewPager.setAdapter(new MusicPagerAdapter())方法,具体的代码就不展示了,就是将mPagers中view与mViewPager进行绑定;
③初始化虚拟menu键,createFakeMenu();
代码7:
private void createFakeMenu() {
if (mPermissionReqProcessed == true) {
//当不存在物理menu键时,才会创建假的menu键
if (mHasMenukey) {
return;
}
/*横屏的时候,menu键存在于布局buttonbar中,也就是mOverflowMenuButton在标签栏中;
* 竖屏的时候,menu键存在于布局nowplaying中,布局nowplaying是一个正在播放音乐的view,它在手机屏幕正下方的,类似于一个悬浮框*/
if (mOrientaiton == Configuration.ORIENTATION_LANDSCAPE) {
mOverflowMenuButtonId = R.id.overflow_menu;
mOverflowMenuButton = findViewById(R.id.overflow_menu);
} else {
mOverflowMenuButtonId = R.id.overflow_menu_nowplaying;
mOverflowMenuButton = findViewById(R.id.overflow_menu_nowplaying);
View parent = (View) mOverflowMenuButton.getParent();
if (parent != null) {
parent.setVisibility(View.VISIBLE);
}
}
//mOverflowMenuButton就表示menu键
if(mOverflowMenuButton != null){
mOverflowMenuButton.setVisibility(View.VISIBLE);
//menu键的点击事件
mOverflowMenuButton.set Listener(new View. Listener() {
public void (View v) {
if (v.getId() == mOverflowMenuButtonId) {
//点击menu键,弹出一个PopupMenu的悬浮框
final PopupMenu popupMenu = new PopupMenu(MusicBrowserActivity.this,
mOverflowMenuButton);
mPopupMenu = popupMenu;
final Menu menu = popupMenu.getMenu();
/*给悬浮框PopupMenu设置内容,其实就是添加了4个item和1个搜索按钮,4个item是以文字形式出现,分别是:全部播放,派对随机播放,全部随机播放和音效;
*搜索按钮是一个图标,后面会详细分析*/
onCreateOptionsMenu(menu);
//悬浮框PopupMenu中item的点击事件
popupMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onOptionsItemSelected(item);
}
});
//悬浮框PopupMenu消失时的监听
popupMenu.setOnDismissListener(new OnDismissListener() {
public void onDismiss(PopupMenu menu) {
mPopupMenuShowing = false;
return;
}
});
//悬浮框PopupMenu显示前的准备工作,也就是设置一些item以及搜索图标在某些特定的情况下可见或者不可见等
onPrepareOptionsMenu(menu);
mPopupMenuShowing = true;
if (popupMenu != null) {
//悬浮框PopupMenu显示
popupMenu.show();
}
}
}
});
}
}
点评:这段代码,主要是给虚拟菜单按键设置点击事件以及添加item内容;我们来看下PopupMenu中item的添加,即onCreateOptionsMenu方法;
代码8:
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
if (mPermissionReqProcessed == true) {
//PLAY_ALL,PARTY_SHUFFLE等字段,是在MusicUtils中定义的静态变量
menu.add(0, PLAY_ALL, 0, R.string.play_all);
menu.add(0, PARTY_SHUFFLE, 0, R.string.party_shuffle);
menu.add(0, SHUFFLE_ALL, 0, R.string.shuffle_all);
menu.add(0, EFFECTS_PANEL, 0, R.string.effects_list_ );
//给menu菜单添加一个搜索图标,mQueryTextListener是搜索框中文字变化时的监听
mSearchItem = MusicUtils.addSearchView(this, menu, mQueryTextListener, null);
}
return true;
}
其中:mQueryTextListener是搜索框中文字变化时的监听,看下它的监听内容是什么?
代码9:
SearchView.OnQueryTextListener mQueryTextListener = new SearchView.OnQueryTextListener() {
public boolean onQueryTextSubmit(String query) {
//当系统搜索框中的文字改变的时候,会跳转到QueryBrowserActivity进行相应的搜索动作
Intent intent = new Intent();
intent.setClass(MusicBrowserActivity.this, QueryBrowserActivity.class);
intent.putExtra(SearchManager.QUERY, query);
startActivity(intent);
return true;
}
public boolean onQueryTextChange(String newText) {
return false;
}
};
给虚拟菜单设置内容的时候,还见到onOptionsItemSelected()和onPrepareOptionsMenu(),它们分别是菜单中item选项的点击操作以及菜单显示前的准备工作,看下代码:
代码10:
public boolean onOptionsItemSelected(MenuItem item) {
Cursor cursor;
Intent intent;
//点击菜单按钮中的item
switch (item.getItemId()) {
//全部播放
case PLAY_ALL:
cursor = MusicUtils.query(this,
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[] { MediaStore.Audio.Media._ID },
MediaStore.Audio.Media.IS_MUSIC + \"=1\",
null,
/// M: add for chinese sorting
MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
if (cursor != null) {
MusicUtils.playAll(this, cursor);
cursor.close();
}
//派对随机播放
case PARTY_SHUFFLE:
MusicUtils.togglePartyShuffle();
return true;
//全部随机播放
case SHUFFLE_ALL:
cursor = MusicUtils.query(this,
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[] { MediaStore.Audio.Media._ID },
MediaStore.Audio.Media.IS_MUSIC + \"=1\",
null,
/// M: add for chinese sorting
MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
if (cursor != null) {
MusicUtils.shuffleAll(this, cursor);
cursor.close();
}
return true;
//音效
case EFFECTS_PANEL:
return MusicUtils.startEffectPanel(this);
//搜索按钮
case R.id.search:
onSearchRequested();
mSearchViewShowing = true;
return true;
default:
break;
}
return super.onOptionsItemSelected(item);
}
代码11:
public boolean onPrepareOptionsMenu(Menu menu) {
MusicUtils.setPartyShuffleMenuIcon(menu);
super.onPrepareOptionsMenu(menu);
if (mPermissionReqProcessed == true) {
//没有安装SD卡
if (!mIsSdcardMounted) {
return false;
}
//\"全部播放\"item只能在\"歌曲\"界面显示
menu.findItem(PLAY_ALL).setVisible(mCurrentTab == SONG_INDEX);
//\"全部随机播放\"item不能在\"播放列表界面\"显示
menu.findItem(SHUFFLE_ALL).setVisible(mCurrentTab != PLAYLIST_INDEX);
//\"音效\"item只能在音效功能有用的时候才会显示
MusicUtils.setEffectPanelMenu(getApplicationContext(), menu);
//搜索按钮只在横屏状态下才显示
mSearchItem.setVisible(mOrientaiton == Configuration.ORIENTATION_LANDSCAPE);
}
return true;
}
④初始化搜索按钮, initSearchButton();
代码12:
private void initSearchButton() {
mSearchButton = (ImageButton) findViewById(R.id.search_menu_nowplaying);
//blankView是搜索按钮和菜单按钮之间的空格部分
final View blankView = this.findViewById(R.id.blank_between_search_and_overflow);
final View nowPlayingView = this.findViewById(R.id.nowplaying);
if (mSearchButton != null) {
//搜索按钮的点击事件
mSearchButton.set Listener(new View. Listener() {
@Override
public void (View v) {
if (mOverflowMenuButton != null) {
mOverflowMenuButton.setEnabled(false);
}
//一旦点击了搜索按钮,搜索按钮就需要被隐藏,因为此时会调用系统的搜索框
mSearchButton.setVisibility(View.GONE);
//调用Activity的onSearchRequested方法,该方法会弹出系统的搜索框
onSearchRequested();
if (blankView.getVisibility() == View.VISIBLE) {
blankView.setVisibility(View.GONE);
}
//mSearchViewShowing表示系统搜索框的显示与否
mSearchViewShowing = true;
}
});
//系统搜索运用了SearchManager中的逻辑
SearchManager searchManager = (SearchManager) this
.getSystemService(Context.SEARCH_SERVICE);
//系统搜索框消失时的监听
searchManager.setOnDismissListener(new SearchManager.OnDismissListener() {
@Override
public void onDismiss() {
if (mOverflowMenuButton != null) {
mOverflowMenuButton.setEnabled(true);
}
//系统搜索框消失时,搜索按钮由隐藏状态转变为可见状态
mSearchButton.setVisibility(View.VISIBLE);
if (nowPlayingView.getVisibility() != View.VISIBLE && !mHasMenukey) {
blankView.setVisibility(View.VISIBLE);
}
mSearchViewShowing = false;
InputMethodManager imm = (InputMethodManager)
getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
MusicLogUtils.v(TAG, \"IIME getService failed\");
}
MusicLogUtils.v(TAG, \"IME getService success\");
//隐藏输入法
if (imm != null) {
MusicLogUtils.v(TAG, \"Search Dialog hiding the IME\");
imm.hideSoftInputFromWindow(mSearchButton.getWindowToken(), 0);
}
}
});
}
}
}
点评:分析完了onCreate中的初始化动作,MusicBrowserActivity的核心代码也差不多分析完了,再看一下其他的也是比较重要的代码;
3.activity的生命周期;
刚才已经分析完了activity的oncrate周期,接着看一下其他的生命周期;
代码13:
public void onResume() {
super.onResume();
if (mPermissionReqProcessed == true) {
onResumeContinue();
}
}
public void onResumeContinue() {
IntentFilter f = new IntentFilter();
//监听音乐播放状态的广播
f.addAction(MediaPlaybackService. _CHANGED);
registerReceiver(mTrackListListener, f);
mTabHost.setCurrentTab(mCurrentTab);
mActivityManager.dispatchResume();
}
private BroadcastReceiver mTrackListListener = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mService != null) {
//监听音乐播放状态的广播,当音乐状态改变时,改变nowPlaying的布局的可见性以及\"正在播放\"标签,当没有正在播放的音乐时,正在播放\"标签不可点击
MusicUtils.updateNowPlaying(MusicBrowserActivity.this, mOrientaiton);
updatePlaybackTab();
}
}
};
public void onPause() {
super.onPause();
if (mPermissionReqProcessed == true) {
unregisterReceiver(mTrackListListener);
mActivityManager.dispatchPause(false);
//activity不可见时,保存当前的标签,供下次activity显示时使用
MusicUtils.setIntPref(this, SAVE_TAB, mCurrentTab);
}
public void () {
if (mPermissionReqProcessed == true) {
//activity后台不可见时,悬浮框mPopupMenu也会消失
if (mPopupMenu != null) {
mPopupMenu.dismiss();
mPopupMenuShowing = false;
}
mActivityManager.dispatchStop();
}
super. ();
}
public void onDestroy() {
if (mPermissionReqProcessed == true) {
if (mToken != null) {
//activity销毁时与服务解绑
MusicUtils.unbindFromService(mToken);
mService = null;
}
unregisterReceiver(mSdcardstatustListener);
mActivityManager.dispatchDestroy(false);
}
super.onDestroy();
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mPermissionReqProcessed == true) {
int startActivityTab = mCurrentTab;
if (data != null) {
startActivityTab = data.getIntExtra(MusicUtils.START_ACTIVITY_TAB_ID, mCurrentTab);
}
Activity startActivity = mActivityManager.getActivity(getStringId(startActivityTab));
if (startActivity == null) {
return;
}
//activity的跳转结果会派发给相应的页面activity去执行
switch (startActivityTab) {
case ARTIST_INDEX:
((ArtistAlbumBrowserActivity) startActivity).onActivityResult(requestCode,
resultCode,
data);
break;
case ALBUM_INDEX:
((AlbumBrowserActivity) startActivity).onActivityResult(requestCode,
resultCode,
data);
break;
case SONG_INDEX:
((TrackBrowserActivity) startActivity).onActivityResult(requestCode,
resultCode,
data);
break;
case PLAYLIST_INDEX:
((PlaylistBrowserActivity) startActivity).onActivityResult(requestCode,
resultCode,
data);
break;
default:
break;
}
}
}
public void onConfigurati d(Configuration newConfig) {
super.onConfigurati d(newConfig);
//当系统配置改变的时候,需要进行一些处理,比如横竖屏
if (mPermissionReqProcessed == true) {
TabWidget tabWidgetTemp = mTabHost.getTabWidget();
View tabView;
Activity activity;
int viewStatusForTab = View.GONE;
//此时是横屏
if (mOrientaiton == Configuration.ORIENTATION_LANDSCAPE) {
viewStatusForTab = View.VISIBLE;
}
//标签栏中的\"正在播放\"以及\"虚拟menu\"标签由不可见变为可见状态
for (int i = PLAYBACK_INDEX; i < mTabCount; i++) {
tabView = tabWidgetTemp.getChildTabViewAt(i);
if (tabView != null) {
tabView.setVisibility(viewStatusForTab);
}
}
for (int i = 0; i < PLAYBACK_INDEX; i++) {
activity = mActivityManager.getActivity(getStringId(i));
if (activity != null) {
//通知\"正在播放\"以及\"虚拟menu\"页面系统配置已经发生了改变
activity.onConfigurati d(newConfig);
}
}
if (!mHasMenukey) {
boolean popupMenuShowing = mPopupMenuShowing;
/*如果此时悬浮框popupMenu是正在显示的状态,需要先将popupMenu隐藏*/
if (popupMenuShowing && mPopupMenu != null) {
mPopupMenu.dismiss();
}
//重新加载虚拟menu键
createFakeMenu();
if (!mSearchViewShowing && mOverflowMenuButton != null) {
mOverflowMenuButton.setEnabled(true);
}
/*在横竖屏状态改变之前,如果悬浮框popupMenu已经显示出来了,那么在横竖屏状态改变之后,悬浮框popupMenu还需要继续显示;
* 这里是模拟了菜单按键的点击事件达到了悬浮框popupMenu继续显示的效果*/
if (popupMenuShowing && mOverflowMenuButton != null) {
mOverflowMenuButton.setSoundEffectsEnabled(false);
mOverflowMenuButton.performClick();
mOverflowMenuButton.setSoundEffectsEnabled(true);
}
}
if (mService != null) {
//根据系统显示框的显示与否来更新搜索按钮的状态
if (mSearchViewShowing) {
mSearchButton.setVisibility(View.GONE);
} else {
mSearchButton.setVisibility(View.VISIBLE);
}
//更新屏幕正下方的nowplaying布局的可见性
MusicUtils.updateNowPlaying(MusicBrowserActivity.this, mOrientaiton);
//更新标签栏中\"正在播放\"标签的图标,以及当没有正在播放的音乐时,正在播放\"标签不可点击
updatePlaybackTab();
}
//重新设置标签栏和viewpager
mTabHost.setCurrentTab(mCurrentTab);
mViewPager.setAdapter(new MusicPagerAdapter());
onTabChanged(getStringId(mCurrentTab));
}
}
4.接下来是三个监听;
①绑定服务成功的监听
public void onServiceConnected(ComponentName className, IBinder service) {
if (mPermissionReqProcessed == true) {
//与MediaPlaybackService进行绑定
mService = IMediaPlaybackService.Stub.asInterface(service);
String shuf = getIntent().getStringExtra(\"autoshuffle\");
if (mService != null) {
if (Boolean.valueOf(shuf).booleanValue()) {
try {
//设置音乐播放的音效,默认是自动
mService.setShuffleMode(MediaPlaybackService.SHUFFLE_AUTO);
}
catch (RemoteException ex) {
}
}
//更新nowPlaying布局,没有正在播放的音乐时nowPlaying会不可见,playbackTab会不可点击
MusicUtils.updateNowPlaying(MusicBrowserActivity.this, mOrientaiton);
updatePlaybackTab();
}
}
}
public void onServiceDisconnected(ComponentName className) {
if (mPermissionReqProcessed == true) {
mService = null;
finish();
}
}
②标签栏中的标签发生了点击变化后的监听
public void onTabChanged(String tabId) {
//当标签栏中的标签发生了点击变化后,会通知ViewPager发生改变,由ViewPager去处理;除了\"正在播放\"和\"菜单\"标签;
// \"正在播放\"标签会直接跳转至MediaPlaybackActivity,而\"菜单\"标签在这里不做处理*/
int tabIndex = TAB_MAP.get(tabId);
if ((tabIndex >= ARTIST_INDEX) && (tabIndex <= PLAYLIST_INDEX)) {
//ARTIST_INDEX <= tabIndex <= PLAYLIST_INDEX,也就是只处理前4个标签
mViewPager.setCurrentItem(tabIndex);
mCurrentTab = tabIndex;
//\"正在播放\"标签
Intent intent = new Intent(this, MediaPlaybackActivity.class);
startActivity(intent);
}
}
③iewpager发生改变后的监听
public void onPageSelected(int position) {
MusicLogUtils.v(TAG, \"onPageSelected-position:\" + position);
//viewpager发生改变时通知标签栏
mTabHost.setCurrentTab(position);
}
继续阅读与本文标签相同的文章
-
尘埃落定!美盟友明确表示不排除华为5G:特朗普颜面尽失!
2026-05-18栏目: 教程
-
“中本聪”一词被收入牛津英语词典
2026-05-18栏目: 教程
-
中山5G建设传重磅消息!市民何时能用?时间定了!
2026-05-18栏目: 教程
-
调查显示中国88%员工信任机器人超过经理
2026-05-18栏目: 教程
-
在如今,人们谈到科技,可能最先想到的就是电子技术
2026-05-18栏目: 教程
