一、实践——编写一个聊天框
笔者昨天玩游戏把女朋友惹生气了,编写了一个道歉室哄女朋友,给女朋友真诚的表示错了。(生气的女朋友比过年的猪都难抓)
1、制作9-Patch图片
在Android stdio中点击图片右键,找到9-Patch然后一键生成,
然后使用鼠标将不需要变化的地方涂黑,这个目的是为了让图标更好的适配,只有涂黑的部分才会发生长宽的变化,类比一下QQ的聊天气泡,有多少文字就有多大的聊天气泡。
2、构建主页面activity_main.xml
<?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="wrap_content"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/RecyclerView1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:hint="Type something here"
android:maxLines="2"/>
<Button
android:id="@+id/Button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Send"/>
</LinearLayout>
</LinearLayout>
2.2 复习hint、maxLines
android:hint="Type something here"的作用是显示了一段提示性文本
android:maxLines="2"的作用是设置最大可见行数
效果如图:
3、构建实体类Msg
public class Msg {
public static final int TYPE_RECEIVED = 0; //收到的消息
public static final int TYPE_SENT = 1; //发送的消息
private String content; //消息内
private int type; //消息类型
public Msg(String content, int type) {
this.content = content;
this.type = type;
}
public String getContent() {
return content;
}
public int getType() {
return type;
}
}
用来储存消息
4、编写RecycleView子项的布局
新建msg_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:id="@+id/Left_chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:background="@drawable/chat_left"
android:orientation="horizontal">
<TextView
android:id="@+id/Left_Msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:gravity="center|left"
android:layout_margin="10dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/Right_chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:background="@drawable/chat_right"
android:orientation="horizontal">
<TextView
android:id="@+id/Right_Msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_margin="10dp"/>
</LinearLayout>
</LinearLayout>
这里我们让收到的消息居左对齐,发出的消息居右对齐,你可能会有些疑虑,怎么能让收到的消息和发出的消息都放在同一个布局里呢?不用担心,还记得我们前面学过的可见属性吗?只要稍后在代码中根据消息的类型来决定隐藏和显示哪种消息就可以了。
5、创建RecycleView的适配器类——MsgAdaoter
如果忘记了,可以看看上一篇博客有关于RecycleView的使用
5.1 复习setVisibility隐藏与显示
所有的Android控件都具有这个属性。
visible表示控件是可见的,这个值是默认值,不指定android:visibility时,控件都是可见的。
invisbale 表示控件不可见,但是它仍然占据着原来的位置和大小,可以理解成控件变成透明状态了。
gone 则表示控件不仅不可见,而且不再占用任何屏幕空间。
public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder> {
private List<Msg> mMsgList;
public MsgAdapter(List<Msg> mMsgList) {
this.mMsgList = mMsgList;
}
@NonNull
@Override
public MsgAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate
(R.layout.msg_item,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MsgAdapter.ViewHolder holder, int position) {
Msg msg = mMsgList.get(position);
if(msg.getType() == Msg.TYPE_RECEIVED){
//如果收到消息则显示左布局,不显示右布局
holder.binding.LeftChat.setVisibility(View.VISIBLE);
holder.binding.RightChat.setVisibility(View.GONE);
holder.binding.LeftMsg.setText(msg.getContent());
} else if (msg.getType() == Msg.TYPE_SENT) {
//如果发出消息则显示右布局,不显示左布局
holder.binding.LeftChat.setVisibility(View.GONE);
holder.binding.RightChat.setVisibility(View.VISIBLE);
holder.binding.RightMsg.setText(msg.getContent());
}
}
@Override
public int getItemCount() {
return mMsgList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
MsgItemBinding binding;
public ViewHolder(@NonNull View itemView) {
super(itemView);
binding = MsgItemBinding.bind(itemView);
}
}
}
6、修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private List<Msg> msgList = new ArrayList<>(); //保存消息
private List<Msg> ListmsgList = new ArrayList<>(); //保存消息
private MsgAdapter msgAdapter;
private int LeftMsgNumber = 0; //确定左边回复哪一条消息
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initMsgs();//初始化所有消息
LeftinitMsgs();//初始化左侧消息
ActivityMainBinding activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(activityMainBinding.getRoot());
//制作布局方式
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
//设置布局方式
activityMainBinding.RecyclerView1.setLayoutManager(linearLayoutManager);
//制作适配器并传递数据
msgAdapter = new MsgAdapter(msgList);
activityMainBinding.RecyclerView1.setAdapter(msgAdapter);
activityMainBinding.Button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String text = activityMainBinding.text1.getText().toString();
Log.d("TAg",text+("".equals(text)));
if(!"".equals(text)){
Msg msg = new Msg(text,Msg.TYPE_SENT);
msgList.add(msg);
//清空输入框内容
activityMainBinding.text1.setText("");
//左边回的消息
msgList.add(ListmsgList.get(LeftMsgNumber++));
//如果预定的消息数回复完了,从头开始
if(LeftMsgNumber == ListmsgList.size()) LeftMsgNumber = 0;;
//当有新的消息时刷新在RecycleView中的显示
msgAdapter.notifyItemInserted(msgList.size()-1);
//RecycleView定位到最后一行
activityMainBinding.RecyclerView1.scrollToPosition(msgList.size()-1);
}
}
});
}
private void initMsgs(){
Msg msg1 = new Msg("XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",Msg.TYPE_RECEIVED);
msgList.add(msg1);
}
private void LeftinitMsgs(){
Msg msg1 = new Msg("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",Msg.TYPE_RECEIVED);
ListmsgList.add(msg1);
ListmsgList.add(msg18);
}
}
解释一下这两行代码:
//当有新的消息时刷新在RecycleView中的显示
msgAdapter.notifyItemInserted(msgList.size()-1);
//RecycleView定位到最后一行
activityMainBinding.RecyclerView1.scrollToPosition(msgList.size()-1);
msgAdapter.notifyItemInserted(msgList.size()-1);
- 通知适配器
msgAdapter
有一个新的消息被插入到了列表中,以便更新RecyclerView的显示。在这里,msgList.size()-1
表示最后一条消息的位置索引。这意味着当有新的消息加入到msgList
列表中时,RecyclerView会刷新并显示新的消息。
activityMainBinding.RecyclerView1.scrollToPosition(msgList.size()-1);
- 这行代码通过RecyclerView的
scrollToPosition
方法将RecyclerView定位到指定位置。在这里,msgList.size()-1
表示最后一条消息的位置索引,所以这行代码的作用是将RecyclerView滚动到最后一条消息,确保用户能够直接看到最新的消息。
注: 笔者在这里也附带上打包好的APK文件
baidu网盘下载地址:点击即可下载。
「下载地址」