日常手機(jī)使用中,常常會(huì)遇到頭部彈出一條新消息,有的還伴隨著震動(dòng),音樂(lè)以及燈光閃爍,它們是怎么實(shí)現(xiàn)的呢?許多app的個(gè)人設(shè)置頁(yè)面都有頭像設(shè)置的功能,一般都有使用者從相冊(cè)中選取或者立即拍照兩種選擇.很多時(shí)候,app中也會(huì)有播放音視頻的功能.它們都是如何實(shí)現(xiàn)的呢?
1通知
通過(guò)通知,能夠在app打開(kāi),但是被切換到后臺(tái)時(shí),及時(shí)通知用戶(hù)app的變化.發(fā)送通知類(lèi)似啟動(dòng)Activity,只不過(guò)啟動(dòng)對(duì)象換成了PendingIntent,用于啟動(dòng)通知時(shí)對(duì)Intent進(jìn)行再次封裝.示例如下:
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)
//設(shè)置通知的取消
.setAutoCancel(true)
//設(shè)置聲音
.setSound(Uri.fromFile(new File("/system/media/audio/ringtones/Luna.ogg")))
//設(shè)置震動(dòng)
.setVibrate(new long[] {0, 1000, 1000, 1000})
//設(shè)置閃光燈
.setLights(Color.GREEN, 1000, 1000)
.build();
notificationManager.notify(2, notification);
示例中PendingIntent中需要四個(gè)參數(shù),第一個(gè)參數(shù)是上下文context,第三個(gè)參數(shù)是封裝的Intent,第一/四個(gè)參數(shù)一般傳入0即可.
2拍照和訪問(wèn)相冊(cè)
我們可以把常見(jiàn)的場(chǎng)景簡(jiǎn)化成如下圖的效果.
點(diǎn)擊TAKE PHOTO調(diào)用相機(jī)功能拍照,并將拍攝成功的照片設(shè)置給下面的ImageView.點(diǎn)擊CHOOSE_FROM_ALBUM啟動(dòng)相冊(cè),將選擇的照片設(shè)置給下面的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>
點(diǎn)擊TAKE PHOTO后,調(diào)用相機(jī)功能.
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);
}
調(diào)用相機(jī)通過(guò)匿名Intent,傳入action名稱(chēng)android.media.action.IMAGE_CAPTURE后,系統(tǒng)會(huì)自動(dòng)識(shí)別.在intent傳入imageUri存儲(chǔ)拍照成功照片.在onActivityResult中對(duì)存儲(chǔ)的照片進(jìn)行處理.
@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;
}
}
點(diǎn)擊CHOOSE_FROM_ALBUM后調(diào)用相冊(cè)功能.
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);
}
調(diào)用相冊(cè)實(shí)質(zhì)是讀SD卡,屬于Android系統(tǒng)的危險(xiǎn)權(quán)限,使用之前需要申請(qǐng).在onRequestPermissionsResult中對(duì)申請(qǐng)結(jié)果進(jìn)行處理.
@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;
}
}
類(lèi)似啟動(dòng)相機(jī)功能,調(diào)用相冊(cè)也是通過(guò)匿名Intent的方式.在onActivityResult的回調(diào)中可以得到選擇的圖片.
@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以后對(duì)回調(diào)的Uri進(jìn)行了封裝,需要通過(guò)getContentResolver進(jìn)一步處理.所以通過(guò)Android版本和19的比較,對(duì)回調(diào)的Uri進(jìn)行了handleImageOnKitKat和handleImageBeforeKitKat不同的處理.
3播放音視頻
播放音頻通過(guò)MediaPlayer類(lèi)來(lái)實(shí)現(xiàn),可以很方便的控制音頻的開(kāi)始,暫停和重新播放.
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();
}
}
}
播放音頻,也需要申請(qǐng)權(quán)限.并且在onDestroy中需要釋放掉MediaPlayer初始化時(shí)占用的資源.
播放視頻通過(guò)VideoView來(lái)實(shí)現(xiàn).因?yàn)閂ideoView是對(duì)MediaPlayer進(jìn)行的封裝,所以使用方式和播放音頻非常類(lèi)似,只不過(guò)在重置時(shí)調(diào)用的是resume方法;OnDestroy中釋放資源時(shí),調(diào)用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();
}
}
}
總結(jié)
Android中的多媒體知識(shí),包括高階的通知使用,調(diào)用相機(jī)和相冊(cè)的功能以及音視頻的播放.取消通知需要設(shè)置setAutoCancel屬性為true.調(diào)用相冊(cè)和音視頻的播放都需要申請(qǐng)權(quán)限.調(diào)用相冊(cè)后選擇的圖片根據(jù)Android版本的不同需要做不同的處理.