Android学习(运用手机多媒体)

一、使用通知Notification:

当某个应用程序希望向用户发出一些提示信息,而该程序又不在前台运行时,就可以借助通知来实现。

1.通知的基本用法:

通知可以在活动、在广播接收器或在服务里创建。

创建通知的步骤:

a.获取NotificationManager实例:

NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

b.使用Builder构造器创建一个Notification对象。由于Android每个版本API不稳定,使用support库中的兼容API(support-4v中提供NotificationCompat类),保证程序在所有Android系统版本上正常工作:

Notification notification = new NotificationCompat.Builder(context).bulid();

上面的代码仅仅创建了一个Notification对象,可在build()方法之前连缀任意多设置方法来创建丰富的Notification对象:

Notification notification = new NotificationCompat.Builder(context).setContentTitle("This is content title").setContentText("This is content text").setWhen(System.currentTimeMillis()).setSmallIcon(R.drawable.small_icon).setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.large_icon)).build();

调用了五个设置方法:指定通知的标题内容(下拉时显示);指定通知的正文内容(下拉时显示);指定通知被创建的时间(单位毫秒,下拉时显示在通知上);设置通知的小图标(只能用纯alpha图层的图片,显示在系统状态栏上);设置通知的大图标(下拉时显示)。

c.调用通知管理器类的notify方法即可使通知显示出来:

manager.notify(1, notification);

notify方法有两个参数:第一个参数是id,保证为每个通知创建的id是不同的;第二个参数是通知对象。

2.具体范例:创建一个NotificationTest项目。

(1)修改主活动的界面文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/send_notice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send notice"
        />
    
</LinearLayout>

(2)修改主活动代码:

package com.example.notificationtest;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);  //重写超类方法
        setContentView(R.layout.activity_main);  //绑定布局
        Button button = (Button) findViewById(R.id.send_notice);  //获取按钮id
        button.setOnClickListener(this);
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.send_notice:
                String id ="channel_1";//channel的id
                String description = "123";//channel的描述信息
                int importance = NotificationManager.IMPORTANCE_LOW;//channel的重要性
                NotificationChannel channel = new NotificationChannel(id, description, importance);//生成channel
                //创建通知管理器
                NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
                manager.createNotificationChannel(channel);//添加channel
                Notification notification = new Notification.Builder(MainActivity.this,id)
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        .setWhen(System.currentTimeMillis())
                        .setContentTitle("这是通知的标题内容")
                        .setContentText("这是通知的内容")
                        .build();
                manager.notify(1,notification);
                break;
            default:
                break;
        }
    }
}

注:如果根据上述操作仍无法显示消息,则需检查一下手机对当前应用的通知设置。

(3)消息通常情况下是可以点进去的,但根据上述操作显示的通知点击它时,没有任何效果,如果想要实现通知的点击效果,则需要PendingIntent。

PendingIntent与Intent类似,相同点:都可以用于指名一个“意图”,都可以用于启动活动、服务以及广播等。不同点:Intent更倾向于立刻执行某个动作,而PendingIntent更倾向于在某个合适的时机去执行某个动作。(可以把PendingIntent理解为延迟执行的Intent)

PendingIntent提供了几个静态方法用于获取PendingIntent实例,根据需求选择getActivity()方法、getBroadcast()方法、getService()方法,这几个方法接收的参数是相同的:

            a.Context

            b.该参数一般用不到,通常传入0即可

            c.Intent对象

            d.确定PendingIntent的行为(FLAG_ONE_SHOT/FLAG_NO_CREATE/FLAG_CANCLE_CURRENT/FLAG_UPDATE_CURRENT四种值可选,通常传入0)

NotificationCompat.Builder还可再连缀一个SetContent()方法(参数正是PendingIntent对象)。

*行动:优化NotificationTest项目。新建另一个活动NotificationActivity,在其布局文件上添加一个TextView:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInparent="true"
        android:text="This is notification layout!"
        android:textSize="24sp"
        />

</LinearLayout>

修改主活动的代码,将onClick方法的代码修改为:

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.send_notice:
                String id ="channel_1";//channel的id
                String description = "123";//channel的描述信息
                int importance = NotificationManager.IMPORTANCE_LOW;//channel的重要性
                NotificationChannel channel = new NotificationChannel(id, description, importance);//生成channel
                Intent intent = new Intent(MainActivity.this, NotificationActivity.class);
                PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0,intent, 0);       //消息触发后调用
                //创建通知管理器
                NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
                manager.createNotificationChannel(channel);//添加channel
                Notification notification = new Notification.Builder(MainActivity.this,id)
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        .setWhen(System.currentTimeMillis())
                        .setContentIntent(pi)
                        .setContentTitle("这是通知的标题内容")
                        .setContentText("这是通知的内容")
                        .build();
                manager.notify(1,notification);
                break;
            default:
                break;
        }
    }

