Android老师布置了个播放音乐跟视频的小实验,本来看了下感觉没啥难度......但是自己写的时候一直告诉我获取不到指定的文件路径,这才想起来Android M(即Android 6.0及以上)版本以上让人蛋疼的权限问题。
Tip:模拟器在Android 6.0一下的可以暂时不用考虑动态权限的申请,然后,不管你模拟器是什么版本,都请在注册文件中先申明读SD卡的权限。
首先,根据作业要求要实现一个淡入淡出的动画效果,那我们就新建一个Activity,并且命名为SplashActivity:
public class SplashActivity extends Activity {
Animation animation;
ImageView img;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
img= (ImageView) findViewById(R.id.welcome);
animation = new AlphaAnimation(0,1);
animation.setDuration(2000);
img.startAnimation(animation);
Timer timer=new Timer();
TimerTask task=new TimerTask() {
@Override
public void run() {
Thread t=new Thread(){
@Override
public void run() {
super.run();
Intent intent=new Intent(SplashActivity.this,TabHostDemo.class);
startActivity(intent);
//防止点击返回键之后停止在登录欢迎界面,因此跳转完成后直接结束该Activity
finish();
}
};
runOnUiThread(t);
}
};
timer.schedule(task,2000);
}
}
这部分要注意的是,在跳转Activity完成之后,需要在startActivity的下一行执行finish操作,将该Activity结束掉,否则跳转完成之后,用户点击返回按钮,将会卡在该欢迎页面。
淡入淡出的动画效果实现:
animation = new AlphaAnimation(0,1);
animation.setDuration(2000);
img.startAnimation(animation);
这部分其实就是将图片从透明变成不透明......
该Activity的layout文件:
然后就是实验要求使用的TableHostDemo部分(仅为满足实验要求,使用了该已过时的Activity,在实际开发中建议使用Fragment+ViewPager替代)
如果你的模拟器是Android 6.0以下,请直接看下列的代码部分即可:
public class TabHostDemo extends TabActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_host_demo);
TabHost tabHost=getTabHost();
TabHost.TabSpec spec;
spec=tabHost.newTabSpec("0");
spec.setIndicator("帧动画");
Intent intent;
intent=new Intent(TabHostDemo.this,FrameActivity.class);
spec.setContent(intent);
tabHost.addTab(spec);
spec=tabHost.newTabSpec("1");
spec.setIndicator("播放音乐");
Intent intent1;
intent1=new Intent(TabHostDemo.this,PlayMusicActivity.class);
spec.setContent(intent1);
tabHost.addTab(spec);
spec=tabHost.newTabSpec("3");
spec.setIndicator("播放视频");
Intent intent2;
intent2=new Intent(TabHostDemo.this,PlayVideoActivity.class);
spec.setContent(intent2);
tabHost.addTab(spec);
}
}
就是简单的TabHost的使用。
重点来了,如果你的模拟器是Android 6.0及以上的Android版本的,请看这里的代码部分:
public class TabHostDemo extends TabActivity {
private String[] permissions = {Manifest.permission.READ_EXTERNAL_STORAGE};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_host_demo);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
//检查该权限是否已经获取
int i = ContextCompat.checkSelfPermission(this, permissions[0]);
// 权限是否已经 授权 GRANTED---授权 DENIED---拒绝
if (i != PackageManager.PERMISSION_GRANTED){
// 如果没有授予该权限,就去提示用户请求
showDialogTipUserRequestPermission();
}
}
TabHost tabHost=getTabHost();
TabHost.TabSpec spec;
spec=tabHost.newTabSpec("0");
spec.setIndicator("帧动画");
Intent intent;
intent=new Intent(TabHostDemo.this,FrameActivity.class);
spec.setContent(intent);
tabHost.addTab(spec);
spec=tabHost.newTabSpec("1");
spec.setIndicator("播放音乐");
Intent intent1;
intent1=new Intent(TabHostDemo.this,PlayMusicActivity.class);
spec.setContent(intent1);
tabHost.addTab(spec);
spec=tabHost.newTabSpec("3");
spec.setIndicator("播放视频");
Intent intent2;
intent2=new Intent(TabHostDemo.this,PlayVideoActivity.class);
spec.setContent(intent2);
tabHost.addTab(spec);
}
// 提示用户该请求权限的弹出框
private void showDialogTipUserRequestPermission(){
new AlertDialog.Builder(this)
.setTitle("没有SD卡读取权限!")
.setMessage("没有SD卡读权限将造成程序异常")
.setPositiveButton("立即开启", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startRequestPermission();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).setCancelable(false).show();
}
private void startRequestPermission(){
ActivityCompat.requestPermissions(this,permissions,321);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 321){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if (grantResults[0] != PackageManager.PERMISSION_GRANTED){
//判断用户是否点击了不再提醒
boolean b = shouldShowRequestPermissionRationale(permissions[0]);
if (!b){
//提示用户去设置界面手动开启权限
showDialogTipUserGoToAppSetting();
} else {
finish();
}
} else {
Toast.makeText(this, "权限获取成功", Toast.LENGTH_SHORT).show();
}
}
}
}
private void showDialogTipUserGoToAppSetting(){
new AlertDialog.Builder(this)
.setTitle("存储权限不可用!")
.setMessage("请在设置中立即开启!")
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",getPackageName(),null);
intent.setData(uri);
startActivityForResult(intent,123);
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).setCancelable(false).show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 123){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
int i = ContextCompat.checkSelfPermission(this,permissions[0]);
//权限是否已经授权:GRANTED--授权 DENIED--拒绝
if (i != PackageManager.PERMISSION_GRANTED){
//如果没有授权
Toast.makeText(this, "请到设置界面允许授权", Toast.LENGTH_SHORT).show();
}
}
}
}
}
考虑到部分同学IDE不是Android Studio的情况,因此在这里没有直接compile封装好的权限包,而是遵照百度到的教程老老实实的敲了一遍。大体上就是打开app时,会弹出窗口叫用户进行权限授予:由于读写SD卡被认为是危险权限,因此必须进行动态权限的申请。
具体过程不再赘述,想要了解的请看下方链接中的文章。
具体链接:https://www.cnblogs.com/xmcx1995/p/5870191.html
该Activity的layout文件:
后面便是多媒体文件应用的几个Activity了
第一个FrameActivity:
public class FrameActivity extends Activity {
private SoundPool sp;
private int id1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_frame);
ImageView imageView = (ImageView) findViewById(R.id.frame_image);
imageView.setImageResource(R.drawable.frame_animation);
Button buttonStart = (Button) findViewById(R.id.start);
Button buttonPause = (Button) findViewById(R.id.pause);
sp=new SoundPool(2, AudioManager.STREAM_MUSIC,0);
id1=sp.load(FrameActivity.this,R.raw.readygo,1);
final AnimationDrawable frameAnimation = (AnimationDrawable) imageView
.getDrawable();
buttonStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
frameAnimation.start();
sp.play(id1,1,1,1,0,1);
}
});
buttonPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
frameAnimation.stop();
}
});
}
}
layout文件:
根据实验的要求,在我们点击开始按钮的时候,同时要播放一次"ready go"这个mp3文件,因此我们需要实例化一个SoundPool,用于播放该音乐文件。
首先,我们在res文件夹下新建一个raw文件夹,用于存放音乐文件:
之后将音乐文件放入raw文件夹,然后用
id1=sp.load(FrameActivity.this,R.raw.readygo,1);
这行代码获取音乐文件的SoundId。
接下来是动画部分:我们可以看到,在layout文件中我们给imageView的src指定的是
android:src="@drawable/frame_animation"
这样一行代码,@drawable/frame_animation
其实是我们在drawable文件夹下新建的一个XML文件,其实就是多张图片连起来显示让人认为是动画的一个效果;
frame_animation下的代码:
我们给每张图片通过duration
来指定显示时间为50ms,来达到多张图片连续播放像是在播放动画的效果,其实就是“帧动画”。
然后是播放音乐部分的Activity:
public class PlayMusicActivity extends Activity {
ListView listView;
List listName,listPath;
MediaPlayer mediaPlayer;
SeekBar seekBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_music);
listView = (ListView) findViewById(R.id.listView);
//指定要播放的音乐文件的路径
File path = new File(Environment.getExternalStorageDirectory()+"/Music");
listName = new ArrayList();
listPath = new ArrayList();
getMusicLists(path);
ArrayAdapter adapter = new ArrayAdapter(PlayMusicActivity.this,
android.R.layout.simple_list_item_1,listName);
listView.setAdapter(adapter);
mediaPlayer = new MediaPlayer();
seekBar = (SeekBar) findViewById(R.id.seekBar);
setListener();
//拖动SeekBar改变歌曲进度
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//用seekTo直接跳转进度条进度到seekBar当前拖动到的进度位置
mediaPlayer.seekTo(seekBar.getProgress());
}
});
}
//listView的单击事件,根据点击到的item来播放对应的歌曲
private void setListener() {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
try {
if( mediaPlayer != null){
mediaPlayer.stop();
mediaPlayer.reset();
}
//根据position来播放点击到的歌曲
mediaPlayer.setDataSource(PlayMusicActivity.this, Uri.parse(listPath.get(position)));
//mediaPlayer的准备过程
mediaPlayer.prepare();
mediaPlayer.start();
//seekBar获取进度条最大值、设置进度条初始进度为0
seekBar.setMax(mediaPlayer.getDuration());
seekBar.setProgress(0);
ChangeSeekBar();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
//进度条当前位置的获取
private void ChangeSeekBar() {
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//用msg.arg1来传递进度值
seekBar.setProgress(msg.arg1);
}
};
Thread t = new Thread(){
@Override
public void run() {
super.run();
while (seekBar.getProgress() < seekBar.getMax()){
//获取当前进度
int progress = mediaPlayer.getCurrentPosition();
Message msg = handler.obtainMessage();
//设置msg.arg1的值为progress,实现进度获取
msg.arg1 = progress;
handler.sendMessage(msg);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
//将获取到的音乐文件的名字显示在listView中
private void getMusicLists(File path) {
if (path != null){
if (path.isDirectory()){
File[] files = path.listFiles();
for (int i = 0; i < files.length; i++) {
getMusicLists(files[i]);
}
} else {
String musicName = path.getName();
String musicPath = path.getAbsolutePath();
if (musicName.endsWith(".mp3")){
listName.add(musicName);
listPath.add(musicPath);
}
}
}
}
}
该说的我都写在注释里了......文章最下面会给出源码文件的链接。
layout布局:
这里ListView的大小我是在IDE内直接拖动更改的,不同的人显示的效果不一样,有需要还麻烦自己更改大小了。
接下来是播放视频的PlayVideoActivity:
public class PlayVideoActivity extends Activity {
ListView listView;
List listName,listPath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_playvideo);
listView = (ListView) findViewById(R.id.videoList);
//指定要读的视频文件存放的路径
final File path = new File(Environment.getExternalStorageDirectory()+"/Movies");
listName = new ArrayList();
listPath = new ArrayList();
getVideoLists(path);
ArrayAdapter video_adapter = new ArrayAdapter(PlayVideoActivity.this,
android.R.layout.simple_list_item_1,listName);
listView.setAdapter(video_adapter);
//listView点击事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Intent intent = new Intent(PlayVideoActivity.this, VideoPlayTest.class);
//获取当前ListView中被选中的item的text值,其中text1是simple_list_item_1中的textView的id
TextView name = (TextView) view.findViewById(android.R.id.text1);
String getName = name.getText().toString();
//传递选中的视频文件的路径
intent.putExtra("path",path+"/"+getName);
startActivity(intent);
}
});
}
//将视频文件的名字显示在listView中
private void getVideoLists(File path) {
if (path != null){
if (path.isDirectory()){
File[] files = path.listFiles();
for (int i = 0; i < files.length; i++) {
getVideoLists(files[i]);
}
} else {
String movieName = path.getName();
String moviePath = path.getAbsolutePath();
if (movieName.endsWith(".3gp")){
listName.add(movieName);
listPath.add(moviePath);
}
}
}
}
}
(没啥好说的,看注释吧...)
对应的layout文件:
该activity中单击视频名字之后,将会跳转到一个视频播放的VideoPlayTest:
public class VideoPlayTest extends Activity {
private VideoView videoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_play_test);
videoView = (VideoView) findViewById(R.id.video_view);
initVideoPath();
}
private void initVideoPath() {
//获取刚才的Activity传输过来的播放路径
String filePath = getIntent().getStringExtra("path");
File file = new File(filePath);
videoView.setVideoPath(file.getPath()); // 指定视频文件的路径
videoView.start();//播放视频
}
@Override
protected void onDestroy() {
super.onDestroy();
if (videoView != null) {
videoView.suspend();
}
}
}
对应的layout:
其中yout中就放了一个VideoView,然后JAVA代码只管播放就好了,基本上能满足本次安卓作业的需求吧。
附带实验5的源码文件连接:https://gitee.com/bbchond/Exp5_Media_Frame_Video