设计思路:

首先搭建聊天界面,想要的效果如下

整体为linearlayout线性布局 :
1.顶部是聊天界面的名称 一个TextView
2.中间是ListView 用来显示聊天信息
3.底部是一个水平布局的linearlayout,包含表情 发送按钮 输入框EditText

然后搭建聊天信息的布局

此处的布局效果如下

1.整体为水平布局的linearlayout,分为ImageView和右边的linearlayout
2.右边又是一个竖直的linearlayout,分为上面的linearlayout(包括两个TextView)和下面的TextView.

代码实现

两个布局文件

1、聊天界面布局,layout下的chat.xml文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/chat_background"//整体的背景android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="50dp"android:background="@color/green"android:gravity="center"字体位于中间android:text="自娱自乐聊天室"android:textSize="20sp" /><ListViewandroid:id="@+id/listview"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="1"//自由充满,防止list下面的内容被挤出去android:background="@drawable/chat_background"android:cacheColorHint="#00000000"//设置屏幕滑动的颜色为透明android:divider="@null"></ListView>//设置每条消息之间没有横线隔开<LinearLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:background="@color/hui"android:gravity="center"><ImageViewandroid:id="@+id/chat_image"android:layout_width="50dp"android:layout_height="wrap_content"android:layout_margin="5dp"android:src="@mipmap/addsimle" />//添加表情的按钮图标<Buttonandroid:id="@+id/button_back"android:layout_width="50dp"android:layout_height="match_parent"android:layout_margin="5dp"android:background="@drawable/btn_background"android:text="回复" /><EditTextandroid:id="@+id/chat_edittext"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:background="@drawable/denglu"android:hint="输入内容" /><Buttonandroid:id="@+id/button_send"android:layout_width="50dp"android:layout_height="match_parent"android:layout_margin="5dp"android:background="@drawable/btn_background"android:text="发送" /></LinearLayout>
</LinearLayout>
————————————————————————————————————————————————————
//这里面android:background="@drawable/chat_background"整体背景的设置使用渐变色 在@drawable/chat_background下的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><gradient//由上到下颜色渐变android:startColor="#ace7ef"android:centerColor="#c5e1dd"android:endColor="#e7d9c5"android:type="linear" //线性渐变 此外还有radial圆形渐变,sweep扇形渐变android:angle="-90"/>
</shape>

完成后布局如下:

2、消息布局,layout下的message.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent">
<TextViewandroid:id="@+id/message_time"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="星期几 时间"//在每条消息上方显示该消息发送的日期和时间android:gravity="center"android:padding="10dp"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><ImageView//头像,插入图片 设置大小android:id="@+id/message_img"android:layout_width="60dp"android:layout_height="60dp"android:src="@mipmap/t1"/><LinearLayout//竖直分布的LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:layout_marginLeft="5dp"><LinearLayout//水平布局的LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextViewandroid:id="@+id/textview_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="管理员"android:padding="5dp"android:textColor="@color/red"android:background="@drawable/message_title"//设置头衔的背景颜色android:textSize="15dp"/><TextViewandroid:id="@+id/textview_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="昵称"android:textColor="@color/black"android:textSize="15dp"android:layout_marginLeft="5dp"/></LinearLayout><TextViewandroid:id="@+id/textview_message"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="我是一只小萌兔"android:textColor="@color/black"android:background="@mipmap/chat"//添加聊天框效果 ,这里用到了.9图android:layout_marginRight="60dp"/></LinearLayout></LinearLayout>
</LinearLayout>

布局完成后效果如下:

关于发送表情

1、首先在chat.xml文件中添加一个Gridview

    <GridViewandroid:id="@+id/gridview"android:layout_width="match_parent"android:layout_height="200dp"android:numColumns="3"//设置为3列android:visibility="gone"></GridView>//设置没有添加时为空

2、然后写一个layout布局表情

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center">
<ImageViewandroid:id="@+id/imageview"android:layout_width="60dp"android:layout_height="60dp"android:src="@mipmap/biaoqing1"android:padding="10dp"/>
</LinearLayout>

3、写一个smileadapt与数据建立桥梁

