【Android】安卓多媒体之通知、摄像头、相册、播放音乐、视频用法总结

news2024/11/14 20:45:12

文章目录

  • 一、通知
    • 1. 申请权限
    • 2. 创建通道
    • 3. 创建通知
    • 4. 发送通知
    • 拓展功能
      • 点击行为
      • 更新通知
      • 取消通知
      • 锁屏通知
      • 富文本通知
  • 二、摄像头
    • 1. 申请权限
    • 2. 调用逻辑
    • 3. 声明内容提供器
  • 三、打开相册
    • 1. 申请权限
      • 检查并请求权限
      • 处理权限请求结果
    • 2.处理图片
      • 从相册中选择图片
      • 处理选择图片的结果
      • 处理图片
      • 获取图片真实路径
  • 四、播放音乐
    • 1.申请权限
    • 2.处理音乐
    • MediaPlayer
  • 五、播放视频
    • 方法
    • 属性
    • 示例

一、通知

Android 8.0(API26)后对通知更改了一些内容,以前是通过Notification,Builder(Context context).se..来设置通知的震动、灯光、音效的设置,新内容加了NotificationChannel(通知渠道),通过NotificatonChannel来进行震动、灯光、音效的设置,且通知必须添加通知渠道,同样需进行版本判断,否则通知不会被发送。

步骤:

  1. 首先创建NotificationManager对象
  2. 创建Notificationchannel对象
  3. 通过Notificationcompat.Builder(Context.Notification)对象指定channel并设置通知的各项信息,调用build()方法创建Notifcation对象
  4. 调用NotificationManagernotify(id,Notification)方法发送通知

1. 申请权限

AndroidManifest.xml 文件中声明权限

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

在运行时请求权限

if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
                != PackageManager.PERMISSION_GRANTED) {
            // 权限尚未授予,申请权限
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.POST_NOTIFICATIONS},1);
}

2. 创建通道

使用NotificationChannel类创建通知通道的实例
创建通知通道:

  • 设置唯一的id
  • 通道的用户可见的名称
  • 通道的重要性

image-20240731095312726

private static final String MESSAGES_CHANNEL = "default_channel";
private void createMessagesNotificationChannel(Context context) {
    // 检查当前设备的系统版本是否大于或等于 Android 8.0 (API 级别 26)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        // 从资源中获取通知渠道的名称
        CharSequence name = context.getString(R.string.message_channel_name);
        
        // 创建一个通知渠道对象
        NotificationChannel channel = new NotificationChannel(
                MESSAGES_CHANNEL, // 渠道 ID,用于标识这个通知渠道
                name, // 渠道名称,从资源文件中获取
                NotificationManager.IMPORTANCE_HIGH // 渠道的重要性级别,决定通知的行为和显示方式
        );
        
        // 获取 NotificationManager 系统服务
        NotificationManager manager = context.getSystemService(NotificationManager.class);
        
        // 创建通知渠道
        manager.createNotificationChannel(channel);
    }
}

这样后就创建了渠道,下来直接构建通知然后发送即可

3. 创建通知

image-20240731095422811

    public void onClick(View v) {
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        Notification notification = new NotificationCompat.Builder(this, MESSAGES_CHANNEL)
                .setContentTitle("重要通知")
                .setContentText("您今天睡眠时间不足")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.drawable.messages_1)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.messages_2))
                .build();
        manager.notify(1, notification);
    }

4. 发送通知

使用NotificationManager类发送通知

  • 调用getSystemService()方法创建NotificationManager的对象
  • 调用它的notify()方法发送消息,传递通知idNotification对象
 manager.notify(1, notification); // 参数 1 是通知的 ID,用于更新或取消通知,notification 是要显示的通知对象

拓展功能

点击行为

所有通知在点击后都必须作出响应,一般是启动一个Activity

  • 调用setContentlntent()方法设置一个内容Intent;

  • 然后通过一个PendingIntent对象传递内容Intent

