首页 > 其他分享 >团队项目冲刺——DAY3

团队项目冲刺——DAY3

时间:2024-11-17 22:40:32浏览次数:1  
标签:SongCondition val DAY3 private 冲刺 fun import android 团队

团队项目冲刺——DAY3

每天举行站立式会议

img

昨天已完成的工作

成员 任务
徐嘉炜 组织会议,说明项目进度,指导项目发展
陈祥意 参与会议,简要讲述应用程序测试的各个模块
林楦 参与会议,讲述有关功能界面的UI开发
陈大锴 参与会议,协调开发技术与实际需求,记录需求
蔡家显 参与会议,讲述测试时的注意事项
陈祖民 参与会议,讲述有关登录界面的UI开发
肖商 参与会议,详细讲述测试模块的测试用例

今天计划完成的工作

成员 任务
徐嘉炜 开发登录界面交互逻辑
陈祥意 测试网络API
林楦 开发注册UI界面
陈大锴 开发注册界面交互逻辑
蔡家显 汇总测试记录
陈祖民 开发登录UI界面
肖商 生成各个测试模块的测试案例

工作中遇到的困难

部分 API 请求与文档有些出入,需要通过 Charles 具体捕获字段去分析数据并观察其具体含义。

MVVM 交互时 LifeData 在 Activity / Fragment 重建时 observe 会重走生命周期,重写 onSaveInstanceState / onConfigChange 方法可传入 bundle 标记防止逻辑重走。

项目燃尽图

image-20241117224204861

适当的项目程序/模块的最新(运行)截图

image-20241117224441983

package com.timi.utils

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.HttpURLConnection
import java.net.URL

object BitmapRequest {

    /**
     * 通过图片的Url获取相应的bitmap资源
     * @param url 需要加载的图片Url地址
     * @param onSuccess 请求成功后的回调
     */
    fun getImageBitmap(
       url: String,
       onSuccess: (Bitmap) -> Unit
    ){
       CoroutineScope(Dispatchers.Main).launch {
          withContext(Dispatchers.IO){
             var connection: HttpURLConnection? = null
             try {
                connection = URL(url).openConnection() as HttpURLConnection
                connection.apply {
                   requestMethod = "GET"
                   readTimeout = 8000
                   onSuccess(BitmapFactory.decodeStream(inputStream))
                }
             }catch (e: Exception){
                e.printStackTrace()
             }finally {
                connection?.disconnect()
             }
          }
       }
    }

}

package com.timi.utils

import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment

/**
 * 取消虚拟键盘
 */
fun View.hideKeyboard(){
    val inputMethodManager = context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
    inputMethodManager.hideSoftInputFromWindow(windowToken, 0)
}


//标志上一次点击的时刻
private var lastClickTime: Long = 0
/**
 * 判断是否短时间内进行了快速点击,用于过滤多余的点击
 * @param mills 拦截相隔时长内的多次点击
 * @return 该次点击是否有效
 */
fun View.isClickEffective(mills: Int = 500): Boolean{
    val currentClickTime = System.currentTimeMillis()
    val clickEffective = currentClickTime - lastClickTime >= mills
    lastClickTime = currentClickTime
    if (!clickEffective)
        Logger.w("ViewClicked", "This quick click was intercepted.\nThe click position was $this")
    return clickEffective
}


private var toast: Toast? = null
/**
 * 显示Toast,防止Toast多次连续显示一直提示而不消失
 * @param text Toast展示的文本
 * @param time 展示时长,默认为[Toast.LENGTH_SHORT]
 */
fun Fragment.makeToast(text: String, time: Int = Toast.LENGTH_SHORT){
    if (toast == null){
        toast = Toast.makeText(requireContext(), text, time)
    }else{
        toast!!.setText(text)
    }
    toast!!.show()
}

package com.timi.utils

/**
 * 存放各个功能的API接口
 */
object WebConstant{
    
    const val currentUrl = "http://192.168.11.147:3000/" // 三方 API 所开启的 ip 地址

