日常手机使用中,常常会遇到头部弹出一条新消息,有的还伴随着震动,音乐以及灯光闪烁,它们是怎么实现的呢?许多app的个人设置页面都有头像设置的功能,一般都有使用者从相册中选取或者立即拍照两种选择.很多时候,app中也会有播放音视频的功能.它们都是如何实现的呢?
1通知
通过通知,能够在app打开,但是被切换到后台时,及时通知用户app的变化.发送通知类似启动Activity,只不过启动对象换成了PendingIntent,用于启动通知时对Intent进行再次封装.示例如下:
Intent intent = new Intent(this, NotificationActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("this is title")
.setContentText("this is text")
.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(
getResources(), R.drawable.banana
)))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.left)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setContentIntent(pi)
//设置通知的取消
.setAutoCancel(true)
//设置声音
.setSound(Uri.fromFile(new File("/system/media/audio/ringtones/Luna.ogg")))
//设置震动
.setVibrate(new long[] {0, 1000, 1000, 1000})
//设置闪光灯
.setLights(Color.GREEN, 1000, 1000)
.build();
notificationManager.notify(2, notification);
示例中PendingIntent中需要四个参数,第一个参数是上下文context,第三个参数是封装的Intent,第一/四个参数一般传入0即可.
2拍照和访问相册
我们可以把常见的场景简化成如下图的效果.
点击TAKE PHOTO调用相机功能拍照,并将拍摄成功的照片设置给下面的ImageView.点击CHOOSE_FROM_ALBUM启动相册,将选择的照片设置给下面的ImageView.
按照布局-处理逻辑的思路,先在layout中添加Button和ImageView的组件.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Take photo"
android:layout_gravity="center"
/>
<Button
android:id="@+id/choose_from_album"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="choose_from_album"
android:layout_gravity="center"
/>
<ImageView
android:id="@+id/imageView_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
点击TAKE PHOTO后,调用相机功能.
public static final int TAKE_PHOTO = 1;
private ImageView picture;
private Uri imageUri;
Button button_photo = (Button)findViewById(R.id.button_photo);
picture = (ImageView)findViewById(R.id.imageView_picture);
button_photo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
try {
if (outputImage.exists()){
outputImage.delete();
}
outputImage.createNewFile();
}catch (IOException e){
e.printStackTrace();
}
if (Build.VERSION.SDK_INT >= 24){
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.zhudong.cameraalbumtest.file" +
"provider", outputImage);
}else {
imageUri = Uri.fromFile(outputImage);
}
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO);
}
调用相机通过匿名Intent,传入action名称android.media.action.IMAGE_CAPTURE后,系统会自动识别.在intent传入imageUri存储拍照成功照片.在onActivityResult中对存储的照片进行处理.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case TAKE_PHOTO:
if (resultCode == RESULT_OK) {
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
picture.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
点击CHOOSE_FROM_ALBUM后调用相册功能.
Button button_choose = (Button)findViewById(R.id.choose_from_album);
button_choose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
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_ALBUM);
}
调用相册实质是读SD卡,属于Android系统的危险权限,使用之前需要申请.在onRequestPermissionsResult中对申请结果进行处理.
@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, "You denied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
类似启动相机功能,调用相册也是通过匿名Intent的方式.在onActivityResult的回调中可以得到选择的图片.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case CHOOSE_ALBUM:
if (resultCode == RESULT_OK){
if (Build.VERSION.SDK_INT >= 19){
handleImageOnKitKat(data);
}else {
handleImageBeforeKitKat(data);
}
}
default:
break;
}
}
@TargetApi(19)
private void handleImageOnKitKat(Intent data) {
String imagePath = null;
Uri uri = data.getData();
if (DocumentsContract.isDocumentUri(this, uri)) {
String id = DocumentsContract.getDocumentId(uri);
if ("com.android.providers.media.documents".equals(uri.getAuthority())){
String id1 = id.split(":")[1];
String selection = MediaStore.Images.Media._ID + "=" + id1;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
}else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
Uri contentUri = ContentUris.withAppendedId(Uri.parse("conent://downloads/public_downloads"), Long.valueOf(id));
imagePath = getImagePath(contentUri, null);
}
}else if("content".equalsIgnoreCase(uri.getScheme())) {
imagePath = getImagePath(uri,null);
}else if("file".equalsIgnoreCase(uri.getScheme())) {
imagePath = uri.getPath();
}
displayImage(imagePath);
}
private void handleImageBeforeKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri, null);
displayImage(imagePath);
}
private String getImagePath(Uri uri, String selection) {
String path = null;
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
if (cursor != null){
if (cursor.moveToFirst()){
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
private void displayImage(String imagePath) {
if (imagePath !=null){
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
picture.setImageBitmap(bitmap);
}else {
Toast.makeText(MainActivity.this, "U don't choose album", Toast.LENGTH_SHORT).show();
}
}
Android4.4以后对回调的Uri进行了封装,需要通过getContentResolver进一步处理.所以通过Android版本和19的比较,对回调的Uri进行了handleImageOnKitKat和handleImageBeforeKitKat不同的处理.
3播放音视频
播放音频通过MediaPlayer类来实现,可以很方便的控制音频的开始,暂停和重新播放.
public class MainActivity extends AppCompatActivity {
// private MediaPlayer mediaPlayer = new MediaPlayer();
private MediaPlayer mediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}else {
initMediaPlayer();
}
}
private void initMediaPlayer(){
try{
// File file = new File(Environment.getExternalStorageDirectory(), "music.mp3");
// ;
// mediaPlayer.setDataSource(file.getPath());
mediaPlayer = MediaPlayer.create(this, R.raw.voa);
mediaPlayer.prepare();
}catch (Exception e){
e.printStackTrace();
}
}
@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) {
initMediaPlayer();
}else {
Toast.makeText(MainActivity.this, "U don't permission", Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
}
}
public void play(View v){
if (!mediaPlayer.isPlaying()){
mediaPlayer.start();
}
}
public void pause(View v){
if (mediaPlayer.isPlaying()){
mediaPlayer.pause();
}
}
public void stop(View v){
if (mediaPlayer.isPlaying()){
mediaPlayer.reset();
initMediaPlayer();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mediaPlayer != null){
mediaPlayer.stop();
mediaPlayer.release();
}
}
}
播放音频,也需要申请权限.并且在onDestroy中需要释放掉MediaPlayer初始化时占用的资源.
播放视频通过VideoView来实现.因为VideoView是对MediaPlayer进行的封装,所以使用方式和播放音频非常类似,只不过在重置时调用的是resume方法;OnDestroy中释放资源时,调用suspend的方法.
public class MainActivity extends AppCompatActivity {
private VideoView videoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
videoView = (VideoView)findViewById(R.id.videoView);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ==
PackageManager.PERMISSION_GRANTED){
initVideoView();
}else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
1);
}
}
private void initVideoView(){
File file = new File(Environment.getExternalStorageDirectory(), "movie.mp4");
videoView.setVideoPath(file.getPath());
}
@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){
initVideoView();
}else {
Toast.makeText(this, "U have reject!", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
public void play() {
if (!videoView.isPlaying()){
videoView.start();
}
}
public void pause() {
if (videoView.isPlaying()){
videoView.pause();
}
}
public void stop() {
if (videoView.isPlaying()){
videoView.resume();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (videoView != null){
videoView.suspend();
}
}
}
总结
Android中的多媒体知识,包括高阶的通知使用,调用相机和相册的功能以及音视频的播放.取消通知需要设置setAutoCancel属性为true.调用相册和音视频的播放都需要申请权限.调用相册后选择的图片根据Android版本的不同需要做不同的处理.