public void onClick(View v) {
		//----在这里设置PendingIntent----
        Intent intent = new Intent(this, NotificationActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this,
                0,
                intent,
                PendingIntent.FLAG_IMMUTABLE
        );

        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        Notification notification = new NotificationCompat.Builder(this, MESSAGES_CHANNEL)
                .setContentTitle("重要通知")
                .setContentText("您今天睡眠时间不足")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.drawable.messages_1)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.messages_2))
                .setContentIntent(pendingIntent)// 设置点击通知时的 PendingIntent
                .setAutoCancel(true)// 点击通知后自动取消
                .build();
        manager.notify(1, notification);
    }
public static PendingIntent getActivity
(Context context, int requestCode, Intent intent, int flags)

PendingIntent 是一种封装了 Intent 的机制,可以在将来某个时刻代替应用程序的进程执行特定的操作,即使应用程序不在运行中。

flags: 标志位,指定 PendingIntent 的行为。常用标志包括:

通常使用FLAG_IMMUTABLE

  • PendingIntent.FLAG_UPDATE_CURRENT: 如果 PendingIntent 已存在,更新其内容。
  • PendingIntent.FLAG_CANCEL_CURRENT: 如果 PendingIntent 已存在,先取消它,然后创建一个新的。
  • PendingIntent.FLAG_IMMUTABLE: 使 PendingIntent 不可变,即创建后不会被修改。
  • PendingIntent.FLAG_MUTABLE: 使 PendingIntent 可变,即创建后可以被修改。

更新通知

  • 使用NotificationCompat.Builder构造其创建有更新内容的通知
  • 将相同的通知Id和更新内容后的通知传递给notify()方法即可实现更新
    • 若之前的通知仍然可见,则系统更新
    • 若之前的通知已被取消,则发送新的通知

取消通知

通知会一直保存可见,以下操作会使消息取消

  • 用户通过滑动或使用"清除所有"取消它
  • 创建通知时调用setAutoCancel(),那么当用户单击通知时会从状态栏消失
  • 通过NotificationManagercancel(通知ld)取消,或调用cancelAll()取消所有通知

锁屏通知

Android 5.0(APl21)开始,通知可以显示在锁屏上。用户可以通过设置选择是否允许敏感的通知内容显示在安全的锁屏上。

android5.0加入一种新的模式Notification的显示等级,共有三种:

  • VISIBILITY_PUBLIC 任何情况都会显示通知
  • VISIBILITY_PRIVATE 显示基本信息隐藏通知内部信息
  • VISIBILITY SECRET 在没有锁屏的情况下才能够显示
  • 调用setVisibility方法设置
builder.setVisibility(Notification.VISIBILITY_PUBLIC);

富文本通知

富文本通知指的是可以展示复杂内容的通知,例如包含多种样式的文本、图片、链接等。要创建富文本通知,可以使用 NotificationCompat 类的一些方法和样式。

setStyle(new NotificationCompat.BigTextStyle())
  1. 使用 BigTextStyleBigTextStyle 用于显示大段文本内容,这些内容会在展开时显示完整。
        Notification notification = new NotificationCompat.Builder(this, MESSAGES_CHANNEL)
                .setContentTitle("富文本通知")
                .setContentText("点击查看详细内容")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.drawable.messages_1)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.messages_2))
                .setStyle(new NotificationCompat.BigTextStyle()
                        .bigText("这是一个长文本示例。在展开通知后,可以看到更多的内容," +
                                "而不是只显示简短的预览文本。这样可以展示更详细的信息。"))
                .setContentIntent(pendingIntent)
                .setAutoCancel(true)
                .build();

VID_20240731_115043

  1. 使用 BigPictureStyleBigPictureStyle 用于显示一张大图片,可以同时显示文本。

  2. 使用 InboxStyleInboxStyle 用于显示一组消息的列表,适合显示多个简短的消息。

  3. MessageingStyle通知

