Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。
Kotlin高仿微信-项目实践58篇,点击查看详情
效果图:

详细的聊天功能请查看Kotlin高仿微信-第8篇-单聊,这里是提取图片功能的部分实现。
实现代码:
显示图片:
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/chat_item_me_img"
app:layout_constraintEnd_toStartOf="@+id/chat_item_me_avatar"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="100dp"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:layout_marginEnd="10dp"
android:src="@drawable/wc_loading_default"
android:visibility="gone"/>
打开相册:
//单选 // 打开相册
ImageSelector.builder()
.useCamera(false) // 设置是否使用拍照
.setSingle(true) //设置是否单选
.canPreview(true) //是否点击放大图片查看,,默认为true
.start(this,REQUEST_PICTURE_CODE)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_PICTURE_CODE && data != null) {
val images = data.getStringArrayListExtra(ImageSelector.SELECT_RESULT)
if(images != null && images.size > 0) {
AddFileListener.sendFile(ChatBean.CONTENT_TYPE_IMG, images[0], toUserId,0)
}
}
}
/**
* 发送图片、小视频、语音
* @param fileType Int
* @param picPath String
* @param toUserID String
*/
fun sendFile(fileType: Int, filePath : String, toUserId : String, time: Int) {
CoroutineScope(Dispatchers.IO).launch {
//语音、小视频多少秒
val toId: String = BaseUtils.getChatId(toUserId) + "/Smack"
var resultFilePath = ""
if(TextUtils.isEmpty(filePath)){
return@launch
}
if(!File(filePath).exists()){
return@launch
}
var second = time
if(fileType == ChatBean.CONTENT_TYPE_VOICE){
second = time
} else if(fileType == ChatBean.CONTENT_TYPE_VIDEO){
second = CommonUtils.Media.getMediaTime(filePath, fileType)
}
//先刷新页面,再慢慢上传文件
var chatBean = processSendChatBean(toUserId, fileType, filePath, second)
if(fileType == ChatBean.CONTENT_TYPE_IMG){
//图片
//压缩图片后路径
var resultPicPath = UploadFileUtils.getCompressFile(filePath)
resultFilePath = resultPicPath
} else if(fileType == ChatBean.CONTENT_TYPE_VOICE){
//语音
resultFilePath = filePath
TagUtils.d("语音时间:${second}")
} else if(fileType == ChatBean.CONTENT_TYPE_VIDEO){
//小视频
var videoFilePath = UploadFileUtils.getVideoCompressFile()
//TagUtils.d("开始发送小视频压缩前文件2:${videoFilePath}")
var compressResult = VideoController.getInstance().convertVideo(filePath, videoFilePath, VideoController.COMPRESS_QUALITY_LOW, object : VideoController.CompressProgressListener {
override fun onProgress(percent: Float) {
//TagUtils.d("压缩小视频进度:${percent}")
}
})
TagUtils.d("小视频时间:${second}")
resultFilePath = videoFilePath
}
TagUtils.d("上传文件:${resultFilePath}")
val filetosend = File(resultFilePath)
if (!filetosend.exists()) {
return@launch
}
try {
var account : String = DataStoreUtils.getAccount()
if(fileType == ChatBean.CONTENT_TYPE_IMG){
//图片
TagUtils.d("图片发送成功。")
var chatBeanServer = UploadFileUtils.uploadChatImages(account, toUserId, resultFilePath,0)
if(chatBeanServer != null){
chatBeanServer.imgPath = chatBeanServer.imgPath
chatBean = chatBeanServer
}
} else if(fileType == ChatBean.CONTENT_TYPE_VOICE){
//录音完成,要转码,等待0.2秒再发送
delay(100)
//语音
var chatBeanServer = UploadFileUtils.uploadChatVoice(account, toUserId, resultFilePath, second)
if(chatBeanServer != null){
chatBeanServer.voiceLocal = resultFilePath
chatBean = chatBeanServer
}
} else if(fileType == ChatBean.CONTENT_TYPE_VIDEO){
//小视频
var chatBeanServer = UploadFileUtils.uploadChatVideo(account, toUserId, resultFilePath, second)
if(chatBeanServer != null){
chatBeanServer.videoLocal = resultFilePath
chatBean = chatBeanServer
}
}
chatBean?.let {
ChatRepository.updateChat(it)
}
var baseSystemBoolean = BaseSystemRepository.getBaseSystemSync(WcApp.getContext().packageName)
if(baseSystemBoolean != null && baseSystemBoolean.sync == CommonUtils.Sync.SERVER_SYNC){
//同步不需要使用transfer上传文件,因为transfer上传太慢了, 直接上传到服务器,然后发送普通的消息
if(fileType == ChatBean.CONTENT_TYPE_VOICE){
var content = CommonUtils.Chat.VOICE_MARK + chatBean.voice
ChatManagerUtils.getInstance().sendMessage(toUserId, content)
} else if(fileType == ChatBean.CONTENT_TYPE_VIDEO){
var content = CommonUtils.Chat.VIDEO_MARK + chatBean.video
ChatManagerUtils.getInstance().sendMessage(toUserId, content)
} else if(fileType == ChatBean.CONTENT_TYPE_IMG){
var content = CommonUtils.Chat.IMAGE_MARK + chatBean.imgPath
ChatManagerUtils.getInstance().sendMessage(toUserId, content)
}
return@launch
}
val fileTransferManager = getFileTransferManager()
val transfer = fileTransferManager.createOutgoingFileTransfer(toId) // 创建一个输出文件传输对象
//对方用户在线才需要上传文件
if(XmppConnectionManager.getInstance().isOnLine(toUserId)){
TagUtils.d("${toUserId} 在线 开始上传。")
transfer.sendFile(filetosend,"recv img")
while (!transfer.isDone) {
if (transfer.status == FileTransfer.Status.error) {
TagUtils.d("聊天文件上传错误 , ERROR!!! " + transfer.error)
} else {
TagUtils.d("聊天文件上传 " + transfer.status +" , " + transfer.progress)
}
Thread.sleep(20)
}
} else {
TagUtils.d("toUserId 不在线")
}
if (transfer.isDone) {
//上传完成
}
} catch (e1: XMPPException) {
e1.printStackTrace()
}
}
}
/**
* Author : wangning
* Email : maoning20080809@163.com
* Date : 2022/5/31 16:31
* Description : 处理聊天信息发来的信息
*/
class ChatManagerListener : ChatManagerListener {
override fun chatCreated(chat: Chat, createdLocally: Boolean) {
TagUtils.d("消息监听回调:chat = ${chat} , createdLocally = ${createdLocally}")
if(!createdLocally){
chat.addMessageListener { chat, message ->
TagUtils.d("获取好友发来的信息 ${message.from} , ${message.to}, ${message.body}")
var content = message.getBody()
if(!TextUtils.isEmpty(content) && content.length > 0){
var fromUser = BaseUtils.getChatAccountFrom(message.from)
var toUser = BaseUtils.getChatAccount(message.to)
var userType = ChatBean.USER_TYPE_OTHER
if(content.startsWith(CommonUtils.Chat.LOCATION_MARK)){
//发送定位
//去掉location###标志
var remarkContent = CommonUtils.Chat.getLocation(content)
//使用逗号分隔符,分别读取经纬度
var contents = remarkContent.split(",")
var latitude = contents[0].toDouble()
var longitude = contents[1].toDouble()
var chatBean = CommonUtils.Chat.getChatBean(fromUser, toUser, userType, content, ChatBean.CONTENT_TYPE_LOCATION, "", latitude, longitude)
ChatRepository.insertChat(chatBean)
chatBean.isReceive = true
EventBus.getDefault().post(chatBean)
} else if(content.startsWith(CommonUtils.Chat.REDPACKET_MARK)){
//发送红包, 去掉redpacket###写入数据库
content = CommonUtils.Chat.getRedpacket(content).toString()
var chatBean = CommonUtils.Chat.getChatBean(fromUser, toUser, userType, content, ChatBean.CONTENT_TYPE_REDPACKET, "",0.0, 0.0)
ChatRepository.insertChat(chatBean)
chatBean.isReceive = true
EventBus.getDefault().post(chatBean)
} else if(content.startsWith(CommonUtils.Chat.VOICE_MARK)){
//发送语音, 去掉voice###写入数据库
content = CommonUtils.Chat.getMedia(content, CommonUtils.Chat.VOICE_MARK)
var chatBean = CommonUtils.Chat.getChatBeanVoiceServer(fromUser, toUser, userType,ChatBean.CONTENT_TYPE_VOICE, content,0)
chatBean.isReceive = true
processDownload(chatBean)
} else if(content.startsWith(CommonUtils.Chat.VIDEO_MARK)){
//发送小视频, 去掉video###写入数据库
content = CommonUtils.Chat.getMedia(content, CommonUtils.Chat.VIDEO_MARK)
var chatBean = CommonUtils.Chat.getChatBeanVideoServer(fromUser, toUser, userType,ChatBean.CONTENT_TYPE_VIDEO, content,0)
chatBean.isReceive = true
processDownload(chatBean)
} else if(content.startsWith(CommonUtils.Chat.IMAGE_MARK)){
//发送图片, 去掉image###写入数据库
content = CommonUtils.Chat.getMedia(content, CommonUtils.Chat.IMAGE_MARK)
var chatBean = CommonUtils.Chat.getChatBeanImageServer(fromUser, toUser, userType,ChatBean.CONTENT_TYPE_IMG, content,0)
chatBean.isReceive = true
processDownload(chatBean)
} else if(content.startsWith(CommonUtils.Chat.TRANSFER_MARK)){
//发送转账, 去掉transfer###写入数据库
content = CommonUtils.Chat.getTransfer(content).toString()
var chatBean = CommonUtils.Chat.getChatBean(fromUser, toUser, userType, content, ChatBean.CONTENT_TYPE_TRANSFER, "",0.0, 0.0)
ChatRepository.insertChat(chatBean)
chatBean.isReceive = true
EventBus.getDefault().post(chatBean)
} else if(content.startsWith(CommonUtils.QRCommon.QR_RECEIVE_CODE)){
//向个人发送收款
var balance = content.substring(CommonUtils.QRCommon.QR_RECEIVE_CODE.length, content.length)
TagUtils.d("MyChatManagerListener 向个人发送收款金额: ${fromUser} , ${toUser}, ${balance}")
updateBalanceServer(fromUser, toUser, CommonUtils.User.OPERATOR_PLUS, balance.toFloat())
} else if(content.startsWith(CommonUtils.QRCommon.QR_PAYMENT_CODE)){
//向商家付款
var balance = content.substring(CommonUtils.QRCommon.QR_RECEIVE_CODE.length, content.length)
TagUtils.d("MyChatManagerListener 向商家付款金额: ${fromUser} , ${toUser}, ${balance}")
updateBalanceServer(fromUser, toUser, CommonUtils.User.OPERATOR_MINUS, balance.toFloat())
} else {
var chatBean = CommonUtils.Chat.getChatBean(fromUser, toUser, userType, content, ChatBean.CONTENT_TYPE_TEXT, "",0.0, 0.0)
ChatRepository.insertChat(chatBean)
chatBean.isReceive = true
EventBus.getDefault().post(chatBean)
}
ChatNotificationUtils.sendNotification(fromUser)
}
}
}
}
/**
* 下载图片、语音、小视频
*/
private fun processDownload(chatBean : ChatBean){
var fileName = ""
if(chatBean.contentType == ChatBean.CONTENT_TYPE_VOICE){
fileName = chatBean.voice
} else if(chatBean.contentType == ChatBean.CONTENT_TYPE_VIDEO){
fileName = chatBean.video
} else if(chatBean.contentType == ChatBean.CONTENT_TYPE_IMG){
fileName = chatBean.imgPath
} else {
return
}
var videoUrl = CommonUtils.Moments.getReallyImageUrl(fileName)
var videoFile = FileUtils.getBaseFile(fileName)
TagUtils.d("下载多媒体Url :${videoUrl} ")
TagUtils.d("下载多媒体File :${videoFile} ")
VideoDownloadManager.downloadFast(videoUrl, videoFile, object : VideoDownloadInter {
override fun onDone(filePath: String) {
TagUtils.d("小视频多媒体完成:${filePath}")
if(chatBean.contentType == ChatBean.CONTENT_TYPE_VOICE){
var second = CommonUtils.Media.getMediaTime(filePath, chatBean.contentType)
chatBean.second = second
chatBean.voiceLocal = filePath
} else if(chatBean.contentType == ChatBean.CONTENT_TYPE_VIDEO){
chatBean.videoLocal = filePath
var second = CommonUtils.Media.getMediaTime(filePath, chatBean.contentType)
chatBean.second = second
} else if(chatBean.contentType == ChatBean.CONTENT_TYPE_IMG){
chatBean.imgPathLocal = filePath
}
ChatRepository.insertChat(chatBean)
EventBus.getDefault().post(chatBean)
}
override fun onError() {
}
override fun onProgress(process: Int) {
}
})
}



















