主頁 > 移動端開發 > Android初體驗——運用手機多媒體

Android初體驗——運用手機多媒體

2021-08-13 07:52:39 移動端開發

使用通知

當某個應用程式希望向用戶發出一些提示資訊,而該應用程式又不在前臺運行時,就可以借助通知來實作,發出一條通知后,手機最上方的狀態欄中會顯示一個通知的圖示,下拉狀態欄后可以看到通知的詳細內容,

基本用法

構建一個通知

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button sendNotice = (Button)findViewById(R.id.send_notice);
        sendNotice.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.send_notice:
                //通過Context的getSystemService方法獲得NotificationManager 可以對通知進行管理
                //傳入的引數用于確定獲取系統的哪個服務
                NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
                //需要Builder構造器來創建Notification物件
                Notification notification = new NotificationCompat.Builder(MainActivity.this,"my_channel_01")
                        //指定通知的標題內容 下拉狀態欄就可以看到這部分內容
                        .setContentTitle("This is content title")
                        //指定通知正文內容 下拉狀態欄就可以看到這部分內容
                        .setContentText("This is content text")
                        //用于指定通知被創建的時間 以毫秒為單位 
                        .setWhen(System.currentTimeMillis())
                        //設定通知的小圖示 只能使用純alpha圖層的圖片進行設定 小圖示會顯示在系統狀態欄上
                        .setSmallIcon(R.mipmap.ic_launcher)
                        //用于設定通知的大圖示
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        .build();
                //讓通知訊息顯示出來  第一個引數是id 第二個是Notification物件
                manager.notify(1, notification);
                break;
            default:
                break;
        }
    }
}

實作通知的點擊效果,

使用PendingIntent.從名字上看起來就和Intent有些類似,它們之間也確實存在著不少共同點,比如它們都可以去指明某一個“意圖”,都可以用于啟動活動、啟動服務以及發送廣播等,不同的是,Intent更加傾向于去立即執行某個動作,而PendingIntent 更加傾向于在某個合適的時機去執行某個動作,所以,也可以把PendingIntent簡單地理解為延遲執行的Intent,

PendingIntent的用法同樣很簡單,它主要提供了幾個靜態方法用于獲取PendingIntent的實體,可以根據需求來選擇是使用getActivity()方法、getBroadcast()方法, 還是getService()方法,這幾個方法接收的物件都相同,第一個引數是context,第二個一般傳入0,第三個是一個Intent物件,可以通過這個物件構建出PendingIntent的“意圖”,第四個引數用于確定PendingIntent的行為,有FLAG_ONE_SHOT,FLAG_UPDATE_CURRENT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,通常情況下傳入0,

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.send_notice:
                //創建一個意圖 使用intent表達出我們的意圖
                Intent intent = new Intent(this, NotificationActivity.class);
                PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
                //通過Context的getSystemService方法獲得NotificationManager 可以對通知進行管理
                //傳入的引數用于確定獲取系統的哪個服務
                NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
                //需要Builder構造器來創建Notification物件
                Notification notification = new NotificationCompat.Builder(MainActivity.this,"my_channel_01")
                        //指定通知的標題內容 下拉狀態欄就可以看到這部分內容
                        .setContentTitle("This is content title")
                        //指定通知正文內容 下拉狀態欄就可以看到這部分內容
                        .setContentText("This is content text")
                        //用于指定通知被創建的時間 以毫秒為單位
                        .setWhen(System.currentTimeMillis())
                        //設定通知的小圖示 只能使用純alpha圖層的圖片進行設定 小圖示會顯示在系統狀態欄上
                        .setSmallIcon(R.mipmap.ic_launcher)
                        //用于設定通知的大圖示
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        //通過pendingIntent構建出一個延遲的意圖
                        .setContentIntent(pendingIntent)
                        .build();
                //讓通知訊息顯示出來  第一個引數是id 第二個是Notification物件
                manager.notify(1, notification);
                break;
            default:
                break;
        }
    }

點擊后狀態欄通知消失

  • 顯式的呼叫NotificationManager的cancel方法
                NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
                //點擊后狀態欄通知消失 想取消哪條通知傳入該通知id
                manager.cancel(1);
  • 在NotificationCompat.Builder后連綴setAutoCancel
                Notification notification = new NotificationCompat.Builder(MainActivity.this,"my_channel_01")
                       ...
                        //點擊后狀態欄通知消失
                        .setAutoCancel(true)
                        .build();

通知的進階

設定收到訊息的音頻

                Notification notification = new NotificationCompat.Builder(MainActivity.this,"my_channel_01")
                        ...
                        //指定收到訊息的音頻 接受一個Uri引數
                        .setSound(Uri.fromFile(new File("/system/media/audio/ringtones/Luna.ogg")))
                        .build();

設定收到訊息時震動

                Notification notification = new NotificationCompat.Builder(MainActivity.this,"my_channel_01")
                        ...
                        //指定收到時手機的震動 傳入一個長整型的陣列 用于設定手機靜止和震動的時長 一毫秒為單位
                        //單數下標代表震動的時長 雙數代表靜止的時長
                        .setVibrate(new long[] {0, 1000, 1000, 1000, 1000, 1000})
                        .build();

控制震動需要宣告權限

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

設定收到訊息時LED燈

三個引數,第一個用于指定顏色,第二個指定亮起的時長,第三個指定暗的時長,

                Notification notification = new NotificationCompat.Builder(MainActivity.this,"my_channel_01")
                ...
                        //指定收到訊息時的LED燈
                        .setLights(Color.WHITE, 1000, 1000)
                        .build();

默認效果

可以根據當前手機環境來決定播放什么鈴聲,以及如何震動

                Notification notification = new NotificationCompat.Builder(MainActivity.this,"my_channel_01")
                       ...
                        //默認模式
                        .setDefaults(NotificationCompat.DEFAULT_ALL)
                        .build();

創建渠道并設定重要性

在 Android 8.0 及更高版本上使用通知,必須先通過向 createNotificationChannel() 傳遞 NotificationChannel 的實體在系統中注冊應用的通知渠道

                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                    NotificationChannel notificationChannel=new NotificationChannel("my_channel_01","R.string.channel_name", NotificationManager.IMPORTANCE_DEFAULT);
                    //notificationChannel.setDescription(CHANNEL_DESCRIPTION);
                    NotificationManager notificationManager=getSystemService(NotificationManager.class);
                    notificationManager.createNotificationChannel(notificationChannel);
                }

通知的高級功能

先來看看setStyle()方法,這個方法允許我們構建出富文本的通知內容,也就是說通知中不光可以有文字和圖示,還可以包含更多的東西,setStyle()方法接收 一個NotificationCompat.Style引數,這個引數就是用來構建具體的富文本資訊的,如長文字、圖片等,

                Notification notification = new NotificationCompat.Builder(MainActivity.this,"my_channel_01")
                        ...
                        //添加富文本資訊 創建一個NotificationCompat.BigTextStyle物件 這個物件用于封裝長文字資訊 呼叫bigText并將文字內容傳入即可
                        .setStyle(new NotificationCompat.BigTextStyle().bigText("街頭的血液等待著迸發,屹立在懸崖邊盛開的紅花 "))
                        //顯示一張大圖片
                        .setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(), R.drawable.big_image)))
                        .build();

設定訊息的重要程度

                Notification notification = new NotificationCompat.Builder(MainActivity.this,"my_channel_01")
                        ...
                        //設定訊息的重要程度
                        .setPriority(NotificationCompat.PRIORITY_MAX)
                        .build();

呼叫攝像頭和相冊

呼叫攝像頭拍照

public class MainActivity extends AppCompatActivity {
    public static final int TAKE_PHOTO = 1;
    private ImageView picture;
    private Uri imageUri;

    @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 view) {
                //創建File物件 用于存盤拍照后的圖片
                //將圖片命名為"output_image.jpg" 并將它存放在手機SD卡的應用關聯快取目錄下  getExternalCacheDir()可以得到這個目錄
                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) {
                    //呼叫FileProvider.getUriForFile方法將File物件轉換成一個封裝過的Uri物件
                    //接收三個引數,第一個Context物件 第二個可以是任意唯一的字串 第三個時創建的File物件
                    //對資料進行保護
                    imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalbumtest.fileprovider", outputImage);

                }else {
                    imageUri = Uri.fromFile(outputImage);
                }
                //啟動相機程式 指定action 隱式Intent
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                //putExtra指定圖片的輸出地址 填入Uri物件
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                //啟動活動
                startActivityForResult(intent, TAKE_PHOTO);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case TAKE_PHOTO:
                if (resultCode == RESULT_OK) {
                    try {
                        //如果拍照成功 呼叫BitmapFactory.decodeStream將照片決議成Bitmap物件
                        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                        picture.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                break;
            default:
                break;

        }
    }
}

應用關聯快取目錄就是指SD卡中專門用于存放當前應用快取資料的位置,呼叫getExternalCacheDir()方法可以得到這個目錄,具體的路徑是/sdcard/Android/data/<package name>/cache
使用應用關聯緩目錄來存放圖片是因為從Android 6.0系統開始,讀寫SD卡被列為了危險權限,如果將圖片存放在SD卡的任何其他目錄,都要進行運行時權限處理才行,而使用應用關聯目錄則可以跳過這一步,

接著會進行一個判斷,如果運行設備的系統版本低于Android 7.0,就呼叫Uri的fromFile()方法將File物件轉換成Uri物件,這個Uri物件標識著output_ jimage.jpg 這張圖片的本地真實路徑,否則,就呼叫FileProvider 的getUriForFile()方法將File 物件轉換成一個封裝過的Uri物件,getUriForFile()方法接收3個引數,第一個引數要求傳人Context物件,第二個引數可以是任意唯一的字串, 第三個引數則是我們剛剛創建的File物件

之所以要進行這樣一層轉換,是因為從Android7.0系統開始,直接使用本地真實路徑的Uri被認為是不安全的,會拋出一個FileUriExposedException 例外,而FileProvider 則是一種特殊的內容提供器,它使用了和內容提供器類似的機制來對資料進行保護,可以選擇性地將封裝過的Uri共享給外部,從而提高了應用的安全性,

接下來構建出了一個Intent物件,并將這個Intent的action指定為android.media.action. IMAGE_ CAPTURE, 再呼叫Intent的putExtra()方法指定圖片的輸出地址,這里填入剛剛得到的Uri物件,最后呼叫startActivityForResult()來啟動活動,由于我們使用的是個隱式Intent,系統會找出能夠回應這個Intent 的活動去啟動,這樣照相機程式就會被打開,拍下的照片將會輸出到output_image.jpg 中,
注意,剛才我們是使用startActivityForResult()來啟 動活動的,因此拍完照后會有結果回傳到onActivityResult()方法中,如果發現拍照成功,就可以呼叫BitmapFactory 的decodeStream( )方法將output_ image.jpg這張照片決議成Bitmap物件,然后把它設定到Image-View中顯示出來,

在AndroidManifest.xml中對內容提供器進行注冊了,如下所示:

    <application
        ...
        <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>
		...
    </application>

其中,android:name 屬性的值是固定的,android: authorities屬性的值必須要和剛才FileProvider.getUriForFile()方法中的第二個引數一致,另外,這里還在<provider>標簽的內部使用<meta- data>來指定Uri的共享路徑,并參考了一個@xml/file_ paths 資源,
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>

其中,external-path 就是用來指定Uri共享的,name 屬性的值可以隨便填path屬性的值表示共享的具體路徑,這里設定/就表示將整個SD卡進行共享,當然你也可以僅共享我們存放output_image.jpg 這張圖片的路徑,
另外還有一點要注意,在Android 4.4系統之前,訪問SD卡的應用關聯目錄也是要宣告權限的,從4.4系統開始不再需要權限宣告,那么我們為了能夠兼容老版本系統的手機,還需要在AndroidManifest.xml中宣告一下訪問SD卡的權限:

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

從相冊中選擇照片

public class MainActivity extends AppCompatActivity {
    public static final int TAKE_PHOTO = 1;
    private ImageView picture;
    private Uri imageUri;
    private static final int CHOOSE_PHOTO = 2;

    @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 view) {
                //創建File物件 用于存盤拍照后的圖片
                //將圖片命名為"output_image.jpg" 并將它存放在手機SD卡的應用關聯快取目錄下  getExternalCacheDir()可以得到這個目錄
                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) {
                    //呼叫FileProvider.getUriForFile方法將File物件轉換成一個封裝過的Uri物件
                    //接收三個引數,第一個Context物件 第二個可以是任意唯一的字串 第三個時創建的File物件
                    //對資料進行保護
                    imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalbumtest.fileprovider", outputImage);

                }else {
                    imageUri = Uri.fromFile(outputImage);
                }
                //啟動相機程式 指定action 隱式Intent
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                //putExtra指定圖片的輸出地址 填入Uri物件
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                //啟動活動
                startActivityForResult(intent, TAKE_PHOTO);
            }
        });

        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}, 1);
                }else {
                    //打開相冊
                    openAlbum();
                }
            }


        });
    }

    //打開相冊
    private void openAlbum() {
        //隱式intent
        Intent intent = new Intent("android.intent.action.GET_CONTENT");
        intent.setType("image/*");
        //打開相冊 第二個引數表示選擇完圖片回到onActivityResult方法時 會進入CHOOSE_PHOTO的case來處理圖片
        startActivityForResult(intent, CHOOSE_PHOTO);
    }

    //動態設定權限的選擇欄
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, 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:
        }
    }

    //活動結果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case TAKE_PHOTO:
                if (resultCode == RESULT_OK) {
                    try {
                        //如果拍照成功 呼叫BitmapFactory.decodeStream將照片決議成Bitmap物件
                        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 >= Build.VERSION_CODES.KITKAT) {
                        //4.4及以上的系統 Android系統從4.4版本開始,選取相冊中的圖片不在回傳圖片真實的Uri 而是一個封裝過的 需要決議
                        handleImageOnKitKat(data);
                    }else {
                        handleImageBeforeKitKat(data);
                    }
                    break;
                }
            default:
                break;

        }
    }

    private void handleImageBeforeKitKat(Intent data) {
        Uri uri = data.getData();
        //獲取真實的路徑
        String imagePath = getImagePath(uri, null);
        //將圖片顯示到界面上
        displayImage(imagePath);
    }

    //將圖片顯示到界面上
    private void displayImage(String imagePath) {
        if(imagePath != null) {
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            picture.setImageBitmap(bitmap);
        }else {
            Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();
        }
    }

    private void handleImageOnKitKat(Intent data) {
        String imagePath = null;
        Uri uri = data.getData();
        if(DocumentsContract.isDocumentUri(this, uri)) {
            String docId = DocumentsContract.getDocumentId(uri);
            if("com.android.providers.media.documents".equals(uri.getAuthority())) {
                //決議出數字格式的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())) {
                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 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;
    }
}

可以看到,在Choose From Album按鈕的點擊事件里我們先是進行了一個運行時權限處理,動態申請WRITE_EXTERNAL_ STORAGE 這個危險權限,因為相冊中的照片都是存盤在SD卡上的,我們要從SD卡中讀取照片就需要申請這個權限,WRITE_EXTERNAL_ STORAGE表示同時授予程式對SD卡讀和寫的能力

當用戶授權了權限申請之后會呼叫openAlbum()方法,這里我們先是構建出了一個Intent物件,并將它的action指定為android. intent . action . GET_ CONTENT, 接著給這個Intent物件設定引數,然后調startActivityForResult() 方法就可以打開相冊程式選擇照片了,注意在呼叫startActivityForResult()方法的時候, 我們給第二個引數傳人的值變成了CHOOSE_ PHOTO,這樣當從相冊選擇完圖片回到onActivityResult()方法時,就會進人CHOOSE_ PHOTO 的case來處理圖片,

為了兼容新老版本的手機,我們做了一個判斷,如果是4.4及以上系統的手機就呼叫handleImage0nKitKat()方法來處理圖片,否則就呼叫handleImageBeforeKitKat()方法來處理圖片,因為Android系統從4.4 版本開始,選取相冊中的圖片不再回傳圖片真實的Uri了,而是個封裝過的Uri,因此如果是4.4版本以上的手機就需要對這個Uri進行決議才行,

那么handleImage0nKitKat()方法中的邏輯就基本是如何決議這個封裝過的Uri了,這里有好幾種判斷情況,如果回傳的Uri是document型別的話,那就取出document id進行處理,如果不是的話,那就使用普通的方式處理,另外,如果Uri的authority是media格式的話, documentid還需要再進行一次決議,要通過字串分割的方式取出后半部分才能得到真正的數字id,取出的id用于構建新的Uri和條件陳述句,然后把這些值作為引數傳人到getImagePath()方法當中, 就可以獲取到圖片的真實路徑了,拿到圖片的路徑之后,再呼叫displayImage()方法將圖片顯示到界面上,

播放多媒體檔案

播放音頻

Android中播放音頻檔案一般都是使用MediaPlayer類來實作,它對多種格式的音頻檔案提供了非常全面的控制方法,下面列出比較常用的控制方法
在這里插入圖片描述
MediaPlayer的作業流程,首先創建出一個MediaPlayer物件,呼叫setDataSource()方法來設定音頻檔案的路徑,呼叫prepare()方法使MediaPlayer進入準備狀態,呼叫start()就可以開始播放音頻,呼叫pause()方法會暫停播放,reset()會停止播放,

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    //創建MediaPlayer實體
    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);
        Button stop = (Button) findViewById(R.id.stop);
        Button pause = (Button)findViewById(R.id.pause);
        play.setOnClickListener(this);
        stop.setOnClickListener(this);
        pause.setOnClickListener(this);
        //運行時權限處理  訪問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}, 1);
        }else {
            //初始化MediaPlayer
            initMediaPlayer();
        }
    }

    private void initMediaPlayer() {
        try {
            //通過File物件來指定音頻檔案的路徑
            File file = new File(Environment.getExternalStorageDirectory(), "music.mp3");
            //指定音頻檔案的路徑
            mediaPlayer.setDataSource(file.getPath());
            //讓MediaPlayer進入到準備狀態
            mediaPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //請求許可的結果
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 1:
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //初始化MediaPlayer物件
                    initMediaPlayer();
                }else {
                    //拒絕后關閉程式
                    Toast.makeText(this, "拒絕權限將無法使用程式", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.play:
                if(!mediaPlayer.isPlaying()) {
                    //開始播放
                    mediaPlayer.start();
                }
                break;
            case R.id.pause:
                if(mediaPlayer.isPlaying()) {
                    //暫停播放
                    mediaPlayer.pause();
                }
                break;
            case R.id.stop:
                if(mediaPlayer.isPlaying()) {
                    //停止播放
                    mediaPlayer.reset();
                    initMediaPlayer();
                }
                break;
            default:
                break;

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
        }
    }
}