使用 MessagingStyle 的通知可以展示一系列的消息,仿佛是一个对话线程,让用户能够在通知栏中查看消息交流的上下文。

// 创建 MessagingStyle 对象
        NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle("聊天机器人")
                .addMessage("你好!有什么我可以帮助你的吗?", System.currentTimeMillis(), "聊天机器人") 
// 添加一条消息,内容为“你好!有什么我可以帮助你的吗?”,时间为当前时间,发送者为“聊天机器人”
                .addMessage("我想知道今天的天气怎么样。", System.currentTimeMillis(), "用户") 
// 添加另一条消息,内容为“我想知道今天的天气怎么样。”,时间为当前时间,发送者为“用户”
                .setConversationTitle("天气咨询"); 
// 设置对话标题为“天气咨询”,用来标识对话的主题



// 创建通知
        Notification notification = new NotificationCompat.Builder(this, MESSAGES_CHANNEL)
                .setContentTitle("消息通知")
                .setContentText("您有新的消息")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.drawable.messages_1)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.messages_2))
                .setStyle(messagingStyle) // 设置 MessagingStyle
                .setContentIntent(pendingIntent) 
                .setAutoCancel(true) 
                .build();
        manager.notify(1, notification);
    }

Screenshot_2024-07-31-11-43-50-051_com.android.sy

二、摄像头

1. 申请权限

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

允许应用使用设备的相机进行拍照或录像

在运行时请求权限

public class MainActivity extends AppCompatActivity {

    private ImageView image;  // 显示拍摄照片的ImageView
    private static final int REQUEST_CAMERA_PERMISSION = 100;  // 请求权限的请求码

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        image = findViewById(R.id.image);

        // 检查并请求相机权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
        }
    }

    // 处理权限请求结果
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CAMERA_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限授予,执行相应操作
            } else {
                // 权限拒绝,提示用户
                Toast.makeText(this, "相机权限被拒绝", Toast.LENGTH_SHORT).show();
            }
        }
    }

2. 调用逻辑

public class MainActivity extends AppCompatActivity {

    private ImageView image;  // 显示拍摄照片的ImageView
    private Uri imageUri;  // 存储照片文件的Uri
    private static final int TAKE_PHOTO = 1;  // 拍照请求码
    private static final int REQUEST_CAMERA_PERMISSION = 100;  // 请求权限的请求码

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        image = findViewById(R.id.image);

        // 检查并请求相机权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
        }
    }

    // 处理权限请求结果
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CAMERA_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限授予,执行相应操作
            } else {
                // 权限拒绝,提示用户
                Toast.makeText(this, "相机权限被拒绝", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 调用相机拍照
    public void invokeCamera(View view) {
        // 创建一个文件对象,用于存储拍摄的照片,存储在应用的外部缓存目录中,文件名为 "output_image.jpg"
        File outputImage = new File(getExternalCacheDir(), "output_image.jpg");

        try {
            // 如果文件存在,删除旧文件
            if (outputImage.exists()) {
                outputImage.delete();
            }
            // 创建新文件
            outputImage.createNewFile();
        } catch (IOException e) {
            // 处理文件操作异常
            throw new RuntimeException(e);
        }

        // 检查安卓版本,如果版本大于等于 24(安卓 7.0),使用 FileProvider 获取文件的 URI
        if (Build.VERSION.SDK_INT >= 24) {
            // FileProvider 是一个安全的方式来分享文件
            imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalubmtest.fileprovider", outputImage);
        } else {
            // 如果安卓版本小于 24,直接获取文件的 URI
            imageUri = Uri.fromFile(outputImage);
        }

        // 创建一个 Intent 对象,指定动作为 "android.media.action.IMAGE_CAPTURE" 以调用系统相机应用
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
        // 将文件 URI 作为额外参数传递给相机应用,指定拍摄的照片应保存到该 URI 对应的文件中
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        // 启动相机应用,并传入请求码 TAKE_PHOTO,等待返回结果
        startActivityForResult(intent, TAKE_PHOTO);
    }


    // 处理拍照结果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case TAKE_PHOTO:
                if (resultCode == RESULT_OK) {
                    try {
                        // 使用内容解析器打开 imageUri 对应的输入流,并将其解码为 Bitmap
                        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                        // 将解码后的 Bitmap 设置为 ImageView 的图像显示
                        image.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        // 如果文件未找到,抛出运行时异常
                        throw new RuntimeException(e);
                    }
                }
                break;
            default:
                break;
        }
    }

