Android小项目————聊天室(UI篇)
一.前言
- 这是所做的第二个android项目,主要目的对暑假所学的java和android知识点进行复习巩固和实践,由于知识所限,目前这个聊天室并不是很完善,而且由于对于多线程的相关知识点不太熟练,所以服务器端经常崩,后期会将其更加完善
- 其中聊天室主界面主要借鉴的是郭霖老师《第一行代码》
二.所用知识点
- java
- IO流
- 网络编程
- 多线程
- android
- 活动
- 布局
- RecyckerView控件
三.界面运行效果截图
- 登录界面
- 客户端
- 服务器端
四.编程前准备
- 添加Recyckerview的依赖库
- 在app/build.gradle(在Project模式下打开,下同)文件中添加一条语句
compile 'com.android.support:recyclerview-v7:24.2.1'
- 添加的地方
- 修改后,点击弹出了提醒语句中的 Sync Now
- 在app/build.gradle(在Project模式下打开,下同)文件中添加一条语句
- 获得联网权限
- 在app/src/main/AndroidManifest.xml添加语句
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
- 在app/src/main/AndroidManifest.xml添加语句
- 准备聊天气泡素材
- 准备两张气泡图片
- 打开android 下的draw9patch.bat文件对图片进行编辑
- 将图片重命名为message_left.9.png和message_right.9.png
- 将图片放在文件夹app/src/main/res/drawable中
- 如何嫌麻烦,可直接使用源码中的素材,源码在下一篇
- 准备两张气泡图片
- 修改app图标
- 修改app/src/main/res/values/Strings.xml文件 修改内容如下:
<resources>
<string name="app_name">冰炭不投de小计算器</string>
</resources>
- 修改app/src/main/res/values/Strings.xml文件 修改内容如下:
- 修改app名称
- 将图标放在app/src/main/res/mipmap文件中
- 修改AndroidManifest.xml文件
android:allowBackup="true"
android:icon="@mipmap/图片名字" //修改这句
android:label="@string/app_name"
五.登录界面
- 目的:获得当前用户的昵称和ip地址
步骤:
- 新建一个android项目,并在最后一步选择Add No Activity
- 新建一个Activity,命名为RegisterActivity,并勾选Generate Layout File(创建一个对应界面)和Launcher Activity(选为主活动)选项
- 创建一个Name类
代码:
- RegisterActivity中的代码
public class RegisterActivity extends AppCompatActivity {
private EditText input_name;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
input_name = (EditText)findViewById(R.id.input_name);
Button confirm = (Button)findViewById(R.id.confirm);
confirm.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Name.name = input_name.getText().toString();
Name.IP = getLocalIpAddress() ;
Log.e("RegisterActivity",Name.IP+Name.name);
if(!Name.name.equals("")) {
Intent intent = new Intent(RegisterActivity.this, ChatroomActivity.class);
startActivity(intent);
}
}
});
}
//获取本机ip
public static String getLocalIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface
.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf
.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress()) {
return inetAddress.getHostAddress().toString();
}
}
}
} catch (SocketException ex) {
Log.e("WifiPreference IpAddress", ex.toString());
}
return null;
}
}
- app/src/main/res/layout/Activity_register.xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#d8e0e8"
>
<EditText
android:id="@+id/input_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="输入一个昵称,最好短点"
android:maxLines="2" />
<Button
android:id="@+id/confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确定"/>
</LinearLayout>
- Name中的代码
public class Name {
public static String name ;
public static String IP;
}
六.聊天界面
- 步骤:
- 新建一个Activity,命名为ChatroomActivity,勾选Generate Layout File不勾选auncher Activity
- 新建一个Msg类
- 新建一个MsgAdapter类
- 在app/src/main/res/layout文件夹中新建一个msg_item.xml文件
代码:
- ChatroomActivity中的代码
public class ChatroomActivity extends AppCompatActivity {
private List<Msg> msgList = new ArrayList<>();
private EditText inputTest;
private Button send;
private RecyclerView msgRecyclerView;
private MsgAdapter adapter;
private ClientThread mClientThread;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chatroom);
//各种赋值初始化
inputTest = (EditText)findViewById(R.id.input_text);
send = (Button)findViewById(R.id.send);
msgRecyclerView =(RecyclerView)findViewById(R.id.msg_recycler_view);
//创建一个LinearLayoutManager(线性布局)对象将它设置到RecyclerView
LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
msgRecyclerView.setLayoutManager(layoutmanager);
//调用构造方法创造实例,参数消息集合
adapter = new MsgAdapter(msgList);
msgRecyclerView.setAdapter(adapter);
//重写Handler类handleMessage方法,并将对象赋给mHandler
//该Handler绑定主线程,如果主线程的what为0则更新输入
mHandler=new Handler() {
@Override
public void handleMessage(Message handleMsg) {
if (handleMsg.what == 0) {
//接受到消息后的操作
String content = handleMsg.obj.toString();
Log.d( "xjj",content);
String [] arr = content.split("#\\$#");
String ip =arr[0];
String name =arr[1];
String str =arr[2];
Log.d( "xjj",ip + name + str);
Msg msg;
if(ip.equals(Name.IP)) {
msg = new Msg(name, str, Msg.TYPE_SENT);
}else {
msg = new Msg(name, str, Msg.TYPE_RECEIVED);
}
msgList.add(msg);
adapter.notifyItemInserted(msgList.size() - 1);//当有新消息时,刷新RecyclView中的显示
msgRecyclerView.scrollToPosition(msgList.size() - 1);//将RecyclerView定位到最后一行
inputTest.setText("");//清空输入框*/
}
}
};
//发送按钮监听器
send.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
String content = inputTest.getText().toString();
if(!"".equals(content)){
try {
//将输入框的信息传递给msg,并标记
Message handleMsg = new Message();
handleMsg.what = 1;
handleMsg.obj = inputTest.getText().toString();
//将msg传递给发送子线程
mClientThread.revHandler.sendMessage(handleMsg);
//输入框变空
inputTest.setText("");
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
//创建实例,将mHandler作为参数传递给mClientThread实例
mClientThread = new ClientThread(mHandler);
//启动子线程
new Thread(mClientThread).start();
}
}
- Msg中的代码
public class Msg {
public static final int TYPE_RECEIVED = 0;//收到的消息
public static final int TYPE_SENT = 1;//发出去的消息
private String name;
private String content;
private int type;
//content表示消息内容,type表示类型
public Msg(String name,String content ,int type){
this.name = name;
this.content = content;
this.type = type;
}
public String getContent(){
return content;
}
public int getType(){
return type;
}
public String getName() {return name;}
}
- MsgAdapter中的代码
public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder>{
private List<Msg> mMsgList;
//ViewHolder内部类
static class ViewHolder extends RecyclerView.ViewHolder{
LinearLayout leftLayout;
LinearLayout rightLayout;
TextView leftMsg;
TextView rightMsg;
TextView leftname;
//构造方法
public ViewHolder(View view){
super(view);
leftLayout = (LinearLayout)view.findViewById(R.id.left_layout);
rightLayout = (LinearLayout) view.findViewById(R.id.right_layout);
leftMsg = (TextView)view.findViewById(R.id.left_msg);
rightMsg = (TextView)view.findViewById(R.id.right_msg);
leftname = (TextView) view.findViewById(R.id.left_name);
}
}
//MsgAdapter构造方法,参数是消息类的集合
public MsgAdapter(List<Msg> msgList){
mMsgList = msgList;
}
@Override
//创建ViewHolder实例
public ViewHolder onCreateViewHolder(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(ViewHolder holder ,int position){
Msg msg = mMsgList.get(position);
if(msg.getType() == Msg.TYPE_RECEIVED){
//如果是收到的消息,则显示左边的消息布局,将右边的消息布局隐藏
holder.leftLayout.setVisibility(View.VISIBLE);
holder.rightLayout.setVisibility(View.GONE);
holder.leftMsg.setText(msg.getContent());
holder.leftname.setText(msg.getName());
}else if(msg.getType()==Msg.TYPE_SENT){
//如果是发出去的消息,则显示右边的消息布局,将左边的消息布局隐藏
holder.rightLayout.setVisibility(View.VISIBLE);
holder.leftLayout.setVisibility(View.GONE);
holder.rightMsg.setText(msg.getContent());
}
}
@Override
public int getItemCount(){
return mMsgList.size();
}
}
- msg_item.xml中的代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="6dp">
<LinearLayout
android:id="@+id/left_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:id="@+id/left_name"
android:layout_width="40dp"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="17sp"
android:ellipsize="end"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:background="@drawable/message_left">
<TextView
android:id="@+id/left_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="6dp"
android:textColor="#fff"
/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/right_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:background="@drawable/message_right">
<TextView
android:id="@+id/right_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="6dp"/>
</LinearLayout>
</LinearLayout>
- activity chatroom.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#d8e0e8"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/msg_recycler_view"
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/input_text"
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>