运行程序:点击通知消息,会进入NotificationActivity。

2.通知的进阶技巧:

//NotificationCompat.Builder还可再连缀下列方法
//发出通知时播放一段音频
.setSound(Uri.fromFile(new File("system/media/audio/ringtones/Luna.ogg")))
//发出通知时震动
.setVibrate(new long[] {0,1000,1000,1000})
//发出通知时闪光灯提示
.setLights(Color.GREEN,1000,1000)
//发出通知时使用默认的通知效果
.setDefaults(NotificationCompat.DEFAULT_ALL)

3.通知的高级功能:

(1)通知内容较长时,是无法完整显示的,多余的部分会用省略号代替,但如果需要显示完整的内容,setStyle()方法可以实现:

.setStyle(new Notification.BigTextStyle().bigText("这是通知的内容,在这里我想说明,我真的很想养只狗或者猫,我也想买一台拍立得!但是我没有充分的预算,这算不算白日做梦、痴心妄想呢?"))

上述操作是在setStyle()方法中创建了一个Notification.BigTextStyle对象,这个对象是用于封装长文字信息的,调用它的bigText()方法并将文字内容传入即可。

(2)在通知里显示一张大图片,在setStyle()方法中创建了一个Notification.BigPictureStyle对象,调用它的bigPicture()方法并将图片传入即可。

.setStyle(new Notification.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(),R.drawable.pear)))

(3)setPriority()方法用于设置通知的重要程度。5个常量值可选:PRIORITY_DEFAULT(默认,不设置即为默认)、PRIORITY_MIN(最低,可能只会在特定场景显示,如下拉状态栏时)、PRIORITY_LOW(较低,可能会缩小或改变显示顺序在更重要通知之后)、PRIORITY_HIGH(较高,能能会放大或排在比较靠前位置)、PRIORITY_MAX(最高,必须让用户立即看到并做出响应)。

二、调用摄像头和相册:

1.调用摄像头拍照:

(1)新建一个CameraAlbumTest项目,修改activity_main.xml的代码:

<?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/take_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Take Photo" />

    <ImageView  //显示拍到的图片
        android:id="@+id/picture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>

</LinearLayout>

(2)修改MainActivity的代码:

package com.example.cameraalbumtest;

import androidx.activity.result.contract.ActivityResultContract;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MainActivity<GoogleApiClient> extends AppCompatActivity {

    public static final int TAKE_PHOTO = 1;
    private ImageView picture;
    private Uri imageUri;
    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    private GoogleApiClient client;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button takePhoto = (Button) findViewById(R.id.take_photo);  //获取按钮的实例
        picture = (ImageView) findViewById(R.id.picture);  //获取图片显示控件的实例
        takePhoto.setOnClickListener(new View.OnClickListener() {  //注册监听事件
            @Override
            public void onClick(View v) {
                /*
                 * 创建File对象,用于存储摄像头拍摄的图片,图片命名为output_image.jpg
                 * 将它存放在手机SD卡的应用关联缓存目录下,关联缓存目录指SD卡中专门用于存放当前应用缓存数据的位置,getExternalCacheDir()方法可得到这个目录
                 * 具体路径是/sdcard/Android/data/<package name>/cache
                 * 从Android6.0开始,读写SD卡被列为危险权限,如果存放在SD卡其他目录需进行运行时权限处理,使用此目录则不用
                 */
                File outputImage = new File(getExternalCacheDir(), "output_image.jpg");  //创建一个File对象,用于存放摄像头拍下的图片,这里图片命名为output_image.jpg
                try {
                    if (outputImage.exists()) {  //如果这个文件存在,就删除这个文件
                        outputImage.delete();
                    }
                    outputImage.createNewFile(); //创建这个文件
                } catch (IOException e) {  //捕获异常
                    e.printStackTrace();
                }
                if (Build.VERSION.SDK_INT >= 24) {//如果运行设备的系统版本等于或高于Android7.0
                    imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalbumtest.fileprovider", outputImage);
                    //将File对象转换成一个封装过的Uri对象。三个参数:Context对象,任意唯一字符串,File对象
                    //Android7.0后,直接访问本地真实路径的Uri被认为不安全,会抛出异常,而FileProvider是一种特殊的内容提供器(使用和内容提供器类似的机制保护数据),可选择性地将封装过的Uri共享给外部,提高应用安全
                } else {
                    //如果运行设备的系统版本低于Android7.0,此Uri对象标识着output_image.jpg图片的本地真实路径
                    imageUri = Uri.fromFile(outputImage);
                }
                if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){  //动态申请权限
                    ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA},1);  //向用户申请获取权限
                }else{
                    takePhoto();
                }
            }
        });

    }

    private void takePhoto(){
        try{
            //启动相机程序
            //构建一个Intent对象,将这个Intent的action指定为android.media.action.IMAGE_CAPTURE
            Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//指定图片输出地址为得到的Uri对象
            startActivityForResult(intent,TAKE_PHOTO);//启动相机
        }catch(SecurityException e){
            e.printStackTrace();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) { //回调,不论权限是否申请成功,均会回到这个方法
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    takePhoto();
                } else {
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  //startActivityResult方法的回调
        super.onActivityResult(requestCode, resultCode, 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;
        }
    }
}