可以看到,在類初始化的時候我們就先創建了一個MediaPlayer的實體,然后在onCreate()方法中進行了運行時權限處理,動態申請WRITE_ EXTERNAL_ STORAGE 權限,這是由于待會我們會在SD卡中放置一個音頻檔案,程式為了播放這個音頻檔案必須擁有訪問SD卡的權限才行,

注意,在onRequestPermissionsResult()方法中,如果用戶拒絕了權限申請,那么就呼叫finish()方法將程式直接關掉,因為如果沒有SD卡的訪問權限,我們這個程式將什么都干不了,

用戶同意授權之后就會呼叫initMediaPlayer()方法為MediaPlayer 物件進行初始化操作,在initMediaPlayer()方法中,首先是通過創建一個File物件來指定音頻檔案的路徑,從這里可以看出,我們需要事先在SD卡的根目錄下放置一個名為music.mp3的音頻檔案,后面依次呼叫了setDataSource ()方法和prepare()方法,為MediaPlayer做好了播放前的準備,

接下來我們看一下各個按鈕的點擊事件中的代碼,當點擊Play按鈕時會進行判斷,如果當前MediaPlayer沒有正在播放音頻,則呼叫start()方法開始播放,當點擊Pause按鈕時會判斷,如果當前MediaPlayer正在播放音頻,則呼叫pause()方法暫停播放,當點擊Stop按鈕時會判斷,如果當前MediaPlayer正在播放音頻,則呼叫reset()方法將MediaPlayer重置為剛剛創建的狀態,然后重新呼叫一-遍initMediaPlayer()方法,

最后在onDestroy()方法中,我們還需要分別呼叫stop()方法和 release()方法,將與MediaPlayer相關的資源釋放掉,

添加權限

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

播放視頻

主要使用VideoView類來實作,這個類將視頻的顯示與控制集于一身,
在這里插入圖片描述
這部分代碼相信你理解起來會很輕松,因為它和前面播放音頻的代碼非常類似,首先在onCreate()方法中同樣進行了一個運行時權限處理,因為視頻檔案將會放在SD卡上,當用戶同意授權了之后就會呼叫initVideoPath()方法來設定視頻檔案的路徑,這里我們需要事先在SD卡的根目錄下放置-一個名為movie.mp4的視頻檔案,
VideoView幫我們做了一個很好的封裝,背后使用的仍然是MediaPlayer來對視頻進行控制

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private VideoView videoView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        videoView = (VideoView) findViewById(R.id.video_view);
        Button play = (Button)findViewById(R.id.play);
        Button pause = (Button)findViewById(R.id.pause);
        Button replay = (Button)findViewById(R.id.replay);
        play.setOnClickListener(this);
        pause.setOnClickListener(this);
        replay.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 {
            //初始化MediaPlayer
            initVideoPath();
        }

    }

    private void initVideoPath() {
        File file = new File(Environment.getExternalStorageDirectory(), "video.mp4");
        //Toast.makeText(this, "lll",Toast.LENGTH_SHORT).show();
        //指定視頻檔案的路徑
        //videoView.setVideoPath(file.getPath());
        Log.d("TAG", file.getPath());
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 1:
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    initVideoPath();
                }else {
                    Toast.makeText(this, "拒絕權限將無法使用程式", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.play:
                if(!videoView.isPlaying()) {
                    //開始播放
                    videoView.start();
                }
                break;
            case R.id.pause:
                if(videoView.isPlaying()) {
                    //暫停播放
                    videoView.pause();
                }
                break;
            case R.id.replay:
                if(videoView.isPlaying()) {
                    //開始播放
                    videoView.resume();
                }
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(videoView != null) {
            videoView.suspend();
        }
    }

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/293386.html

標籤:其他

上一篇:Android免權限懸浮窗組件 - FloatingX

下一篇:【Android逆向】Android smali與java代碼介紹1

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more