    object Login{
        const val API_TOURIST = "register/anonimous"            //游客登陆
    }

    object Register{
        const val API_REGISTER = "register/cellphone"           //注册
    }

    object Captcha{
        const val API_CAPTCHA = "captcha/sent"                  //发送登录验证码
    }

    object Found{
        const val API_BANNER = "banner?type=1"                  //发现页轮播图
        const val API_RECOMMANDPLAY = "personalized?limit=15"   //发现页推荐歌单
    }

    object PlaylistSquare{
        const val API_PLAYLIST_CATEGORY = "playlist/catlist"    //歌单广场-歌单分类
        const val API_PLAYLIST_CATEGORY_DETAIL = "top/playlist"//歌单广场-网友精选歌单
        const val API_PLAYLIST_CATEGORY_DETAIL_QUALITY = "top/playlist/highquality"//歌单广场-获取精品歌单
    }

    object Search{
        const val API_HOT_RECOMMAND = "search/hot"              //简略热搜(猜你喜欢)
        const val API_HOT_SEARCH = "search/hot/detail"          //热搜榜
        const val API_HOT_ARTISTS = "top/artists?limit=20"      //热门歌手榜
        const val API_SEARCH = "search"                         //搜索
    }

    object General{
        const val API_PLAYLIST_DETAIL = "playlist/detail"       //获取歌单详情
    }

    object Song{
        const val API_SONG_URL = "song/url"                     //获取歌曲url
        const val API_SONG_LYRIC = "lyric"                      //获取歌曲歌词
    }

}

image-20241117224821958

image-20241117224835318

package com.example.player

import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Context.BIND_AUTO_CREATE
import android.content.Context.RECEIVER_NOT_EXPORTED
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.Navigation
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.player.BroadcastConstant.INTENT_KEY_MUSIC
import com.example.player.databinding.FragmentPlayerBinding
import com.example.player.SongCondition.PLAY_MODE_LIST
import com.example.player.SongCondition.PLAY_MODE_SHUFFLE
import com.example.player.SongCondition.PLAY_MODE_SINGLE
import com.example.player.SongCondition.currentSongIndex
import com.example.player.SongCondition.songDataList
import com.example.player.adapter.BottomSongListAdapter
import com.example.player.store.bean.Song
import com.example.utils.Logger
import com.example.utils.isClickEffective
import com.example.utils.makeToast
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.squareup.picasso.Picasso
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch


class PlayerFragment : Fragment() {

    private var initView = false      //界面信息加载完成标志
    private var lastView: View? = null

    companion object{
       private const val PROGRESS_UPDATE = 0
       private const val TAG = "PlayerFragment"
    }

    private lateinit var binding: FragmentPlayerBinding
    private lateinit var navController: NavController

    private val handler = Handler(Looper.getMainLooper()) { msg ->
       if (msg.what == PROGRESS_UPDATE) updateCurrentPosition()
       true
    }

    private lateinit var song: Song
    private lateinit var songUrl: String

    private var serviceCreate = false
    private val musicConnection by lazy { MusicConnection() }
    private lateinit var serviceBinder: MusicService.MusicBinder
    private val notificationReceiver: NotificationReceiver by lazy { NotificationReceiver() }

    private var playEnd = false          //播放是否结束

    private lateinit var bottomSheetBehavior: BottomSheetBehavior<ConstraintLayout>
    private lateinit var songAdapter: BottomSongListAdapter

