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、app\build文件夾,這樣的項目是最精簡的。