3. 声明内容提供器

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.CameraAlubmTest"
        tools:targetApi="31">
        <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>

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

指定路径配置文件的位置:

新建file_paths.xml 文件

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="my_images"
        path="." />
</paths>

三、打开相册

1. 申请权限

检查并请求权限

onCreate 方法中,应用检查相机和存储权限是否已经被授予。如果未被授予,则请求这些权限。

if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
        || ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_PERMISSION);
}

这里用到的 ContextCompat.checkSelfPermission 方法返回一个整数,表示指定权限是否被授予。如果返回值为 PackageManager.PERMISSION_GRANTED,则表示权限已被授予;否则表示未被授予。

如果任何一个权限未被授予,则通过 ActivityCompat.requestPermissions 方法请求这些权限。请求码 REQUEST_CAMERA_PERMISSION 用于在权限请求结果中识别这个请求。

处理权限请求结果

当用户响应权限请求时,系统会调用 onRequestPermissionsResult 方法。在这里处理用户的响应。

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case REQUEST_CAMERA_PERMISSION:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限授予,执行相应操作
            } else {
                // 权限拒绝,提示用户
                Toast.makeText(this, "相机权限被拒绝", Toast.LENGTH_SHORT).show();
            }
    }
}

在这个方法中,我们检查请求码是否为 REQUEST_CAMERA_PERMISSION,然后检查 grantResults 数组,确认权限是否被授予。如果权限被拒绝,我们通过 Toast 提示用户权限被拒绝。

2.处理图片

从相册中选择图片

public void chooseFromAlbum(View view) {
    // 创建一个 Intent 对象,用于启动系统的图片选择器
    Intent intent = new Intent("android.intent.action.GET_CONTENT");
    // 设置选择器类型为图片
    intent.setType("image/*");
    // 启动图片选择器,并传入请求码 CHOOSE_PHOTO,等待返回结果
    startActivityForResult(intent, CHOOSE_PHOTO);
}

处理选择图片的结果

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case TAKE_PHOTO:
            // 拍照
        case CHOOSE_PHOTO:
            if (resultCode == RESULT_OK) {
                if (Build.VERSION.SDK_INT >= 19) {
                    // 针对安卓 4.4 及以上系统处理图片
                    handleImageOnKitKat(data);
                } else {
                    // 针对低版本安卓系统处理图片
                }
            }
            break;
        default:
            break;
    }
}

处理图片

@TargetApi(19)
private void handleImageOnKitKat(Intent data) {
    String imagePath = null;
    // 获取用户选择的图片的 URI
    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())) {
            // 如果是媒体类型的 Document Uri,解析出数字 ID
            String id = docId.split(":")[1];
            String selection = MediaStore.Images.Media._ID + "=" + id;
            // 获取图片的真实路径
            imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
        } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
            // 如果是下载类型的 Document Uri,解析出文件 ID
            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);
}

获取图片真实路径

@SuppressLint("Range")
private String getImagePath(Uri uri, String selection) {
    String path = null;
    // 通过内容解析器查询指定 URI 的数据
    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;
}

四、播放音乐

image-20240801121942815

