安卓开发学习笔记_UI开发_编写界面的最佳实践
制作9-Patch图片
9-Patch图片是一种被特殊处理过的png图片,能够指定哪些区域可以被拉伸、哪些区域不可以.
首先对着message_left.png
图片右击→Create 9-Patch file.
在上边框和左边框绘制的部分表示当图片需要拉伸时就拉伸黑点标记的区域, 在下边框和右边框绘制的部分表示内容允许被放置的区域. 使用鼠标在图片的边缘拖动就可以进行绘制了, 按住Shift键拖动可以进行擦除.
最后记得要将原来的message_left.png图片
删除,只保留制作好的message_left.9.png图片
即可,因为Android项目中不允许同一文件夹下有两张相同名称的图片(即使后缀名不同也不行)。
编写精美的聊天界面
编写主界面
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#d8e0e8" >
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<EditText
android:id="@+id/inputText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Type something here"
android:maxLines="2" />
<Button
android:id="@+id/send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send" />
</LinearLayout>
</LinearLayout>
定义消息的实体类
// content: 消息的内容
// type: 消息的类型
class Msg(val content: String, val type: Int) {
companion object {
// const关键字只能在单例类, companion object, 顶层方法中使用
// 收到的消息
const val TYPE_RECEIVED = 0
// 发出的消息
const val TYPE_SENT = 1
}
}
编写RecyclerView的子项布局
发送消息的布局
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:background="@drawable/message_right" >
<TextView
android:id="@+id/rightMsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:textColor="#000" />
</LinearLayout>
</FrameLayout>
接收消息的布局
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:background="@drawable/message_left" >
<TextView
android:id="@+id/leftMsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:textColor="#fff" />
</LinearLayout>
</FrameLayout>
适配器
class MsgAdapter(val msgList: List<Msg>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
inner class LeftViewHolder(view: View) : RecyclerView.ViewHolder(view)
{
val leftMsg: TextView = view.findViewById(R.id.leftMsg)
}
inner class RightViewHolder(view: View) : RecyclerView.ViewHolder(view)
{
val rightMsg: TextView = view.findViewById(R.id.rightMsg)
}
override fun getItemViewType(position: Int): Int {
val msg = msgList[position]
return msg.type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if(viewType == Msg.TYPE_RECEIVED){
val view = LayoutInflater.from(parent.context).inflate(R.layout.msg_left_item, parent, false)
return LeftViewHolder(view)
}else{
val view = LayoutInflater.from(parent.context).inflate(R.layout.msg_right_item, parent, false)
return RightViewHolder(view)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val msg = msgList[position]
when(holder){
is LeftViewHolder -> holder.leftMsg.text = msg.content
is RightViewHolder -> holder.rightMsg.text = msg.content
else -> throw IllegalArgumentException()
}
}
override fun getItemCount() = msgList.size
MainActivity
class MainActivity : AppCompatActivity(), View.OnClickListener {
private val msgList = ArrayList<Msg>()
private var adapter: MsgAdapter? = null
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
initMsg()
val layoutInflater = LinearLayoutManager(this)
binding.recyclerView.layoutManager = layoutInflater
adapter = MsgAdapter(msgList)
binding.recyclerView.adapter = adapter
binding.send.setOnClickListener(this)
}
override fun onClick(v: View?)
{
when(v)
{
binding.send -> {
// 获取输入内容
val content = binding.inputText.text.toString()
if(content.isNotEmpty()){
val msg = Msg(content, Msg.TYPE_SENT)
msgList.add(msg)
// 当有新消息时, 刷新RecyclerView中的显示(将RecyclerView中所有可见的元素全部刷新)
adapter?.notifyItemInserted(msgList.size - 1)
// 将RecyclerView定位到最后一行
binding.recyclerView.scrollToPosition(msgList.size - 1)
// 清空输入框中的内容
binding.inputText.setText("")
}
}
}
}
private fun initMsg() {
val msg1 = Msg("Hello guy.", Msg.TYPE_RECEIVED)
msgList.add(msg1)
val msg2 = Msg("Hello. Who is that?", Msg.TYPE_SENT)
msgList.add(msg2)
val msg3 = Msg("This is Tom. Nice talking to you. ", Msg.TYPE_RECEIVED)
msgList.add(msg3)
}
}