public class SmileAdapter extends BaseAdapter {private int [] mDate={R.mipmap.biaoqing1,R.mipmap.biaoqing2,R.mipmap.biaoqing3,R.mipmap.b4,R.mipmap.b5, R.mipmap.b6,};//定义表情数组private LayoutInflater mFlater;public SmileAdapter(LayoutInflater mFlater) {this.mFlater = mFlater;}@Overridepublic int getCount() {return mDate.length;}@Overridepublic Object getItem(int position) {return position;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder vh = null;if(convertView==null){convertView = mFlater.inflate(R.layout.item_image,null);vh = new ViewHolder();vh.imageview = (ImageView) convertView.findViewById(R.id.imageview);convertView.setTag(vh);}vh = (ViewHolder) convertView.getTag();vh.imageview.setImageResource(mDate[position]);return convertView;}class  ViewHolder{ImageView imageview;}
}

在ChatActivity中:

  //声明表情变量 并找到,添加点击事件private ImageView mImageView;private Html.ImageGetter mImageGetter;private GridView mGridView;private SmileAdapter mSmileAdapter;private String[] mImageName = {"biaoqing1", "biaoqing2", "biaoqing3","b4","b5","b6"};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.chat);mImageView = (ImageView) findViewById(R.id.chat_image);mImageView.setOnClickListener(this);mImageGetter = new Html.ImageGetter() {@Overridepublic Drawable getDrawable(String source) {Drawable drawable = null;if (source != null) {Class clazz = R.mipmap.class;try {Field field = clazz.getDeclaredField(source);//用到了反射int sourceId = field.getInt(clazz);drawable = getResources().getDrawable(sourceId);drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}} else {drawable = getResources().getDrawable(R.mipmap.ic_launcher);drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());}return drawable;}};mData = new ArrayList<>();mAdapter = new MessageAdapter(getLayoutInflater(), mData, mImageGetter);mListView.setAdapter(mAdapter);mSmileAdapter = new SmileAdapter(getLayoutInflater());mGridView.setAdapter(mSmileAdapter);
//点击某个表情时,添加到输入框mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {Log.d("TAG","DFSDFSD");Spanned spanned = Html.fromHtml("<img src=''/>", mImageGetter, null);//富文本,利用Spanned得到图片mEditText.getText().insert(mEditText.getSelectionStart(), spanned);//将spanned添加到mEditText中}});
——————————————————————————————
//点击事件如下@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.chat_image:if (mGridView.getVisibility() == View.VISIBLE) {mGridView.setVisibility(View.GONE);} else {mGridView.setVisibility(View.VISIBLE);}break;

每点击一次时会在输入框中出现一个表情

关于发送消息(重点难点)

1、首先新建一个Message类,包含消息布局中的属性,然后给出setter和getter,及构造器

public class ChatMessage {private long time;//时间private int imgId;//头像图片private String message_title;//头衔private String message_name;//昵称private String message;//聊天内容public ChatMessage(String message_title, long time, String message_name, String message, int imgId) {this.message_title = message_title;this.time = time;this.message_name = message_name;this.message = message;this.imgId = imgId;}public ChatMessage() {}public String getMessage_title() {return message_title;}public void setMessage_title(String message_title) {this.message_title = message_title;}public long getTime() {return time;}public void setTime(long time) {this.time = time;}public String getMessage_name() {return message_name;}public void setMessage_name(String message_name) {this.message_name = message_name;}public String getMessage() {return message;}public void setMessage(String message_title) {this.message = message_title;}public int getImgId() {return imgId;}public void setImgId(int imgId) {this.imgId = imgId;}
}