    inner class MusicConnection : ServiceConnection{
       override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
          Logger.i(TAG, "ServiceConnection initialized.")
          serviceBinder = service as MusicService.MusicBinder

          //当与 service 链接后,开始初始化 service 中 mediaPlayer,播放音乐并且更新ui
          if (!SongCondition.launchFromNotification){
             serviceBinder.initPlayer(url = songUrl, looping = SongCondition.currentPlayMode == PLAY_MODE_SINGLE)
          }else{
             initSeekBar()
             SongCondition.playing = true
             SongCondition.launchFromNotification = false
          }
       }
       override fun onServiceDisconnected(name: ComponentName?) {
          Logger.i(TAG, "Service disconnect.")
       }
    }

    inner class NotificationReceiver: BroadcastReceiver(){
       override fun onReceive(context: Context?, intent: Intent) {
          intent.getStringExtra(INTENT_KEY_MUSIC)?.apply {
             when(this){
                BroadcastConstant.INTENT_VALUE_MUSIC_CONTROL_TO_FRAGMENT -> {
                   //在Service播放器的状态已经经过调整,调整按键与handler需要相反调整
                   if (!SongCondition.playing){
                      handler.removeCallbacksAndMessages(null)
                      binding.ivPlayerFragmentControl.setImageResource(R.drawable.ic_start)
                   }else{
                      handler.sendEmptyMessageDelayed(PROGRESS_UPDATE, 100L)
                      binding.ivPlayerFragmentControl.setImageResource(R.drawable.ic_stop)
                   }
                }
                BroadcastConstant.INTENT_VALUE_MUSIC_PREPARE -> {
                   song = SongUtils.currentSong()
                   setSongInformation()
                   initSeekBar()
                   SongCondition.playing = true
                }
             }
          }
       }
    }

    @SuppressLint("UnspecifiedRegisterReceiverFlag")
    override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setHasOptionsMenu(true)    //让 fragment 中 toolbar 的按钮响应 onOptionItemSelected
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
          requireContext().registerReceiver(notificationReceiver, IntentFilter().apply {
             addAction(BroadcastConstant.BROADCAST_NOTIFICATION_ACTION)
          }, RECEIVER_NOT_EXPORTED)
       else
          requireContext().registerReceiver(notificationReceiver, IntentFilter().apply {
             addAction(BroadcastConstant.BROADCAST_NOTIFICATION_ACTION)
          })
    }

    override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?,
       savedInstanceState: Bundle?
    ): View {
       if (lastView == null){
          binding = FragmentPlayerBinding.inflate(inflater, container, false)
          lastView = binding.root
       }
       return lastView!!
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
       playEnd = false
       navController = Navigation.findNavController(view)

       if (!initView){
          song = SongUtils.currentSong()
          SongUtils.initRandomIndexList()
          setSongInformation()
          initBottomSheet()
          initWidget()
       }
       //如果从歌词fragment重新跳转时,重新加载handler
       if (SongCondition.playing) handler.sendEmptyMessage(PROGRESS_UPDATE)
    }

    override fun onResume() {
       super.onResume()
       //fragment准备好到前台后,开始发送 message 让 handle 刷新进度条
       if (initView && !playEnd){
          handler.removeCallbacksAndMessages(null)
          handler.sendEmptyMessageDelayed(PROGRESS_UPDATE, 100L)
       }
    }

    override fun onStop() {
       super.onStop()
       //fragment停止后,取消所有 message 和 callback,handle停止工作(不再刷新进度条)
       handler.removeCallbacksAndMessages(null)
    }

    override fun onDestroy() {
       super.onDestroy()
       requireContext().unregisterReceiver(notificationReceiver)
    }

    /**
     * 初始化部分控件
     */
    private fun initWidget(){
       binding.apply {
          (activity as AppCompatActivity).apply {
             setSupportActionBar(toolbarPlayerFragment)
             supportActionBar?.setDisplayHomeAsUpEnabled(true)
          }
          ivPlayerFragmentComments.setImageResource(R.drawable.ic_comments)
          ivPlayerFragmentLyric.setImageResource(R.drawable.ic_lyric)
          ivPlayerFragmentControl.setImageResource(if (SongCondition.playing) R.drawable.ic_stop else R.drawable.ic_start)
          ivPlayerFragmentNextSong.setImageResource(R.drawable.ic_next)
          ivPlayerFragmentPreSong.setImageResource(R.drawable.ic_previous)
          ivPlayerFragmentWatchList.setImageResource(R.drawable.ic_songlist)
          ivPlayerFragmentPlayMode.setImageResource(
             when(SongCondition.currentPlayMode){
                PLAY_MODE_LIST -> R.drawable.ic_listplay
                PLAY_MODE_SHUFFLE -> R.drawable.ic_shuffleplay
                else -> R.drawable.ic_singleplay   // PLAY_MODE_SINGLE
             })
          ivPlayerFragmentControl.setOnClickListener {
             if (!playEnd && it.isClickEffective(100)){
                playControl()
             }
          }
          ivPlayerFragmentPlayMode.setOnClickListener {
             if (!playEnd) playModeChange()
          }
          ivPlayerFragmentNextSong.setOnClickListener {
             if (!playEnd && it.isClickEffective(250)) {
                serviceBinder.nextSong()
             }else makeToast("你点得太快咯~~~")
          }
          ivPlayerFragmentPreSong.setOnClickListener {
             if (!playEnd && it.isClickEffective(250)){
                serviceBinder.previousSong()
             }else makeToast("你点得太快咯~~~")
          }
          ivPlayerFragmentLyric.setOnClickListener {
             if (!playEnd && it.isClickEffective()){
                //该跳转不传递信息,直接从SongCondition处获取
                navController.navigate(R.id.action_playerFragment_to_lyricFragment)
             }
          }
          ivPlayerFragmentWatchList.setOnClickListener{
             if (it.isClickEffective()){
                if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_HIDDEN){
                   bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
                }else if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED){
                   bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
                }
             }
          }
          seekBarPlayerFragment.setOnSeekBarChangeListener(object : OnSeekBarChangeListener{
             override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {}
             override fun onStartTrackingTouch(seekBar: SeekBar?) {
                handler.removeCallbacksAndMessages(null)
             }
             override fun onStopTrackingTouch(seekBar: SeekBar) {
                if (!playEnd){
                   serviceBinder.seekTo(seekBar.progress)
                   if (!serviceBinder.isPlaying()){
                      serviceBinder.play()
                   }
                   SongCondition.playing = true
                   handler.sendEmptyMessageDelayed(PROGRESS_UPDATE, 100L)
                }
             }
          })
       }
    }

    /**
     * 歌曲加载后,设置fragment中歌曲名称、歌手信息、图片与颜色等,随后开启服务并开始音乐播放
     */
    private fun setSongInformation(){
       binding.apply {
          tvPlayerFragmentSongName.text = song.songName
          tvPlayerFragmentArtist.text = song.artistAndDescription
          ivPlayerFragmentLike.setImageResource(R.drawable.ic_like)  //喜欢按钮需要按需更改
          (activity as AppCompatActivity).supportActionBar?.title = song.songName

          //加载图片完成后,表示界面初始化完成,播放逻辑开始
          CoroutineScope(Dispatchers.Main).launch {
             Picasso.get().load(song.picUrl).into(ivPlayerFragmentSongPic)
          }.invokeOnCompletion {
             it?.printStackTrace()
             Logger.i(TAG, "图片加载完成.")
             if (!initView){
                seekBarPlayerFragment.visibility = View.VISIBLE
                //界面加载后,开始执行播放逻辑
                SongUtils.songUrlRequest(song.songId){ url ->
                   songUrl = url
                   initView = true
                   startPlayerService()
                }
             }
          }
       }
    }

    @Deprecated("Deprecated in Java")
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
       if (item.itemId == android.R.id.home){
          initView = false
          lastView = null
          activity?.finish()
       }
       return super.onOptionsItemSelected(item)
    }

    /**
     * 初始化底部歌单列表
     */
    @SuppressLint("NotifyDataSetChanged")
    private fun initBottomSheet(){
       bottomSheetBehavior = BottomSheetBehavior.from(binding.constraintLayoutPlayerFragmentBottom)
       bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
       binding.rvPlayerFragmentBottomPlaylist.apply {
          layoutManager = LinearLayoutManager(requireContext())
          addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL))
          songAdapter = BottomSongListAdapter(songDataList, { position ->
             //onItemClicked
             //点击歌曲切换音乐
             //如果点击的是当前正在播放的歌曲,则不进行切换
             if (isClickEffective()){
                if (position != currentSongIndex){
                   SongUtils.changeSongIndex(position)
                   serviceBinder.changeSong()
                }
                if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED){
                   bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
                }
             }
          }){ position ->
             //onDeleteClick
             //删除某歌曲
             if (isClickEffective()){
                val operateList = songDataList.toMutableList()
                operateList.removeAt(position)
                SongUtils.deleteSongData(requireContext(), songDataList[position])
                songDataList = operateList.toList()
                //如果其正在播放,那么切换下一首歌曲
                if (currentSongIndex == position){
                   //如果被删除的歌曲不是原来列表中的最后一项,索引不需要变动即可播放下一首;如果是最后一项,则返回到第一首
                   if (position == songDataList.size){
                      SongUtils.changeSongIndex(0)
                   }
                   //删除后歌曲列表不为空,则切换下一首
                   if (songDataList.isNotEmpty()){
                      serviceBinder.changeSong()
                   }else{
                      playEnd()
                   }
                }
                songAdapter.songList = songDataList
                songAdapter.notifyDataSetChanged()
             }
          }
          adapter = songAdapter
       }
    }

    /**
     * 初始化音乐播放
     * @param onInitCallBack 初始化后的回调
     */
    private fun startPlayerService(onInitCallBack: (() -> Unit)? = null){
       requireContext().apply {
          if (!serviceCreate){
             serviceCreate = true
             startService(Intent(this, MusicService::class.java))
          }
          bindService(Intent(this, MusicService::class.java), musicConnection, BIND_AUTO_CREATE)
       }
       onInitCallBack?.invoke()
    }

    /**
     * 接收实时歌曲播放进度更新当前时长TextView
     */
    private fun updateCurrentPosition(){
       if (::serviceBinder.isInitialized){
          val currentPlayingPosition = serviceBinder.getCurrentPosition()
          Logger.i(TAG, "currentPlayingPosition=$currentPlayingPosition")
          binding.tvPlayerFragmentCurTime.text = SongUtils.updateDuration(currentPlayingPosition)
          binding.seekBarPlayerFragment.progress = currentPlayingPosition
          handler.sendEmptyMessageDelayed(PROGRESS_UPDATE, 1000L)
       }
    }

    /**
     * 控制播放的 imageView 点击逻辑
     */
    private fun playControl(){
       if (SongCondition.playing){
          handler.removeCallbacksAndMessages(null)
          binding.ivPlayerFragmentControl.setImageResource(R.drawable.ic_start)
       }else{
          handler.sendEmptyMessageDelayed(PROGRESS_UPDATE, 100L)
          binding.ivPlayerFragmentControl.setImageResource(R.drawable.ic_stop)
       }
       serviceBinder.playControl()
    }

    /**
     * 修改当前播放模式
     */
    private fun playModeChange(){
       when(SongCondition.currentPlayMode){
          PLAY_MODE_LIST -> {
             //列表播放 -> 随机播放
             SongCondition.currentPlayMode = PLAY_MODE_SHUFFLE
             binding.ivPlayerFragmentPlayMode.setImageResource(R.drawable.ic_shuffleplay)
             makeToast("随机播放")
          }
          PLAY_MODE_SHUFFLE -> {
             //随机播放 -> 单曲循环
             serviceBinder.setLooping(true)
             SongCondition.currentPlayMode = PLAY_MODE_SINGLE
             binding.ivPlayerFragmentPlayMode.setImageResource(R.drawable.ic_singleplay)
             makeToast("单曲循环")
          }
          PLAY_MODE_SINGLE -> {
             //单曲循环 -> 列表播放
             serviceBinder.setLooping(false)
             SongCondition.currentPlayMode = PLAY_MODE_LIST
             binding.ivPlayerFragmentPlayMode.setImageResource(R.drawable.ic_listplay)
             makeToast("列表播放")
          }
       }
    }

    /**
     * 每次歌曲重新加载好后,初始化seekBar
     */
    private fun initSeekBar(){
       val duration = serviceBinder.getDuration()
       Logger.i(TAG, "duration=$duration")
       binding.seekBarPlayerFragment.max = duration
       binding.tvPlayerFragmentEndTime.text = SongUtils.updateDuration(duration)
       binding.ivPlayerFragmentControl.setImageResource(R.drawable.ic_stop)
       handler.removeCallbacksAndMessages(null)   //先清除之前更新进度条的信息
       handler.sendEmptyMessageDelayed(PROGRESS_UPDATE, 100L) //更新seekbar进度条
    }

    /**
     * 当集合中没有任何歌曲,将页面信息置空
     */
    @SuppressLint("SetTextI18n")
    private fun playEnd(){
       playEnd = true
       SongCondition.playing = false
       handler.removeCallbacksAndMessages(null)
       binding.apply {
          ivPlayerFragmentSongPic.setImageBitmap(null)
          tvPlayerFragmentSongName.text = ""
          tvPlayerFragmentArtist.text = ""
          tvPlayerFragmentCurTime.text = "00:00"
          tvPlayerFragmentEndTime.text = "00:00"
          ivPlayerFragmentControl.setImageResource(R.drawable.ic_start)
          seekBarPlayerFragment.progress = 0
       }
       if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED){
          bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
       }
       (activity as AppCompatActivity).supportActionBar?.title = ""
       requireContext().apply {
          unbindService(musicConnection)
          stopService(Intent(this, MusicService::class.java))
       }
    }

}

