設計思路:
首先搭建聊天界面,想要的效果如下
整體爲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
}
});
}
最終效果如下
: