背景
每次在StudyDemos工程里,添加知识点Demo时,总有一部分工作是重复的,那就是在首页添加入口。省掉这个重复添加的过程就是我的目的。
实现思路
与APIDemo App类似,实现思路如下:
- 对manifest里的activity添加两个约定,查找规范和目录规范
- 首页自动扫描上面特定的intent-filter,获取Demo列表和名称
- 通过RecyclerView显示,同时支持多级目录
效果如下所示:
此Demo的源码:AndroidStudyDemo
实现过程
查找规范和目录规范
查找规范:通过指定特殊的intent-filter实现过滤出真正的Demo Activity,复用Android已有的SAMPLE_CODE类别,如下所示:
1 2 3 4
| <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.SAMPLE_CODE"/> </intent-filter>
|
目录规范:通过“/”分割多级目录和名称,如下所示:
1
| android:label="Binder/AIDL例子"
|
首页显示
由于有目录的概念,不是简单的查找出所有的Demo Activity后直接显示,需要判断类型:
- 当前如果是目录,跳转的Intent还首页,但只显示当前目录下的子目录或Demo
- 当前如果是Demo,跳转的Intent就是Demo界面
查询所有的Demo
1 2 3 4 5 6 7
| val intent: Intent = Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_SAMPLE_CODE) .setPackage(this.packageName)
val list: List<ResolveInfo> = this.packageManager.queryIntentActivities(intent, 0) ?: return demos
|
对所有的Demo进行过滤处理,只获取当前目录下的子目录或Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| private fun getData(prefix: String): MutableList<DemosAdapter.Demo> { val demos: MutableList<DemosAdapter.Demo> = mutableListOf<DemosAdapter.Demo>()
val intent: Intent = Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_SAMPLE_CODE) .setPackage(this.packageName)
val list: List<ResolveInfo> = this.packageManager.queryIntentActivities(intent, 0) ?: return demos
var prefixPath: List<String>? = null var prefixWithSlash: String = prefix if(prefix != "") { prefixPath = prefix.split("/") prefixWithSlash = "$prefix/" }
val map:MutableMap<String, Boolean> = mutableMapOf<String, Boolean>()
for (info in list) { val label:String = info.activityInfo.loadLabel(this.packageManager).toString()
if(prefixPath == null || label.startsWith(prefixWithSlash)) {
val labelPath = label.split("/")
val nextLabel: String = labelPath[prefixPath?.size ?: 0]
if ((prefixPath?.size ?: 0) == (labelPath.size - 1)) { val result = Intent() result.setClassName(info.activityInfo.packageName, info.activityInfo.name) demos.add(DemosAdapter.Demo(nextLabel, result)) } else { if(map.get(nextLabel) == null){ val result = Intent(this, StudyDemos::class.java) result.putExtra("com.example.android.apis.Path", if(prefix == "") nextLabel else "${prefix}/${nextLabel}") demos.add(DemosAdapter.Demo(nextLabel, result)) map.put(nextLabel, true) } } } } return demos }
|
RecyclerView显示
初始化RecyclerView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| val recyclerView: RecyclerView = findViewById<RecyclerView>(R.id.recycler_view) recyclerView.setHasFixedSize(true)
val linearLayoutManager = LinearLayoutManager(this) linearLayoutManager.orientation = LinearLayoutManager.VERTICAL recyclerView.layoutManager = linearLayoutManager
val demos: MutableList<DemosAdapter.Demo> = getData(path?:"") recyclerView.adapter = DemosAdapter(demos, object: DemosAdapter.OnItemClickListener { override fun onClick(holder: DemosAdapter.DemosViewHolder, demo: DemosAdapter.Demo) { this@StudyDemos.startActivity(demo.intent) } })
recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
|
Adapter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| class DemosAdapter(var demos: MutableList<Demo>, var itemClickListener: OnItemClickListener) : RecyclerView.Adapter<DemosAdapter.DemosViewHolder>() {
override fun getItemViewType(position: Int): Int { return 0 }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DemosViewHolder { val inflater = LayoutInflater.from(parent.context)
val itemView = inflater.inflate(R.layout.item_demo, parent, false) return DemosViewHolder(itemView) }
override fun getItemCount(): Int { return demos.size }
override fun onBindViewHolder(holder: DemosViewHolder, position: Int) { val demo = demos[position] holder.titleView.setText(demo.title) holder.itemView.setOnClickListener { itemClickListener.onClick(holder, demo) } }
interface OnItemClickListener { fun onClick(holder: DemosViewHolder, demo: Demo) }
class Demo(var title: String, var intent: Intent) {}
class DemosViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val titleView: TextView
init { titleView = itemView.findViewById(R.id.title_view) } } }
|
参考
- ApiDemos