Android学习-聊天功能

学习android不久,想实现一个聊天功能,发现主要时间还是花在了聊天界面的布局上面。主要功能类似于一个聊天室。客户端启动后,发送的内容都通过socket传给服务端,服务端收到消息后,遍历socket列表中每一个socket,当然除掉消息来源的这个socket,然后发送该消息。

首先附上xml的代码

main.xml聊天的主界面


    
    

    
     
     

        
      
      

        
      
      

            
       
       
        
      
      

        
      
      
    
     
     

    
     
     
    
     
     


    
    

一个item的布局,就拿右边的对话框举例吧,右边的对话框多一个 android:gravity="right"


   
   

    
    
    

    
    
    


   
   

聊天界面的对话框是一个listview,但是listview的每一个item的布局取决于消息的来源

所以自定义一个adaptar,继承BaseAdapter,最重要的是getview的重写。我这里在获取内容的时候,就加上标志,根据标志判断用哪个布局。感觉总有点怪怪的。如果有好方法,还请大神指教。

package com.hitwhwlp.love;

import java.util.HashMap;
import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class MyAdapter extends BaseAdapter {

	private Context context;
	private List
   
   
    
    
     
     > list;

	public MyAdapter(Context context, List
     
     
      
      
       
       > listItems) {
		// TODO Auto-generated constructor stubthis.context = context;
		this.list = listItems;
		this.context = context;
	}

	@Override
	public int getCount() {
		return list.size();
	}

	@Override
	public Object getItem(int position) {
		return list.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}



	// ViewHolder静态类
	static class ViewHolder {
		public TextView content;
	}

	// 重写getView
	//这边写的。。。需要优化
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder;

		// if (convertView == null) {
		holder = new ViewHolder();
		if ("0" == list.get(position).get("flag").toString()) {
			convertView = LayoutInflater.from(context).inflate(
					R.layout.left_item, null);
			holder.content = (TextView) convertView.findViewById(R.id.content1);
		} else {
			convertView = LayoutInflater.from(context).inflate(
					R.layout.right_item, null);
			holder.content = (TextView) convertView.findViewById(R.id.content2);

		}

		// convertView.setTag(holder);

		// } else {
		// holder = (ViewHolder) convertView.getTag();

		// }

		holder.content.setText(list.get(position).get("content").toString());
		return convertView;

	}
}

      
      
     
     
    
    
   
   
准备工作做好了,上MainActivity啦

package com.hitwhwlp.love;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private ImageButton faceButton;
	private EditText inputText;
	private Button sendButton;


	Handler handler;
	ClientThread clientThread;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		inputText = (EditText) findViewById(R.id.input);
		sendButton = (Button) findViewById(R.id.send);
		inputText = (EditText) findViewById(R.id.input);
		sendButton = (Button) findViewById(R.id.send);

		
		final List
   
   
    
    
     
     > ListItems = new ArrayList
     
     
      
      
       
       >();
		ListView list = (ListView) findViewById(R.id.list);
		final MyAdapter adapter = new MyAdapter(MainActivity.this, ListItems);
		list.setAdapter(adapter);

		handler = new Handler() 
		{
			@Override
			public void handleMessage(Message msg) {
				// 如果消息来自于子线程
				if (msg.what == 0x123) {
					
					// 将读取的内容追加显示在listview
					HashMap
       
       
         tempListItems = new HashMap 
        
          (); tempListItems.put("flag", "0"); tempListItems.put("content", msg.obj.toString()); ListItems.add(tempListItems); adapter.notifyDataSetChanged(); } } }; clientThread = new ClientThread(handler); // 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据 new Thread(clientThread).start(); // ① sendButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub try { // 当用户按下发送按钮后,将用户输入的数据封装成Message, // 然后发送给子线程的Handler Message msg = new Message(); msg.what = 0x345; msg.obj = inputText.getText().toString(); clientThread.revHandler.sendMessage(msg); HashMap 
         
           tempListItems = new HashMap 
          
            (); tempListItems.put("flag", "1"); tempListItems.put("content",inputText.getText().toString() ); ListItems.add(tempListItems); adapter.notifyDataSetChanged(); // 清空input文本框 inputText.setText(""); } catch (Exception e) { e.printStackTrace(); } } }); } } 
           
          
         
       
      
      
     
     
    
    
   
   