2、新建MessageAdapter,重写四个方法,与message.xml布局的信息获得联系
Adapter的作用就是ListView界面与数据之间的桥梁,当列表里的每一项显示到页面时,都会调用Adapter的getView方法返回一个View。

    //**Inflater英文意思是膨胀,在Android中应该是扩展的意思吧。 LayoutInflater的作用类似于 findViewById(),不同点是LayoutInflater是用来找layout文件夹下的xml布局文件,并且实例化!而 findViewById()是找具体某一个xml下的具体 widget控件(如:Button,TextView等)。**public class MessageAdapter extends BaseAdapter {private LayoutInflater mFlater;private List<ChatMessage> mData;private Html.ImageGetter mImageGetter;private SimpleDateFormat mFormat;public static  final  int MESSAG_SEND=0;public static  final  int MESSAGE_BACK=1;public static  final  int TYPE=2;//构造器public MessageAdapter(LayoutInflater mFlater, List<ChatMessage> mData, Html.ImageGetter mImageGetter) {this.mFlater = mFlater;this.mImageGetter = mImageGetter;this.mData = mData;mFormat = new SimpleDateFormat("EEE HH:mm");//日期和时间}@Overridepublic int getViewTypeCount() {return TYPE;//类型。message的类型 分为发送MESSAG_SEND和回复MESSAGE_BACK,所以返回2}@Overridepublic int getItemViewType(int position) {return mData.get(position).getType();}//返回当前view的布局显示的类型@Overridepublic int getCount() {return mData.size();}@Overridepublic Object getItem(int position) {return position;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// 分为发送信息和回复信息,分别适配两个布局中的数据ViewHolder vh = null;ViewHolderBack vhBack=null;int type = getItemViewType(position);if (convertView == null) {switch (type) {case MESSAG_SEND:convertView = mFlater.inflate(R.layout.message, null);vh = new ViewHolder();vh.textView_time = (TextView) convertView.findViewById(R.id.message_time);vh.imageView_head = (ImageView) convertView.findViewById(R.id.message_img);vh.textView_title = (TextView) convertView.findViewById(R.id.textview_title);vh.textView_name = (TextView) convertView.findViewById(R.id.textview_name);vh.textView_message = (TextView) convertView.findViewById(R.id.textview_message);convertView.setTag(vh);break;case MESSAGE_BACK:convertView = mFlater.inflate(R.layout.backmessage, null);vhBack = new ViewHolderBack();vhBack.textView_time = (TextView) convertView.findViewById(R.id.message_time);vhBack.imageView_head = (ImageView) convertView.findViewById(R.id.message_img);vhBack.textView_title = (TextView) convertView.findViewById(R.id.textview_title);vhBack.textView_name = (TextView) convertView.findViewById(R.id.textview_name);vhBack.textView_message = (TextView) convertView.findViewById(R.id.textview_message);convertView.setTag(vhBack);break;default:break;}}ChatMessage msg = mData.get(position);switch (type){case MESSAG_SEND:vh = (ViewHolder) convertView.getTag();Spanned spanned = Html.fromHtml(msg.getMessage(), mImageGetter, null);vh.imageView_head.setImageResource(msg.getImgId());vh.textView_time.setText(mFormat.format(new Date(msg.getTime())));vh.textView_title.setText(msg.getMessage_title());vh.textView_name.setText(msg.getMessage_name());vh.textView_message.setText(spanned);break;case MESSAGE_BACK:vhBack = (ViewHolderBack) convertView.getTag();Spanned spanned2 = Html.fromHtml(msg.getMessage(), mImageGetter, null);vhBack.imageView_head.setImageResource(msg.getImgId());//得到图片vhBack.textView_time.setText(mFormat.format(new Date(msg.getTime())));vhBack.textView_title.setText(msg.getMessage_title());vhBack.textView_name.setText(msg.getMessage_name());vhBack.textView_message.setText(spanned2);break;default:break;}return convertView;}class ViewHolder {TextView textView_time;ImageView imageView_head;TextView textView_title;TextView textView_name;TextView textView_message;}class ViewHolderBack{TextView textView_time;ImageView imageView_head;TextView textView_title;TextView textView_name;TextView textView_message;}
}

3、ChatActivity

public class ChatActivity extends Activity implements View.OnClickListener {private Button mButtonSend;private Button mButtonBack;private ImageView mImageView;private EditText mEditText;private Html.ImageGetter mImageGetter;private List<ChatMessage> mData;private MessageAdapter mAdapter;private ListView mListView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.chat);//找到相应的数据mButtonSend = (Button) findViewById(R.id.button_send);mButtonBack = (Button) findViewById(R.id.button_back);mListView = (ListView) findViewById(R.id.listview);mEditText = (EditText) findViewById(R.id.chat_edittext);mImageView = (ImageView) findViewById(R.id.chat_image);//设置点击事件mButtonSend.setOnClickListener(ChatActivity.this);mButtonBack.setOnClickListener(ChatActivity.this);mImageView.setOnClickListener(this);mImageGetter = new Html.ImageGetter() {@Overridepublic Drawable getDrawable(String source) {Drawable drawable = getResources().getDrawable(R.mipmap.biaoqing2);drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());return drawable;}};mData = new ArrayList<>();mAdapter = new MessageAdapter(getLayoutInflater(), mData, mImageGetter);//与数据连接mListView.setAdapter(mAdapter);}
//下面是点击事件方法@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.chat_image://点击表情是添加表情if (mGridView.getVisibility() == View.VISIBLE) {mGridView.setVisibility(View.GONE);} else {mGridView.setVisibility(View.VISIBLE);}break;case R.id.button_send://发送按钮//下面给message中的属性赋值ChatMessage message = new ChatMessage();message.setImgId(R.mipmap.t2);message.setMessage_title("盟主");message.setMessage_name("小萌兔");message.setType(MessageAdapter.MESSAG_SEND);//信息类型是发送message.setTime(System.currentTimeMillis());//显示时间message.setMessage(filterHtml(Html.toHtml(mEditText.getText())));//filterHtml方法用于出去消息框中多余的部分mData.add(message);mAdapter.notifyDataSetChanged();//刷新mListView.setSelection(mData.size() - 1);//设置自动下滚mEditText.setText("");//清空输入框break;//回复按钮case R.id.button_back:ChatMessage messageBack = new ChatMessage();messageBack.setImgId(R.mipmap.t1);messageBack.setMessage_title("圣子");messageBack.setMessage_name("潇洒哥");messageBack.setType(MessageAdapter.MESSAGE_BACK);messageBack.setTime(System.currentTimeMillis());messageBack.setMessage(filterHtml(Html.toHtml(mEditText.getText())));mData.add(messageBack);mAdapter.notifyDataSetChanged();mListView.setSelection(mData.size() - 1);mEditText.setText("");break;default:break;}}}
//filterHtml方法用于出去消息框中多余的部分private String filterHtml(String str) {str = str.replaceAll("<(?!br|img)[^>]+>","").trim();return str;}

补充

在这个过程中需要注意一些没有提到的细节
1、设置全屏: 再次用到的方法是 在androidManifest.xml文件中,activity中添加

    <activityandroid:name=".ChatActivity"android:theme="@android:style/Theme.NoTitleBar" />//设置为全屏
/>

2.注意去掉每条list之间的黑线
3.关于按钮的点击效果,在drawable下:

//btn_normal
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><corners android:radius="@dimen/bnt_corners" /><solid android:color="@color/blue" />
</shape>
——————————————————————————————————————————————————————
//btn_pressed
<shape xmlns:android="http://schemas.android.com/apk/res/android"><corners android:radius="@dimen/bnt_corners" /><solid android:color="#ddcccc" />
</shape>
//btn_background
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:drawable="@drawable/btn_pressed" android:state_pressed="true" /><item android:drawable="@drawable/" />
</selector>
//头衔背景的drawable
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/green"></solid>//颜色<corners android:radius="20dp"></corners>//角的圆滑程度
</shape>

4、在布局回复信息时

<LinearLayoutandroid:layout_width="wrap_content"//不要写成match_parentandroid:layout_height="wrap_content"android:layout_weight="1"//自由充满android:orientation="vertical"android:gravity="right">

