栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android笔记(五)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android笔记(五)

Android笔记(五)

文章目录

Android笔记(五)

1. 通知

1.1 通知渠道1.2 基本用法1.3 进阶技巧 2. 调用摄像头和相册

2.1 调用摄像头拍照2.2 从相册中选择图片 3. 播放多媒体文件

3.1 音频3.2 视频

1. 通知 1.1 通知渠道

为了避免通知被滥用,Android 8.0引入了通知渠道概念

通知渠道: 每条通知都要属于一个对应的渠道,每个应用程序都可以自由地创建当前应用拥有哪些通知渠道,但通知渠道的控制权在用户手上,可以自由选择这个通知渠道的重要程度,是否响铃、是否震动或者是否关闭这个渠道的通知

创建通知渠道:

    需要一个NotificationManager对通知进行管理

    使用NotificationChannel类创建一个通知渠道,并调用NotificationManager.createNotificationChannel()方法创建

val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.o) {
    // NotificationChannel和createNotificationChannel()都是Android 8.0新增API,所以使用时需要进行版本判断
    val channel = NotificationChannel(channelId, channelName, importance)
    manager.createNotificationChannel(channel)
    
}
1.2 基本用法

可以在Activity(应用场景较少)、BroadcastReceiver、Service中创建通知

    使用一个Builder构造器来创建Notification对象

    避免Android版本兼容问题,使用AndroidX库中提供的兼容API。提供了一个NotificationCompat类,使用这个类的构造器来构造Notification对象 调用NotificationManager的notify()方法让通知显示

    第一个参数:指定的通知id第二个参数:Notification对象

val notification = NotificationCompat.Builder(context, channelId)
	.setContentTitle("This is content title")
	.setContentText("This is content text")
	.setSmallIcon(R.drawable.small_icon)
	.setLargeIcon(BitmapFactory.decodeResource(getResources(),  R.drawable.large_icon))
	.build()

manager.notify(1, notification)

PendingIntent:

和Intent一样可以用于启动Activity、Service以及发送广播等,不同在于Intent倾向于立即执行某个动作,PerndingIntent倾向于在某个合适的时机执行某个动作(可以理解为延迟执行的Intent)

根据需求使用getActivity()、getBroadcast()、getSerice()获取PendingIntent实例

第一个参数:Context第二个参数:一般不用,传入0即可第三个参数:Intent对象第四个参数:确定PendingIntent行为,通常传入0即可

FLAG_ONE_SHOTFLAG_NO_CREATEFLAG_CANCEL_CURRENTFLAG_UPDATe_CURRENT

NotificationCompat.Builder有一个setContentIntent(PendingIntent)方法,当用户点击通知时会执行相应的逻辑

通知取消:

如果没有在代码中对通知进行取消,就会一致显示在系统的状态栏上

在NotificationCompat.Builder中连缀一个setAutoCancel(),当点击通知时,通知会自动取消

val notification = NotificationCompat.Builder(context, channelId)
	...
	.setAutoCancel(true)	//cancel:取消
	.build()

显示调用NotificationManager的cancel()

val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.cancel(1) // 传入的是指定的通知id
1.3 进阶技巧

NotificationCompat.Builder中的API:

setStyle():构建富文本通知内容,接收一个NotificationCompat.Style参数,这个参数用于构建具体的富文本信息,如长文字、图片等

...
	.setStyle(NotificationCompat.BigTextStyle().bigText(这里是长文字,我也不知到要敲什么,只要够长的就行了,随便写些什么吧,凑字数凑字数凑字数凑字数凑字数凑字数凑字数凑字数凑字数))
	.build()

重要等级越高越容易获得用户注意(弹出横幅、发出声音等),开发者只能在创建通知渠道时为其指定初始等级,用户可以随时进行修改,通知渠道一旦创建不能再通过代码修改

相关知识:

Notification.Builder API参考文档

2. 调用摄像头和相册 2.1 调用摄像头拍照
class MainActivity : AppCompatActivity() {
    
    val takePhoto = 1
    lateinit var imageUri: Uri
    lateinit var outputImage: File
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Btn.setOnClickListener { 
            
            outputImage = File(externalCacheDir, "output_image.jpg")
            if (outputImage.exists()) {
                outputImage.delete()
            }
            outputImage.createNewFile()
            // 判断版本
            imageUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) 
            {
                
                FileProvider.getUriForFile(this, "com.example.myapplication.filepovider", outputImage)
            } else {
                // 低于Android 7.0调用Uri的fromFile()将File对象转换为Uri对象
                Uri.fromFile(outputImage)
            }
            // 启动相机程序
            val intent = Intent("android.media.action.IMAGE_CAPTURE")
            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
            startActivityForResult(intent, takePhoto)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            takePhoto -> {
                if (resultCode == Activity.RESULT_OK) {
                    // 调用BitmapFactory的decodeStream()将output_image.jpg图片解析成Bitmap对象
                    val bitmap =  BitmapFactory.decodeStream(contentResolver.openInputStream(imageUri))
                    image.setImageBitmap(bitmap)
                }
            }
        }
    }
    
    
    private fun rotateIfRequired(bitmap: Bitmap): Bitmap {
        val exif = ExifInterface(outputImage.path)
        val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
        ExifInterface.ORIENTATION_NORMAL)
        return when (orientation) {
            ExifInterface.ORIENTATION_ROTATE_90 -> rotateBitMap(bitmap, 90)
            ExifInterface.ORIENTATION_ROTATE_180 -> rotateBitMap(bitmap, 180)
            ExifInterface.ORIENTATION_ROTATE_270 -> rotateBitMap(bitmap, 270)
            else -> bitmap
        }
    }
    
    private fun rotateBitMap(bitmap: Bitmap, degree: Int): Bitmap {
        val matrix = Matrix()
        matrix.postRotate(degree.toFloat())
        val rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height,
        matrix, true)
        bitmap.recycle()    // 将不需要的Bitmap对象回收
        return rotatedBitmap
    }
}