每日每人总结

成员 总结
徐嘉炜 请求模块已经进行封装使用
陈祥意 测试模块已经全部通过,可以进行下一个模块的测试
林楦 其余模块的 UI 正在制作
陈大锴 即将开发好具体播放实现模块,还有些难度
蔡家显 测试时需要全面考虑,尽可能测出有问题的地方
陈祖民 具体的用户 UI 交互可以再清晰一些
肖商 对请求的测试需要注意在多种条件下,保证请求的成功

标签:SongCondition,val,DAY3,private,冲刺,fun,import,android,团队
From: https://www.cnblogs.com/melonJess/p/18551322

相关文章

  • 第 7 篇 Scrum 冲刺博客
    团队作业4——第7篇Scrum冲刺博客作业要求这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13234这个作业的目标团队集体协作完成项目开发队名雄狮......
  • 团队冲刺-7
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13234这个作业的目标记录每日进展和问题,对问题进行解决1.每日会议|成员|昨天任务|今......
  • 团队冲刺-1
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13234这个作业的目标记录每日进展和问题,对问题进行解决成员任务伍绍雄前端开发,进行界面的设......
  • 团队冲刺-2
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13234这个作业的目标记录每日进展和问题,对问题进行解决1.每日会议成员昨天任务今天任务工作中......
  • 团队冲刺-3
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13234这个作业的目标记录每日进展和问题,对问题进行解决1.每日会议成员昨天任务今天任务工作中......
  • 团队冲刺-4
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13234这个作业的目标记录每日进展和问题,对问题进行解决1.每日会议成员昨天任务今天任务工作中......
  • 团队冲刺-5
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13234这个作业的目标记录每日进展和问题,对问题进行解决1.每日会议成员昨天任务今天任务工作中......
  • 团队冲刺-6
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13234这个作业的目标记录每日进展和问题,对问题进行解决1.每日会议|成员|昨天任务|今......
  • Day32--属性
    Day32--属性基本数据类型的默认值byte类型:默认值是0。short类型:默认值为0。int类型:默认值是0。long类型:默认值为0L(注意要加L后缀来表示长整型常量)。float类型:默认值是0.0f(需要加f后缀来表示单精度浮点数常量)double类型:默认值是0.0d(d后缀可以省略,因为0.0默认是双......
  • Day32--封装
    Day32--封装该露的露,该藏的藏。就像电视,大多数的数据线都隐藏起来了,只暴露出少量的接口和按键。我们不需要知道电视的具体构造,只用知道如何使用遥控器就行了。我们程序设计要追求高内聚、低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方......