您的位置:首页 > 其它

简单的仿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
            }
        });
    }


最终效果如下


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: