每天举行站立式会议
昨天已完成的工作
成员 | 任务 |
---|---|
徐嘉炜 | 组织会议,说明项目进度,指导项目发展 |
陈祥意 | 参与会议,简要讲述应用程序测试的各个模块 |
林楦 | 参与会议,讲述有关功能界面的UI开发 |
陈大锴 | 参与会议,协调开发技术与实际需求,记录需求 |
蔡家显 | 参与会议,讲述测试时的注意事项 |
陈祖民 | 参与会议,讲述有关登录界面的UI开发 |
肖商 | 参与会议,详细讲述测试模块的测试用例 |
今天计划完成的工作
成员 | 任务 |
---|---|
徐嘉炜 | 开发登录界面交互逻辑 |
陈祥意 | 测试网络API |
林楦 | 开发注册UI界面 |
陈大锴 | 开发注册界面交互逻辑 |
蔡家显 | 汇总测试记录 |
陈祖民 | 开发登录UI界面 |
肖商 | 生成各个测试模块的测试案例 |
工作中遇到的困难
UI界面与交互逻辑开发需要同步,所以需要UI组付出比较大的努力。
项目燃尽图
适当的项目程序/模块的最新(运行)截图
最新模块的代码
package com.timi.music import android.os.Bundle import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContentView(R.layout.activity_main) ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } } } package com.timi.music import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.View.OnClickListener import android.view.ViewGroup import androidx.lifecycle.ViewModelProvider import androidx.navigation.NavController import androidx.navigation.Navigation import com.google.android.material.snackbar.Snackbar import com.timi.music.databinding.FragmentLoginBinding import com.timi.utils.Logger import com.timi.utils.isClickEffective import com.timi.utils.makeToast class LoginFragment : Fragment(), OnClickListener { companion object{ private const val TAG = "LoginFragment" } private lateinit var binding: FragmentLoginBinding private lateinit var navController: NavController private val loginViewModel: LoginViewModel by lazy { ViewModelProvider(this)[LoginViewModel::class.java] } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentLoginBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { navController = Navigation.findNavController(view) observeChange() initListener() if (Logger.currentLevel < 5){ binding.btnLoginFragmentTest.visibility = View.GONE } } /** * 检测LoginViewModel中变量的变化 */ private fun observeChange(){ loginViewModel.apply { //验证码发送返回结果 captchaCode.observe(viewLifecycleOwner){ if (it != 0){ if (it == 200){ Logger.i(TAG, "登录验证码发送成功.") makeToast("验证码发送成功!") }else{ Logger.i(TAG, "验证码发送失败.") makeToast("验证码发送失败!请检查你的手机号是否正确!") } captchaCode.value = 0 //重置变量 } } //游客登陆返回结果 touristData.observe(viewLifecycleOwner){ it?.apply { if (this.code == 200){ //登录后跳转至主程序 Logger.i(TAG, "游客登陆成功.") makeToast("游客登陆成功!") //携带cookie跳转至CentreActivity startActivity(Intent(context, CentreActivity::class.java).apply { //toString防止cookie为null引发空指针异常 putExtra("cookie", cookie.toString()) }) }else{ Logger.w(TAG, "游客登陆失败.") makeToast("游客登陆失败") } touristData.value = null } } } } private fun initListener(){ binding.apply { btnLoginFragmentLogin.setOnClickListener(this@LoginFragment) btnLoginFragmentRegister.setOnClickListener(this@LoginFragment) btnLoginFragmentSendCode.setOnClickListener(this@LoginFragment) btnLoginFragmentTouristLogin.setOnClickListener(this@LoginFragment) binding.btnLoginFragmentTest.setOnClickListener(this@LoginFragment) } } override fun onClick(v: View?) { when(v){ binding.btnLoginFragmentLogin -> { if (checkInput()){ // if (tryLogin()){ // // } } } binding.btnLoginFragmentRegister -> navController.navigate(R.id.action_loginFragment_to_registerFragment) binding.btnLoginFragmentSendCode -> getCode() binding.btnLoginFragmentTouristLogin -> { if (v.isClickEffective()) loginViewModel.touristLoginRequest() } binding.btnLoginFragmentTest -> { if (v.isClickEffective()){ startActivity(Intent(context, CentreActivity::class.java).apply { putExtra("cookie", "") }) } } } } private fun getCode(){ if (binding.edtLoginFragmentPhoneNumber.text.length != 11){ //如果输入格式不满足要求,无法获取验证码 makeToast("请输入11位手机号!") }else{ loginViewModel.codeRequest(binding.edtLoginFragmentPhoneNumber.text.toString()) } } private fun checkInput(): Boolean{ return if (binding.edtLoginFragmentPhoneNumber.text.toString().isNotEmpty()){ if (binding.edtLoginFragmentCode.text.toString().isNotEmpty()){ true }else{ Snackbar.make(requireView(), "请输入验证码!", Snackbar.LENGTH_SHORT).show() false } }else{ Snackbar.make(requireView(), "请输入手机号!", Snackbar.LENGTH_SHORT).show() false } } // private fun tryLogin(): Boolean{ // // } } package com.timi.music import com.timi.music.bean.Captcha import com.timi.music.bean.CaptchaService import com.timi.music.bean.Tourist import com.timi.music.bean.TouristService import com.timi.utils.Logger import com.timi.utils.ServiceBuilder import retrofit2.Call import retrofit2.Callback import retrofit2.Response class LoginModel { private val TAG = "LoginModel" /** * 发出获取验证码请求 */ fun codeRequest( loginVM: LoginViewModel, phoneNumber: String ){ val captchaService = ServiceBuilder.create(CaptchaService::class.java) captchaService.getCaptchaData(phoneNumber).enqueue(object : Callback{ override fun onResponse(call: Call , response: Response ) { response.body()?.apply { Logger.i(TAG, "注册验证码请求:$this") loginVM.captchaCode.value = this.code } } override fun onFailure(call: Call , t: Throwable) { t.printStackTrace() } }) } /** * 进行游客登陆 */ fun touristLoginRequest(loginVM: LoginViewModel){ val touristService = ServiceBuilder.create(TouristService::class.java) touristService.getTouristData().enqueue(object : Callback { override fun onResponse(call: Call , response: Response ) { response.body()?.apply { Logger.i(TAG, "游客登陆请求:$this") loginVM.touristData.value = this } } override fun onFailure(call: Call , t: Throwable) { t.printStackTrace() } }) } } package com.timi.music import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.timi.music.bean.Tourist class LoginViewModel: ViewModel() { private val TAG = "LoginViewModel" private val loginModel: LoginModel by lazy { LoginModel() } val captchaCode = MutableLiveData () //验证码发送返回结果 val touristData = MutableLiveData<tourist?>() //游客登陆返回结果 init { captchaCode.value = 0 touristData.value = null } /** * 发出获取验证码请求 */ fun codeRequest(phoneNumber: String){ loginModel.codeRequest(this, phoneNumber) } fun touristLoginRequest(){ loginModel.touristLoginRequest(this) } } package com.timi.music import android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.View.OnClickListener import android.view.ViewGroup import androidx.lifecycle.ViewModelProvider import androidx.navigation.NavController import androidx.navigation.Navigation import com.timi.music.databinding.FragmentRegisterBinding import com.timi.utils.Logger import com.timi.utils.makeToast class RegisterFragment : Fragment(), OnClickListener{ companion object { private const val TAG = "RegisterFragment" } private lateinit var binding: FragmentRegisterBinding private lateinit var navigationController: NavController private val registerViewModel: RegisterViewModel by lazy { ViewModelProvider(this)[RegisterViewModel::class.java] } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentRegisterBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { navigationController = Navigation.findNavController(view) observeChange() initListener() } /** * 检测RegisterViewModel中变量的变化 */ private fun observeChange(){ registerViewModel.apply { captchaCode.observe(viewLifecycleOwner){ if (it != 0){ if (it == 200){ Logger.i(TAG, "注册验证码发送成功.") makeToast("验证码发送成功!") }else{ Logger.i(TAG, "验证码发送失败.") makeToast("验证码发送失败!请检查你的手机号是否正确!") } captchaCode.value = 0 //重置变量 } } } } private fun initListener(){ binding.ivRegisterFragmentBack.setOnClickListener(this) binding.btnRegisterFragmentGetCode.setOnClickListener(this) } override fun onClick(v: View?) { when(v){ binding.ivRegisterFragmentBack -> navigationController.navigateUp() //返回到LoginFragment binding.btnRegisterFragmentGetCode -> getCode() //请求发送注册验证码 } } /** * 获取手机验证码 */ private fun getCode(){ if (binding.edtRegisterFragmentPhoneNumber.text.length != 11){ //如果输入格式不满足要求,无法获取验证码 makeToast("请输入11位手机号!") }else{ registerViewModel.codeRequest(binding.edtRegisterFragmentPhoneNumber.text.toString()) } } } package com.timi.music import android.util.Log import com.timi.music.bean.Captcha import com.timi.music.bean.CaptchaService import com.timi.utils.ServiceBuilder import retrofit2.Call import retrofit2.Callback import retrofit2.Response class RegisterModel { private val TAG = "RegisterModel" /** * 发出获取验证码请求 */ fun codeRequest( registerVM: RegisterViewModel, phoneNumber: String ){ val captchaService = ServiceBuilder.create(CaptchaService::class.java) captchaService.getCaptchaData(phoneNumber).enqueue(object : Callback { override fun onFailure(call: Call , t: Throwable) { t.printStackTrace() } override fun onResponse(call: Call , response: Response ) { response.body()?.apply { Log.i(TAG, "注册验证码请求:${this}") registerVM.captchaCode.value = this.code } } }) } } package com.timi.music import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class RegisterViewModel: ViewModel() { private val registerModel: RegisterModel by lazy { RegisterModel() } val captchaCode = MutableLiveData () //验证码发送返回的结果 init { captchaCode.value = 0 } /** * 发出获取验证码请求 */ fun codeRequest(phoneNumber: String){ registerModel.codeRequest(this, phoneNumber) } }
运行结果的截图
每日每人总结
成员 | 总结 |
---|---|
徐嘉炜 | 登录注册模块已经做得比较完善,团队各个小组都发挥了各自的优势 |
陈祥意 | 测试模块已经全部通过,可以进行下一个模块的测试 |
林楦 | 基础的UI界面已经定好,可以在不改变数据对象的前提下继续美化UI界面 |
陈大锴 | 代码在逻辑上基本没有问题,也说是有一点成就,希望下一个模块的开发也顺顺利利。 |
蔡家显 | 在测试的方式上,可以写一些自动化脚本进行测试,但是手写测试案例还是有用的,希望能够学到更多的测试方法 |
陈祖民 | UI界面的交互比较简洁明了,是一个有点,可以往简约风格方面发展,期待主功能界面的UI界面开发 |
肖商 | 测试用例发送和接收的报文均为有效报文,测试成功的喜悦促使着我们进一步开发下一个模块 |