5、这里我还写了一个登陆界面,后面将加入数据库来完善。现在只是简单的两个activity之间的跳转,在MainActivity中

   @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mButtonLogin = (Button) findViewById(R.id.button_login);mButtonLogin.setOnClickListener(new View.OnClickListener() {//设置点击事件@Overridepublic void onClick(View v) {Intent intent = new Intent(MainActivity.this,ChatActivity.class);//定义一个Intent,指向下一个界面startActivity(intent);//直接启动intent}});}

最终效果如下

简单的仿QQ聊天(自娱自乐聊天室)相关推荐

  1. java 飞秋 demo_Java实现仿QQ、飞秋聊天程序

    [实例简介] 用java CS 模式实现的简单版的java实现的仿qq聊天程序,有上线下线弹窗提醒.聊天.上传头像.自动更新好友列表.自动加载好友信息.发送消息.发送文件等功能,减压后的文件夹下附有说 ...

  2. Android—简单的仿QQ聊天界面

    最近仿照QQ聊天做了一个类似界面,先看下界面组成(画面不太美凑合凑合呗,,,,): 其中聊天背景可以是一个LinearLayout或者RelativeLayout里面存放的是ListView(将Lis ...

  3. java仿qq聊天系统 多人聊天室

    目录 项目介绍 项目截图 服务器与客户端 新用户注册 注册新账号成功 进入聊天室 多人在线 选择发送文件 文件接收提醒 项目代码参考 服务器入口程序 服务器请求处理 原理解析 服务器多人网络连接: 如 ...

  4. JSP简单实现仿QQ群聊

    先来了解一下JSP四大作用域吧! page里的变量没法从index.jsp传递到test.jsp.只要页面跳转了,它们就不见了. request里的变量可以跨越forward前后的两页.但是只要刷新页 ...

  5. 用基本控件简单地仿QQ登录界面

    2019独角兽企业重金招聘Python工程师标准>>> //  Created by 妖精的尾巴 on 15-8-14. //  Copyright (c) 2015年 妖精的尾巴. ...

  6. java简单实现仿QQ登陆界面

    实现思路: 1.总体用南北布局,北部为一个label图片,中部是一个自定义面板 2.面板不采用默认布局,自己给组建设置布局方式 package com.shl; import javax.swing. ...

  7. ios音乐播放器-仿QQ音乐

    这篇文章主要写一个iOS系统下的音乐播放器 , 包括简单的仿QQ音乐播放器界面.音乐播放.歌词解析.后台控制等  ,如果你正好需要 , 希望你看完后能够对你的提升有所帮助 , 当然,阅读中如果发现什么 ...

  8. 高仿QQ即时聊天软件开发系列之三登录窗口用户选择下拉框

    上一篇高仿QQ即时聊天软件开发系列之二登录窗口界面写了一个大概的布局和原理 这一篇详细说下拉框的实现原理 先上最终效果图 一开始其实只是想给下拉框加一个placeholder效果,让下拉框在未选择未输 ...

  9. java仿qq思路_java仿QQ聊天软件OIM艰辛之路(开源项目)

    既然QQ能仿ICQ, 咱java也来个仿QQ. 在我刚学完java后,就想做点什么项目锻炼下自己的技能.凑巧的是,我一个同样学java的朋友在做一个仿qq的项目,不过他做的实在太丑了. 然后他想让我也 ...

最新文章

  1. 【每日一算法】删列造序
  2. c# 获取所有的进程的cpu使用率_Linux CPU使用率很高,但为啥却找不到高CPU的进程
  3. LeetCode Longest Increasing Subsequence(动态规划、二分法)
  4. flutter 判断是不是调试模式_女人怎么判断男人是不是喜欢自己//男人真正喜欢女人的追求方式...
  5. 160 - 31 cracking4all.2
  6. 查看oracle资源使用情况,Oracle查询表空间使用情况
  7. AI手机会怎么样?那不得看高通骁龙的AI能怎样
  8. Spark--安装和配置遇到的所有问题
  9. Tkinter模拟发送邮箱验证码并在指定时间后验证码过期
  10. Fedora 安装 QQ音乐
  11. mysql升级 增删改查询
  12. 来电铃声播放流程总结
  13. 光纤接头截面工艺分类
  14. OSPF网络类型以及不规则区域练习
  15. linux 设置创建用户设置密码
  16. GCS_SERVER_PROCESSES
  17. 【机器学习炼丹炉】使用便宜的x99和二手显卡,单显卡的学习机器在3000元,多显卡更贵,可以用支架,架起显卡,用做算法的学习使用,模型训练需要显卡的算力支持,低成本做算法研究,万一从入门到放弃了呢
  18. linux通过xdm开启端口,Red Hat Linux 5.4 开启xdm远程访问
  19. 《那些年啊,那些事——一个程序员的奋斗史》——68
  20. 如何才能成为数字IC后端ECO专家?

热门文章

  1. 【计算机毕业设计】交通事故档案管理系统
  2. 双十一怎样开直通车,开直通车需要注意的六大点
  3. JAVA商城 B2B2C商城系统 小程序 多用户商城系统 直播带货 新零售商城 o2o商城 电子商务 分销商城 直播商城 短视频商城 springcloud商城 spring cloud商
  4. 【Kevin Learn QMUI】-->QMUIDialog
  5. 液晶显示器黑色彩条泛蓝原因。
  6. sdf文件使用plugin
  7. python 爬虫案例:爬取百度贴吧图片
  8. 从大型语言模型LLM走向人工通用智能AGI的改进方向(public)
  9. 浅浅的,淡淡的,久久的
  10. qtp中type方法的按键常量