1.申请权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int REQUEST_PERMISSION_CODE = 1; // 权限请求代码
    private MediaPlayer mediaPlayer = new MediaPlayer(); // 媒体播放器实例

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); // 设置布局文件

        // 初始化按钮并设置点击监听器
        Button btn1 = findViewById(R.id.btn_start);
        Button btn2 = findViewById(R.id.btn_pause);
        Button btn3 = findViewById(R.id.btn_stop);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
        btn3.setOnClickListener(this);

        // 检查是否具有读取外部存储的权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            // 请求权限
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_CODE);
        } else {
            // 已有权限,初始化媒体播放器
            initMediaPlayer();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSION_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限被授予,初始化媒体播放器
                initMediaPlayer();
            } else {
                // 权限被拒绝,显示提示并关闭应用
                Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }
}

2.处理音乐

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int REQUEST_PERMISSION_CODE = 1; // 权限请求代码
    private MediaPlayer mediaPlayer = new MediaPlayer(); // 媒体播放器实例

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); // 设置布局文件

        // 初始化按钮并设置点击监听器
        Button btn1 = findViewById(R.id.btn_start);
        Button btn2 = findViewById(R.id.btn_pause);
        Button btn3 = findViewById(R.id.btn_stop);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
        btn3.setOnClickListener(this);

        // 检查是否具有读取外部存储的权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            // 请求权限
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_CODE);
        } else {
            // 已有权限,初始化媒体播放器
            initMediaPlayer();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSION_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限被授予,初始化媒体播放器
                initMediaPlayer();
            } else {
                // 权限被拒绝,显示提示并关闭应用
                Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }

    private void initMediaPlayer() {
        // 获取音乐文件的路径
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), "music.map3");
        
        // 检查文件是否存在
        if (!file.exists()) {
            Toast.makeText(this, "文件不存在: " + file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            // 设置数据源并准备播放器
            mediaPlayer.setDataSource(file.getPath());
            mediaPlayer.prepare();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_start) {
            if (!mediaPlayer.isPlaying()) {
                mediaPlayer.start(); // 开始
            }
        } else if (v.getId() == R.id.btn_pause) {
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.pause(); // 暂停
            }
        } else if (v.getId() == R.id.btn_stop) {
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.reset(); // 停止
                initMediaPlayer();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 确保在销毁活动时释放媒体播放器资源
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
        }
    }
}

MediaPlayer

MediaPlayer 是 Android 提供的一个类,用于播放各种音频和视频文件。你可以用它来播放本地文件、网络流媒体或资源文件。下面是一些基本的用法:

  1. 创建 MediaPlayer 实例:

    MediaPlayer mediaPlayer = new MediaPlayer();
    
  2. 设置数据源: 你可以使用本地文件路径、资源 ID 或网络 URL 作为数据源。

    // 设置本地资源文件
    mediaPlayer.setDataSource(context, Uri.parse("file:///path/to/your/audio/file.mp3"));
    
    // 设置网络流媒体
    mediaPlayer.setDataSource("http://www.example.com/audiofile.mp3");
    
    // 设置资源 ID(例如 raw 文件夹中的文件)
    mediaPlayer = MediaPlayer.create(this, R.raw.music);
    
  3. 准备和开始播放:

    mediaPlayer = MediaPlayer.create(this, R.raw.music);
    // 如果使用的是本地文件或资源 ID
    
    mediaPlayer.prepareAsync(); // 如果使用的是网络流媒体
    mediaPlayer.start();
    
    1. 控制播放: 你可以暂停、继续或停止播放。
  mediaPlayer.pause();
  mediaPlayer.start();
  mediaPlayer.stop();
  1. 释放资源: 播放结束后或不再需要时,要释放资源。

    mediaPlayer.release();
    
  2. 设置监听器: 你可以设置播放完成、错误等事件的监听器。

    mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            // 播放完成
        }
    });
    
    mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
        @Override
        public boolean onError(MediaPlayer mp, int what, int extra) {
            // 处理错误
            return false;
        }
    });
    

