Xamarin.Forms ChatKit 的使用 快速搭建即時通訊 UI
大家完成好,本期為大家實作 Android 端 SocketChat 的實作(需要原始碼請滑到最下面有博主通訊方式)
專案介紹:
ChatKit 控制元件來源:
https://github.com/jingliancui/XamarinFormsChatKitSample
通信組件:Socket
組成部分:服務端(PC,Winforms) 客戶端(PC,Winfrom,Xamarin.Forms Android)
畫個簡單的實作草圖:

看過這張圖相信會有點輕微的理解:
下面看代碼實作:
第一步安裝 依賴包
安裝 NewGet 依賴包:
Install-Package XamarinLibrary.Xamarin.Android.ChatKit -Version 0.3.3
這個是 類似 QQ的聊天 UI
第二步 撰寫UI


代碼量太多 就不一一展示
貼上重要代碼:
Socket Client Code:
創建 一個單例XamarinSockectClient 類 防止客戶端重復創建
0000100020003 在線串列協議頭 ,
000200030006 好友訊息協議頭
/// <summary>
/// Sockect通訊客戶端
/// </summary>
public class XamarinSockectClient
{
private static XamarinSockectClient _Singleton = null;
public static XamarinSockectClient CreateInstance()
{
if (_Singleton == null)
{
_Singleton = new XamarinSockectClient();
}
return _Singleton;
}
public static XamarinSockectClient CreateInstance(string Ip, string Port)
{
if (_Singleton == null)
{
_Singleton = new XamarinSockectClient(Ip, Port);
}
return _Singleton;
}
public string ServerIp = "192.168.1.105";
public string ServerPort = "9000";
//public XamarinSockectClient()
//{
// XamarinConnectToServer();
//}
public XamarinSockectClient(string Ip = "192.168.1.105", string Port = "9000")
{
ServerIp = Ip;
ServerPort = Port;
XamarinConnectToServer();
}
Socket socketClient = null;
Thread threadClient = null;
public const int SendBufferSize = 2 * 1024;
public const int ReceiveBufferSize = 8 * 1024;
private void XamarinConnectToServer()
{
//定義一個套位元組監聽 包含3個引數(IP4尋址協議,流式連接,TCP協議)
socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//獲取文本框輸入的服務端IP和Port
IPAddress serverIPAddress = IPAddress.Parse(ServerIp);
int serverPort = int.Parse(ServerPort);
IPEndPoint endpoint = new IPEndPoint(serverIPAddress, serverPort);
//向指定的ip和埠號的服務端發送連接請求 用的方法是Connect 不是Bind
socketClient.Connect(endpoint);
//創建一個新執行緒 用于監聽服務端發來的資訊
threadClient = new Thread(RecMsg);
//將表單執行緒設定為與后臺同步
threadClient.IsBackground = true;
//啟動執行緒
threadClient.Start();
//登錄成功
//txtMsg.AppendText("已與服務端建立連接,可以開始通信...\r\n");
// btnConnectToServer.Enabled = false;
}
/// <summary>
/// 接受服務端發來資訊的方法
/// </summary>
private void RecMsg()
{
while (true) //持續監聽服務端發來的訊息
{
string strRecMsg = null;
int length = 0;
byte[] buffer = new byte[SendBufferSize];
try
{
//將客戶端套接字接收到的位元組陣列存入記憶體緩沖區, 并獲取其長度
length = socketClient.Receive(buffer);
}
catch (SocketException ex)
{
// txtMsg.AppendText("套接字例外訊息:" + ex.Message + "\r\n");
// txtMsg.AppendText("服務端已斷開連接\r\n");
break;
}
catch (Exception ex)
{
// txtMsg.AppendText("系統例外訊息: " + ex.Message + "\r\n");
break;
}
//將套接字獲取到的位元組陣列轉換為人可以看懂的字串
strRecMsg = Encoding.UTF8.GetString(buffer, 0, length);
string[] ServerValue= strRecMsg.Split('*');
switch (ServerValue[0])
{
case "0000100020003":
if (DevHelp.DeviceHelp.devDelegateUpdateUi != null)
{
DevHelp.DeviceHelp.devDelegateUpdateUi.InitUI(ServerValue[1]);
}
break;
case "000200030006":
if (DevHelp.DeviceHelp.devDelegateMessagesList != null)
{
DevHelp.DeviceHelp.devDelegateMessagesList.AddMsg(ServerValue[1]);
}
break;
default:
break;
}
//JsonConvert.DeserializeObject<MessageAmanager>()
//將文本框輸入的資訊附加到txtMsg中 并顯示 誰,什么時間,換行,發送了什么資訊 再換行
// txtMsg.AppendText("服務端在 " + GetCurrentTime() + " 給您發送了:\r\n" + strRecMsg + "\r\n");
}
}
/// <summary>
/// 發送字串資訊到服務端的方法
/// </summary>
private void ClientSendMsg(string sendMsg, byte symbol)
{
byte[] arrClientMsg = Encoding.UTF8.GetBytes(sendMsg);
//實際發送的位元組陣列比實際輸入的長度多1 用于存取識別符號
byte[] arrClientSendMsg = new byte[arrClientMsg.Length + 1];
arrClientSendMsg[0] = symbol; //在索引為0的位置上添加一個識別符號
Buffer.BlockCopy(arrClientMsg, 0, arrClientSendMsg, 1, arrClientMsg.Length);
socketClient.Send(arrClientSendMsg);
// txtMsg.AppendText("SoFlash:" + GetCurrentTime() + "\r\n" + sendMsg + "\r\n");
}
//向服務端發送資訊
public void XamarinSend(string Message)
{
ClientSendMsg(Message, 0);
}
//獲取在線用戶資訊
public void XamarinGetUserInfo()
{
ClientSendMsg("001001001", 5);
}
//發送登錄資訊
public void XamarinSendUserLogin()
{
string MyLogin = XamarinMessageAmanager("", $"{DeviceHelp.MyUserID}@{DevHelp.DeviceHelp.MyUserName}");
ClientSendMsg($"002002002#{MyLogin}", 4);
}
快捷鍵 Enter 發送資訊
//private void txtCMsg_KeyDown(object sender, KeyEventArgs e)
//{ //當游標位于輸入文本框上的情況下 發送資訊的熱鍵為回車鍵Enter
// if (e.KeyCode == Keys.Enter)
// {
// //則呼叫客戶端向服務端發送資訊的方法
// ClientSendMsg(txtCMsg.Text, 0);
// }
//}
string filePath = null; //檔案的全路徑
string fileName = null; //檔案名稱(不包含路徑)
選擇要發送的檔案
//private void XamarinSelectFile(object sender, EventArgs e)
//{
// OpenFileDialog ofDialog = new OpenFileDialog();
// if (ofDialog.ShowDialog(this) == DialogResult.OK)
// {
// fileName = ofDialog.SafeFileName; //獲取選取檔案的檔案名
// txtFileName.Text = fileName; //將檔案名顯示在文本框上
// filePath = ofDialog.FileName; //獲取包含檔案名的全路徑
// }
//}
/// <summary>
/// 發送檔案的方法
/// </summary>
/// <param name="fileFullPath">檔案全路徑(包含檔案名稱)</param>
private void XamarinSendFile(string fileFullPath)
{
if (string.IsNullOrEmpty(fileFullPath))
{
// MessageBox.Show(@"請選擇需要發送的檔案!");
return;
}
//發送檔案之前 將檔案名字和長度發送過去
long fileLength = new FileInfo(fileFullPath).Length;
string totalMsg = string.Format("{0}-{1}", fileName, fileLength);
ClientSendMsg(totalMsg, 2);
//發送檔案
byte[] buffer = new byte[SendBufferSize];
using (FileStream fs = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read))
{
int readLength = 0;
bool firstRead = true;
long sentFileLength = 0;
while ((readLength = fs.Read(buffer, 0, buffer.Length)) > 0 && sentFileLength < fileLength)
{
sentFileLength += readLength;
//在第一次發送的位元組流上加個前綴1
if (firstRead)
{
byte[] firstBuffer = new byte[readLength + 1];
firstBuffer[0] = 1; //告訴機器該發送的位元組陣列為檔案
Buffer.BlockCopy(buffer, 0, firstBuffer, 1, readLength);
socketClient.Send(firstBuffer, 0, readLength + 1, SocketFlags.None);
firstRead = false;
continue;
}
//之后發送的均為直接讀取的位元組流
socketClient.Send(buffer, 0, readLength, SocketFlags.None);
}
fs.Close();
}
// txtMsg.AppendText("SoFlash:" + GetCurrentTime() + "\r\n您發送了檔案:" + fileName + "\r\n");
}
//點擊檔案發送按鈕 發送檔案
private void btnSendFile_Click(object sender, EventArgs e)
{
XamarinSendFile(filePath);
}
/// <summary>
/// 獲取當前系統時間
/// </summary>
public DateTime GetCurrentTime()
{
DateTime currentTime = new DateTime();
currentTime = DateTime.Now;
return currentTime;
}
//關閉客戶端
private void btnExit_Click(object sender, EventArgs e)
{
//Application.Exit();
}
/// <summary>
/// 封裝發送訊息
/// </summary>
/// <param name="SReceiveUserID"></param>
/// <param name="SMessage"></param>
/// <returns></returns>
public string XamarinMessageAmanager(string SReceiveUserID, string SMessage)
{
MessageAmanager message = new MessageAmanager() { Message = SMessage, ReceiveUserID = SReceiveUserID, SendUserID = DeviceHelp.MyUserID };
return JsonConvert.SerializeObject(message);
}
}
/// <summary>
///訊息管理
/// </summary>
public class MessageAmanager
{
/// <summary>
/// 發送者ID
/// </summary>
public string SendUserID { get; set; }
/// <summary>
/// 訊息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 接收者ID
/// </summary>
public string ReceiveUserID { get; set; }
}
NavigationClass UI 頁面跳出工具類
public static class NavigationClass
{
/// <summary>
/// 創建副本
/// </summary>
public static INavigation Navigation { get; set; }
/// <summary>
/// 跳轉頁面
/// </summary>
/// <param name="obj"></param>
public static void PushAsync(this Page obj)
{
Navigation.PushAsync(obj, true);
}
/// <summary>
/// 關閉視窗
/// </summary>
/// <param name="obj"></param>
public static void PopAsync(this Page obj)
{
Navigation.PopAsync(true);
}
public static void RemovePage(this Page obj)
{
Navigation.RemovePage(obj);
}
}
DeviceHelp 設備引數 UI委托 檔案讀寫工具類
public class DeviceHelp
{
/// <summary>
/// 客戶端唯一ID
/// </summary>
public static string MyUserID = Guid.NewGuid().ToString("N");
/// <summary>
/// 寬度
/// </summary>
public static double ScreenWidth { get; set; }
/// <summary>
/// 高度
/// </summary>
public static double ScreenHeight { get; set; }
/// <summary>
/// 密度
/// </summary>
public static double Density { get; set; }
/// <summary>
/// 背景關系物件
/// </summary>
public static Context Context { get; set; }
/// <summary>
/// Sockect 實體化物件
/// </summary>
public static XamarinSockectClient xamarinSockectClient { get; set; }
/// <summary>
/// UI委托
/// </summary>
public static DevDelegateUpdateUi devDelegateUpdateUi { get; set; }
/// <summary>
/// 訊息串列
/// </summary>
public static DevDelegateMessagesList devDelegateMessagesList { get; set; }
/// <summary>
/// 用戶名
/// </summary>
public static string MyUserName { get; set; }
/// <summary>
/// 更新標題
/// </summary>
public static DevDelegateTitle devDelegateTitle { get; set; }
//var width = Resources.DisplayMetrics.WidthPixels;
//var height = Resources.DisplayMetrics.HeightPixels;
//var density = Resources.DisplayMetrics.Density; //
public void SaveImage(string filepath)
{
var imageData = System.IO.File.ReadAllBytes(filepath);
var dir = Android.OS.Environment.GetExternalStoragePublicDirectory(
Android.OS.Environment.DirectoryDcim);
var pictures = dir.AbsolutePath;
var filename = System.DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".jpg";
var newFilepath = System.IO.Path.Combine(pictures, filename);
System.IO.File.WriteAllBytes(newFilepath, imageData);
//mediascan adds the saved image into the gallery
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(newFilepath)));
DevHelp.DeviceHelp.Context.SendBroadcast(mediaScanIntent);
}
/// <summary>
/// 保存檔案
/// </summary>
/// <param name="filepath"></param>
/// <param name="Data"></param>
public static void SaveFile(string filepath, byte[] Data)
{
// var imageData = System.IO.File.ReadAllBytes(filepath);
var dir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDcim);
var pictures = dir.AbsolutePath;
// var filename = System.DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".jpg";
var newFilepath = System.IO.Path.Combine(pictures, filepath);
System.IO.File.WriteAllBytes(newFilepath, Data);
//mediascan adds the saved image into the gallery
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(newFilepath)));
DevHelp.DeviceHelp.Context.SendBroadcast(mediaScanIntent);
}
/// <summary>
/// 讀取指定檔案
/// </summary>
/// <param name="filepath"></param>
/// <returns></returns>
public static byte[] ReadFile(string filepath)
{
try
{
var Data = System.IO.File.ReadAllBytes(filepath);
return Data;
}
catch (Exception ex)
{
return null;
}
}
}
public class DevDelegateUpdateUi
{
public delegate void InitUi(string message);
public InitUi InitUI;
}
public class DevDelegateMessagesList
{
public delegate void InitUi(string message);
public InitUi AddMsg;
}
public class DevDelegateTitle
{
public delegate void UpdateTitle(string Title);
public UpdateTitle updateTitle;
}
DialogsListControlRenderer 在線串列資料更新實作
IOnDialogClickListener 串列單擊事件介面
必須注冊: dialogsListAdapter.OnDialogClickListener = this;
public class DialogsListControlRenderer : ViewRenderer<DialogsListControl, LinearLayout>, IOnDialogClickListener
{
public DialogsListControlRenderer(Context context) : base(context)
{
}
private Com.Stfalcon.Chatkit.Dialogs.DialogsList dialogsList;
private LinearLayout linearLayout;
private Com.Stfalcon.Chatkit.Dialogs.DialogsListAdapter dialogsListAdapter;
protected override void OnElementChanged(ElementChangedEventArgs<DialogsListControl> e)
{
DevDelegateUpdateUi devDelegateUpdateUi = new DevDelegateUpdateUi();
devDelegateUpdateUi.InitUI += new DevDelegateUpdateUi.InitUi(AddItem);
DeviceHelp.devDelegateUpdateUi = devDelegateUpdateUi;
var x = Inflate(Context, Resource.Layout.DialogsListLayout, null);
x.Click += DialogsListView_Click;
if (linearLayout == null)
{
linearLayout = x as LinearLayout;
}
if (dialogsList == null)
{
var dialogsListView = linearLayout.FindViewById<Com.Stfalcon.Chatkit.Dialogs.DialogsList>(Resource.Id.dialogsList1);
dialogsList = dialogsListView;
}
dialogsList.Click += DialogsListView_Click;
dialogsList.LongClick += DialogsListView_Click;
dialogsListAdapter = new Com.Stfalcon.Chatkit.Dialogs.DialogsListAdapter(new SampleImageLoader(Context));
dialogsList.SetAdapter(dialogsListAdapter);
DevHelp.DeviceHelp.xamarinSockectClient.XamarinGetUserInfo();
//JsonConvert.DeserializeObject<MessageAmanager>()
linearLayout.Click += DialogsListView_Click;
dialogsListAdapter.OnDialogClickListener = this;
SetNativeControl(linearLayout);
}
public void AddItem(string message)
{
Device.BeginInvokeOnMainThread(() =>
{
string[] strUser = message.Split("#");
dialogsListAdapter.Clear();
foreach (var item in strUser)
{
var itValue = item.Split('@');
SampleUser sample1 = new SampleUser();
sample1.Avatar = itValue[1];
sample1.Id = itValue[0];
sample1.Name = itValue[1];
SampleMessage message1 = new SampleMessage {Id= itValue[0],Text="您有新的訊息!!!", User= sample1 };
SampleDialog sample = new SampleDialog(sample1, message1) { Id= itValue[0] ,DialogName= itValue[1] };
dialogsListAdapter.AddItem(sample);
}
});
}
///串列單擊事件
public void OnDialogClick(Java.Lang.Object p0)
{
MessagesListPage messagesListPage = new MessagesListPage();
UserInfoHelp.SampleDialog = p0 as SampleDialog;
messagesListPage.PushAsync();
}
///串列長按事件
public void OnDialogLongClick(Java.Lang.Object p0)
{
}
}
MessagesListControlRenderer 訊息動態更新實作
IOnMessageClickListener 訊息單擊事件介面
IOnMessageLongClickListener 訊息長按事件介面
介面注冊不然不會觸發 messagesListAdapter.SetOnMessageClickListener(this);
messagesListAdapter.SetOnMessageLongClickListener(this);
//好友聊天頁面標題 方法注冊
DeviceHelp.devDelegateTitle.updateTitle(UserInfoHelp.SampleDialog.DialogName);
//好友訊息動態更新方法注冊
DevDelegateMessagesList devDelegateMessages = new DevDelegateMessagesList();
devDelegateMessages.AddMsg += new DevDelegateMessagesList.InitUi(AddFendMsg);
DeviceHelp.devDelegateMessagesList = devDelegateMessages;
public class MessagesListControlRenderer : ViewRenderer<MessagesListControl, LinearLayout>, IOnMessageClickListener, IOnMessageLongClickListener
{
public MessagesListControlRenderer(Context context) : base(context)
{
DevHelp.DeviceHelp.Context = context;
}
private LinearLayout linearLayout;
private Com.Stfalcon.Chatkit.Messages.MessagesList messagesList;
private Com.Stfalcon.Chatkit.Messages.MessagesListAdapter messagesListAdapter;
protected override void OnElementChanged(ElementChangedEventArgs<MessagesListControl> e)
{
var x = Inflate(Context, Resource.Layout.MessagesListLayout, null);
// MessagesListControl messagesListControl= e.NewElement;
// messagesListControl.AddMsg += AddMsg;
MessageManager message = new MessageManager();
message.AddMsg += AddMsg;
MessageHelpObj.messageManager = message;
DevDelegateMessagesList devDelegateMessages = new DevDelegateMessagesList();
devDelegateMessages.AddMsg += new DevDelegateMessagesList.InitUi(AddFendMsg);
DeviceHelp.devDelegateMessagesList = devDelegateMessages;
if (linearLayout == null)
{
linearLayout = x as LinearLayout;
}
if (messagesList == null)
{
var messagesListView = linearLayout.FindViewById<Com.Stfalcon.Chatkit.Messages.MessagesList>(Resource.Id.messagesList);
messagesList = messagesListView;
}
//Guid.NewGuid().ToString()
messagesListAdapter = new Com.Stfalcon.Chatkit.Messages.MessagesListAdapter(DeviceHelp.MyUserID, new SampleImageLoader(Context));
messagesList.SetAdapter(messagesListAdapter);
messagesListAdapter.SetOnMessageClickListener(this);
messagesListAdapter.SetOnMessageLongClickListener(this);
SetNativeControl(linearLayout);
DeviceHelp.devDelegateTitle.updateTitle(UserInfoHelp.SampleDialog.DialogName);
}
//public string guid = Guid.NewGuid().ToString();
public string xAvatar = "xiaoming";
public string xName = "張三";
/// <summary>
/// 增加訊息
/// </summary>
/// <param name="message"></param>
public void AddMsg(SampleMessageControl message)
{
SampleMessage sample = new SampleMessage { Text = message.Text, Id = DeviceHelp.MyUserID };
sample.User = new SampleUser() { Id = DeviceHelp.MyUserID, Avatar = xAvatar, Name = xName };
messagesListAdapter.AddToStart(sample, true);
}
/// <summary>
/// 增加訊息
/// </summary>
/// <param name="message"></param>
public void AddFendMsg(string message)
{
Device.BeginInvokeOnMainThread(() =>
{
MessageBox box = JsonConvert.DeserializeObject<MessageBox>(message);
SampleMessage sample = new SampleMessage { Text = box.Message, Id = box.SendID };
sample.User = new SampleUser() { Id = box.SendID, Avatar = box.Name, Name = box.Name };
messagesListAdapter.AddToStart(sample, true);
});
}
public void OnMessageClick(Java.Lang.Object p0)
{
}
public void OnMessageLongClick(Java.Lang.Object p0)
{
p0.NotifyAll();
}
}
public class MessageBox
{
/// <summary>
/// 接收人
/// </summary>
public string RID { get; set; }
public string Message { get; set; }
public string Name { get; set; }
/// <summary>
/// 發送人
/// </summary>
public string SendID { get; set; }
}
MessageInputControlRenderer 發送訊息實作
IInputListener 訊息提交 介面
//OnSubmit 發送資訊
介面注冊 messageInput.SetInputListener(this);
// 發送訊息到服務器方法實作
DevHelp.DeviceHelp.xamarinSockectClient.XamarinSend(Message);
public class MessageInputControlRenderer : ViewRenderer<MessageInputControl, LinearLayout>, IInputListener
{
public MessageInputControlRenderer(Context context) : base(context)
{
}
private Com.Stfalcon.Chatkit.Messages.MessageInput messageInput;
private LinearLayout linearLayout;
protected override void OnElementChanged(ElementChangedEventArgs<MessageInputControl> e)
{
var x = Inflate(Context, Resource.Layout.MessageInputLayout, null);
if (linearLayout == null)
{
linearLayout = x as LinearLayout;
}
if (messageInput == null)
{
var messageInputView = linearLayout.FindViewById<Com.Stfalcon.Chatkit.Messages.MessageInput>(Resource.Id.theMessageInput);
messageInput = messageInputView;
}
messageInput.SetInputListener(this);
SetNativeControl(linearLayout);
}
public bool OnSubmit(ICharSequence p0)
{
var msg = p0.ToString();
SampleMessageControl sample = new SampleMessageControl { Text = msg };
MessageHelpObj.messageManager.AddMsg(sample);
MessageBox box = new MessageBox { RID = UserInfoHelp.SampleDialog.Id, SendID = DevHelp.DeviceHelp.MyUserID, Message = p0.ToString(), Name = DevHelp.DeviceHelp.MyUserName };
var Sedmsg = JsonConvert.SerializeObject(box);
string Message = "000200030006#"+ Sedmsg;
DevHelp.DeviceHelp.xamarinSockectClient.XamarinSend(Message);
return true;
}
}
##獲取設備寬度,高度 螢屏 密度
代碼實作:
var width = Resources.DisplayMetrics.WidthPixels; //高度
var height = Resources.DisplayMetrics.HeightPixels;//寬度
var density = Resources.DisplayMetrics.Density; //螢屏密度
DeviceHelp.ScreenWidth = width / density; //螢屏寬度
DeviceHelp.ScreenHeight = height / density; //螢屏高度 含24個單位的標題欄高度 通過OnSizeAllocated獲取的高度不含標題欄高度
DeviceHelp.Density = density;
以上是代碼實作:
下面展示實際效果:

好友串列

好友Caozhen001 與好友Caozhen002 對話實作

好友Caozhen002與好友Caozhen001 對話實作
這里是客戶端的實作 下期介紹 服務端的實作
本期就到這里 謝謝大家,不足之處請指點一二;
需要原始碼請假博主微信

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/298977.html
標籤:其他
