聊天软件界面开发
前言:
这是开始学习Android的开发的第5天,一直是跟着郭霖大师的第一行代码
学习,
这里边发篇博文记录,边帮自己整理下思路,毕竟思路顺了,才是真的学会。
一、整体构思:
1. 准备3张背景图片
① chat_bg; //用于当作聊天背景图片
② chat__ed_left.9.png; //用于当作左聊天气泡
③ chat__ed_right.9.png; //用于当作右聊天气泡
注:*.9.png怎么制作在之前说过。
2. 编写主界面chatmain_layout.xml,主界面的特点:
① 主界面由聊天对话框部分和输入框部分组成,且聊天对话框部分位于
输入框的上部,
可以理解为这俩部分是纵向线性排列,所以主界面
采用<
LinearLayout/>
② 分析聊天对话框部分:聊天就是你一句我一句,先说的在上,后说的
在下依次纵
向排列,所以我们对这部分使用
<
ListView/>
控件。具体
ListView怎么进行布局,
后面再说,我们现在直说主界面。
③ 分析输入框部分:输入部分由俩部分组成,一个输入框(
<
EditText/>
)
和一个
发送按钮(
<
Button/>
),且它们处于同一条直线上,是横向线性排列。
④
聊天对话框部分和输入框部分用透明分隔线分割
android
:divider=
"#0000"
。
⑤
主界面的背景图片是chat_bg
。
3. 编写聊天对话框子界面msg_layout.xml,子界面的特点:
① 子界面就是前面说的ListView的界面,我们不能就让聊天时按普通ListView
的格式
显示吧,
也得美化一下,所以我们新增此布局。ListView就是纵向线
性排列,所以
主布局我们用<LinearLayout/>。
② 对于每一条聊天记录,又是由一个气泡背景图+一串字符串组成的。聊天背景
图我
们借用LinearLayout的background设置,字符串由<TextView/>设置。
③ 一般聊天分左右气泡,俩格式一样,只是区分好id和背景气泡就好。
4
. 编写聊天信息实体类Msg.java:
①里面包含消息内容和消息类型(接受/发送)。
5. 编写ListView适配器类MsgAdapter,它的特点如下:
① 此类继承适配器类ArrayAdapter,并指定泛型为<Msg>。
②
为什么要用到此类:ListView是用来给我们展示数据的,而需要展示的这些数据
是无法
直接通过数组传递给ListView的,我们还需借助适配器来完成;再者此类
还需将自定义的
ListView布局加载到主布局内。
③ ArrayAdapter的构造函数很多,我们选用其中的一种,它有3个参数:
Context
context
, // 传入上下文,一会将传入主活动类
int
textViewResourceId
, //ListView子项布局的id,
//我们已经为List自定义好了布局,一会传入msg_layout的id
List
<
Msg
>
objects //我们准备加载的数据
④ 本类里面还有一个重要的方法getView(),它的作用在ListView的子项滚动到屏幕内
的时候
被调用,它用来处理子布局的逻辑。具体解释看代码里面的注释。
6
. 编写主活动ChatActivity
,它的特点如下:
① 加载主布局
② 初始化数据
③ 初始化适配器(传入上下文,传入ListView子布局,传入显示的数据)
④ 为主布局里面的控件创建实例(主活动只加载主布局里面的控件,适配器加载子布局里面的控件)
⑤ 为发送按钮,设置点击事件。
二、代码部分:
1.chatmain_layout.xml
<pre style="background-color:#ffffff;color:#000000;font-family:'Courier New';font-size:15.0pt;"><pre class="html" name="code"><span style="font-size:14px;"><?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="match_parent"
android:orientation="vertical"
android:background="@drawable/chat_bg">
<!--聊天对话框部分
android:divider="#0000"透明分隔线-->
<ListView
android:id="@+id/msg_list_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="#0000"/>
<!--输入框部分(横排排列)-->
<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:textAllCaps="false"
android:text="Send"/>
</LinearLayout>
</LinearLayout>
</span>
<pre style="background-color:#ffffff;color:#000000;font-family:'Courier New';font-size:15.0pt;"><span><span><span><span style="font-size:18px;"><span style="font-size:24px;"><strong><span style="color:#cc33cc;">2.msg</span></strong><span style="font-size:24px;"><strong><span style="color:#cc33cc;">_layout.xml</span></strong></span></span></span></span></span></span>
<span style="font-size:14px;"><?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="match_parent"
android:orientation="vertical"
android:padding="10dp">
<!--左chat-->
<LinearLayout
android:id="@+id/left_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:background="@drawable/chat_ed_left">
<TextView
android:id="@+id/left_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:textColor="#fff"/>
</LinearLayout>
<!--右chat-->
<LinearLayout
android:id="@+id/right_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:background="@drawable/chat_ed_right">
<TextView
android:id="@+id/right_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:textColor="#a40d88"/>
</LinearLayout>
</LinearLayout>
</span>
3. Msg.java
<span style="font-size:14px;">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;
}
}</span>
4.MsgAdapter.java
<span style="font-size:14px;">package com.example.cpj.UICustomerViews;
import android.content.Context;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.List;
import com.example.cpj.uiwidgettest.R;
/**
* Created by cpj on 2016/3/15.
*/
public class MsgAdapter extends ArrayAdapter<Msg>{
private int resourceId;
public MsgAdapter(Context context, int textViewResourceId,
List<Msg> objects){
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
/**
* 功能描述:这个方法在每个子项滚动到屏幕内的时候被调用
* 1.首先通过 getItem()获取当前项的ToolBar实例
* 2.使用LayoutInflater将这个子布局项加载并传入我们的主布局
* 3.调用View的findViewById()分别获取到左右Layout、左右Msg的实例
* 4.调用它们的setText()来显示文字
* 5.最后返回布局
* */
public View getView(int position, View convertView, ViewGroup parent){
Msg msg = getItem(position);
/*
* 新增内部类ViewHolder,用于对控件的实例进行缓存。
* 1.当convertView为空时,创建一个ViewHolder对象,并将控件的实例对象存放在ViewHolder里。
* 2.然后调用View的setTag()方法,将ViewHolder对象存储在View中。
* 3.当convertView不为空时,调用View的getTag()方法把ViewHolder重新取出来。
* */
View view;
ViewHolder viewHolder;
/*加载自定义布局与控件实例*/
if(convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
//创建控件实例并进行缓存
viewHolder = new ViewHolder();
viewHolder.leftLayout = (LinearLayout) view.findViewById(R.id.left_layout);
viewHolder.rightLayout = (LinearLayout) view.findViewById(R.id.right_layout);
viewHolder.leftMsg = (TextView) view.findViewById(R.id.left_msg);
viewHolder.rightMsg = (TextView) view.findViewById(R.id.right_msg);
view.setTag(viewHolder);
} else {
//convertView参数用于将之前加载好的布局进行缓存,以便之后可以进行复用(提高效率)
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
/*接受与发送消息的分类处理*/
//如果为收到的消息,则显示左边的消息布局,将右边的消息布局隐藏
if(msg.getType() == Msg.TYPE_RECEIVED){
viewHolder.leftLayout.setVisibility(View.VISIBLE);
viewHolder.rightLayout.setVisibility(View.GONE);
viewHolder.leftMsg.setText(msg.getContent());
} else if(msg.getType() == Msg.TYPE_SENT){
viewHolder.rightLayout.setVisibility(View.VISIBLE);
viewHolder.leftLayout.setVisibility(View.GONE);
viewHolder.rightMsg.setText(msg.getContent());
}
return view;
}
//新增内部类ViewHolder,用于对控件的实例进行缓存。
class ViewHolder{
LinearLayout leftLayout;
LinearLayout rightLayout;
TextView leftMsg;
TextView rightMsg;
}
}
</span>
5.ChatActivity.java
<span style="font-size:14px;">package com.example.cpj.UICustomerViews;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import com.example.cpj.uiwidgettest.BaseActivity;
import com.example.cpj.uiwidgettest.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by cpj on 2016/3/15.
*/
public class ChatActivity extends BaseActivity{
private ListView msgListView;
private EditText inputText;
private Button send;
private MsgAdapter adapter;
private List<Msg> msgList = new ArrayList<Msg>();
protected void onCreate(Bundle saveInstanceState){
super.onCreate(saveInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.chatmain_layout);
initMsgs();//初始化消息数据
</span><span style="font-size:14px;">
adapter = new MsgAdapter(ChatActivity.this, R.layout.msg_layout, msgList);
inputText = (EditText)findViewById(R.id.input_text);
send = (Button)findViewById(R.id.send);
msgListView = (ListView)findViewById(R.id.msg_list_view);
msgListView.setAdapter(adapter);
//发送按钮的点击事件
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String content = inputText.getText().toString();
if(!"".equals(content)){
Msg msg = new Msg(content, Msg.TYPE_SENT);
msgList.add(msg);
adapter.notifyDataSetChanged();//当有消息时刷新
msgListView.setSelection(msgList.size());//将ListView定位到最后一行
inputText.setText("");//清空输入框的内容
}
}
});
}
/**
* 初始化消息数据
* */
private void initMsgs(){
Msg msg1 = new Msg("Hello cpj.", Msg.TYPE_RECEIVED);
msgList.add(msg1);
Msg msg2 = new Msg("Hello Who is that?", Msg.TYPE_SENT);
msgList.add(msg2);
Msg msg3 = new Msg("This is pengpeng,Nice talking to you.", Msg.TYPE_RECEIVED);
msgList.add(msg3);
}
}
</span>
三、思路回顾:
1.我们创建了2个布局:
第一个是主布局,这是一个聊天界面的大框架,分为对话框部分(ListView)和
消息输入部分(EditText和Button)。
第二个布局子布局,是为了对话框部分的美化而设置的布局(气泡背景图+聊天文字)。
2.聊天时消息时主体,所以创建消息实体类Msg,就是对消息的封装。
3. 创建适配器类MsgAdapter去对应子布局,对子布局加载并加入处理逻辑。
4. 创建主活动类ChatActivity,加载主布局,并初始化数据和适配器。
附一张没那么漂亮的图片(气泡图片好难找):