注:

为什么使用应用关联缓存目录存放图片

Android 6.0 开始读写SD卡为危险权限,存放在SD卡需要进行运行时权限处理,使用应用关联缓存目录则可以跳过这一步从Android 10.0开始,公有SD卡目录不在允许被应用程序直接访问,而是要使用作用域存储 为什么要进行版本判断

Android 7.0开始直接使用本地真实路径的Uri被认为不安全,会抛出FileUriExposedExceptionFileProvider是一种特殊的ContentProvider,使用类似ContentProvider机制来对数据进行保护,可选择性地将封装过的Uri共享给外部




    
        ...
        
            
        
    







    
    

2.2 从相册中选择图片
class MainActivity : AppCompatActivity() {
    ...
    val fromAlbum = 2

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        ...
        fromAlbumBtn.setOnClickListener {
            // 打开文件选择器
            val intent = Intent(Intent.ACTION_OPEN_document)
            intent.addCategory("Intent.CATEGORY_OPENABLE")
            // 指定值显示图片
            intent.type = "image/*"
            startActivityForResult(intent, fromAlbum)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            ...
            fromAlbum -> {
                if (resultCode == Activity.RESULT_OK && data != null) {
                    data.data?.let { uri ->
                        // 将选择的图片显示
                        val bitmap = getBitmapFromUri(uri)
                        image.setImageBitmap(bitmap)
                    }
                }
            }
        }
    }

    private fun getBitmapFromUri(uri: Uri) = contentResolver
        .openFileDescriptor(uri, "r")?.use {
            BitmapFactory.decodeFileDescriptor(it.fileDescriptor)
        }

   ...

相关知识:

Android 10适配要点,作用域存储 - 掘金

3. 播放多媒体文件 3.1 音频

一般使用MediaPlayer类实现

方法描述
setDataSource()设置播放音频文件的位置
prepare()开始播放之前调用,已完成准备工作
start()开始或继续播放音频
pause()暂停播放音频
reset()将MediaPlayer对象重置到刚刚创建的状态
seekTo()从指定位置开始播放音频
stop()停止播放音频,调用后MediaPlayer对象无法再播放音频
release()释放有MediaPlayer对象相关的资源
isPlaying()判断当前MediaPlayer是否正在播放音频
getDuration()获取载入的音频文件时长

基本流程:

    创建MediaPlayer对象调用setDataSource()方法设置音频文件路径调用prepare()方法使对象进入准备状态调用start()播放音频调用pause()暂停音频调用reset()停止播放

Android允许在项目中创建assets目录,这个目录下可以存放任意文件和子目录,这些文件会在项目打包时一并被打包到安装包中,在程序中国可以借助AssetManager类提供的接口对assets目录下文件进行读取

class MainActivity : AppCompatActivity() {

    private val mediaPlayer = MediaPlayer()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initMediaPlayer()
        play.setOnClickListener {
            if (!mediaPlayer.isPlaying) {
                mediaPlayer.start()
            }
        }
        pause.setOnClickListener {
            if (mediaPlayer.isPlaying) {
                mediaPlayer.pause()
            }
        }
        stop.setOnClickListener {
            if (mediaPlayer.isPlaying) {
                mediaPlayer.reset()
                initMediaPlayer()
            }
        }
    }

    private fun initMediaPlayer() {
        val assetManager = assets
        val fd = assetManager.openFd("music.mp3")
        mediaPlayer.setDataSource(fd.fileDescriptor, fd.startOffset, fd.length)
        mediaPlayer.prepare()
    }

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer.stop()
        mediaPlayer.release()
    }
}
3.2 视频

主要使用VideoView控件类实现,将视频显示和控制集于一身

方法描述
seVideoPath()设置播放视频文件的位置
start()开始或继续播放视频
pause()暂停播放音频
resume()将视频从头开始播放
seekTo()从指定位置开始播放视频
isPlaying()判断是否正在播放视频
getDuration()获取载入的视频文件时长

VideoView不支持直接俄播放assets目录下的视频资源,所以创建res/raw目录,像音频、视频之类的资源文件可以放在里面

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val uri = Uri.parse("android.resource://$packageName/${R.raw.video}")
        videoView.setVideoURI(uri)
        play.setOnClickListener {
            if (!videoView.isPlaying) {
                videoView.start()
            }
        }
        pause.setOnClickListener {
            if (videoView.isPlaying) {
                videoView.pause()
            }
        }
        replay.setOnClickListener {
            if (videoView.isPlaying) {
                videoView.resume()
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        videoView.suspend()	// 释放资源
    }
}
          }
        }
        pause.setOnClickListener {
            if (videoView.isPlaying) {
                videoView.pause()
            }
        }
        replay.setOnClickListener {
            if (videoView.isPlaying) {
                videoView.resume()
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        videoView.suspend()	// 释放资源
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/733173.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号