簡單的仿QQ聊天(自娛自樂聊天室)

設計思路:

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

這裏寫圖片描述

整體爲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
            }
        });
    }

最終效果如下
這裏寫圖片描述

發佈了59 篇原創文章 · 獲贊 6 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章