1. 自定义标题栏。(标题栏不做任何功能)
2. 有左右发送按钮。(这个只能自己和自己聊天哦,所以有左右发送按钮)
(1)点击左边按钮发送按钮,在ListView的左侧显示。
(2)点击右边按钮发送按钮,在ListView的右侧显示。
3.有表情发送按钮。
(1)当点击表情发送按钮时, 弹出表情框,点击想要发送的表情将其添加输入框中。
(2)当在此点击表情按钮时,表情框收回。
(3)当表情框处在显示状态时, 点击输入框时,表情框收回。
聊天界面的制作(一)——基本布局的实现
聊天界面的制作(三)——表情列表发送功能
源码下载链接
1. 定义一个点击左边发送按钮时,显示在左边的消息布局。
2. 定义一个点击右边发送按钮时,显示在左边的消息布局。
3. 数据M:定义消息的类ChatMessage。
public class ChatMessage {
private int imageViewPerson;//人物头像
private long textViewTime;//显示的时间
private String textViewHonour;//人物头衔
private String textviewName;//人物昵称
private String textViewInput;//说话内容
private int type;//信息类型,是在左边显示还是右边显示。
/* 定义两个构造器,一个无参,一个传值。 */
public ChatMessage() {
}
public ChatMessage(int imageViewPerson, long textViewTime, String textViewHonour, String textviewName, String textViewInput) {
this.imageViewPerson = imageViewPerson;
this.textViewTime = textViewTime;
this.textViewHonour = textViewHonour;
this.textviewName = textviewName;
this.textViewInput = textViewInput;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getImageViewPerson() {
return imageViewPerson;
}
public void setImageViewPerson(int imageViewPerson) {
this.imageViewPerson = imageViewPerson;
}
public long getTextViewTime() {
return textViewTime;
}
public void setTextViewTime(long textViewTime) {
this.textViewTime = textViewTime;
}
public String getTextViewHonour() {
return textViewHonour;
}
public void setTextViewHonour(String textViewHonour) {
this.textViewHonour = textViewHonour;
}
public String getTextviewName() {
return textviewName;
}
public void setTextviewName(String textviewName) {
this.textviewName = textviewName;
}
public String getTextViewInput() {
return textViewInput;
}
public void setTextViewInput(String textViewInput) {
this.textViewInput = textViewInput;
}
}
4. 视图V: 定义一个MessageAdapter.
首先定义左右显示的两个布局,左边信息布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<TextView android:id="@+id/textview_time" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:layout_margin="5dp"/>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal">
<ImageView android:id="@+id/imageview_person" android:layout_width="50dp" android:layout_height="50dp" android:src="@mipmap/person" android:layout_marginRight="10dp" android:layout_marginLeft="10dp"/>
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical">
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1">
<TextView android:id="@+id/textview_honor" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="管理员" android:textSize="15sp" android:textColor="@color/white" android:background="@drawable/textview_honour_background"/>
<TextView android:id="@+id/textview_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="名称" />
</LinearLayout>
<TextView android:id="@+id/textview_input" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:layout_marginRight="50dp" android:text="这是显示的信息" android:background="@mipmap/textview_input_left"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
右边信息布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<TextView android:id="@+id/textview_time" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:gravity="center" />
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right" android:orientation="horizontal">
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="right" android:orientation="vertical">
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1">
<TextView android:id="@+id/textview_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:text="名称" />
<TextView android:id="@+id/textview_honor" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/textview_honour_background" android:text="管理员" android:textColor="@color/white" android:textSize="15sp" />
</LinearLayout>
<TextView android:id="@+id/textview_input" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="50dp" android:layout_marginTop="5dp" android:background="@mipmap/textview_input_right" android:text="这是显示的信息这是显示的信息这是显示的信息这是显示的信息这是显示的信息这是显示的信息这是显示的信息" android:textColor="@color/white" />
</LinearLayout>
<ImageView android:id="@+id/imageview_person" android:layout_width="50dp" android:layout_height="50dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:src="@mipmap/person" />
</LinearLayout>
</LinearLayout>
定义Adapter适配器,将数据适配到布局中:
public class MessageAdapter extends BaseAdapter {
public final static int SEND_LEFT = 0;
public final static int SEND_RIGHT = 1;
private LayoutInflater mInflater;
private List<ChatMessage> mData;
private Html.ImageGetter mImageGetter;
private SimpleDateFormat format;
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
if (0 == mData.get(position).getType()) {
return SEND_LEFT;//消息类型在左边
} else if (1 == mData.get(position).getType()) {
return SEND_RIGHT;//消息类型在右边
} else {
return 0;
}
}
public MessageAdapter(LayoutInflater mInflater, List<ChatMessage> mData, Html.ImageGetter mImageGetter) {
this.mInflater = mInflater;
this.mData = mData;
this.mImageGetter = mImageGetter;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
ViewHolder viewHolder = null;
if (convertView == null) {
viewHolder = new ViewHolder();
/* 通过判断消息类型的不同,加载不同的布局。这里使用到了getItemViewType()方法。 */
switch (getItemViewType(position)) {
case SEND_LEFT:
convertView = mInflater.inflate(R.layout.listview_lyaout_left, null);
break;
case SEND_RIGHT:
convertView = mInflater.inflate(R.layout.listview_layout_right, null);
break;
default:
break;
}
viewHolder.imageViewPerson = (ImageView) convertView.findViewById(R.id.imageview_person);
viewHolder.textViewTime = (TextView) convertView.findViewById(R.id.textview_time);
viewHolder.textViewHonour = (TextView) convertView.findViewById(R.id.textview_honor);
viewHolder.textViewName = (TextView) convertView.findViewById(R.id.textview_name);
viewHolder.textViewInput = (TextView) convertView.findViewById(R.id.textview_input);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
ChatMessage data = mData.get(position);
//如果不是第一个Item且发送消息的两次时间在1min之内,则不再显示时间;否则显示时间
if (position != 0) {
ChatMessage dataBefore = mData.get(position - 1);
long dateDifference = data.getTextViewTime() - dataBefore.getTextViewTime();
if (dateDifference < 60000) {
viewHolder.textViewTime.setVisibility(View.GONE);
} else {
format = new SimpleDateFormat("EEE HH:mm:ss");
String time = format.format(new Date(data.getTextViewTime()));
viewHolder.textViewTime.setText(time);
}
} else {
format = new SimpleDateFormat("EEE HH:mm:ss");
String time = format.format(new Date(data.getTextViewTime()));
viewHolder.textViewTime.setText(time);
}
viewHolder.imageViewPerson.setImageResource(data.getImageViewPerson());
viewHolder.textViewHonour.setText(data.getTextViewHonour());
viewHolder.textViewName.setText(data.getTextviewName());
//将受到的数据以富文本的形式显示。
Spanned spanned = Html.fromHtml(data.getTextViewInput(), mImageGetter, null);
viewHolder.textViewInput.setText(spanned);
return convertView;
}
class ViewHolder {
ImageView imageViewPerson;
TextView textViewHonour;
TextView textViewName;
TextView textViewTime;
TextView textViewInput;
}
}
这里用到了一个知识点,重写BaseAdapter中的两个方法:
有时候ListView中每个Item的布局不同,这时需要用到Adapter中的两个方法:
public int getViewTypeCount()
:返回不同布局类型的数量。
public int getItemViewType(int position)
:返回当前布局类型。
因为消息类型的不同,以至于显示的Item布局不同,因此需要在ChatMessage中定义一个int 类型的type,通过type来判断显示的消息类型,从而加载不同的布局。
private void showListViewRight() {
ChatMessage dataRight = new ChatMessage();
dataRight.setTextViewTime(System.currentTimeMillis());
dataRight.setTextViewHonour("营长");
dataRight.setTextviewName("虫虫");
/* 判断发送的消息是否为空,如果为空则弹出提示不允许发送 */
if (mEditTextInput.getText().equals("")) {
Toast.makeText(getApplicationContext(), "发送的消息不能为空!", Toast.LENGTH_SHORT).show();
return;
}
dataRight.setTextViewInput(mEditTextInput.getText());
dataRight.setType(MessageAdapter.SEND_RIGHT);
mMessageAdapter.notifyDataSetChanged();
mData.add(dataRight);
mListView.setAdapter(mMessageAdapter);
mListView.setSelection(mData.size() - 1);
mEditTextInput.setText("");
}
private void showListViewLeft() {
ChatMessage dataLeft = new ChatMessage();
dataLeft.setTextViewTime(System.currentTimeMillis());
dataLeft.setTextViewHonour("营长");
dataLeft.setTextviewName("虫虫");
/* 判断发送的消息是否为空,如果为空则弹出提示不允许发送 */
if (mEditTextInput.getText().equals("")) {
Toast.makeText(getApplicationContext(), "发送的消息不能为空!", Toast.LENGTH_SHORT).show();
return;
}
//将解析的数据添加到输入框中。
dataLeft.setTextViewInput(HmEditTextInput.getText());
dataLeft.setType(MessageAdapter.SEND_LEFT);
mMessageAdapter.notifyDataSetChanged();
mData.add(dataLeft);
mListView.setAdapter(mMessageAdapter);
mListView.setSelection(mData.size() - 1);
mEditTextInput.setText("");
}