使用 MediaPlayer 的时候,确保在合适的生命周期阶段(如 onPauseonResume)管理其状态,避免资源泄漏。

五、播放视频

image-20240801121909033

VideoView 是 Android 提供的一个控件,用于在应用中播放视频。以下是 VideoView 常用的方法和属性的介绍:

方法

  1. setVideoPath(String path)
    • 设置视频的路径。可以是一个文件路径、URL 或资源路径。
    • 示例: videoView.setVideoPath("android.resource://" + getPackageName() + "/" + R.raw.video_file);
  2. setVideoURI(Uri uri)
    • 设置视频的 URI(统一资源标识符),这可以是文件 URI、内容 URI 或网络 URI。
    • 示例: videoView.setVideoURI(Uri.parse("http://www.example.com/video.mp4"));
  3. start()
    • 开始播放视频。如果视频已经在播放,调用此方法会重新开始播放视频。
    • 示例: videoView.start();
  4. pause()
    • 暂停视频播放。如果视频当前正在播放,调用此方法会暂停播放。
    • 示例: videoView.pause();
  5. stopPlayback()
    • 停止视频播放并释放资源。调用此方法后,视频将停止播放,并且需要重新设置视频路径或 URI。
    • 示例: videoView.stopPlayback();
  6. seekTo(int msec)
    • 将视频进度跳转到指定的时间位置,单位是毫秒。
    • 示例: videoView.seekTo(30000); // 跳转到 30 秒处
  7. getCurrentPosition()
    • 获取视频的当前播放位置,单位是毫秒。
    • 示例: int currentPosition = videoView.getCurrentPosition();
  8. getDuration()
    • 获取视频的总时长,单位是毫秒。
    • 示例: int duration = videoView.getDuration();
  9. isPlaying()
    • 检查视频是否正在播放。返回 true 如果视频正在播放,返回 false 如果视频处于暂停状态或未播放。
    • 示例: boolean isPlaying = videoView.isPlaying();

属性

  • setMediaController(MediaController controller)

    • 设置媒体控制器,用于提供播放、暂停、进度条等控制界面。可以使用默认的 MediaController 或自定义控制器。
    • 示例:
      MediaController mediaController = new MediaController(this);
      videoView.setMediaController(mediaController);
      mediaController.setAnchorView(videoView);
      
  • setKeepScreenOn(boolean keepScreenOn)

    • 设置是否保持屏幕常亮。可以用来防止视频播放时屏幕变暗。
    • 示例: `videoView.setKeepScreenOn(true);

示例

使用资源文件中的资源,不需要再申请权限

public class SecondActivity extends AppCompatActivity implements View.OnClickListener {

    // VideoView 用于播放视频
    private VideoView videoView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        videoView = findViewById(R.id.video_view);

        Button btn1 = findViewById(R.id.btn_start2);
        Button btn2 = findViewById(R.id.btn_pause2);
        Button btn3 = findViewById(R.id.btn_stop2);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
        btn3.setOnClickListener(this);

        // 初始化视频路径
        initVideoPath();
    }

    // 设置视频的路径
    private void initVideoPath() {
        // 使用资源文件中的视频路径
        videoView.setVideoPath("android.resource://" + getPackageName() + "/" + R.raw.xbox);
    }

    @Override
    public void onClick(View v) {
        // 根据点击的按钮 ID 执行相应的操作
        if (v.getId() == R.id.btn_start2) {
            // 播放视频
            if (!videoView.isPlaying())
                videoView.start();
        } else if (v.getId() == R.id.btn_pause2) {
            // 暂停视频播放
            if (videoView.isPlaying())
                videoView.pause();
        } else if (v.getId() == R.id.btn_stop2) {
            // 停止视频播放并重新设置视频路径
            if (videoView.isPlaying()) {
                videoView.stopPlayback();
                initVideoPath();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 当活动销毁时,暂停视频播放
        if (videoView != null)
            videoView.suspend();
    }
}


感谢您的阅读
如有错误烦请指正


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1966611.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

反应力场lammps和reaxff,再加上智能计算模拟,你恐怕没见过这种绝妙组合

“第一性原理分子动力学机器学习”三位一体的综合手段&#xff0c;已经成为模拟计算的一个前沿方向&#xff0c;为解决传统计算化学方法面临的挑战提供了新的解决方案。国内外已有科研团队在深化第一性原理与分子动力学的研究与应用拓展&#xff0c;利用机器学习优化大规模计算…

yolov5训练的pt模型,转换为rknn并部署在瑞芯微RK3588开发板

一、下载源码 在GitHub中搜索并下载yolov5的源代码&#xff0c;然后放在自己部署好的项目环境。 直接用下面的命令即可部署适合yolov5的环境&#xff0c;至于缺什么库&#xff0c;什么版本不对的可以百度查一下。 pt模型转onnx模型 我这里已经把源码下载好并加载好了我的虚拟…

Redis CLI常用命令

Redis CLI常用命令 1. 设置和获取数据 SET Redis 中的数据是以键值对的形式存储的&#xff0c;所以需要指定一个键和一个值&#xff0c;键和值用空格隔开&#xff1b; Redis 中默认使用字符串存储数据&#xff1b; 3. 删除键 DEL 4. 判断键是否存在 exists 5. 查找键 KEYS 查…

环境参数自动调节设备:智能生活的绿色守护者

在当今社会&#xff0c;随着科技的飞速发展和人们生活水平的不断提升&#xff0c;对居住环境的要求也日益增高。一个舒适、健康的生活空间&#xff0c;不仅关乎温度、湿度、空气质量等基本环境因素&#xff0c;还涉及到光照、噪音控制等多个维度。为了满足这些多元化的需求&…

【Docomo】5G

我们想向您介绍第五代移动通信系统“5G”。 5G 什么是5G&#xff1f;支持5G的技术什么是 5G SA&#xff08;独立&#xff09;&#xff1f;实现高速率、大容量的5G新频段Docomo的“瞬时5G”使用三个宽广的新频段 什么是5G&#xff1f; 5G&#xff08;第五代移动通信系统&#x…

技术设计评审的重要性及实战指南:让每一行代码都熠熠生辉

在这个快速迭代的软件开发时代,技术设计评审(Technical Design Review, 简称TDR)不仅是项目成功的关键一环,更是每位程序员职业生涯中不可或缺的“导航灯”。它如同一面镜子,让团队能够清晰地看到设计的全貌,及时修正偏差,确保项目朝着既定的目标稳健前行。关注【程序员…

服务器给根目录扩展磁盘(不使用lvm逻辑券)两种方式

因业务需求磁盘存储增加现在需要给/目录进行扩容&#xff0c;因为是云服务器直接在原有的磁盘增加了100G空间现在把新增的100G扩容到/目录分区。 有两种方法一种是使用growpart 工具扩容&#xff0c;一种是使用fdisk命令 使用growpart工具扩容 yum install -y cloud-utils-grow…

为什么我工作 10 年后转行当程序员?逆袭翻盘!

今天文章的主人公暂且称他为 A 君。不过 A 君有点特别&#xff0c;非科班&#xff0c;工作 10 年后才转行 iOS 程序员。今年 36 岁&#xff0c;目前在某行业头部企业任职前端负责人&#xff0c;管理 40 人的前端团队。 废话不多说&#xff0c;我们开始 A 君&#xff08;为了描…

并发编程工具集——Future(二十七)

简介&#xff1a; 创建完线程池&#xff0c;该如何使用获取任务的执行结果&#xff08;execute() 方法没有返回值&#xff09;。 如何获取任务执行结果 Java 通过 ThreadPoolExecutor 提供的 3 个 submit() 方法和 1 个 FutureTask 工具类来支持获得任务执行结果的需求。三个su…

2024年哪些充电宝建议买?最建议买的四款充电宝排行榜!

在 2024 年&#xff0c;充电宝依然是我们生活中不可或缺的电子伴侣。然而&#xff0c;面对市场上众多的充电宝品牌和型号&#xff0c;要挑选到一款既实用又安全可靠的产品并非易事。充电宝的安全性至关重要&#xff0c;毕竟它与我们随身携带的电子设备紧密相连。劣质的充电宝可…

C++软件开发值得推荐的十大高效软件分析工具

目录 1、概述 2、高效软件工具介绍 2.1、窗口查看工具SPY++ 2.2、Dependency Walker 2.3、剪切板查看工具Clipbrd 2.4、GDI对象查看工具GDIView 2.5、Process Explorer 2.6、Prcoess Monitor 2.7、API Monitor 2.8、调试器Windbg 2.9、反汇编工具IDA 2.10、抓包工…

[240801] 类 C 语言 C3 是一种进化,而不是一场革命 | 趣文: find + mkdir 是图灵完备

目录 类 C 语言 C3 是一种进化&#xff0c;而不是一场革命C3 编程语言特征C3 设计原则安装 C3 编程语言第一个 C3 项目 趣文&#xff1a;find mkdir 是图灵完备 类 C 语言 C3 是一种进化&#xff0c;而不是一场革命 C3 是基于 C 的编程语言&#xff0c;它是 C 的一种演变&…

2024最新全开源付费进群系统源码二开修复版 支持易支付

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 全开源付费进群系统源码&#xff0c;开源无加密无授权&#xff0c;优化电脑端访问布局&#xff0c;支持dai理&#xff0c;对接易支付通道&#xff0c;dai理可以配置自己易支付接口&am…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第四篇 嵌入式Linux系统移植篇-第七十二章 内核配置屏幕驱动

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

ChatGPT:数据库不符合第二范式示例

ChatGPT&#xff1a;数据库不符合第二范式示例 这张图片为什么不符合数据库第二范式 这个表格不符合数据库第二范式&#xff08;2NF&#xff09;的原因如下&#xff1a; 1. 数据库第二范式&#xff08;2NF&#xff09;定义 第二范式要求一个数据库表格在满足第一范式&#xf…

【Hot100】LeetCode—169. 多数元素

目录 题目1- 思路2- 实现⭐169. 多数元素——题解思路 3- ACM 实现 题目 原题连接&#xff1a;169. 多数元素 1- 思路 定义两个变量 一个是 count&#xff1a;维护当前元素的出现次数一个是 ret &#xff1a;维护当前元素 思路 遍历整个数组**①如果 count 0 **&#xff…

了解对称加密与密钥协商技术

1.对称加密算法 加密的理论基础是替代和换位。替代主要用于扰乱&#xff0c;使用不同的位、字符或字符分组来替换原来的位、字符或字符分组。换位主要用于扩散&#xff0c;并不使用不同的文本来替换原来的文本&#xff0c;而是对原有的值进行置换&#xff0c;即重新排列原来的位…

Django开发企业官网

1.在本地创建一个website的文件夹&#xff0c;在vc里面打开文件夹 2.创建虚拟环境和安装django 终端执行命令 python -m venv venv&#xff08;创建一个虚拟文件名字叫venv&#xff09; venv\Scripts\activate(启动虚拟环境) pip install django&#xff08;安装django&#x…

CRC的手算过程——MODBUS

软件计算结果&#xff1a; 原理参考下面的文章&#xff1a; https://www.cnblogs.com/esestt/archive/2007/08/09/848856.html https://blog.csdn.net/weixin_44256803/article/details/105805628 https://blog.csdn.net/d_leo/article/details/73572373 手算过程如下&#x…

Day27 | 贪心算法 452. 用最少数量的箭引爆气球 435. 无重叠区间 763.划分字母区间

语言 Java 452. 用最少数量的箭引爆气球 用最少数量的箭引爆气球 题目 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐…