8.0+引入通知渠道
创建通知渠道
一旦创建,其重要性不可修改,HIGH是d出横幅,MIN是没通知栏小图标
NotificationManager manager= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);//获取通知管理器,在管理器上 *** 作 if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){//如果当前API版本>=8.0(8.0代号为字母O) NotificationChannel channel=new NotificationChannel("normal","Normal",NotificationManager.importANCE_DEFAULT);//创建通知渠道,参数为(渠道id,渠道名,重要性) manager.createNotificationChannel(channel);//添加到通知管理器 }
创建点击通知后的跳转
Intent是立即执行,PendingIntent相当于延迟执行的Intent
方式一:
点击通知后跳转到AnotherActivity,再按返回键就会回到桌面
Intent intent = new Intent(this, AnotherActivity.class); //参数为(上下文,请求码,Intent,flags) PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0);
方式二:
点击通知后跳转到AnotherActivity,再按返回键会进入MainActivity
//创建1个任务栈构建器 TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); //将在Manifest中指定好的父Activity加入到构建器中 stackBuilder.addParentStack(AnotherActivity.class); //给构建器设定未来会发生的任务:执行intent stackBuilder.addNextIntent(intent); //得到要执行的任务intent的pending版本 PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, 0);
创建通知并发送
Button button=findViewById(R.id.button); button.setonClickListener((View v)->{ Notification notification= new NotificationCompat.Builder(this,"normal")//使用compat可以兼容所有安卓版本,参数为(上下文,渠道id) .setContentTitle("通知标题") .setContentText("通知内容") .setSmallIcon(R.drawable.xxx) .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.xxx))//将图片解析为Bitmap对象 .setContentIntent(pending)//点击通知后跳转到的页面 .setAutoCancel(true)//点击通知后通知消失(或者是在AnotherActivity中写manager.cancel(1)) .build();//创建通知对象 manager.notify(1,notification);//通过管理器发送通知,参数为(通知id,通知对象) });
还可以通过setStyle()指定更多样式,如大量文字,大图片,同一应用通知折叠InboxStyle,多媒体
.setStyle(new NotificationCompat.BigTextStyle().bigText("很长的一段文字"))
取消通知
//所有通知全部消失!! manager.cancelAll();调用相机
调用摄像头拍照并保存
public class MainActivity extends AppCompatActivity { public static String authority = "com.example.cameraalbumtest.fileprovider"; ActivityMainBinding binding; Uri imageUri; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); binding.button.setonClickListener((View v) -> { //先在代码中新建File对象,意为预期在当前应用在内置SD卡中的沙盒cache目录创建output_image.jpg File outputImage = new File(getExternalCacheDir(), "output_image.jpg"); if (outputImage.exists()) {//存在则删除 outputImage.delete(); } try { outputImage.createNewFile();//不存在则创建 } catch (IOException e) { e.printStackTrace(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //Android 7.0+ File→Uri要使用这种方式,因为content URI更安全(content://...) //此步骤需要为本应用创造一个Authority为"包名.fileprovider"的ContentProvider //FileProvider是一种特殊的ContentProvider,其getUriForFile()方法获得file的content URI imageUri = FileProvider.getUriForFile(this, authority, outputImage); } else { //低版本使用file URI(file://...) Uri.fromFile(outputImage); } Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);将拍到的照片转存到指定Uri startActivityForResult(intent, 1); }); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1) { //如果成功,说明在我们指定的imageUri处已经有了新拍的照片文件 if (resultCode == RESULT_OK) { try { //Uri→InputStream→Bitmap //ContentResolver中的openInputStream(Uri uri)是一种把给定的Uri→InputStream方式 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); binding.imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } } } }
因为创建了ContentProvider,所以要注册
因为要使用content://uri替代file://uri,那么,content://的uri如何定义呢?总不能使用文件路径,因为目的就是安全,不暴露真实路径
所以,需要一个虚拟的路径对文件路径进行映射,所以需要编写个xml文件,通过path以及xml节点确定可访问的目录,通过name属性来映射真实的文件路径
//是否允许授权文件的临时访问权限
file_paths支持的节点:
当这么声明以后,代码可以使用你所声明的当前文件夹以及其子文件夹
这里我们在xml目录下创建file_paths的内容
//每个节点都支持两个属性:name+path //path:需要临时授权访问的路径(.或/代表所有路径) //name:就是你给这个访问路径起个名字调用相册
binding.photo.setonClickListener((View v) -> { Intent intent = new Intent(Intent.ACTION_OPEN_document); intent.addCategory(Intent.CATEGORY_OPENABLE);//源码中说这两个是必须指定的 intent.setType("image*,image/*,documents/*,audio/*,...) startActivityForResult(intent, 2); });
BitmapFactory.decodeFileDescriptor似乎比BitmapFactory.decodeStream占用内存更小
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { ... else if (requestCode == 2) { if (resultCode == RESULT_OK && data != null) { Bitmap bitmap = null; try { Uri uri = data.getData();//返回的就是选择的文件content URI FileDescriptor fd = getContentResolver().openFileDescriptor(uri, "r").getFileDescriptor(); bitmap = BitmapFactory.decodeFileDescriptor(fd); } catch (FileNotFoundException e) { e.printStackTrace(); } binding.imageView.setImageBitmap(bitmap); } } }播放音频
在main包下新建assets文件夹
MediaPlayer
start()对应开始
pause()对应暂停
reset()对应停止
stop()+release()对应程序结束
MediaPlayer mMediaPlayer = new MediaPlayer(); AssetManager assetManager; @Override protected void onCreate(Bundle savedInstanceState) { ... assetManager = getAssets(); initMediaPlayer(); binding.play.setonClickListener((View v) -> { if (!mMediaPlayer.isPlaying()) mMediaPlayer.start(); }); binding.pause.setonClickListener((v) -> { if (mMediaPlayer.isPlaying()) mMediaPlayer.pause(); }); binding.stop.setonClickListener((v) -> { mMediaPlayer.reset(); //停止播放后,准备好下一次播放的资源 initMediaPlayer(); }); } public void initMediaPlayer() { try { AssetFileDescriptor afd = assetManager.openFd("music.mp3"); FileDescriptor fd = afd.getFileDescriptor(); //setDataSource()的其它重载也可以 mMediaPlayer.setDataSource(fd, afd.getStartOffset(), afd.getLength()); mMediaPlayer.prepare(); } catch (IOException e) { e.printStackTrace(); } } @Override protected void onDestroy() { ... //释放资源 mMediaPlayer.stop(); mMediaPlayer.release(); }
SoundPool
具体应用见BeatBox项目
//音频属性,指定音频的用途 AudioAttributes audioAttributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(); //声音池 SoundPool mSoundPool = new SoundPool.Builder() .setMaxStreams(5)//最多容纳多少个音频流,设置太多小心内存限制 .setAudioAttributes(audioAttributes) .build();
加载进声音池
AssetFileDescriptor afd=mAssetManager.openFd("music.mp3"); //把音频文件载入声音池 int soundId=mSoundPool.load(afd,1);//这个soundId播放要用,需要保留 afd.close();
播放的时候需要指定soundId,我这里将它放入了Sound对象
int soundId = sound.getSoundId(); int streamId = mSoundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);//可以指定左右音量,循环,倍速等播放属性 mSoundPool.pause(streamId);//暂停播放
最后要释放内存
mSoundPool.release();播放视频
VideoView本质上是MediaPlayer的封装
对视频格式的支持以及播放效率方面还存在着不足
Uri uri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.video);//固定写法 binding.videoView.setVideoURI(uri); binding.play.setonClickListener((v) -> { if (!binding.videoView.isPlaying()) binding.videoView.start();//播放 }); binding.pause.setonClickListener((v) -> { if (binding.videoView.isPlaying()) binding.videoView.pause();//暂停 }); binding.replay.setonClickListener((v) -> { binding.videoView.resume();//重播 });
最后释放内存
binding.videoView.suspend();
tips:获取视频资源Uri的Kotlin写法为
Uri uri = Uri.parse("android.resource://$packageName/${R.raw.video}");
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)