Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。
Kotlin高仿微信-项目实践58篇,点击查看详情
效果图:
实现代码:
<?xml version="1.0" encoding="utf-8"?> <layout> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/wc_base_bg"> <include layout="@layout/wc_base_top_title"/> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/register_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="120dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" android:src="@drawable/wc_logo3"/> <TextView android:id="@+id/regiter_account_tv" android:layout_width="90dp" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginTop="60dp" android:gravity="right" android:text="@string/user_account" android:textSize="16sp" android:textColor="@color/black" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/register_icon"/> <EditText android:id="@+id/regiter_account" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="60dp" android:layout_marginStart="110dp" android:paddingLeft="10dp" android:paddingVertical="6dp" android:inputType="textEmailAddress" app:layout_constraintStart_toEndOf="@+id/regiter_account_tv" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/regiter_account_tv" app:layout_constraintBottom_toBottomOf="@+id/regiter_account_tv" android:background="@drawable/base_edittext_selector" android:text="" android:hint="@string/login_user_tip" android:textSize="18sp" android:textColor="@color/gray_text" /> <TextView android:id="@+id/regiter_name_tv" android:layout_width="90dp" android:layout_height="wrap_content" android:gravity="right" android:layout_marginStart="20dp" android:layout_marginTop="30dp" android:text="@string/login_nickname" android:textSize="16sp" android:textColor="@color/black" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/regiter_account_tv"/> <EditText android:id="@+id/regiter_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="60dp" android:layout_marginStart="110dp" android:paddingLeft="10dp" android:paddingVertical="6dp" android:visibility="visible" android:text="" app:layout_constraintEnd_toStartOf="@+id/regiter_name_tv" app:layout_constraintTop_toTopOf="@+id/regiter_name_tv" app:layout_constraintBottom_toBottomOf="@+id/regiter_name_tv" android:background="@drawable/base_edittext_selector" android:hint="@string/login_nickname_tip" android:textSize="18sp" android:textColor="@color/gray_text" /> <TextView android:id="@+id/regiter_password_tv" android:layout_width="90dp" android:layout_height="wrap_content" android:gravity="right" android:layout_marginStart="20dp" android:layout_marginTop="30dp" android:text="@string/login_password" android:textSize="16sp" android:textColor="@color/black" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/regiter_name_tv" /> <EditText android:id="@+id/regiter_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="60dp" android:layout_marginStart="110dp" android:paddingLeft="10dp" android:paddingVertical="6dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/regiter_password_tv" app:layout_constraintTop_toTopOf="@+id/regiter_password_tv" app:layout_constraintBottom_toBottomOf="@+id/regiter_password_tv" android:hint="@string/login_password_tip" android:textSize="18sp" android:inputType="textPassword" android:background="@drawable/base_edittext_selector" android:text="" android:textColor="@color/gray_text" /> <TextView android:id="@+id/regiter_password_confirm_tv" android:layout_width="90dp" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:layout_marginTop="30dp" android:gravity="right" android:text="@string/login_password_confirm" android:textSize="16sp" android:textColor="@color/black" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/regiter_password_tv" /> <EditText android:id="@+id/regiter_password_confirm" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="60dp" android:layout_marginStart="110dp" android:paddingLeft="10dp" android:paddingVertical="6dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/regiter_password_confirm_tv" app:layout_constraintTop_toTopOf="@+id/regiter_password_confirm_tv" app:layout_constraintBottom_toBottomOf="@+id/regiter_password_confirm_tv" android:hint="@string/login_password_tip" android:textSize="18sp" android:inputType="textPassword" android:background="@drawable/base_edittext_selector" android:text="" android:textColor="@color/gray_text" /> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btn_user_register" android:layout_width="140dp" android:layout_height="wrap_content" android:layout_marginTop="80dp" android:background="@drawable/wc_base_green_selector" app:layout_constraintTop_toBottomOf="@+id/regiter_password_confirm" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" android:textColor="@color/white" android:text="@string/register" android:textSize="18sp" android:textStyle="bold"/> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
/** * Author : wangning * Email : maoning20080809@163.com * Date : 2022/4/19 14:50 * Description : 注册页面 */ class RegisterFragment : BaseDataBindingFragment<WcRegisterBinding>() { override fun getLayoutRes() = R.layout.wc_register private val userViewModel: UserViewModel by viewModels() private var account: String = "" private var name: String = "" private var password: String = "" private var passwordConfirm: String = "" private var email : String = "" private var navController : NavController? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initViews() } private fun initViews() { super.builder().setTitleContent(R.string.wc_base_top_register) btn_user_register.setOnClickListener { register() } navController = Navigation.findNavController(btn_user_register) userViewModel.insertUserData.observe(viewLifecycleOwner){ TagUtils.d("register 服务器返回:${it} ") //如果同步成功,则重新登录 if(it){ DataStoreUtils.put(DataStoreParams.User.DS_OPENFILE_REGISTER, true) } } } //注册 private fun register() { account = regiter_account.getText().toString().trim() name = regiter_name.getText().toString().trim() password = regiter_password.getText().toString().trim() passwordConfirm = regiter_password_confirm.getText().toString().trim() if(TextUtils.isEmpty(account)){ ToastUtils.makeText(requireActivity(), BaseUtils.getString(R.string.register_account_empty_tip)) } else if(account.length < 4 || account.length > 18){ ToastUtils.makeText(requireActivity(), BaseUtils.getString(R.string.register_account_length_tip)) } else if(TextUtils.isEmpty(name)){ ToastUtils.makeText(requireActivity(), BaseUtils.getString(R.string.register_nickname_empty_tip)) } else if(name.length < 2 || name.length > 16){ ToastUtils.makeText(requireActivity(), BaseUtils.getString(R.string.register_nickname_length_tip)) } else if(TextUtils.isEmpty(password)){ ToastUtils.makeText(requireActivity(), BaseUtils.getString(R.string.register_password_empty_tip)) } else if(password.length < 4 || password.length > 18){ ToastUtils.makeText(requireActivity(), BaseUtils.getString(R.string.register_password_length_tip)) } else if(TextUtils.isEmpty(passwordConfirm)){ ToastUtils.makeText(requireActivity(), BaseUtils.getString(R.string.register_password_confirm_empty_tip)) } else if(!password.equals(passwordConfirm)){ ToastUtils.makeText(requireActivity(), BaseUtils.getString(R.string.register_password_different_tip)) } else { email = account +"@163.com"//默认设置email信息 DataStoreUtils.put(DataStoreParams.User.DS_OPENFILE_REGISTER, false) showLoadingDialog() CoroutineScope(Dispatchers.IO).launch { try { TagUtils.d("register 开始注册") val reg = Registration() //设置类型 reg.type = IQ.Type.SET //发送到服务器 reg.to = WcApp.getXmppConnection().serviceName //设置用户名 reg.setUsername(account) //设置密码 reg.setPassword(password) //设置其余属性 不填可能会报500异常 连接不到服务器 amack一个Bug reg.addAttribute("name", name) reg.addAttribute("email", email) reg.addAttribute("android", "geolo_createUser_android") //设置安卓端登录 var connection = WcApp.getXmppConnection() TagUtils.d("register 开始注册 connect ") connection.connect() //创建包过滤器 val filter: PacketFilter = AndFilter(PacketIDFilter(reg.packetID), PacketTypeFilter(IQ::class.java)) //创建包收集器 val collector = connection.createPacketCollector(filter) //发送包 connection.sendPacket(reg) //获取返回信息 val result = collector.nextResult(SmackConfiguration.getPacketReplyTimeout().toLong()) as IQ // 停止请求results(是否成功的结果) collector?.cancel() TagUtils.d("register 开始注册 connect2 ${Gson().toJson(result)}") //通过返回信息判断 if (result == null) { registerResultTip(BaseUtils.getString(R.string.wc_register_xmpp_no_response)) } else if (result.type === IQ.Type.ERROR) { if (result.error.toString() .equals("conflict(409)", ignoreCase = true) ) { //账户已经存在, registerResultTip(BaseUtils.getString(R.string.wc_register_account_exist, account)) CoroutineScope(Dispatchers.Main).launch { //注册成功跳转登录页面,重新登录。有可能没同步到web服务器 Navigation.findNavController(btn_user_register).popBackStack() } } else { TagUtils.d("register 注册失败 connect2 ${Gson().toJson(result.error)}") registerResultTip(BaseUtils.getString(R.string.wc_register_failure)) } } else if (result.type === IQ.Type.RESULT) { TagUtils.d("register 开始注册 connect account = ${account}, ${password}") connection.login(account, password) val presence = Presence(Presence.Type.available ) connection.sendPacket(presence) var userBean = UserBean(account = account, name = name, nickName = name, address = "", email = "", phone = "", avatar = "", birthday = "", note = "") DataStoreUtils.put(DataStoreParams.User.DS_ACCOUNT, account) DataStoreUtils.put(DataStoreParams.User.DS_PASSWORD, password) DataStoreUtils.put(DataStoreParams.User.DS_NICKNAME, name) DataStoreUtils.put(DataStoreParams.User.DS_TEMP_USER_BEAN, Gson().toJson(userBean)) //WcApp.setUserBean(userBean) //插入本地数据库 userViewModel.insertUserLocal(userBean) //上传到服务器 userViewModel.insertUser(userBean) registerResultTip(BaseUtils.getString(R.string.wc_register_success)) TagUtils.d("register 开始注册 成功 ") CoroutineScope(Dispatchers.Main).launch { //注册成功跳转登录页面,重新登录。有可能没同步到web服务器 navController?.previousBackStackEntry?.savedStateHandle?.set(CommonUtils.User.IS_REGISTER_BACK, true) navController?.popBackStack() } } else { registerResultTip(BaseUtils.getString(R.string.wc_register_failure)) } } catch (e: Exception) { TagUtils.d("register 开始注册 异常:${e.message} ") e.printStackTrace() registerResultTip(BaseUtils.getString(R.string.wc_register_failure)) } } } } /** * 注册失败提示 */ private fun registerResultTip(msg : String){ TagUtils.d("register 开始注册 对话框消失 ") CoroutineScope(Dispatchers.Main).launch { ToastUtils.makeText(requireActivity(), msg) dismissLoadingDialog() } } private var loadingUtils : BaseDialogUtils? = null //显示加载对话框 private fun showLoadingDialog(){ loadingUtils = BaseDialogUtils(requireActivity()) loadingUtils!!.builder() .hideCancel() .hideConfirm() .setCancelable(true) .setOnLoadingClick(object : BaseDialogUtils.OnLoadingClick{ override fun onClickCancel() { ToastUtils.makeText(requireActivity(), "对话框取消按钮") } override fun onClickConfirm() { ToastUtils.makeText(requireActivity(), "对话框确定按钮") } }) loadingUtils?.show() } //隐藏加载对话框 private fun dismissLoadingDialog(){ loadingUtils?.dismiss() } }