一直以来都认为不必分散太多精力在UI控件上,但是逆向的过程中看到有许多控件都没有实践过,所以趁着这个机会学习一下几个UI。
1.Adapter
在学习UI之前首先学习下适配器Adapter,它的作用类似于MVC模式下的C,用来控制数据以怎样的方式显示到V上:
网上找到的一张Adapter的继承结构图:
常用的有BaseAdapter(经常重写来使用)、ArrayAdapter(最简单的适配器,只显示一行文字)、SimpleAdapter(比ArrayAdapter效果好一点,可以自定义多种效果)。
2.ListView
多说无益,首先来学习ListView,虽然ListView逐渐被RecyclerView所取代,但是还是有学习的必要,之后学习RecyclerView的时候会看到两者的区别。适配器首先选择简单的ArrayAdapter和SimpleAdapter,当然重写BaseAdapter也可以。
首先定义activity_main布局:布局里放了一个listview作为MVC里面的V
然后要准备M然后定义C:
这里通过数组的方式准备数据源,适配器选择最简单的ArrayAdapter,静态或动态添加数据都可以:
这样看起来太单调,考虑在此基础上添加水果的图片:
item布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="horizontal">
<ImageView
android:id="@+id/fruit_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/fruit_name"
android:textSize="20sp"
android:textColor="#000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/fruit_says"
android:textSize="15sp"
android:textColor="#000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
首先新建水果类:定义了三个变量,并设置相应的setter与getter
使用自定义适配器,新建FruitAdapter继承自BaseAdapter,并重写其getView方法:
然后在MainActivity中动态添加:
效果如下:
Kotlin Fruit类:
class Fruit(var icon:Int,var name:String,var says:String)
FruitAdapter继承ArrayAdapter,与Java逻辑相同:
class FruitAdapter(activity: Activity,resourceid:Int,fruits:List<Fruit>):ArrayAdapter<Fruit>(activity,resourceid,fruits) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val viewHolder:ViewHolder
val view:View
if(convertView == null){
view = LayoutInflater.from(context).inflate(R.layout.fruit_item,parent,false)
viewHolder = ViewHolder(view.findViewById(R.id.fruit_icon),view.findViewById(R.id.fruit_name),view.findViewById(R.id.fruit_says))
view.tag = viewHolder
}else{
view = convertView
viewHolder = view.tag as ViewHolder
}
val fruit = getItem(position)
if(fruit!=null){
viewHolder.icon.setImageResource(fruit.icon)
viewHolder.name.text = fruit.name
viewHolder.says.text = fruit.says
}
return view;
}
inner class ViewHolder(val icon:ImageView,val name:TextView,val says:TextView)
}
MainActivity中添加了点击事件:
class MainActivity : AppCompatActivity() {
private val fruits = ArrayList<Fruit>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fruits.add(Fruit(R.drawable.apple_pic,"苹果","我是苹果"))
fruits.add(Fruit(R.drawable.cherry_pic,"樱桃","我是樱桃"))
fruits.add(Fruit(R.drawable.mango_pic,"芒果","我是芒果"))
val adapter = FruitAdapter(this,R.layout.fruit_item,fruits)
listview.adapter = adapter
listview.setOnItemClickListener { parent, view, position, id ->
val fruit = fruits[position]
Toast.makeText(this,"点击了${fruit.name}",Toast.LENGTH_SHORT).show()
}
}
}
效果如下:
之后要完成对listview的数据更新操作,这里添加功能是靠按钮来实现的,依次添加苹果,当然也可以选择位置插入添加,删除操作使用长按item子项弹出菜单实现的:
(1)添加功能
自定义适配器中添加了add功能:
Mainactivity中的调用:
效果如下:
Kotlin版本:
FruitAdapter:
class FruitAdapter(activity: Activity,resourceid:Int,val fruits:ArrayList<Fruit>):ArrayAdapter<Fruit>(activity,resourceid,fruits) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val viewHolder:ViewHolder
val view:View
if(convertView == null){
view = LayoutInflater.from(context).inflate(R.layout.fruit_item,parent,false)
viewHolder = ViewHolder(view.findViewById(R.id.fruit_icon),view.findViewById(R.id.fruit_name),view.findViewById(R.id.fruit_says))
view.tag = viewHolder
}else{
view = convertView
viewHolder = view.tag as ViewHolder
}
val fruit = getItem(position)
if(fruit!=null){
viewHolder.icon.setImageResource(fruit.icon)
viewHolder.name.text = fruit.name
viewHolder.says.text = fruit.says
}
return view;
}
**fun add(fruit:Fruit){
//添加功能
fruits?.add(fruit)
notifyDataSetChanged()
}**
inner class ViewHolder(val icon:ImageView,val name:TextView,val says:TextView)
}
MainActivity,增加按钮点击监听:
class MainActivity : AppCompatActivity() {
private var fruits = ArrayList<Fruit>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fruits.add(Fruit(R.drawable.apple_pic,"苹果","我是苹果"))
fruits.add(Fruit(R.drawable.cherry_pic,"樱桃","我是樱桃"))
fruits.add(Fruit(R.drawable.mango_pic,"芒果","我是芒果"))
val adapter = FruitAdapter(this,R.layout.fruit_item,fruits)
listview.adapter = adapter
add.setOnClickListener{
adapter.add(Fruit(R.drawable.apple_pic,"苹果","我是苹果"))
}
listview.setOnItemClickListener { parent, view, position, id ->
val fruit = fruits[position]
Toast.makeText(this,"点击了${fruit.name}",Toast.LENGTH_SHORT).show()
}
}
}
点击增加苹果,效果如下:
(2)删除功能
首先为listview添加注册:
之后重写onCreateContextMenu方法和点击事件onContextItemSelected方法:
效果如下:
长按芒果:
点击删除:
Kotlin版本,修改MainActivity即可,逻辑和Java 相同:
class MainActivity : AppCompatActivity() {
private var fruits = ArrayList<Fruit>()
private var item_position:Int = 0
private var adapter : FruitAdapter ? = null
override fun onContextItemSelected(item: MenuItem): Boolean {
when(item.itemId){
R.id.remove -> {
fruits.removeAt(item_position)
adapter?.notifyDataSetChanged()
listview.invalidate()
}
}
return true
}
override fun onCreateContextMenu(
menu: ContextMenu?,
v: View?,
menuInfo: ContextMenu.ContextMenuInfo?
) {
val info = menuInfo as AdapterView.AdapterContextMenuInfo
item_position = info.position
var inflator = MenuInflater(this)
inflator.inflate(R.menu.menu,menu)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fruits.add(Fruit(R.drawable.apple_pic,"苹果","我是苹果"))
fruits.add(Fruit(R.drawable.cherry_pic,"樱桃","我是樱桃"))
fruits.add(Fruit(R.drawable.mango_pic,"芒果","我是芒果"))
adapter = FruitAdapter(this,R.layout.fruit_item,fruits)
listview.adapter = adapter
add.setOnClickListener{
adapter?.add(Fruit(R.drawable.apple_pic,"苹果","我是苹果"))
}
listview.setOnItemClickListener { parent, view, position, id ->
val fruit = fruits[position]
Toast.makeText(this,"点击了${fruit.name}",Toast.LENGTH_SHORT).show()
}
registerForContextMenu(listview)
}
}
长按子项,效果如下:
点击删除:
3.RecyclerView
需要引入依赖包:
RecyclerView相对ListView来说在性能方面进行了优化,例如子项中控件的点击事件比ListView简洁;也可以实现ListView不能实现的横向滚动。
首先先利用RecyclerView实现上面ListView中实现的布局,activity_main.xml添加RecyclerView控件作为V
与ListView一样也要有C来控制数据的显示方式:
ViewHolder类通过onCreateViewHolder方法中LayoutInflater.from(parent.getContext()).inflate方法将item布局加载并传给ViewHolder类的构造函数,并在构造函数里通过findViewbyId绑定实例。onBindViewHolder方法中利用ViewHolder实例进行赋值。
MainActivity与ListView类似,完成数据的动态加载:
与ListView不同的是增加了设置LinearLayoutManager实例,以用来指定RecyclerView的布局方式,LinearLayoutManager是线性布局的意思,因此效果与ListView类似:指定不同的布局方式显示的效果也不尽相同
Kotlin:逻辑与Java相同
子项布局文件:
FruitAdapter:首先定义了一个内部类ViewHolder,ViewHolder的主构造函数中需要传入一个View参数,这个参数就是Recycler子项的最外层布局,有了这个最外层布局,就可以通过findViewById获取子项布局中的各个控件实例
class FruitAdapter(val fruits:List<Fruit>): RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val fruit_icon:ImageView = itemView.findViewById(R.id.fruit_icon)
val fruit_name:TextView = itemView.findViewById(R.id.fruit_name)
val fruit_says:TextView = itemView.findViewById(R.id.fruit_says)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
//继承RecyclerView.Adapter必须要重写的方法
val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item,parent,false)
return ViewHolder(view)
}
//继承RecyclerView.Adapter必须要重写的方法
override fun getItemCount(): Int = fruits.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
//继承RecyclerView.Adapter必须要重写的方法 对子项控件实例进行赋值
val fruit = fruits[position]
holder.fruit_icon.setImageResource(fruit.icon)
holder.fruit_name.text = fruit.name
holder.fruit_says.text = fruit.says
}
}
MainActivity:layoutManager用于指定Recyclerview的布局方式,这里使用LinearLayoutManager就是现形布局的意思,可以实现和listview相同的效果
class MainActivity : AppCompatActivity() {
private val fruits = ArrayList<Fruit>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fruits.add(Fruit(R.drawable.apple_pic,"苹果","我是苹果"))
fruits.add(Fruit(R.drawable.cherry_pic,"樱桃","我是樱桃"))
fruits.add(Fruit(R.drawable.mango_pic,"芒果","我是芒果"))
val adapter = FruitAdapter(fruits)
val layoutManager = LinearLayoutManager(this)
recyclerview.layoutManager = layoutManager
recyclerview.adapter = adapter
}
}
效果如下:
接着看下上面说的与listview不同的横向滚动和子项控件点击事件优化。
横向滚动:
改动的地方有2:
1是item布局的宽度,当然这里没有设置图片与文字的对齐方式
2是通过 linearLayoutManager设置布局的排列方式
Kotlin:
再看点击事件,这里设置了子项view的点击事件以及子项中三个字控件的点击事件,在onCreateViewHolder中实现:
效果如下:
Kotlin:
同样在FruitAdapter的onCreateViewHolder方法中实现,逻辑与Java相同
效果如下: