Search and Display APP介绍
- 使用
kotlin
编写 - 主要有那些功能?
- 展示一个100条内容的
recyclerview
- 通过
searchview
输入关键词,改变recyclerview
显示列表 - 点击列表项会跳转到新的展示Activity用于展示其中的内容
- 展示一个100条内容的
效果图
前置技能点
本人虽然之前通过别人的项目修改过两个安卓APP,但是都是java语言,而且我没系统学过java,此前也没接触过kotlin。
Android开发入门http://hukai.me/android-training-course-in-chinese/basics/index.html
kotlin入门https://www.runoob.com/kotlin/kotlin-basic-syntax.html
安卓新闻App开发参考视频https://www.bilibili.com/video/BV12p4y1n7JV
关于Adapter的API参考:https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter
本项目地址在本人github
项目结构
项目最初是使用Android Studio的Scrolling Activity模板创建的,剩下的文件都是通过右键包名创建的,这样应该是最规范的方法。
Kotlin文件
从上到下依次为:
- 点击列表中某个item后进入的新的Activity的程序
- RecyclerView的自定义Adapter类,主要是规定了如何装载和控制视图内容,包括item的点击事件
- RecyclerView的自定义ViewHolder类,为每个item中的变量对应的子view起一个对应的成员名称,本例只有一个textView我把他命名为了text,更多的可以参考上面提到的视频
- 主界面程序,规定了搜索、展示等(创建项目时自动就生成了)
layout样式文件
从上到下依次为:
- 点击列表中某个item后进入的新的Activity的界面布局:通过右键可以很方便地添加居中
- 主界面上方的彩色区域的布局(创建项目时自动就生成了)
- 主界面下方自定义区域的布局(创建项目时自动就生成了):
- 这个很简单,就是个垂直的线性布局,上面是SearchView,下面是RecyclerView。
- 如果RecyclerView左右设置了margin,这样之后添加的分割线就不会填满屏幕,但是上下滑动到末端时的动画也不会填满屏幕,所以建议margin保持为0,
- 展示列中每一个item的子布局,单独弄一个xml文件可以很方便地统一规定其布局
- 每个item布局包括一个TextView和一个分割线,前者居中并加上四周的一个margin
- 分割线的实现如下,左右设置了margin,这样分割线就不会填满屏幕
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="#BFBFBF" />
values数据文件
从上到下依次为:
- 规定了色彩关键字映射,比如white对应#FFFFFF
- 规定了几何关键字映射,比如app_bar_height为180dp
- 规定了字符串变量关键字映射,比如large_text对应“这是一串很长的文字”
- 规定了主题关键字映射,可以一键修改对应主题样式
代码说明
主界面控制ScrollingActivity.kt
class ScrollingActivity : AppCompatActivity() { private val list = mutableListOf<String>() //private lateinit var myRecycler: RecyclerView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_scrolling) setSupportActionBar(findViewById(R.id.toolbar)) findViewById<CollapsingToolbarLayout>(R.id.toolbar_layout).title = title findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show() }//这个图标被我禁止显示了,所以这个功能并没有什么用 for (i in 1..100) list.add("这里是第 $i 行") //这个是kotlin独特的字符串模板,用$套一个变量,可以引用其值 //添加RecyclerView的样式和数据更新方法 val myRecycler = findViewById<RecyclerView>(R.id.recyclerView) myRecycler.layoutManager = LinearLayoutManager(this) myRecycler.adapter = MyAdapter(list) //规定SearchView的侦听事件 val searchView = findViewById<SearchView>(R.id.searchView) searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(keyWord: String): Boolean { //当提交了输入时的操作 return false } override fun onQueryTextChange(keyWord: String): Boolean { // 当修改了输入时的操作,根据关键字过滤列表,让Adapter填入新列表 // 如果只是更新部分数据,推荐使用notifyItemRangeChanged()或者notifyItemChanged() // notifyItemChanged(int) // notifyItemInserted(int) // notifyItemRemoved(int) // notifyItemRangeChanged(int, int) // notifyItemRangeInserted(int, int) // notifyItemRangeRemoved(int, int) val filterList = filter(keyWord) myRecycler.adapter = MyAdapter(filterList) return false } }) } override fun onCreateOptionsMenu(menu: Menu): Boolean { // Inflate the menu; this adds items to the action bar if it is present. menuInflater.inflate(R.menu.menu_scrolling, menu) return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. return when (item.itemId) { R.id.action_settings -> true else -> super.onOptionsItemSelected(item) } } private fun filter(keyWord: String): List<String> { // 过滤原本的列表,返回一个新的列表 val filterList = mutableListOf<String>() for (l in list) { if (l.contains(keyWord)) filterList.add(l) } return filterList } }
桥接适配器MyAdapter.kt
class MyAdapter(private val contentList: List<String>) : RecyclerView.Adapter<MyViewHolder>() { // 创建一个成员Context变量,否则onBindViewHolder()无法访问主活动的context // 如果此类和主活动在同一个kt文件中,直接使用this即可获得context private lateinit var mContext: Context //下面这三个函数是创建时继承了RecyclerView.Adapter类会自动提示你需要实现的 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { mContext = parent.context // 获取整个列表组展示View的context // 在列表组展示View中使用item_layout.xml规定的样式进行填充,把整个View撑起来 val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false) return MyViewHolder(itemView) } override fun getItemCount(): Int { // 获取有多少个item return contentList.size } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { // 给每个itemView绑定显示内容,注意要绑定到holder的对应成员中,这个成员是我们指定好的对应view val item = contentList[position] // 根据位置获取列表中对应item,这个Int来源是默认的,不用指定 holder.text.text = item // 修改了TextView中的text属性 // 给每个holder添加一个点击事件 holder.itemView.setOnClickListener { val intent = Intent(mContext, Display::class.java) // 通过Intent传递数据到新的名叫Display的Activity中,"msg"是在Display.kt中指定的成员常量 intent.putExtra("msg", item) mContext.startActivity(intent) } } }
自定义快捷View映射: MyViewHolder.kt
ViewHolder通常出现在适配器里,为的是listview滚动的时候快速设置值,而不必每次都重新创建很多对象,从而提升性能。
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { val text: TextView = itemView.findViewById(R.id.textView) }
点击后进入的Activity: Display.kt
class Display : AppCompatActivity() { @RequiresApi(Build.VERSION_CODES.P) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_display) // 这里指定了要获取的常量的名字,通过名字传递不同的消息 val msg=intent.getStringExtra("msg") // 修改新活动的TextView,显示msg内容 findViewById<TextView>(R.id.textDisplay).text =msg } }
小提示
如果想要生成的apk尽量小,记得在app的build.gradle中设置这两个关键字为true
项目在上传的时候记得删除任何build、.gradle、.idea、appuild文件夹,这样的项目是最精简的。