1.使用通知
1.1創建通知渠道
首先需要一個NotificationManager對通知進行管理
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
接下來使用NotifactionChannel類創建一個通知渠道,並調用NotifiactionManager的createNotificationChannel()方法創建。由於NotifiactionManager類和createNotificationChannel()方法都是Androidd 8.0系統新增API,所以我們還需要鏡像版本判斷纔行
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.0) {
val channel =NotificationChannel(channelId,channelName,importance)
manager.createNotificationChannel(channel)
}
通知的重要等級主要有IMPORTANCE_HIGH,IMPORTANCE_DEFAULT,IMPORTANCE_LOW,IMPORTANCE_MIN這幾種,對應由高到低。
1.2通知基本用法
創建一個NotificationTest項目
修改MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val manager =getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//判斷Android版本
if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.O){
val channel = NotificationChannel("normal","Normal",NotificationManager.IMPORTANCE_DEFAULT)
manager.createNotificationChannel(channel)
}
sendNotice.setOnClickListener {
//添加點擊讀取消息頁面
val intent=Intent(this,NotificationActivity::class.java)
val pi= PendingIntent.getActivity(this,0,intent,0)
//
val notifiaction = NotificationCompat.Builder(this,"normal")
.setContentTitle("這是一條通知的標題")
.setContentText("這是一條通知的內容")
.setSmallIcon(R.drawable.icon)
.setLargeIcon(BitmapFactory.decodeResource(resources,R.drawable.icon))
.setContentIntent(pi)
.build()
manager.notify(1,notifiaction)
}
}
}
右鍵com.example.notificationtest包新建NotificationActivity。
PendingIntent簡單理解爲延遲執行Intent
PendingIntent的用法很簡單,它主要提供幾個靜態方法用於獲取PendingIntent實例,可以選擇使用getActivity()方法,getBroadcast()方法,還是getService()方法。第四個參數用於確定PendingIntent的行爲,有FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT這四種值可以選,通常情況傳入0就可以了。
點擊消息後系統上的圖標消失有兩種解決方式:
第一種:
val notification = NotificationCompat.Builder(this,"normal")
...
.setAutoCancel(true)
.build()
第二種:
class NotificationActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_notification)
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.cancel(1)
}
}
創建通知時候我們給每條通知指定的id爲1,所以我們在cancel()方法中傳入該通知的id就行了。
1.3通知的進階技巧
1.3.1setStyle()方法 允許構建富文本的通知內容
val notifiaction = NotificationCompat.Builder(this,"normal")
...
.setStyle(NotificationCompat.BigTextStyle().bigText("這是一條很長的內容,內容絕對不會被省略!!這是一條很長的內容,內容絕對不會被省略!!這是一條很長的內容,內容絕對不會被省略!!這是一條很長的內容,內容絕對不會被省略!!這是一條很長的內容,內容絕對不會被省略!!"))
.build()
1.3.2顯示一張大照片
val notifiaction = NotificationCompat.Builder(this,"normal")
...
.setStyle(NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(resources,R.drawable.q)))
.build()
事先準備好一張圖片,通過BitmapFactory的decodeRsource()方法將圖片解析成Bitmap對象,再傳入bigPicture()方法中。
1.3.3創建一個新的通知
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.O){
...
val channel2 = NotificationChannel("normal","Normal",NotificationManager.IMPORTANCE_HIGH)
manager.createNotificationChannel(channel2)
}
sendNotice.setOnClickListener {
//添加點擊讀取消息頁面
val intent=Intent(this,NotificationActivity::class.java)
val pi= PendingIntent.getActivity(this,0,intent,0)
//
val notifiaction = NotificationCompat.Builder(this,"important")
...
}
2.調用攝像頭和相冊
首先創建一個CrameraAlbumTest項目,然後修改activity_main.xml中的代碼
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/takePhotoBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="照相"
></Button>
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
></ImageView>
</LinearLayout>
修改MainActivity中的代碼
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)
takePhotoBtn.setOnClickListener {
//創建File對象,用於儲存拍照後的照片
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.cameraalbumtest.fileprovider",outputImage)
}else{
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){
//將拍到的照片顯現出來
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(imageUri))
imageView.setImageBitmap(rotateIfRequired(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 rotateBitmap = Bitmap.createBitmap(bitmap,0,0,bitmap.width,bitmap.height,matrix,true)
bitmap.recycle()//將不再需要的bitmap對象回收
return rotateBitmap
}
}
調用getExternalCacheDir()方法可以得到這個目錄,具體路徑是/sdcard/Android/data/< package name>/cache .
如果允許的設備系統版本低於Android 7.0,就調用Uri的fromFile()方法將File對象轉換成Uri對象,這個Uri對象標識這output_image.jpg這張照片的本地真實路徑。否則就調用FileProvider的getUriForFile()方法將File對象轉換成一個封裝的Uri對象。
getUriForFile()方法接收3個參數;第一個要求傳入Context對象,第二個參數是唯一的字符串,第三個是剛創建的File對象
FileProvider是一種特殊的ContentProvider,它使用了和ContentProvider類似的機制來來對數據保護
在AndroidManifest.xml中註冊ContentProvider
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cameraalbumtest">
<application ...>
<activity android:name=".MainActivity">
...
</activity>
<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"></meta-data>
</provider>
</application>
android:name屬性的值是固定的,而android:authorities屬性的值必須和剛纔FileProvider.getUriForFile()方法中第二個參數一致。 < provider>標籤的內部使用< meta_data>指定Uri的共享路徑,並引用一個@xml/file_paths資源。
右鍵res目錄創建一個xml目錄,在創建一個file_path.xml文件,內寫
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="/"></external-path>
</paths>
external—path就是用來指定Uri共享的,name屬性的值可以隨便填,path屬性值表示共享的具體路徑。使用一個但斜槓表示將整個SD卡進行共享。
2.2使用相冊
修改activity_main.xml中的代碼
...
<Button
android:id="@+id/fromAlbumBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="相冊"
></Button>
...
修改MainActivity中的代碼
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)
imageView.setImageBitmap(bitmap)
}
}
}
}
}
private fun getBitmapFromUri(uri : Uri) =contentResolver.openFileDescriptor(uri,"r")?.use {
BitmapFactory.decodeFileDescriptor(it.fileDescriptor)
}
...
3.播放多媒體
3.1播放音頻
方法名 | 功能描述 |
---|---|
setDataSource() | 設置要播放的音頻文件的位置 |
prepare() | 在開始播放之前調用,以完成準備工作 |
start() | 開始或繼續播放音頻 |
pause() | 暫停播放音頻 |
reset() | 將MediaPlayer對象重置到剛剛創建的狀態 |
seekTo() | 從指定的位置開始播放音頻 |
stop() | 停止播放音頻。調用後的MediaPlayer對象無法再播放音頻 |
release() | 釋放與MediaPlayer對象相關的資源 |
isPlaying() | 判斷當前MediaPlayer是否正在播放音頻 |
getDuration() | 獲取載入的音頻文件的時長 |
首先創建新項目MediaPlayer
修改activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:src="@drawable/bg"
></ImageView>
<Button
android:id="@+id/play"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="播放"
></Button>
<Button
android:id="@+id/pause"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="暫停"
></Button>
<Button
android:id="@+id/stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="停止播放"
></Button>
</LinearLayout>
在app/src/main目錄下創建assets目錄,將music.mp3文件複製到其中
修改MainActivity
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()
}
}
stop.setOnClickListener {
if (mediaPlayer.isPlaying){
mediaPlayer.reset()
initMediaPlayer()
}
}
pause.setOnClickListener {
if (mediaPlayer.isPlaying){
mediaPlayer.pause()
}
}
}
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()
}
}
在initMediaPlayer()方法中,通過getAssets()方法得到了一個AssetManager的實例,AssetManager可以讀取assets目錄下的任何資源。接着我們調用openFd()方法將音頻文件句柄打開,後又依次調用了setDataSource()方法和prepare()方法,爲MediaPlayer做好播前準備。
3.2播放視頻
方法名 | 功能描述 |
---|---|
setVideoPath() | 設置要播放的視頻文件的位置 |
start() | 開始或繼續播放視頻 |
pause() | 暫停播放視頻 |
resume() | 將視頻從頭開始播放 |
seekTo() | 從指定的位置開始播放視頻 |
isPlaying() | 判斷當前是否正在播放視頻 |
getDuration() | 獲取載入的視頻文件的時長 |
首先我們新建PlayVideoTest項目
右鍵app/src/main/res ->新建raw目錄,將準備好多video.mp4放在裏面
修改activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
>
<VideoView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
></VideoView>
<Button
android:id="@+id/play"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="播放"
></Button>
<Button
android:id="@+id/pause"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="暫停"
></Button>
<Button
android:id="@+id/replay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="重播"
></Button>
</LinearLayout>
修改MainActivity類
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val url = Uri.parse("android.resource://$packageName/${R.raw.video}")
videoView.setVideoURI(url)
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()//釋放所佔的資源
}
}
首先在onCreate()方法中調用Uri.parse()方法,將raw目錄下的video.mp4文件解析成了一個Uri對象。然後調用VideoView的setVideoURI()方法將Uri對象傳入。
最後在onDestory()方法中調用suspend()方法,將VideoView所佔資源釋放。
4.Kotlin 課堂 : 使用infix構建函數
to並不是kotlin關鍵字,之所以我們能使用 A to B 這樣的語法結構,是因爲Kotlin提供了一種高級語法糖個性:infix函數。比如 A to B 這樣的寫法,實際上等於 A.to(B)
4.1從簡單的例子來看
String startsWith()函數可以判斷一個字符串是否以某個指定參數開頭,藉助infix函數後
新建一個infix.kt文件,編寫下列代碼
infix fun String.beginsWith(prefix : String) = startsWith(prefix)
這樣我們就可以這樣使用begins With()函數了
if( "Hello Kotlin" beginsWith "Hello" ){
//處理具體的邏輯
}
infix函數的語法堂的特殊性:1.不能定義成頂層函數,它必須是某個類的成員函數,可以使用擴展函數的方式將它定義到某個類中。2.infix函數必須接收且只能接收一個參數,參數類型沒有限制
4.2複雜的例子
val list = listOf("Apple","Banana","Orange","Pear","Grape")
if(list.contains("Banana")){
//處理具體邏輯
}
在infix.kt文件中添加代碼
infix fun <T> Collection<T>.has(element : T) = contains(element)
可以直接用
val list = listOf("Apple","Banana","Orange","Pear","Grape")
if(list has "Banana"){
//處理具體邏輯
}
這裏調用Collection接口中的contains()函數。
4.3 A to B 原理
按Ctrl(Mac 是command)查看源碼 :
public infix fun <A,B> A.to(that : B) : Pair<A,B> =Pair(this ,that)
5.Git時間:版本控制工具進階
這次我們選擇在PlayVideoTest項目中創建
打開cmd,在項目的根目錄下進行,執行 git init命令。
G:\Android stdio\work\PlayVideoTest>git init
Initialized empty Git repository in G:/Android stdio/work/PlayVideoTest/.git/
5.1忽略文件
代碼倉庫下存在一個.gitignore文件,在Android studio 創建項目時會自動幫我們創建兩個.gitignore文件,一個在app模塊下,一個在根目錄下。
根目錄下的.gitignore文件內容:
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
除了*.iml表示任意以.iml結尾的文件,其他都是指定的具體文件名或目錄名,上面配置得到所有內容都不會被添加到版本控制當中。
app模塊下的.gitignore文件內容:
/build
由於app模塊下大部分都是我們編寫的代碼,所以默認情況下build目錄不會被添加到版本控制當中。
我們也可修改來滿足特殊需求,比如我app模塊下單所有測試文件都只是給我自己使用,我並不想把他加入到版本控制當中,可以這樣修改gitignore文件:
/build
/src/test
/src/androidTest
現在我們可以提交代碼
git add .
然後執行commit命令完成提交
git commit -m "First commit"
5.2查看修改內容
查看文件修改情況,只需要status命令就行
git status
查看所有文件修改內容
git diff
查看MainActivity.java文件修改內容
git diff app/src/main/java/com/example/playvideotest/MainActivity.kt
變更部最左側加號代表新添加內容,左側減號代表刪除內容。
5.3撤銷未提交的修改
如果我們想撤銷這個修改可以使用checkout命令
git checkout app/src/main/java/com/example/playvideotest/MainActivity.kt
再使用git status命令檢查一下
這種撤銷方式只適用於還沒有執行過add命令的文件。
當我們修改完MainActivity.java後執行git add .命令
我們再輸入git status檢查一下
MainActivity仍然處於添加狀態,所以內容無法撤銷掉。
我們應該先對其取消添加,使用的命令時reset命令
git reset HEAD app/src/main/java/com/example/playvideotest/MainActivity.kt
再允許一遍 git status命令,就會發現MainActivity.java變回未添加的狀態
5.4查看提交記錄
git log
如果提交的記錄非常多的話,只想查看其中一條記錄,我們可以命令指定該記錄id
git log c243070fcf69bc063b184cdafa19617649dab2ee
如果想查看最近幾次的提交,比如-1表示我們只想看最後一次的提交記錄。
git log -1