iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 30
1
自我挑戰組

練習程式系列 第 30

Android 、Content Provider 、Kotlin學習資源

  • 分享至 

  • xImage
  •  

紀錄學習資源。

Create a Photo Gallery App in Android – Android Studio

整理:

1

第三方類別庫: zoomage(也是用來縮放圖片的),有XML的說明:
https://github.com/jsibbold/zoomage

This is the inherited ImageView.ScaleType from Android.
繼承ImageView.ScaleType的意思嗎?
總之zoomage好像可以當成ImageView

2

Manifest的:
android:configChanges="orientation|screenSize"
是處理螢幕方向變更 和 螢幕大小。
https://developer.android.com/guide/topics/resources/runtime-changes

用log觀察這些東西是什麼:

getResources().getDisplayMetrics().widthPixels(寬)
getResources().getDisplayMetrics().heightPixels(高)
螢幕橫的:
widthPixels -- > 1184
heightPixels -- > 768
螢幕直的:
widthPixels -- > 768
heightPixels -- > 1184
resources.getDisplayMetrics();
橫:
metricsDisplayMetrics{density=2.0, width=1184, height=768, scaledDensity=2.0, xdpi=320.0, ydpi=320.0}
直:
DisplayMetrics{density=2.0, width=768, height=1184, scaledDensity=2.0, xdpi=320.0, ydpi=320.0}
resources.getDisplayMetrics().densityDpi
橫320
直320
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
content://media/external/images/media

android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI
content://media/internal/images/media
cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA));
/storage/emulated/0/Pictures/20191010_1422581118066235.jpg

cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME));
Pictures(資料夾名稱)

cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED));
1570717381
android.os.Build.VERSION.SDK_INT(就是你正在測試程式的手機SDK版本號碼)
28
Build.VERSION_CODES.M (原本以為M代表 build geadle允許最低的SDK號碼。但M其實是Marshmallow)
23

所以

android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
就是手機的SDK要大於SDK 23(Marshmallow)

參考:
[Android]取得 Android 系統的 SDK 版本

Build.VERSION.CODENAME // 目前開發代號;如果是 REL 表示是發佈版本
REL

Build.VERSION.INCREMENTAL // 內部識別碼
5464897

Build.VERSION.RELEASE; // 軟體版本
7.1.1 

官網:
Build.VERSION

Build.VERSION_CODES

整理:

android.os.Build.VERSION.SDK_INT:
The SDK version of the software currently running on this hardware dev

Build.VERSION_CODES.M
M is for Marshmallow!(SDK23)

Build.VERSION_CODES.N
N is for Nougat.(SDK24)

Build.VERSION_CODES.P
Android Pie(SDK28)

Marshmallow(梅花糖)、Nougat(牛軋糖)、 Pie(派),都是Android的版本號。
參考:Android版本列表

為什麼要android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M?????????
可能是因為:

Android Marshmallow之前的版本,在安裝程式前,必須一次同意該程式所有請求的權限,否則不能裝那個程式。Android Marshmallow的新SDK可以讓程式設計成使用者可以只同意某部份的權限,且事後也可以隨時打開或關閉某些權限細項。

來觀察一下版本有甚麼功能:

Android Marshmallow

Android Marshmallow底層直接支援指紋辨識。還增加了一個名為「Doze」的電源管理方案,若判斷手機沒有被使用者拿著,就會關閉一些背景的行程以節省電源。
Android Marshmallow也支援USB Type-C

Android Nougat

在螢幕上同時顯示多個應用程式的能力

3

練習java collection:
Java Collections | Collections Framework in Java | Java Tutorial For Beginners | Edureka

走訪queue程式:
https://www.techiedelight.com/iterate-through-queue-java/

一 Iterator不保證照順序:
https://docs.oracle.com/javase/7/docs/api/java/util/Collection.html#iterator()

Returns an iterator over the elements in this collection. There are no guarantees concerning the order in which the elements are returned (unless this collection is an instance of some class that provides a guarantee).

二 如果queue.iterator();之後再queue.add();會有java.util.ConcurrentModificationException錯誤


why does List.toArray() return Object[] and not String[]? how to work around this?

原本要直接這樣寫,但是toArray()傳回的是obeject:

Integer[] priorityQueues = priorityQueue.toArray();

所以會有錯誤:

error: incompatible types: Object[] cannot be converted to Integer[]

改成這樣:

Integer[] priorityQueues = priorityQueue.toArray(new Integer  [0]);

4

kotlin開發環境:

一 命令列:

教學:Run Kotlin in Windows cmd
kotlin-命令列開發環境

檔案下載:
https://github.com/JetBrains/kotlin/releases/tag/v1.3.50
https://ithelp.ithome.com.tw/upload/images/20191013/20111994mibMwV3NFm.png

二 在android studio 直接執行單一.kt檔:

參考:Run single kotlin class with main function in android studio

解答1:

Open kotlin REPL as Tool -> kotlin -> kotlin REPL

直接在上面寫程式:
https://ithelp.ithome.com.tw/upload/images/20191013/20111994nntsbSyOyt.png