当发送一个消息时,本地消息是直接加到listview中的,如果没有发送成功,但是已经显示了,是不是怪怪的?但其实如果没有发送成功,会检测到异常,直接到了下面的catch里根本不会执行,连清空输入框都没有执行。

至于如何接收来自服务端的消息,以及将发送的内容写到网络,上ClientThread(由于本人才结束大一的生活,网络没有学习过,只看了一点点,这一块是参考《Android疯狂讲义》的)

package com.hitwhwlp.love;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;


public class ClientThread implements Runnable
{
	private Socket s;
	// 定义向UI线程发送消息的Handler对象
	private Handler handler;
	// 定义接收UI线程的消息的Handler对象
	public Handler revHandler;
	// 该线程所处理的Socket所对应的输入流
	BufferedReader br = null;
	OutputStream os = null;
	int flag;

	public ClientThread(Handler handler)
	{
		this.handler = handler;
	}

	public void run()
	{
		try
		{
			s = new Socket("192.168.1.230", 30000);//这是我的电脑的本机ip地址
			br = new BufferedReader(new InputStreamReader(
				s.getInputStream()));
			os = s.getOutputStream();
			// 启动一条子线程来读取服务器响应的数据
			new Thread()
			{
				@Override
				public void run()
				{
					String content = null;
					// 不断读取Socket输入流中的内容。
					try
					{
						while ((content = br.readLine()) != null)
						{
							// 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据
							Message msg = new Message();
							msg.what = 0x123;
							msg.obj = content;
							handler.sendMessage(msg);
						}
					}
					catch (IOException e)
					{
						e.printStackTrace();
					}
				}
			}.start();
			// 为当前线程初始化Looper
			Looper.prepare();
			// 创建revHandler对象
			revHandler = new Handler()
			{
				@Override
				public void handleMessage(Message msg)
				{
					// 接收到UI线程中用户输入的数据
					if (msg.what == 0x345)
					{
						// 将用户在文本框内输入的内容写入网络
						try
						{
							os.write((msg.obj.toString() + "\r\n")
								.getBytes("utf-8"));
						}
						catch (Exception e)
						{
							e.printStackTrace();
						}
					}
				}
			};
			// 启动Looper
			Looper.loop();
		}
		catch (SocketTimeoutException e1)
		{
			System.out.println("网络连接超时!!");
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

至于服务端嘛~默默贴出来,不说话 闭嘴

public class MyServer
{
	// 定义保存所有Socket的ArrayList
	public static ArrayList<Socket> socketList
		= new ArrayList<Socket>();
    public static void main(String[] args)
		throws IOException
    {
		ServerSocket ss = new ServerSocket(30000);
		while(true)
		{
			// 此行代码会阻塞,将一直等待别人的连接
			Socket s = ss.accept();
			socketList.add(s);
			// 每当客户端连接后启动一条ServerThread线程为该客户端服务
			new Thread(new ServerThread(s)).start();
		}
    }
}

// 负责处理每个线程通信的线程类
public class ServerThread implements Runnable {
	// 定义当前线程所处理的Socket
	Socket s = null;
	// 该线程所处理的Socket所对应的输入流
	BufferedReader br = null;

	public ServerThread(Socket s) throws IOException {
		this.s = s;
		// 初始化该Socket对应的输入流
		br = new BufferedReader(new InputStreamReader(s.getInputStream(),
				"utf-8")); 
	}

	public void run() {
		try {
			String content = null;
			// 采用循环不断从Socket中读取客户端发送过来的数据
			while ((content = readFromClient()) != null) {
				// 遍历socketList中的每个Socket,
				// 将读到的内容向每个Socket发送一次,除了自己(这个方法自己想的,如果有更高端方法,还请大神赐教,网络的现在暂时不懂)
				for (Socket i : MyServer.socketList) {
					if (i != s) {
						OutputStream os = i.getOutputStream();
						os.write((content + "\n").getBytes("utf-8"));
					}

				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 定义读取客户端数据的方法
	private String readFromClient() {
		try {
			return br.readLine();
		}
		// 如果捕捉到异常,表明该Socket对应的客户端已经关闭
		catch (IOException e) {
			// 删除该Socket。
			MyServer.socketList.remove(s); 
		}
		return null;
	}
}


上一张效果图,这是拿两个机子的对话







  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值