Android学习笔记——手机多媒体运用

xiaoxiao2021-02-28  63

参考书籍:Android第一行代码(第二版).郭霖著

1、通知

Notification是Android中较有特色的功能,当应用程序希望向用户发出提示信息但不在前台运行时可使用(手机顶部显示通知图标,下拉后看到详细内容)。

(1)基本用法 可在活动(较少,一般进入后台才需要)、广播接收器、服务里创建。

步骤: a、获取NotificationManager实例:

NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); //调用Context的getSystemService()方法,参数用于确定获取系统哪个服务

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

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();//可在build()方法之前连缀任意多设置方法来创建丰富的Notification对象

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

c、调用NotificationManager的notify()方法显示通知。

manager.notify(1, notification);//第一个参数为id(保证为每个通知指定的都不同)

例:新建NotificationTest项目,修改布局文件,添加一个用于发送通知的按钮即可。修改主程序:

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 v) { switch (v.getId()){ case R.id.send_notice: NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); Notification notification = new NotificationCompat.Builder(this) .setContentTitle("This is content title") .setContentText("This is content text") .setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) .build(); manager.notify(1, notification); break; default: break; } } }

运行程序,点击按钮。

通知现在点击无反应。需使用PendingIntent(与Intent(更倾向于立即执行某个动作)类似,但更倾向于在某个合适的时机去执行某个动作)。它主要提供几个静态方法用于获取PendingIntent实例:getActivity()/getBroadcast()/getService()(参数相同:第一个是Context, 第二个一般用不到(传入0即可),第三个是Intent对象(可通过其构建PendingIntent的“意图”),第四个用于确定PendingIntent的行为(FLAG_ONE_SHOT/FLAG_NO_CREATE/FLAG_CANCLE_CURRENT/FLAG_UPDATE_CURRENT四种值可选,通常传入0))。

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

优化NotificationTest项目。需准备另一个活动NotificationActivity,布局文件notification_layout,修改其中代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="24sp" android:text="This is notification layout"/> </RelativeLayout>

修改MainActivity:

... public void onClick(View v) { switch (v.getId()){ case R.id.send_notice: **Intent intent = new Intent(this, NotificationActivity.class); PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);** NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); Notification notification = new NotificationCompat.Builder(this) .setContentTitle("This is content title") .setContentText("This is content text") .setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) **.setContentIntent(pi)** .build(); manager.notify(1, notification); break; default: break; } } ...

重新运行程序,点击按钮,点击通知,就会看到NotificationActivity活动界面。

此时发现系统状态上的通知图标没消失(代码中没取消)。两种方法可解决:NotificationCompat.Builder连缀setAutoCancel()方法(设置为true),显式调用NotificationManager的cancel()方法。第二种方法如下,在NotificationActivity中修改:

public class NotificationActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.notification_layout); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); manager.cancel(1);//参数为通知的id } }

运行程序:

(2)进阶技巧

NotificationCompat.Builder中提供了丰富的API。常用的:

setSound():在通知发出时播放一段音频。每个手机的/system/media/audio/ringtones目录下都有很多音频文件。例:

Notification notification = new NotificationCompat.Builder(this) ... .setSound(Uri.fromFile(new File("/system/media/audio/ringtones/Luna.ogg"))) .build();

setVibrate:让手机振动,接收长整型数组(设置手机静止和振动时长,毫秒,下标为0的值表静止时长,下标为1的表振动时长,下标为2表静止,依次交替)。例:

Notification notification = new NotificationCompat.Builder(this) ... .setVibrate(new long[]{0, 1000, 1000, 1000})//通知来到时立即振动1秒,后静止1秒,再振动1秒 .build();

控制振动需声明权限:

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

setLights:控制手机前置的LED灯。接收三个参数:指定LED灯颜色,指定亮起时长(毫秒),指定暗去时长(毫秒)。例:

Notification notification = new NotificationCompat.Builder(this) ... .setLights(Color.GREEN, 1000, 1000) .build();

以上方法具体参数需根据手机做适当修改。

也可直接使用通知的默认效果setDefaults。例:

Notification notification = new NotificationCompat.Builder(this) ... .setDefaults(NotificationCompat.DEFAULT_ALL) .build();

(3)高级功能

NotificationCompat.Builder中更强大的API方法。 setStyle():允许构建富文本的通知内容(除文字和图标外,还有更多元素,如长文字、图片等)。接收NotificationCompat.Style()参数,例:

Notification notification = new NotificationCompat.Builder(this) ... .seSyle(new NotificationCompat.BigTextStyle().bigTest("Learn how to buid notifications, send and sync data, and use voice actions.Get the official Android IDE and developer tools to build apps for Android.")) .build();

还可显示一张大图:

Notification notification = new NotificationCompat.Builder(this) ... .seSyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(), R.drawable.big_image))) .build();

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

Notification notification = new NotificationCompat.Builder(this) ... .sePriority(NotificationCompat.PRIORITY_MAX) .build();

运行效果如下:

2、摄像头和相册

(1)调用摄像头拍照

在应用程序里调用手机摄像头进行拍照。新建CameraAlbumTest项目,修改布局文件:

<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>

一个用于拍照,一个用于显示。修改MainActivity:

public class MainActivity 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对象,用于存储拍照后的图片, // 存放在手机SD卡的应用关联缓存目录(SD卡中专门用于存放当前应用缓存数据的位置)下,getExternalCacheDir()方法可得到 //具体路径是/sdcard/Android/data/<package name>/cache //Android6.0开始,读写SD卡被列为危险权限,如果存放在SD卡其他目录需进行运行时权限处理,使用此目录则不用 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) {//如果运行设备的系统版本等于或高于Android7.0 imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.jojo.cameraalbumtest.fileprovider", outputImage); //将File对象转换成一个封装过的Uri对象。三个参数:Context对象,任意唯一字符串,File对象 //Android7.0后,直接访问本地真实路径的Uri被认为不安全,会抛出异常, // 而FileProvider是一种特殊的内容提供器(使用和内容提供器类似的机制保护数据),可选择性地将封装过的Uri共享给外部,提高应用安全 } else { //如果运行设备的系统版本低于Android7.0,此Uri对象标识着output_image.jpg图片的本地真实路径 imageUri = Uri.fromFile(outputImage); } //启动相机程序 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//指定图片输出地址为刚刚得到的Uri对象 startActivityForResult(intent, TAKE_PHOTO); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode){ case TAKE_PHOTO: if (resultCode == RESULT_OK){ try { //将照片显示出来 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); picture.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } break; default: break; } }

还需注册内容提供器:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.jojo.cameraalbumtest"> <!--android4.4系统之前,访问SD卡的应用关联目录要声明权限--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity><!-- ATTENTION: This was auto-generated to add Google Play services to your project for App Indexing. See https://g.co/AppIndexing/AndroidStudio for more information. --> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> <!--其中,name属性固定,authorities值必须与FileProvider.getUriForFile()中第二个参数一致, 内部使用<meta-data>来指定Uri的共享路径,并引用了@xml/file_paths资源(res->New->Directory创建)--> <provider android:authorities="com.example.jojo.cameraalbumtest.fileprovider" android:name="android.support.v4.content.FileProvider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider> </application> </manifest>

在xml文件夹下创建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)从相册中选择照片

编辑activity_main.xml文件,添加一个选择照片的按钮,修改主程序:

public class MainActivity extends AppCompatActivity { ... public 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); picture = (ImageView) findViewById(R.id.picture); ... Button chooseFromAlbum = (Button)findViewById(R.id.choose_from_album); chooseFromAlbum.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ //相册中的照片都是存储在SD卡上的,需要申请运行时权限,WRITE_EXTERNAL_STORAGE是危险权限,表示同时授予程序对SD卡的读和写的能力 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); }else { openAlbum(); } } }); } private void openAlbum(){ Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent,CHOOSE_PHOTO);//打开相册 } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ openAlbum(); }else { Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show(); } break; default: } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode){ ... case CHOOSE_PHOTO: if (resultCode == RESULT_OK){ //判断手机系统版本号 if (Build.VERSION.SDK_INT >= 19){ //4.4及以上系统使用此方法处理图片(此版本开始,选取相册中的图片不再返回图片的真实Uri,而是封装过的Uri) handleImageOnKitKat(data); }else { //4.4以下系统使用此方法处理图片 handleImageBeforeKitKat(data); } } break; default: break; } } @TargetApi(Build.VERSION_CODES.KITKAT) private void handleImageOnKitKat(Intent data){ 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格式的话,id还需再进行一次解析 String id = docId.split(":")[1];//字符串分割解析出数字格式的id(字符串后半部分才是真正的数字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){ Uri uri = data.getData(); String imagePath = getImagePath(uri, null); 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; } 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(); } }

将程序重新运行,授予权限后,随意选择一张照片回到程序界面。(手机如没有SD卡,选择图片后返回没任何图片显示,也没Toast弹出,这里是模拟器的效果)

3、播放多媒体文件

(1)音频

一般使用MediaPlayer类实现,常用的控制方法如下:

工作流程:创建MediaPlayer对象;调用setDataSource()设置音频文件路径; 调用prepare()进入到准备状态;调用start()开始播放音频;调用pause()暂停播放; 调用reset()停止播放。

新建PlayAudioTest项目,修改布局文件:

<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>

修改MainActivity:

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); Button pause = (Button) findViewById(R.id.pause); Button stop = (Button) findViewById(R.id.stop); 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) { switch (requestCode){ case 1: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ initMediaPlayer(); }else { Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show(); finish(); } break; default: } } @Override public void onClick(View v) { switch (v.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();//释放资源 } } }

还需声明权限:

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

在手机上运行程序,正常。

(2)视频

主要使用VideoView类实现,其将视频的显示和控制集于一身。常用方法如下:

新建PlayVideoTest项目,修改布局文件:

<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" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Play"/> <Button android:id="@+id/pause" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Pause"/> <Button android:id="@+id/replay" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Replay"/> </LinearLayout> <VideoView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>

修改MainActivity:

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); 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 { initVideoPath(); } } private void initVideoPath(){ File file = new File(Environment.getExternalStorageDirectory(), "/movie.mp4"); videoView.setVideoPath(file.getPath());//指定视频文件路径 } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ initVideoPath(); }else { Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show(); finish(); } break; default: } } @Override public void onClick(View v) { switch (v.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; default: break; } } @Override protected void onDestroy() { super.onDestroy(); if (videoView != null){ videoView.suspend(); } } }

同样声明权限。在手机上运行程序。 VideoView只是做了很好的封装,背后仍是使用MediaPlayer对视频文件进行控制的。但VideoView在视频格式的支持及播放效率方面有较大不足,只用于播放一些游戏片头动画或视频宣传还是够的。

转载请注明原文地址: https://www.6miu.com/read-79372.html

最新回复(0)