寫檔案的方式還是可以
注意:
一 REPL的作用地方,如果是app,應該就是在app下的檔案都可以。
二 import路徑: package的那串 和 檔名
三 變成物件後,才讓我執行
https://ithelp.ithome.com.tw/upload/images/20191013/201119945WUrhVLDjZ.png

三 IntelliJ IDEA安裝Kotlin:

這邊有教
Get started with Kotlin/JVM
https://kotlinlang.org/docs/jvm-get-started.html

IntelliJ IDEA安裝ktor :

原本不知道ktor怎麼安裝,改用2020年的IntelliJ IDEA
https://ithelp.ithome.com.tw/upload/images/20210220/20111994m8rtvd6Tip.png

說明那邊寫了,ktor沒辦法在android studio 裝 。
https://ithelp.ithome.com.tw/upload/images/20210220/20111994kE55QcxlUv.png

要注意kotlin、 ktor、java 好像不能在同一個project 。要開不同的project:
https://ithelp.ithome.com.tw/upload/images/20210220/20111994M5YPpvdfLH.png

5 了解什麼是ContentProvider

context.getContentResolver().query()详细用法详解

內容供應程式基本概念

如何使用ContentProvider-簡介

Content Provider - Part 1, A brief introduction

Content Provider - Part 2, All about ContentResolver and Cursor

ContentProvider是一組讓你跟其他應用程式存取的資料庫。
Android提供一套通用的介面讓每個應用程式可以進行CRUD(新增、讀取、更新、刪除)的操作。
ContentProvider用於管理音訊、影片、圖片、個人聯絡資訊等資料
ContentResovler類別是實作ContentProvider的子類別
通常會透過ContentResovler類別去操作已經實作CURD

影片的截圖:

全部:
https://ithelp.ithome.com.tw/upload/images/20191106/20111994C1sqqKXB2O.png

透過getContentResolver()CRUD:
https://ithelp.ithome.com.tw/upload/images/20191106/201119949Zk8wZgA9F.png

Cursor就是查詢(R)(query)回來的結果:
https://ithelp.ithome.com.tw/upload/images/20191106/20111994Noq0rv7s4J.png

getContentResolver()的五個參數:
https://ithelp.ithome.com.tw/upload/images/20191106/20111994OMdkLXUuCA.png

getContentResolver()五個參數了解一下::

1 Uri-->目前知道以下幾個(類似哪個table:圖片表格、聯絡人表格):

MediaStore.Images.Media.EXTERNAL_CONTENT_URI
ContactsContract.Contacts.CONTENT_URI
UserDictionary.Words.CONTENT_URI

結果:

content://media/external/images/media
content://com.android.contacts/contacts
content://user_dictionary/words

2 projection-->(類似哪個欄位:資料夾名稱欄位、圖片路徑欄位)

3 selection-->(where條件,像是要ScrennShots資料夾)

4 selectionArgs -->如果前面的selection用?代替變數,這邊就要寫變數;如果前面沒有?,這邊就null

5 sortOrder -->order by

練習getContentResolver:

獲得截圖資料夾裡的最後一個圖片檔(嘗試過要獲得資料夾檔案,但失敗):

Uri uriExternal = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.MediaColumns.DATA,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME, 
MediaStore.MediaColumns.DATE_MODIFIED};

Cursor cursorExternal = getContentResolver().query(uriExternal, projection, "_data IS NOT NULL) AND bucket_display_name=?  GROUP BY (bucket_display_name",
new String[]{"Screenshots"}, null);

getContentResolver()的select語法就會是(程式打錯,觀看logcat,發現的):

SELECT _data, bucket_display_name, date_modified FROM images WHERE (_data IS NOT NULL)   AND bucket_display_name="Screenshots"  GROUP BY (bucket_display_name)

如果要直接獲的截圖的圖片就:

Cursor cursorExternal = getContentResolver().query(uriExternal, projection, "_data IS NOT NULL) AND ( bucket_display_name=? ",
                   new String[]{"Screenshots"}, null);

繼續了解ContentProvider

問題:

一:

為什麼明明新增了圖片,可是用getContentResolver().query,卻讀不到?
解答:java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.MEDIA_MOUNTED on KitKat only
使用:

MediaScannerConnection.scanFile

二:

如何把file:///storage/emulated/0/DCIM/Camera/20191129.jpg轉成content://media/external/images/media/777 ?

這行程式得到的路徑是:

android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
content://media/external/images/media

