简单的仿QQ聊天(自娱自乐聊天室)
2015-08-31 19:56
459 查看
设计思路:
首先搭建聊天界面,想要的效果如下
整体为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"> <TextView android:layout_width="match_parent" android:layout_height="50dp" android:background="@color/green" android:gravity="center"字体位于中间 android:text="自娱自乐聊天室" android:textSize="20sp" /> <ListView android: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>//设置每条消息之间没有横线隔开 <LinearLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="@color/hui" android:gravity="center"> <ImageView android:id="@+id/chat_image" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_margin="5dp" android:src="@mipmap/addsimle" />//添加表情的按钮图标 <Button android:id="@+id/button_back" android:layout_width="50dp" android:layout_height="match_parent" android:layout_margin="5dp" android:background="@drawable/btn_background" android:text="回复" /> <EditText android:id="@+id/chat_edittext" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/denglu" android:hint="输入内容" /> <Button android: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"> <TextView android:id="@+id/message_time" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="星期几 时间"//在每条消息上方显示该消息发送的日期和时间 android:gravity="center" android:padding="10dp"/> <LinearLayout android: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//竖直分布的LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginLeft="5dp"> <LinearLayout//水平布局的LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android: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"/> <TextView android: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> <TextView android: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<GridView android: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"> <ImageView android: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; } @Override public int getCount() { return mDate.length; } @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 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"}; @Override protected 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() { @Override public 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() { @Override public 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中 } }); —————————————————————————————— //点击事件如下 @Override public 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");//日期和时间 } @Override public int getViewTypeCount() { return TYPE;//类型。message的类型 分为发送MESSAG_SEND和回复MESSAGE_BACK,所以返回2 } @Override public int getItemViewType(int position) { return mData.get(position).getType(); }//返回当前view的布局显示的类型 @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 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; @Override protected 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() { @Override public 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); } //下面是点击事件方法 @Override public 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中添加
<activity android: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、在布局回复信息时
<LinearLayout android:layout_width="wrap_content"//不要写成match_parent android:layout_height="wrap_content" android:layout_weight="1"//自由充满 android:orientation="vertical" android:gravity="right">
5、这里我还写了一个登陆界面,后面将加入数据库来完善。现在只是简单的两个activity之间的跳转,在MainActivity中
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButtonLogin = (Button) findViewById(R.id.button_login); mButtonLogin.setOnClickListener(new View.OnClickListener() {//设置点击事件 @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,ChatActivity.class);//定义一个Intent,指向下一个界面 startActivity(intent);//直接启动intent } }); }
最终效果如下
:
相关文章推荐
- python中遍历dict的value
- [Leetcode] Basic Calculator
- java学习个人笔记---java内存管理之Java对象的引用方式
- C语言--变量的存储
- FI中替代和校验的几个相关函数
- HDU3435A new Graph Game(KM最小费用圈覆盖)
- 使用VMware WorkStation安装fedora出现section does not end with %%end错误的解决
- 石子归并问题(nyoj737)
- 社説 20150831 地熱発電 豊富な資源を有効活用しよう
- 关于JSONArray明明添加add JSONObject却为空
- Fortran向C传递NULL值
- QT5中QString与char *的相互转换
- 专家来谈下置换贴图,法线贴图和凹凸贴图的区别
- centos下安装gitlab
- HDOJ1272 小希的迷宫(并查集)
- 线程安全的简单的通用的cache
- 【bzoj1600】【USACO2008 Oct】Building A Fence 建造栅栏
- 【bzoj2111】【zjoi2010】【perm排列计数】【dp+Lucas定理】
- gcd,lcm HDU4497
- TCP 的那些事儿(下)