效果演示:
列表采用一個recyclerview實現(xiàn)痰驱,定義一個公共父實體類,定義基礎屬性瞳浦,各個item類型的實體類擁有自己的屬性担映,繼承公共父實體類。然后adapter采用多itemType叫潦,多viewHolder處理蝇完。
Adapter代碼:
class MutipleAdapter(val context: Context): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var itemList = CopyOnWriteArrayList<AdapterItem>()
fun appendDatas(datas: List<AdapterItem>): MutipleAdapter {
this.itemList.addAll(datas)
notifyDataSetChanged()
return this
}
fun appendData(data: AdapterItem): MutipleAdapter {
this.itemList.add(data)
return this
}
fun clearData() {
itemList.clear()
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == ITEM_TYPE_FILE) {
ViewHolderFile(ItemFileBinding.inflate(LayoutInflater.from(parent.context), parent, false))
} else if (viewType == ITEM_TYPE_CONTACT) {
ViewHolderContact(ItemContactBinding.inflate(LayoutInflater.from(parent.context), parent, false))
} else if (viewType == ITEM_TYPE_App) {
ViewHolderApp(ItemAppBinding.inflate(LayoutInflater.from(parent.context), parent, false))
} else {
ViewHolderHeader(ItemHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
}
override fun getItemCount() = itemList.size
override fun getItemViewType(position: Int): Int {
return itemList[position].itemType
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
var itemData = itemList[position]
if (holder is ViewHolderHeader) {
holder.bind(itemData)
} else if (holder is ViewHolderFile) {
holder.bind(itemData)
} else if (holder is ViewHolderContact) {
holder.bind(itemData)
} else if (holder is ViewHolderApp) {
holder.bind(itemData)
}
}
inner class ViewHolderHeader(val binding: ItemHeaderBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: AdapterItem) {
binding.tvHeaderTitle.text = item.headerTitle
}
}
inner class ViewHolderFile(val binding: ItemFileBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: AdapterItem) {
if (item is FileBean) {
binding.tvFileName.text = item.fileName
binding.tvFileInfo.text = "" + (item.fileSize/1000) + "KB"
setFileIcon(binding.ivIcon, item.fileName)
}
}
}
inner class ViewHolderContact(val binding: ItemContactBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: AdapterItem) {
if (item is ContactBean) {
binding.tvName.text = item.name
binding.tvNumber.text = item.number
binding.tvCall.setOnClickListener {
callPhone(item.number)
}
}
}
}
inner class ViewHolderApp(val binding: ItemAppBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: AdapterItem) {
if (item is AppBean) {
binding.tvAppName.text = item.name
item.intent?.let {
binding.ivIcon.background = context.packageManager.getActivityIcon(it)
}
}
}
}
/**
* 根據(jù)文件后綴類型設置對應類型圖標
*/
private fun setFileIcon(imageView: ImageView, fileName: String) {
if (fileName.endsWith(".jpg") || fileName.endsWith(".mp4")) {
imageView.background = context.resources.getDrawable(R.drawable.category_file_icon_pic_phone)
} else {
var drawableId = 0
if (fileName.endsWith(".txt") || fileName.endsWith(".pdf")) {
drawableId = R.drawable.category_file_icon_doc_phone
} else if (fileName.endsWith(".zip")) {
drawableId = R.drawable.category_file_icon_zip_phone
} else if (fileName.endsWith(".mp3")) {
drawableId = R.drawable.category_file_icon_music_phone
} else if (fileName.endsWith(".apk")) {
drawableId = R.drawable.category_file_icon_apk_phone
} else {
drawableId = R.drawable.ic_local_file
}
imageView.background = context.resources.getDrawable(drawableId)
}
}
/**
* 撥打電話
*/
fun callPhone(phoneNumber: String) {
var intent = Intent(Intent.ACTION_CALL)
var data = Uri.parse("tel:$phoneNumber")
intent.data = data
context.startActivity(intent)
}
}
各個實體類代碼:
open class AdapterItem(var itemType: Int = 0, val headerTitle: String = "")
class AppBean(val pkg: String, val icon: Int, val name: String, val intent: Intent?): AdapterItem() {
init {
itemType = ItemType.ITEM_TYPE_App
}
}
class ContactBean(val name: String, val number: String): AdapterItem() {
init {
itemType = ItemType.ITEM_TYPE_CONTACT
}
}
data class FileBean(val fileName: String, val path: String, val fileSize: Int): AdapterItem() {
init {
itemType = ITEM_TYPE_FILE
}
}
獲取本地應用:
object SearchAppProvider {
fun searchInstallApps(context: Context): List<AppBean>? {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
checkInstallAppsBeforeL(context)
} else {
checkInstallAppsAfterL(context)
}
}
private fun checkInstallAppsBeforeL(context: Context): List<AppBean> {
val apps: MutableList<AppBean> = ArrayList()
val pm = context.packageManager
try {
val packageInfos = pm.getInstalledPackages(0)
for (i in packageInfos.indices) {
val pkgInfo = packageInfos[i]
val AppBean = pkgInfo.applicationInfo
if (TextUtils.equals(context.packageName, pkgInfo.packageName)) continue
val intent = getLaunchIntent(pm, pkgInfo.packageName)
intent!!.flags =
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED or Intent.FLAG_ACTIVITY_CLEAR_TOP
val app = AppBean(
pkgInfo.packageName,
AppBean.icon,
AppBean.loadLabel(pm).toString(),
intent
)
apps.add(app)
}
} catch (e: Exception) {
//
}
return apps
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun checkInstallAppsAfterL(context: Context): List<AppBean>? {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return null
val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
?: return null
val apps: MutableList<AppBean> = ArrayList()
try {
val activityInfos = launcherApps.getActivityList(null, Process.myUserHandle())
for (activityInfo in activityInfos) {
val AppBean = activityInfo.applicationInfo
val intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_LAUNCHER)
intent.setPackage(AppBean.packageName)
intent.component = activityInfo.componentName
intent.flags =
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED or Intent.FLAG_ACTIVITY_CLEAR_TOP
val app = AppBean(
AppBean.packageName,
AppBean.icon,
activityInfo.label.toString(),
intent
)
apps.add(app)
}
} catch (e: Exception) {
}
return apps
}
private fun getLaunchIntent(pm: PackageManager, pkg: String): Intent? {
var intent = pm.getLaunchIntentForPackage(pkg)
return if (intent != null) {
intent
} else {
intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_LAUNCHER)
intent.setPackage(pkg)
val apps = pm.queryIntentActivities(intent, 0)
if (apps == null || apps.isEmpty()) {
return null
}
val ri = apps.iterator().next() ?: return null
intent.component = ComponentName(pkg, ri.activityInfo.name)
intent
}
}
}
模糊查詢聯(lián)系人:
object SearchContactProvider {
@SuppressLint("Range")
fun readContacts(context: Context) {
//ContactsContract.CommonDataKinds.Phone 聯(lián)系人表
var cursor: Cursor? = context.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null)
cursor?.let {
while (it.moveToNext()) {
//讀取通訊錄的姓名
var name = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
//讀取通訊錄的號碼
var number = cursor.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
Log.i("minfo", "$name--$number")
}
}
}
/**
* 模糊查詢聯(lián)系人
*/
@SuppressLint("Range")
fun searchContact(context: Context, key: String): List<ContactBean> {
//ContactsContract.CommonDataKinds.Phone 聯(lián)系人表
var list = ArrayList<ContactBean>()
val projection = arrayOf(
ContactsContract.PhoneLookup.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
)
val selection = StringBuilder()
selection.append(ContactsContract.Contacts.DISPLAY_NAME)
selection.append(" LIKE '%$key%' ")
var cursor: Cursor? = context.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection, selection.toString(), null, null)
cursor?.let {
while (it.moveToNext()) {
//讀取通訊錄的姓名
var name = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
//讀取通訊錄的號碼
var number = cursor.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
Log.i("minfo", "$name--$number")
list.add(ContactBean(name, number))
}
it.close()
}
return list
}
}
模糊查詢本地文件:
/**
* 使用contentResolver查詢本地各種文件
*/
object SearchFileProvider {
private const val MAX_FILE_COUNT = 20
/**
* 模糊查詢本地文件
*/
suspend fun searchLocalFile(context: Context, key: String): List<FileBean> {
var list = ArrayList<FileBean>()
val volumeName = "external"
val columns = arrayOf(MediaStore.Files.FileColumns.DATA)
val selection = MediaStore.Files.FileColumns.DATA + " LIKE '%$key%.mp3' OR " +
MediaStore.Files.FileColumns.DATA + " LIKE '%$key%.json' OR " +
MediaStore.Files.FileColumns.DATA + " LIKE '%$key%.log' OR " +
MediaStore.Files.FileColumns.DATA + " LIKE '%$key%.apk' OR " +
MediaStore.Files.FileColumns.DATA + " LIKE '%$key%.mp4' OR " +
MediaStore.Files.FileColumns.DATA + " LIKE '%$key%.pdf' OR " +
MediaStore.Files.FileColumns.DATA + " LIKE '%$key%.txt' OR " +
MediaStore.Files.FileColumns.DATA + " LIKE '%$key%.jpg' OR " +
MediaStore.Files.FileColumns.DATA + " LIKE '%$key%.zip'"
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(
MediaStore.Files.getContentUri(volumeName),
columns,
selection,
null,
null
)
if (cursor != null) {
while (cursor.moveToNext()) {
if (list.size < MAX_FILE_COUNT) {
val absolutePath = cursor.getString(0)
File(absolutePath).apply {
if (exists() && !TextUtils.isEmpty(name) && name.contains(".")) {
if (!TextUtils.isEmpty(name)) {
var bean = FileBean(name, path, readBytes().size)
list.add(bean)
}
}
}
} else {
return list
}
}
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
} finally {
try {
if (cursor != null) {
cursor.close()
cursor = null
}
} catch (e: java.lang.Exception) {
}
}
return list
}
}
異步加載各類數(shù)據(jù),再使用async/await同步使用數(shù)據(jù)結(jié)果
/**
* 搜索各類數(shù)據(jù)
*/
private fun loadData(key: String) {
adapter.clearData()
//搜索本地App
val localAppsDeferred = GlobalScope.async(Dispatchers.IO) {
SearchAppProvider.searchInstallApps(applicationContext)
}
//搜索聯(lián)系人
val contactsDeferred = GlobalScope.async(Dispatchers.IO) {
SearchContactProvider.searchContact(applicationContext, key)
}
//搜索本地文件
val localFilesDeferred = GlobalScope.async(Dispatchers.IO) {
SearchFileProvider.searchLocalFile(applicationContext, key)
}
GlobalScope.launch {
// 通過 await 獲取異步任務的結(jié)果
val localApps = localAppsDeferred.await()
val contacts = contactsDeferred.await()
val localFiles = localFilesDeferred.await()
withContext(Dispatchers.Main) {
adapter.appendData(AdapterItem(0, "本機應用"))
adapter.appendDatas(localApps!!.take(10))
adapter.appendData(AdapterItem(0, "聯(lián)系人"))
.appendDatas(contacts)
adapter.appendData(AdapterItem(0, "文件管理")).appendDatas(localFiles) //先添加內(nèi)容的header诅挑,再添加內(nèi)容
}
}
}
然后四敞,需要能夠訪問文件泛源,別忘了加上6.0訪問權限拔妥,獲取本地文件、讀取聯(lián)系人訪問的權限达箍。并且注意没龙,在targetsdk 29及以下,可以訪問所有問題缎玫,高于29硬纤,則只能夠訪問到圖片,視頻赃磨,音樂這樣的多媒體文件筝家。
requestPermissions(arrayOf(Manifest.permission.READ_CONTACTS,
Manifest.permission.CALL_PHONE, Manifest.permission.WRITE_EXTERNAL_STORAGE), this)