然後好像是所有的圖片,不管絕對路徑(storage/emulated/0/DCIM/Camera/20191129.jpg)都會有一個
(content://media/external/images/media/XXX)

所以有把file:///storage/emulated/0/DCIM/Camera/20191129.jpg (類似檔案的絕對路徑,只是前面有file://),轉成這個content://media/external/images/media/777 的方法?
參考:android : file Uri to Content Uri. (converting)
1 有檔案後,用getAbsolutePath()取的檔案的絕對路徑(storage/emulated/0/DCIM/Camera/20191129.jpg)

2 context.getContentResolver()查詢檔案,取得MediaStore.Images.Media._ID

3 content://media/external/images/media 再加 MediaStore.Images.Media._ID,完成!!!

三:

原本在找某個資料夾下的所有圖片,是先找到資料夾,然後再迴圈跑的,類似這樣:
Android 9 (Pie) ContentResolver query MediaStore.Images.Media.EXTERNAL_CONTENT_URI returns null on api 28

可是 Environment.getExternalStorageDirectory() deprecated in API level 29 java

Environment.getExternalStorageDirectory()好像以後會不能用,因為deprecated in API level 29 java(更正:deprecated不等於不能用!!!),
而且用迴圈跑,之後如果要找的圖片要查詢,像是:某段時間的圖片、或是檔案名查詢,都還要自己想辦法寫程式,如果用context.getContentResolver()的query就很方便。

所以就是改用:

Use getExternalFilesDir(), getExternalCacheDir(), or getExternalMediaDir() (methods on Context) instead of Environment.getExternalStorageDirectory()

context!!.externalMediaDirs     //路徑:/storage/emulated/0/Android/media/com.example.takephoto/

如果要用context.getContentResolver()的query找尋某個路徑下的所有檔案,要怎麼辦?
使用like:How to get list of all music in specific directory and all subdirectories using MediaStore

關於FileProvider和Uri.fromFile():
FileProvider.getUriForFile vs Uri.fromFile - is there other difference but permissions

android 7.0 因为Uri.fromFile引起的FileUriExposedException异常

ContentResolver.query()的limit:
limiting number of rows in a ContentResolver.query() function
加在sortOrder 欄位,為什麼?因為SQL順序:
MySQL 超新手入門(3)SELECT 基礎查詢
http://www.codedata.com.tw/wp-content/uploads/2013/12/mysql_03_snap_64.png

如果用 contentresolver查詢一段時間的資料?
How to query contentresolver between two dates?

MediaStore.MediaColumns.DATA is deprecated:
MediaStore.MediaColumns.DATA is deprecated and i wanna load images from gallery to my app

因為MediaStore.MediaColumns.DATA is deprecated,所以想把MediaStore.MediaColumns.DATA換掉,但是不知道方法,原本是想先獲得content://media/external/images/media 的路徑,透過content://media/external/images/media 的路徑,找尋絕對路徑,但好像不太行。

但是Deprecated不代表不能用,所以可以先不管
Is it wrong to use Deprecated methods or classes in Java?

官網有寫MediaStore.MediaColumns.DATA is deprecated:
MediaStore.MediaColumns

This constant was deprecated in API level 29. Apps may not have filesystem permissions to directly access this path. Instead of trying to open this path directly, apps should use ContentResolver#openFileDescriptor(Uri, String) to gain access.

建議用ContentResolver#openFileDescriptor,目前不會:
ContentResolver

Query MediaStore on Android Q
Replacement for “GROUP BY” in ContentResolver query in Android Q ( Android 10, API 29 changes)

GROUP BY 和 COUNT 不能再Android Q(29)使用,所以要怎麼辦?
就得自己判斷了。group by 就用hashmap 的 containskey(bucketid)解決。
count 就是自己數或是 arraylist的size()。
據說效能影響還好:(留言區的大大留言)

Take into account that you are not iterating through each image in the device by itself, neither accessing or iterating files in the devices at all, instead you are iterating the rows in a cursor from the MediaStore, which is very fast even with a few thousand records – PerracoLabs Mar 12 at 7:48

可能幾千個不影響效能

6

回前頁:
How to Add an Up Button to the AppBar - Android Studio Tutorial

Add an up action

7

關於Manifest.permission:
Manifest.permission
如何使用Runtime Permission

在新版的權限管理將權限分為兩大類:
一般的權限
危險的權限

危險的權限也就是說, 除了在androidMainfes上面宣告以外, 還要額外在程式內進行判斷,
否則APP拿不到危險權限, 則會有閃退的危機。

怎麼知道哪些是危險權限:
像是INTERNET是一般權限:
https://ithelp.ithome.com.tw/upload/images/20191103/20111994nePVtNV5sN.png

WRITE_EXTERNAL_STORAGE是危險權限:
https://ithelp.ithome.com.tw/upload/images/20191103/20111994QacQZdV5eP.png

onRequestPermissionsResult方法就是處理使用者有沒有同意權限
onRequestPermissionsResult的grantResults參數回來的結果如果0, 代表使用者同意權限,
如果回來的是-1, 代表被拒絕。

8

MediaStore

MediaStore可以取得手機的圖片、影片、下載檔案。

MediaStore.Images就會取的裝置的圖片檔:
MediaStore.Images

接著MediaStore.Images.Media:
MediaStore.Images.Media

然後MediaStore.MediaColumns:
MediaStore.MediaColumns

可以獲的BUCKET_DISPLAY_NAME、DATA、DATE_MODIFIED、DISPLAY_NAME(The display name of the media item.

9 kotlin 程式學習

教學來源:
Kotlin使用心得:Sealed Class
Kotlin使用心得(十):object與單例模式
How to get current local date and time in Kotlin
Kotlin get type as string


上一篇
python 安德森鳶尾花卉數據集
下一篇
Android OkHttp and Gson 和 log的問題 、Retrofit、MVVM
系列文
練習程式37
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言