private void updateSeekBar() {
//6. 获取音乐文件的总时长 Gets the duration of the file.
final int duration = mediaPlayer.getDuration();
//7. 构造定时器
Timer timer = new Timer();
//7.1 创建任务
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
//8. 获取歌曲的当前进度
int currentPosition = mediaPlayer.getCurrentPosition();
//9. 通过handler发送歌曲的信息到Activity更新UI
//9.1 通过将数据封装到Message中
Message msg = new Message();
//9.2 封装多条数据到Message中 将那些数据封装到Bundle中,其实Bundle底层就是Map
Bundle bundle = new Bundle();
bundle.putInt(“duration”, duration);
bundle.putInt(“currentPosition”, currentPosition);
msg.setData(bundle);
//10. 发送数据
MainActivity.handler.sendMessage(msg);
}
};
//7.2 300毫秒后 每隔1秒执行一次任务
timer.schedule(timerTask, 300, 1000); //每隔1秒获取歌曲的进度
}
- 在服务中添加一个播放进度的方法
/**
-
设置播放音乐指定位置的方法
-
@param position 该位置由进度条拖动时提供
*/
private void seekToPosition(int position){
mediaPlayer.seekTo(position);
}
-
通过handler将数据传递到Activity更新UI
-
SeekBar处理数据
sb_seekm.setMax(duration); //设置进度条最大值
sb_seekm.setProgress(currentPosition); //设置进度条当前进度
7. SurfaceView介绍
=================
-
SurfaceView控件是一个重量级控件
-
内部维护了2个线程
-
A 获取数据 负责显示
-
B 负责显示 获取数据
-
它直接可以在子线程更新UI 与进度相关的控件可以直接在子线程更新Ui
//找到控件
final SurfaceView sfv = (SurfaceView) findViewById(R.id.sfv);
final SurfaceHolder surfaceHolder = sfv.getHolder();
//添加一个callback
surfaceHolder.addCallback(new Callback() {
//当surfaceview销毁的时候调用
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
System.out.println(“surfaceDestroyed”);
if (player!=null && player.isPlaying()) {
//获取当前视频播放的位置
currentPosition = player.getCurrentPosition();
player.stop();
}
}
//当surfaceview 初始化了
@Override
public void surfaceCreated(SurfaceHolder holder) {
//[1]初始化mediaplayer
player = new MediaPlayer();
//[2]设置要播放的资源 path 可以是本地也可是网络路径
try {
player.setDataSource(“http://192.168.13.89:8080/cc.MP4”);
//[2.1]设置播放视频的内容 SurfaceHolder 是用来维护视频播放的内容
player.setDisplay(surfaceHolder);
//[3]准备播放
// player.prepare();
player.prepareAsync();
//设置一个准备完成的监听
player.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//[4]开始播放
player.start();
//[5]继续上次的位置继续播放
player.seekTo(currentPosition);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
});
补充1 VideoView
-
这个控件就是对SurfaceView和MediaPlayer进行封装
-
MediaPlayer 播放视频只支持3gp mp4格式
-
如果只是播放一些游戏的片头动画,或者某个应用的视频宣传,使用VideoView还是绰绰有余的.
补充2 vitamio框架
vitamio框架是开源的,可以播放大多数视频格式的框架.
8. 照相和录像
=========
// create Intent to take a picture and return control to the calling
// application
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(Environment.getExternalStorageDirectory(),
“1.png”);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
// start the image capture Intent
startActivityForResult(intent, 1);
9. 调用摄像头拍照并显示
==============
下面的demo如果需要加载大图片,其实严谨的来说,应该加如图片缩放.有时候拍的照片太大了,以至于无法加载,程序报错.
-
首先我们决定将照片放到sd卡的应用缓存目录下,通过
getExternalCacheDir()
可以得到这个目录(具体的路径是/sdcard/Android/data/<package name>/cache
).因为从Android6.0开始,读写SD卡被列为危险权限,如果将图片存放在SD卡的任何其他目录,都要进行运行时权限处理才行,而使用管理目录可以跳过这一步. -
如果运行设备的系统版本低于Android 7.0,就调用Uri.fromFile()方法将File对象转换成Uri对象,这个Uri对象标识着图片的本地真实路径.
否则,就调用FileProvider的getUriForFile()方法将File对象转换成一个封装过的Uri对象.
之所以要进行这样一层转换,是因为从Android 7.0开始,直接使用本地真实路径的Uri被认为是不安全的,会抛出FileUriExposedException异常.而FileProvider则是一种特殊的内容提供器,它使用了和内容提供器类似的机制来对数据进行保护,可以选择性的将封装过的Uri共享给外部,从而提高了应用的安全性.
既然是内容提供器,则需要到清单文件中配置,具体配置如下:
<provider
android:authorities=“com.xfhy. 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》无偿开源 徽信搜索公众号【编程进阶路】 cameraalbumtest.fileprovider”
android:name=“android.support.v4.content.FileProvider”
android:exported=“false”
android:grantUriPermissions=“true”>
<meta-data
android:name=“android.support.FILE_PROVIDER_PATHS”
android:resource=“@xml/file_paths”
/>
还需要在res目录下创建xml文件夹,然后新建File,file_paths.xml文件.写入如下内容:
<?xml version="1.0" encoding="utf-8"?>其中external-path
是用来指定Uri共享的,name属性的值可以随便填,path属性的值表示共享的具体路径,这里设置为空值表示将整个SD卡进行共享
还有一点需要注意,在Android 4.4系统之前,访问SD卡的应用关联目录也是要声明权限的,从4.4系统开始不再需要权限申明.那么我们为了能够兼容老版本的系统的手机,需要在清单文件中申明如下权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
3.下面是具体的代码实现:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int TAKE_PHOTO = 1;
private static final String TAG = “MainActivity”;
private Button bt_take_photo; //拍照按钮
private Button bt_choose_from_album;
private ImageView iv_picture; //显示图片
private Uri imageUri; //标示图片路径的uri
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_take_photo = (Button) findViewById(R.id.bt_take_photo);
bt_choose_from_album = (Button) findViewById(R.id.bt_choose_from_album);
iv_picture = (ImageView) findViewById(R.id.iv_picture);
bt_take_photo.setOnClickListener(this);
bt_choose_from_album.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_take_photo:
takePhoto();
break;
case R.id.bt_choose_from_album:
break;
default:
break;
}
}
/**
- 调用摄像头拍照
*/
private void takePhoto() {
//1. 创建File对象,用于存储拍照后的图片
//这里的getExternalCacheDir()是应用的关联缓存目录,在SD卡下面(/sdcard/Android/data//cache)
//这里在Android 6.0运行时不用进行运行时权限处理
File outputImage = new File(getExternalCacheDir(), “output_image.jpg”);
//2. 判断文件是否存在
try {
if (outputImage.exists()) {
outputImage.delete();
outputImage.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
//3. 判断当前用户的设备的系统版本是否大于24(android 7.0)
if (Build.VERSION.SDK_INT >= 24) {
//4. 将File对象转化为一个封装好的Uri对象
imageUri = FileProvider.getUriForFile(MainActivity.this,
“com.xfhy.cameraalbumtest.fileprovider”,outputImage);
} else {
imageUri = Uri.fromFile(outputImage);
}
//5.启动相机程序
Intent intent = new Intent(“android.media.action.IMAGE_CAPTURE”);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent,TAKE_PHOTO);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//请求码
switch (requestCode){
case TAKE_PHOTO:
//如果返回码是RESULT_OK,则是成功拍照了的
if(resultCode == RESULT_OK){
try {
//6. 将拍摄的照片显示出来
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
iv_picture.setImageBitmap(bitmap); //设置显示图片
Log.i(TAG, "onActivityResult: ");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
}
10. 从相册选择照片
============
读取相册中的照片,是需要申请读取SD卡权限.
根据系统版本是否是Android 4.4以上,有2种处理方式.之所以要这样做,是因为Android系统从4.4版本开始,选取相册中的图片不再返回图片的真实Uri了,而是一个封装过的Uri,因此如果是4.4版本以上的手机就需要对这个版本进行解析才行.
下面是demo
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
/**
- 申请码
*/
private static final int TAKE_PHOTO = 1; //照相
private static final int CHOOSE_PHOTO = 2; //打开相册
private static final String TAG = “MainActivity”;
private Button bt_take_photo; //拍照按钮
private Button bt_choose_from_album;
private ImageView iv_picture; //显示图片
private Uri imageUri; //标示图片路径的uri
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_take_photo = (Button) findViewById(R.id.bt_take_photo);
bt_choose_from_album = (Button) findViewById(R.id.bt_choose_from_album);
iv_picture = (ImageView) findViewById(R.id.iv_picture);
bt_take_photo.setOnClickListener(this);
bt_choose_from_album.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_take_photo:
takePhoto();
break;
case R.id.bt_choose_from_album:
requestPermission();
break;
default:
break;
}
}
/**
- 申请权限 需要选择相册中的图片,则需要读SD卡的权限
*/
private void requestPermission() {
//检查是否有读SD卡的权限 不相等则需要申请权限
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
//申请读SD的权限
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
openAlbum();
}
}
/**
- 打开相册 进行图片选择
*/
private void openAlbum() {
//
Intent intent = new Intent(“android.intent.action.GET_CONTENT”);
intent.setType(“image/*”);
startActivityForResult(intent, CHOOSE_PHOTO); //打开相册
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) { //根据申请码进行判断
case 1:
//判断权限是否申请成功
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openAlbum();
} else {
Toast.makeText(this, “申请读SD卡权限失败”, Toast.LENGTH_SHORT).show();
}
break;
default:
break;