注:按照《Android第一行代码》(第2版)闪退是未申请权限的问题,必须动态申请权限才可以使项目正常运行。(在Android6.0+系统以上,除了在AndroidManifest文件里注册权限,还需要在活动里动态申请权限,才可保证APP正常运行

(3)在AndroidManifest.xml中对内容提供器进行注册:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.cameraalbumtest">

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.CAMERA"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.CameraAlbumTest">

        <provider
            android:authorities="com.example.cameraalbumtest.fileprovider"
            android:name="androidx.core.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

(4)在res目录下创建一个文件夹File,在File下创建一个file_paths.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!--指定Uri共享,name属性值随便填,path的值表示共享的具体路径,空值表示将整个SD卡进行共享-->
    <external-path
        name="my_images"
        path="."/>
</paths>

2.从相册中选择照片:

package com.example.cameraalbumtest;

import androidx.activity.result.contract.ActivityResultContract;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;

import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MainActivity<GoogleApiClient> extends AppCompatActivity {

    public static final int TAKE_PHOTO = 1;
    private ImageView picture;
    private Uri imageUri;
    public static final int CHOOSE_PHOTO = 2;
    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    private GoogleApiClient client;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button takePhoto = (Button) findViewById(R.id.take_photo);  //获取按钮的实例
        Button chooseFromAlbum = (Button) findViewById(R.id.choose_from_album);
        picture = (ImageView) findViewById(R.id.picture);  //获取图片显示控件的实例
        takePhoto.setOnClickListener(new View.OnClickListener() {  //注册监听事件
            @Override
            public void onClick(View v) {
                /*
                 * 创建File对象,用于存储摄像头拍摄的图片,图片命名为output_image.jpg
                 * 将它存放在手机SD卡的应用关联缓存目录下,关联缓存目录指SD卡中专门用于存放当前应用缓存数据的位置,getExternalCacheDir()方法可得到这个目录
                 * 具体路径是/sdcard/Android/data/<package name>/cache
                 * 从Android6.0开始,读写SD卡被列为危险权限,如果存放在SD卡其他目录需进行运行时权限处理,使用此目录则不用
                 */
                File outputImage = new File(getExternalCacheDir(), "output_image.jpg");  //创建一个File对象,用于存放摄像头拍下的图片,这里图片命名为output_image.jpg
                try {
                    if (outputImage.exists()) {  //如果这个文件存在,就删除这个文件
                        outputImage.delete();
                    }
                    outputImage.createNewFile(); //创建这个文件
                } catch (IOException e) {  //捕获异常
                    e.printStackTrace();
                }
                if (Build.VERSION.SDK_INT >= 24) {//如果运行设备的系统版本等于或高于Android7.0
                    imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalbumtest.fileprovider", outputImage);
                    //将File对象转换成一个封装过的Uri对象。三个参数:Context对象,任意唯一字符串,File对象
                    //Android7.0后,直接访问本地真实路径的Uri被认为不安全,会抛出异常,而FileProvider是一种特殊的内容提供器(使用和内容提供器类似的机制保护数据),可选择性地将封装过的Uri共享给外部,提高应用安全
                } else {
                    //如果运行设备的系统版本低于Android7.0,此Uri对象标识着output_image.jpg图片的本地真实路径
                    imageUri = Uri.fromFile(outputImage);
                }
                if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){  //动态申请权限
                    ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA},1);  //向用户申请获取权限
                }else{
                    takePhoto();
                }
            }
        });

        chooseFromAlbum.setOnClickListener(new View.OnClickListener(){  //注册监听事件
            @Override
            public void onClick(View view){
                //动态申请权限:WRITE_EXTERNAL_STORAGE表示同时授予程序对SD卡的读和写的能力
                if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},2);
                }else{
                    openAlbum();
                }
            }
        });

    }

    private void takePhoto(){
        try{
            //启动相机程序
            //构建一个Intent对象,将这个Intent的action指定为android.media.action.IMAGE_CAPTURE
            Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//指定图片输出地址为得到的Uri对象
            startActivityForResult(intent,TAKE_PHOTO);//启动相机
        }catch(SecurityException e){
            e.printStackTrace();
        }
    }

    //打开相册程序
    private void openAlbum(){
        Intent intent = new Intent("android.intent.action.GET_CONTENT");  //新建一个意图,将它的action指定为"android.intent.action.GET_CONTENT"
        intent.setType("image/*");  //选择图片
        startActivityForResult(intent,CHOOSE_PHOTO);  //打开相册
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) { //回调,不论权限是否申请成功,均会回到这个方法
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    takePhoto();
                } else {
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;
            case 2:
                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;
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  //startActivityResult方法的回调
        super.onActivityResult(requestCode, resultCode, 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;
            case CHOOSE_PHOTO:
                if (resultCode == RESULT_OK){
                    //判断手机的版本号
                    if (Build.VERSION.SDK_INT>=19){
                        try {
                            handleImageOnKitKat(data);//4.4及以上系统使用这个方法处理图片,因为Android系统从4.4版本开始,选取相册中的图片不再返回真实的Uri了,而是封装后的uri
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                    } else{
                        try {
                            handleImageBeforeKitKat(data);//4.4以下系统使用这个方法处理图片
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
                break;
            default:
                break;
        }
    }

    @TargetApi(19)
    private void handleImageOnKitKat(Intent data) throws FileNotFoundException {  //4.4版本以上的系统需要对图片封装的Uri进行解析
        String imagePath = null;
        Uri uri = data.getData();
       if(DocumentsContract.isDocumentUri(this,uri)){
            //如果是document类型的Uri,则通过document id处理
            String docId = DocumentsContract.getDocumentId(uri);
            if("com.android.providers.media.documents".equals(uri.getAuthority())){  //如果Uri的authority是media格式的话,document id需要载进行一次解析
                String id = docId.split(":")[1];  //解析出数字格式的id
                String selection = MediaStore.Images.Media._ID+"="+id; //构建新的Uri
                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("content://downloads/public_downloads"),Long.valueOf(docId));
                imagePath = getImagePath(contentUri,null);
            }
        }else if("content".equalsIgnoreCase(uri.getScheme())){
            //如果是content类型的Uri,则使用普通方式处理
            imagePath = getImagePath(uri,null);
        }else if("file".equalsIgnoreCase(uri.getScheme())){
            //如果是file类型的Uri,直接获取图片路径即可
            imagePath = uri.getPath();
        }
        displayImage(imagePath); //根据图片路径显示图片
    }

    private void handleImageBeforeKitKat(Intent data) throws FileNotFoundException {  //处理4.4版本以下的Uri
        Uri uri = data.getData();
        String imagePath = getImagePath(uri,null);
        displayImage(imagePath); //根据图片路径显示图片
    }


    @SuppressLint("Range")
    private String getImagePath(Uri uri, String selection){
        String path = null;
        //通过Uri和selection来获取真实的图片路径
        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) throws FileNotFoundException {
        if(imagePath!=null){
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            picture.setImageBitmap(bitmap);
        }else{
            Toast.makeText(this,"failed to get image",Toast.LENGTH_SHORT).show();
        }
    }
}

运行程序:

       如果运行时后提示错误BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: file:/storage/emulated/0/test.jpg: open failed

错误原因:版本问题(如果你正在使用你的“华为”手机进行调试,那么看一下你的系统是否为鸿蒙系统)

解决方案:

//方法声明处声明出可能出现的异常throws FileNotFoundException
bitmap=BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));

(通过获取数据流的方式比较安全)

四、播放多媒体文件:

Android在播放音频和视频方面做了相当不错的支持,提供了一套较为完整的API。

1.播放音频:

在Android中播放音频文件是使用MediaPlayer类来实现的,它对多种格式的音频文件提供了非常全面的控制方法:

 MediaPlayer类的工作流程:

a.创建一个MediaPlayer类的对象,调用setDataSource()方法来设置音频文件的路径;

b.调用prepare()方法使MediaPlayer进入准备状态;

c.调用start()方法开始播放音频;

d.调用pause()方法暂停播放;

e.调用reset()方法会停止播放。

*动手操作:创建一个项目PlayAudioTest。

(1)修改主活动的布局文件:

<?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/play"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Play"
        />

    <Button
        android:id="@+id/pause"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Pause"
        />

    <Button
        android:id="@+id/stop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Stop"
        />
</LinearLayout>

(2)修改主活动代码:

package com.example.playaudiotest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private MediaPlayer mediaPlayer = new MediaPlayer();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button play = (Button) findViewById(R.id.play);  //获取播放按钮控件id
        Button pause = (Button) findViewById(R.id.pause);  //获取暂停按钮控件id
        Button stop = (Button) findViewById(R.id.stop);  //获取停止按钮控件id
        play.setOnClickListener(this);  //注册监听器
        pause.setOnClickListener(this);  //注册监听器
        stop.setOnClickListener(this);  //注册监听器
        //动态申请权限
        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");//SD卡根目录下,音乐文件夹下的音乐
            mediaPlayer.setDataSource(file.getPath());//指定音频文件路径
            mediaPlayer.prepare();//让MediaPlayer进入准备状态
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {  //判断权限是否获取成功
                    initMediaPlayer();
                } else {
                    Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
                    finish();  //拒绝权限直接finish(),因此会直接退出应用
                }
                break;
            default:
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.play:
                if (! mediaPlayer.isPlaying()){  //判断当前mediaPlayer是否在播放音频
                    mediaPlayer.start();  //开始播放
                }
                break;
            case R.id.pause:
                if (mediaPlayer.isPlaying()){  //判断当前mediaPlayer是否在播放音频
                    mediaPlayer.pause();  //暂停播放
                }
                break;
            case R.id.stop:
                if (mediaPlayer.isPlaying()){  //判断当前mediaPlayer是否在播放音频
                    mediaPlayer.reset();  //停止播放
                    initMediaPlayer();  //初始化音频文件播放器
                }
                break;
            default:
                break;
        }
    }

    @Override
    protected void onDestroy() {  //活动销毁时暂停播放并释放资源
        super.onDestroy();
        if (mediaPlayer != null){
            mediaPlayer.stop();
            mediaPlayer.release();  //释放资源
        }
    }
}

注:在AndroidManifest.xml文件中一定要声明使用到的权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2.播放视频:

在Android中播放视频文件是使用VideoView类来实现的,它将视频的显示与控制集于一身,其主要有以下常用方法:

 *动手操作:在上面的项目中新建一个活动。

(1)修改新建活动的页面布局文件代码:

<?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">
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <Button
            android:id="@+id/play_video"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Play"/>
    
        <Button
            android:id="@+id/pause_video"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Pause"/>
    
        <Button
            android:id="@+id/replay_video"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Replay"/>
        
    </LinearLayout>

    <VideoView
        android:id="@+id/video"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    
</LinearLayout>

(2)修改新建活动代码:

package com.example.playaudiotest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.widget.VideoView;

import java.io.File;

public class MainActivity2 extends AppCompatActivity implements View.OnClickListener{
    private VideoView videoView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        Button playVideo = (Button) findViewById(R.id.play_video);
        Button pauseVideo = (Button) findViewById(R.id.pause_video);
        Button replayVideo = (Button) findViewById(R.id.replay_video);
        videoView = (VideoView) findViewById(R.id.video);
        playVideo.setOnClickListener(this);
        pauseVideo.setOnClickListener(this);
        replayVideo.setOnClickListener(this);
        //动态申请权限
        if(ContextCompat.checkSelfPermission(MainActivity2.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(MainActivity2.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
        }else{
            initVideoPath();  //初始化视频播放器
        }
    }
    private void initVideoPath(){
        File file = new File(Environment.getExternalStorageState(),"movie.mp4");  //创建一个文件读取视频文件
        videoView.setVideoPath(file.getPath());
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {  //判断权限是否获取成功
                    initVideoPath();
                } else {
                    Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
                    finish();  //拒绝权限直接finish(),因此会直接退出应用
                }
                break;
            default:
        }
    }
    @Override
    public void onClick(View view){
        switch(view.getId()){
            case R.id.play_video:
                if(!videoView.isPlaying()){
                    videoView.start();
                }
                break;
            case R.id.pause_video:
                if(!videoView.isPlaying()){
                    videoView.pause();
                }
                break;
            case R.id.replay_video:
                if(videoView.isPlaying()){
                    videoView.resume();
                }
                break;
        }
    }
}

——参考《Android第一行代码》整理上述笔记

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值