Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed yesterday.
Improve this question
The app I'm going to write is an obstacle avoidance game.
Enemies and obstacles appear, and enemies move freely, and obstacles are created with random coordinates at a specific time. The game ends when the character hits an enemy or an obstacle. Set a record by raising 1 point per second.
It's my first time developing, so I have a lot of problems with the code.There are many problems with the corresponding kotlin code when writing the obstacle avoidance game. Please solve the problem.
Obstacles or enemies are not visible or are in a fixed position when launching the app. We want to keep our enemies free, and obstacles are created at a particular time and in a particular place.
I don't think the crash was handled.
I want to raise my score by 1 point per second.
I want to move the character to the desired point through a touch event.
The default activity file contains the following:
package com.example.myapplication
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Rect
import android.media.MediaPlayer
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.view.MotionEvent
import android.view.View
import android.widget.Button
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import java.util.*
import kotlin.math.roundToInt
class MainActivity : AppCompatActivity() {
private lateinit var startButton: Button
private lateinit var character: View
private lateinit var scoreTextView: TextView
private lateinit var gameOverTextView: TextView
private lateinit var obstacleList: MutableList<View>
private lateinit var enemyList: List<View>
private lateinit var backgroundMusic: MediaPlayer
private lateinit var collisionSound: MediaPlayer
private lateinit var gameOverText: String
private lateinit var res: Resources
private val handlerThread = HandlerThread("MyHandlerThread")
private var timer = Timer(true)
private val enemyRect = Rect()
private val obstacleRect = Rect()
private lateinit var obstacleMovementTask: ObstacleMovementTask
private lateinit var moveEnemyTask: MoveEnemyTask
private lateinit var gameoverDetectionTask: GameoverDetectionTask
private lateinit var handler: Handler
private var gameover = false
private var score = 0
private var obstacleSpeed = 10
private var enemySpeed = 20
val screenWidth = Resources.getSystem().displayMetrics.widthPixels
val screenHeight = Resources.getSystem().displayMetrics.heightPixels
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startButton = findViewById(R.id.start_button)
startButton.setOnClickListener {
startGame()
}
character = findViewById(R.id.character)
character.setOnTouchListener { _, event -> handleTouch(event) }
scoreTextView = findViewById(R.id.score_textview)
gameOverTextView = findViewById(R.id.game_over_textview)
obstacleList = mutableListOf()
enemyList = getEnemyList()
backgroundMusic = MediaPlayer.create(this, R.raw.background_music)
collisionSound = MediaPlayer.create(this, R.raw.collision_sound)
res = resources
gameOverText = res.getString(R.string.game_over_text)
handlerThread.start()
handler = Handler(handlerThread.looper)
obstacleMovementTask = ObstacleMovementTask()
moveEnemyTask = MoveEnemyTask()
gameoverDetectionTask = GameoverDetectionTask()
}
private fun handleTouch(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
character.x = event.x - character.width / 2f
character.y = event.y - character.height / 2f
}
}
return true
}
private fun View.jump() {
val jumpHeight = 400
val jumpDuration = 800L
val jumpHandler = Handler()
var jumpStartTime: Long = 0
var jumpStartY = 0f
jumpHandler.post(object : Runnable {
override fun run() {
if (jumpStartTime == 0L) {
jumpStartTime = System.currentTimeMillis()
jumpStartY = translationY
}
val elapsedTime = System.currentTimeMillis() - jumpStartTime
val t = elapsedTime.toFloat() / jumpDuration
val y = jumpHeight * 4 * (t - 0.5f) * (t - 0.5f)
translationY = jumpStartY - y
if (t < 1.0f) {
jumpHandler.postDelayed(this, 16)
} else {
jumpHandler.removeCallbacks(this)
}
}
})
}
private fun View.cancelAnimation() {
clearAnimation()
}
private val outRect = Rect()
private fun startGame() {
gameover = false
score = 0
scoreTextView.text = "Score: 0"
gameOverTextView.visibility = View.INVISIBLE
character.translationY = 0f
obstacleList.takeIf { it.size > 0 }?.forEachIndexed { index, obstacle ->
obstacle.translationX = screenWidth.toFloat()
enemyList[index].translationX = screenWidth.toFloat()
}
startButton.visibility = View.GONE
backgroundMusic.isLooping = true
backgroundMusic.start()
character.x = screenWidth / 4f
character.y = screenHeight / 2f
obstacleList = mutableListOf()
val obstacleColors = listOf(
Color.parseColor("#FF4081"),
Color.parseColor("#2196F3"),
Color.parseColor("#4CAF50"),
Color.parseColor("#9C27B0"),
Color.parseColor("#FF5722")
)
findViewById<RelativeLayout>(R.id.obstacles_container).apply {
for (i in 0..4) {
val obstacle = layoutInflater.inflate(R.layout.obstacle_layout, null)
obstacle.setBackgroundColor(obstacleColors[i])
addView(obstacle)
obstacleList.add(obstacle)
}
}
val obstacleMovementRunnable = object : Runnable {
override fun run() {
obstacleList.forEach {
it.translationX -= obstacleSpeed
it.getHitRect(obstacleRect)
character.getHitRect(outRect)
if (obstacleRect.intersect(outRect)) {
gameover = true
}
}
if (gameover) {
gameOver()
}
handler.postDelayed(this, 20)
}
}
val moveEnemyRunnable = object : Runnable {
override fun run() {
enemyList.forEach {
it.translationX -= enemySpeed
it.getHitRect(enemyRect)
character.getHitRect(outRect)
if (enemyRect.intersect(outRect)) {
gameover = true
}
}
}
}
val gameoverDetectionRunnable = object : Runnable {
override fun run() {
if (gameover) {
handler.removeCallbacks(obstacleMovementRunnable)
handler.removeCallbacks(this)
}
score += 1
scoreTextView.text = "Score: $score"
handler.postDelayed(this, 10)
}
}
handler.post(obstacleMovementRunnable)
handler.postDelayed(moveEnemyRunnable, 0)
handler.postDelayed(gameoverDetectionRunnable, 0)
}
private fun updateScore() {
scoreTextView.text = "Score: $score"
}
private fun stopGame() {
gameover = true
timer.cancel()
backgroundMusic.stop()
collisionSound.start()
gameOverTextView.text = "$gameOverText\nScore: $score"
gameOverTextView.visibility = View.VISIBLE
}
private fun getObstacleList(): List<View> {
val obstacleList = mutableListOf<View>()
val obstacleColors = mutableListOf<Int>()
obstacleColors.add(Color.parseColor("#FF4081"))
obstacleColors.add(Color.parseColor("#2196F3"))
obstacleColors.add(Color.parseColor("#4CAF50"))
obstacleColors.add(Color.parseColor("#9C27B0"))
obstacleColors.add(Color.parseColor("#FF5722"))
for (i in 0..4) {
val obstacle = layoutInflater.inflate(R.layout.obstacle_layout, null)
obstacle.setBackgroundColor(obstacleColors[i])
(findViewById<RelativeLayout>(R.id.obstacles_container)).addView(obstacle)
obstacleList.add(obstacle)
}
return obstacleList
}
private fun getEnemyList(): List<View> {
val enemyList = mutableListOf<View>()
for (i in 0..4) {
val enemy = layoutInflater.inflate(R.layout.enemy_layout, null)
val enemyView = enemy.findViewById<View>(R.id.enemy)
enemyList.add(enemyView)
}
return enemyList
}
private inner class ObstacleMovementTask : TimerTask() {
override fun run() {
if (!gameover) {
obstacleList.forEach { obstacle ->
runOnUiThread {
val screenHeight = Resources.getSystem().displayMetrics.heightPixels
val translationY = (0..(screenHeight - obstacle.height)).random().toFloat()
obstacle.translationY = translationY
}
}
} else {
obstacleList.forEach { it.cancelAnimation() }
}
}
}
private val characterHitBox: Rect by lazy {
val left = character.left
val top = character.top
val right = character.right
val bottom = character.bottom
Rect(left, top, right, bottom)
}
private inner class MoveEnemyTask : TimerTask() {
override fun run() {
runOnUiThread {
for (enemy in enemyList) {
enemy.getHitRect(enemyRect)
enemyRect.left -= enemySpeed
enemyRect.right -= enemySpeed
enemy.translationX -= enemySpeed
if (enemy.translationX < -enemy.width) {
enemy.translationX = screenWidth.toFloat()
enemy.translationY = (0..(screenHeight - enemy.height)).random().toFloat()
}
}
}
}
}
private inner class GameoverDetectionTask : TimerTask() {
override fun run() {
if (gameover) {
handler.post {
stopGame()
}
}
val characterRect = Rect(
character.left + character.width / 3,
character.top + character.height / 3,
character.right - character.width / 3,
character.bottom - character.height / 3
)
for (i in 0..4) {
enemyList[i].getHitRect(enemyRect)
if (characterRect.intersect(enemyRect)) {
runOnUiThread {
stopGame()
}
}
obstacleList[i].getHitRect(obstacleRect)
if (characterRect.intersect(obstacleRect)) {
runOnUiThread {
stopGame()
}
}
}
}
}
override fun onDestroy() {
super.onDestroy()
handlerThread.quitSafely()
timer.cancel()
backgroundMusic.release()
collisionSound.release()
}
companion object {
private var screenWidth = 0
private var screenHeight = 0
}
}
I tried various functions such as collision processing, object movement, and score increase.
I am currently creating a chart to display statistics.
The problem is that on the combinedChart I can't display the lineDataChart
The expected result is this
The second image is the superposition of a barChart and a lineChart but the problem is i can't use properly the scroll mode because it will only scroll on one graph.
Here is the code for the first graph
class GraphCombinedChart (context: Context) : CombinedChart(context, null, 0) {
private var chart: CombinedChart? = null
private fun dtoToBarEntry(floats: List<Float>?) : ArrayList<BarEntry> {
return ArrayList<BarEntry>().apply {
floats?.forEachIndexed { index, fl ->
add(BarEntry((index + 1).toFloat() , fl))
}
}
}
private fun dtoToEntry(floats: List<Float>?) : ArrayList<Entry> {
return ArrayList<Entry>().apply {
floats?.forEachIndexed { index, fl ->
add(Entry((index + 1).toFloat() , fl))
}
}
}
fun setupChart(
values: List<PlayerGraphStatModel>?,
landScapeMode: Boolean = false
) {
val indexPerformanceList = dtoToEntry(values?.map { it.timePlayed.toFloat() }?.plus(values.map { it.timePlayed.toFloat() }))
val timePlayedList = dtoToBarEntry( values?.map { it.indexPerf }?.plus(values.map { it.indexPerf }))
val entryData = LineData(LineDataSet(indexPerformanceList, "").apply {
color = Color.White.hashCode()
setDrawValues(true)
})
val barEntryData = BarData(BarDataSet(timePlayedList, "").apply {
color = Color.AiaRed.hashCode()
setDrawValues(false)
})
val combinedData = CombinedData()
combinedData.setData(barEntryData)
combinedData.setData(entryData)
chart = this
formatChart(combinedData, landScapeMode)
configureXAxis()
configureYAxis()
chart?.renderer = BarChartRenderer(chart, chart?.animator, chart?.viewPortHandler)
}
private fun formatChart(combinedData: CombinedData, landScapeMode: Boolean = false) {
val barWidth = 0.75f
chart?.drawOrder = arrayOf(
DrawOrder.LINE,
DrawOrder.BAR,
DrawOrder.LINE,
)
chart?.data = combinedData
chart?.description?.isEnabled = false
chart?.legend?.isEnabled = false
chart?.barData?.barWidth = barWidth
chart?.animateY(1000)
chart?.isDoubleTapToZoomEnabled = false
chart?.setScaleEnabled(false)
chart?.isHorizontalScrollBarEnabled = true
// barChart?.setVisibleXRangeMaximum(8f)
// barChart?.moveViewToX(0f)
if (!landScapeMode) {
chart?.setVisibleXRangeMaximum(8f)
chart?.moveViewToX(11f)
}
chart?.resetViewPortOffsets()
chart?.resetZoom()
chart?.notifyDataSetChanged()
}
private fun configureXAxis() {
chart?.xAxis?.apply {
setDrawGridLines(false)
setDrawAxisLine(false)
setDrawLabels(true)
position = XAxis.XAxisPosition.BOTTOM
valueFormatter = MyXAxisFormatter()
granularity = 1f
labelRotationAngle = +0f
textColor = Color.White.hashCode()
textSize = 12f
textAlignment = TEXT_ALIGNMENT_CENTER
axisMinimum = data.xMin - 0.75f
axisMaximum = data.xMax + 0.75f
disableScroll()
}
}
private fun configureYAxis() {
chart?.axisRight?.apply {
setDrawGridLines(false)
legend?.isEnabled = true
isEnabled = false
}
chart?.axisLeft?.apply {
setAxisMinValue(0f)
setAxisMaxValue(100f)
valueFormatter = MyLeftAxisFormatter()
setDrawGridLines(true)
setDrawAxisLine(true)
textColor = Color.White.hashCode()
textSize = 14f
}
}
inner class MyXAxisFormatter : IndexAxisValueFormatter() {
override fun getAxisLabel(value: Float, axis: AxisBase?): String {
return "${value.toInt()}"
}
}
inner class MyLeftAxisFormatter : IndexAxisValueFormatter() {
override fun getAxisLabel(value: Float, axis: AxisBase?): String {
return "${value.toInt()}"
}
}
}
I have checked and the data is not empty.
Thank you for your help!
I make a navigation that navigates a set destination when a user click an app bar menu item.
First navigation flow: RuleFragment ->(menu item clicked) CsvTopFragment
Above navigation works correctly.
Second navigation flow: RuleFragment ->(menu item clicked) CsvTopFragment ->(automated navigation) RuleFragment ->(menu item clicked) CsvTopFragment
But above last nagigation does not work. Why does this happen? How can I fix this problem?
RuleFragment.kt
import android.app.ActionBar
import android.os.Bundle
import android.util.Log
import android.view.*
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.marginBottom
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.NavigationUI
import com.example.sleeprecorder.R
import com.example.sleeprecorder.database.SleepRecorderDatabase
import com.example.sleeprecorder.databinding.FragmentRuleBinding
import java.util.*
class RuleFragment: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentRuleBinding.inflate(inflater)
binding.addRule.setOnClickListener {
findNavController().navigate(RuleFragmentDirections.actionRuleFragmentToAddRuleFragment())
}
val application = requireNotNull(this.activity).application
val dataSource = SleepRecorderDatabase.getInstance(application).ruleDao
val viewModelFactory = RuleViewModelFactory(dataSource, application)
val ruleViewModel = ViewModelProvider(this, viewModelFactory).get(RuleViewModel::class.java)
ruleViewModel.oneMonthRules.observe(this, Observer { oneMonthRules ->
binding.rulesContainer.removeAllViews()
oneMonthRules.forEach { rule ->
Log.i("RuleFragment", "rule ${rule}")
Log.i("RuleFragment" ,"rule date ${rule!!.date.let { timestamp ->
val calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
calendar.timeInMillis = timestamp
calendar.time
}}")
val dateTextView = TextView(context)
dateTextView.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val calendarRule = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
calendarRule.timeInMillis = rule.date
val dateText = "${calendarRule.get(Calendar.YEAR)}/${calendarRule.get(Calendar.MONTH).let{ "00${it + 1}".substring("00${it + 1}".length - 2)}}/${calendarRule.get(Calendar
.DATE).let{ "00${it}".substring("00${it}".length - 2)}}"
dateTextView.text = dateText
val prohibitSleepTwice = TextView(context)
prohibitSleepTwice.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val prohibitSleepTwiceText = "${resources.getString(R.string.prohibit_sleep_twice)} ${if(rule.prohibitSleepTwice) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
prohibitSleepTwice.text = prohibitSleepTwiceText
val stretchBeforeSleep = TextView(context)
stretchBeforeSleep.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val stretchBeforeSleepText = "${resources.getString(R.string.stretch_before_sleep)} ${if(rule.stretchBeforeSleep) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
stretchBeforeSleep.text = stretchBeforeSleepText
val fixingWakeUpTime = TextView(context)
fixingWakeUpTime.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val fixingWakeUpTimeText = "${resources.getString(R.string.fixing_wake_up_time)} ${if(rule.fixingWakeUpTime) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
fixingWakeUpTime.text = fixingWakeUpTimeText
val exerciseAfterWakeUp = TextView(context)
exerciseAfterWakeUp.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val exerciseAfterWakeUpText = "${resources.getString(R.string.exercise_after_wake_up)} ${if(rule.exerciseAfterWakeUp) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
exerciseAfterWakeUp.text = exerciseAfterWakeUpText
val exerciseAfterGoHome = TextView(context)
exerciseAfterGoHome.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val exerciseAfterGoHomeText = "${resources.getString(R.string.exercise_after_go_home)} ${if(rule.exerciseAfterGoHome) resources.getString(R.string.maru) else resources.getString(R.string.batsu)}"
exerciseAfterGoHome.text = exerciseAfterGoHomeText
val ruleLinearLayout = LinearLayout(context)
val layoutParams = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT)
layoutParams.setMargins(16, 16, 0, 0)
ruleLinearLayout.layoutParams = layoutParams
ruleLinearLayout.orientation = LinearLayout.VERTICAL
ruleLinearLayout.addView(dateTextView)
ruleLinearLayout.addView(prohibitSleepTwice)
ruleLinearLayout.addView(stretchBeforeSleep)
ruleLinearLayout.addView(fixingWakeUpTime)
ruleLinearLayout.addView(exerciseAfterWakeUp)
ruleLinearLayout.addView(exerciseAfterGoHome)
binding.rulesContainer.addView(ruleLinearLayout)
ruleLinearLayout.setOnClickListener {
this.findNavController().navigate(RuleFragmentDirections.actionRuleFragmentToEditRuleFragment(rule.id))
}
}
})
val calendar = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
val thisYear = calendar.get(Calendar.YEAR)
val thisMonthIndex = calendar.get(Calendar.MONTH)
binding.yearMonth.text = getYearMonthStr(thisYear, thisMonthIndex)
binding.yearMonth.setTag(R.id.rule_year, thisYear)
binding.yearMonth.setTag(R.id.rule_month_index, thisMonthIndex)
binding.goToNextMonth.setOnClickListener {
val shownYear = binding.yearMonth.getTag(R.id.rule_year).toString().toInt()
val shownMonthIndex = binding.yearMonth.getTag(R.id.rule_month_index).toString().toInt()
val calendarToNextMonth = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
calendarToNextMonth.timeInMillis = 0
calendarToNextMonth.set(Calendar.YEAR, shownYear)
calendarToNextMonth.set(Calendar.MONTH, shownMonthIndex)
calendarToNextMonth.add(Calendar.MONTH, 1)
val targetYear = calendarToNextMonth.get(Calendar.YEAR)
val targetMonthIndex = calendarToNextMonth.get(Calendar.MONTH)
binding.yearMonth.text = getYearMonthStr(targetYear, targetMonthIndex)
binding.yearMonth.setTag(R.id.rule_year, targetYear)
binding.yearMonth.setTag(R.id.rule_month_index, targetMonthIndex)
ruleViewModel.onChangeMonth(targetYear, targetMonthIndex)
}
binding.goToPreviousMonth.setOnClickListener {
val shownYear = binding.yearMonth.getTag(R.id.rule_year).toString().toInt()
val shownMonthIndex = binding.yearMonth.getTag(R.id.rule_month_index).toString().toInt()
val calendarToPreviousMonth = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"))
calendarToPreviousMonth.timeInMillis = 0
calendarToPreviousMonth.set(Calendar.YEAR, shownYear)
calendarToPreviousMonth.set(Calendar.MONTH, shownMonthIndex)
calendarToPreviousMonth.add(Calendar.MONTH, -1)
val targetYear = calendarToPreviousMonth.get(Calendar.YEAR)
val targetMonthIndex = calendarToPreviousMonth.get(Calendar.MONTH)
binding.yearMonth.text = getYearMonthStr(targetYear, targetMonthIndex)
binding.yearMonth.setTag(R.id.rule_year, targetYear)
binding.yearMonth.setTag(R.id.rule_month_index, targetMonthIndex)
ruleViewModel.onChangeMonth(targetYear, targetMonthIndex)
}
ruleViewModel.setThisYearMonth(thisYear, thisMonthIndex)
setHasOptionsMenu(true)
return binding.root
}
private fun getYearMonthStr(year: Int, monthIndex: Int): String {
val yearStr = year.toString()
val monthStr = (monthIndex + 1).toString()
val zeroPaddingMonthStr = "00${monthStr}".substring("00${monthStr}".length - 2)
return "${yearStr}/${zeroPaddingMonthStr}"
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.defalut_items,menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val controller = requireView().findNavController()
val destination = controller.currentDestination
Log.i("RuleFragment", "onOptionsItemSelected: ${(NavigationUI.onNavDestinationSelected(item, requireView().findNavController()) || super.onOptionsItemSelected(item))}")
Log.i("RuleFragment", "onOptionsItemSelected findNavController: ${NavigationUI.onNavDestinationSelected(item, requireView().findNavController())}")
Log.i("RuleFragment", "super.onOptionsItemSelected: ${super.onOptionsItemSelected(item)}")
Log.i("RuleFragment", "item title :${item.title}")
Log.i("RuleFragment", "itemId: ${item.itemId}")
Log.i("RuleFragment", "destination: ${destination!!.displayName}")
return NavigationUI.onNavDestinationSelected(item, requireView().findNavController()) || super.onOptionsItemSelected(item)
}
}
CsvTopFragment.kt
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.example.sleeprecorder.R
import com.example.sleeprecorder.database.Rule
import com.example.sleeprecorder.database.SleepRecord
import com.example.sleeprecorder.database.SleepRecorderDatabase
import com.example.sleeprecorder.databinding.FragmentCsvTopBinding
import com.example.sleeprecorder.rule.RuleViewModel
import com.example.sleeprecorder.rule.RuleViewModelFactory
import com.example.sleeprecorder.sleeprecord.SleepRecordViewModel
import com.example.sleeprecorder.sleeprecord.SleepRecordViewModelFactory
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
import java.io.File
import java.util.*
class CsvTopFragment: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentCsvTopBinding.inflate(inflater)
val application = requireNotNull(this.activity).application
Log.i("CsvTopFragment", "isExternalWritable() is ${isExternalWritable()}")
Log.i("CsvTopFragment", "isExternalStorageReadable() is ${isExternalStorageReadable()}")
Log.i("CsvTopFragment", "Environment.getExternalStorageDirectory() ${Environment.getExternalStorageDirectory()}")
Log.i("CsvTopFragment", "Environment.getRootDirectory() ${Environment.getRootDirectory()}")
Log.i("CsvTopFragment", " context.fileList() ${ requireContext().fileList()}")
Log.i("CsvTopFragment", "context.getExternalFilesDir(null) ${requireContext().getExternalFilesDir(null)}")
Log.i("CsvTopFragment", "can write: ${requireContext().getExternalFilesDir(null)!!.canWrite()}")
Log.i("CsvTopFragment", "Environment.getExternalStorageDirectory(): ${Environment.getExternalStorageDirectory()}")
if(!isExternalFilesDirWritable()) {
return binding.root
}
val csvDir = File(requireContext().getExternalFilesDir(null).toString(), "csv")
val fileNameListSleepRecord = getSleepRecordFileNameList(csvDir)
val fileNameListRuleRecord = getRuleRecordFileNameList(csvDir)
val fileNameListSleepPressureFactor = getSleepPressureFactorRecordFileNameList(csvDir)
binding.sleepRecordFileListSpinner.adapter = ArrayAdapter(
requireActivity(),
android.R.layout.simple_spinner_dropdown_item,
fileNameListSleepRecord
)
binding.ruleRecordFileListSpinner.adapter = ArrayAdapter(
requireActivity(),
android.R.layout.simple_spinner_dropdown_item,
fileNameListRuleRecord
)
binding.sleepPressureFactorRecordFileListSpinner.adapter = ArrayAdapter(
requireActivity(),
android.R.layout.simple_spinner_dropdown_item,
fileNameListSleepPressureFactor
)
binding.readSleepRecordCsvButton.setOnClickListener SleepRecord# { button ->
Log.i("CsvTopFragment", "read sleep record button pressed")
val sleepRecordDataSource = SleepRecorderDatabase.getInstance(application).sleepRecordDao
val sleepRecordViewModelFactory = SleepRecordViewModelFactory(sleepRecordDataSource, requireNotNull(this.activity).application)
val sleepRecordViewModel = ViewModelProvider(this, sleepRecordViewModelFactory).get(
SleepRecordViewModel::class.java)
val selectedFile = File(csvDir, binding.sleepRecordFileListSpinner.selectedItem.toString())
if(selectedFile.name == resources.getString(R.string.no_file)) {
Log.i("CsvTopFragment", resources.getString(R.string.no_file))
return#SleepRecord
}
val sleepRecordCsvList = getListFromCsv(selectedFile)
// Log.i("CsvTopFragment", sleepRecordCsvList.toString())
val sleepRecordList = csvListToSleepRecordList(sleepRecordCsvList)
Log.i("CsvTopFragment", "sleep record[0]: ${sleepRecordList[0]}")
sleepRecordViewModel.onClickInsertSleepRecordList(sleepRecordList)
findNavController().navigate(CsvTopFragmentDirections.actionCsvTopFragmentToSleepRecordFragment())
}
binding.readRuleRecordCsvButton.setOnClickListener RuleRecord# {
button ->
val ruleDataSource = SleepRecorderDatabase.getInstance(application).ruleDao
val ruleViewModelFactory = RuleViewModelFactory(ruleDataSource, application)
val ruleViewModel = ViewModelProvider(this, ruleViewModelFactory).get(RuleViewModel::class.java)
Log.i("CsvTopFragment", "read rule button pressed")
val selectedFile = File(csvDir, binding.ruleRecordFileListSpinner.selectedItem.toString())
Log.i("CsvTopFragment", "selected item: ${binding.ruleRecordFileListSpinner.selectedItem}")
if(selectedFile.name == resources.getString(R.string.no_file)) {
Log.i("CsvTopFragment", resources.getString(R.string.no_file))
return#RuleRecord
}
val ruleCsvList = getListFromCsv(selectedFile)
val ruleList = csvListToRuleList(ruleCsvList)
ruleViewModel.onClickInsertRuleList(ruleList)
this.findNavController().navigate(CsvTopFragmentDirections.actionCsvTopFragmentToRuleFragment())
}
return binding.root
}
private fun isExternalWritable(): Boolean {
return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}
private fun isExternalStorageReadable(): Boolean {
return Environment.getExternalStorageState() in
setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY)
}
fun isExternalFilesDirWritable(): Boolean {
return requireContext().getExternalFilesDir(null)!!.canWrite()
}
private fun getSleepRecordFileNameList(dir: File): List<String> {
val fileListSleep = dir.listFiles()?.filter {
Regex("sleep_record-[0-9]{4}_[0-9]{1,2}\\.csv$").containsMatchIn(it.name)
}
val fileNameList = fileListSleep!!.map {
it.name
}
return if(fileNameList.size > 0) fileNameList else mutableListOf(resources.getString(R.string.no_file))
}
private fun getRuleRecordFileNameList(dir: File): List<String> {
val fileListSleep = dir.listFiles()?.filter {
Regex("rule_record-[0-9]{4}_[0-9]{1,2}\\.csv$").containsMatchIn(it.name)
}
val fileNameList = fileListSleep!!.map {
it.name
}
return if(fileNameList.size > 0) fileNameList else mutableListOf(resources.getString(R.string.no_file))
}
private fun getSleepPressureFactorRecordFileNameList(dir: File): List<String> {
val fileListSleep = dir.listFiles()?.filter {
Regex("sleep_pressure_factor-[0-9]{4}_[0-9]{1,2}\\.csv$").containsMatchIn(it.name)
}
val fileNameList = fileListSleep!!.map {
it.name
}
return if(fileNameList.size > 0) fileNameList else mutableListOf(resources.getString(R.string.no_file))
}
private fun getListFromCsv(csvFile: File): List<List<String>> {
val csvRows = csvReader().readAll(csvFile)
// Log.i("CsvTopFragment", "file name: ${csvFile.name}")
// Log.i("CsvTopFragment", "csvRows: ${csvRows}")
val rows = mutableListOf<List<String>>()
(1 .. csvRows.size - 1).forEach { index ->
rows.add(csvRows[index])
}
// Log.i("CsvTopFragment", "rows: ${rows}")
Log.i("CsvTopFragment", "first row ${rows[0]}")
Log.i("CsvTopFragment", "last row: ${rows[rows.size - 1]}")
return rows
}
private fun csvListToSleepRecordList(csvList: List<List<String>>): List<SleepRecord> {
val sleepRecordList = mutableListOf<SleepRecord>()
csvList.forEach {
val sleepStartStr = it[0]
val sleepEndStr = it[1]
val kindOfSleep = it[2]
val memo = it[4]
if(isDatetimeStringReadable(sleepStartStr)) {
// Log.i("CsvTopFragment", "${sleepStartStr} is unmatched")
val sleepStartTimeMilli = datetimeStringToTimeStamp(sleepStartStr)
val sleepEndTimeMilli = datetimeStringToTimeStamp(sleepEndStr)
val sleepRecord = SleepRecord(
startTimeMilli = sleepStartTimeMilli,
endTimeMilli = sleepEndTimeMilli,
kind = kindOfSleep,
memo = memo
)
sleepRecordList.add(sleepRecord)
}
}
return sleepRecordList
}
private fun csvListToRuleList(csvList: List<List<String>>): List<Rule> {
val ruleList = mutableListOf<Rule>()
// Log.i("CsvTopFragment", "csvList[0] ${csvList[0]}")
csvList.forEach {
val dateStr = it[0]
val prohibitSleepTwice = if(it[1].toInt() == 1) true else false
val stretchBeforeSleep = if(it[2].toInt() == 1) true else false
val fixingWakeUpTime = if(it[3].toInt() == 1) true else false
val exerciseAfterWakeUp = if(it[4].toInt() == 1) true else false
val exerciseAfterGoHome = if(it[5].toInt() == 1) true else false
if(isDateStringReadable(dateStr)) {
val rule = Rule(
date = dateStringToTimeStamp(dateStr),
prohibitSleepTwice = prohibitSleepTwice,
stretchBeforeSleep = stretchBeforeSleep,
fixingWakeUpTime = fixingWakeUpTime,
exerciseAfterWakeUp = exerciseAfterWakeUp,
exerciseAfterGoHome = exerciseAfterGoHome
)
ruleList.add(rule)
}
}
return ruleList
}
private fun datetimeStringToTimeStamp(datetimeStr: String): Long {
val regex = Regex("^([0-9]{4})/([0-9]{2})/([0-9]{2}) ([0-9]{1,2}):([0-9]{1,2})")
val match = regex.find(datetimeStr)
Log.i("CsvTopFragment", "datetimeStr: ${datetimeStr}")
Log.i("CsvTopFragment", "match: ${match}")
val group = match!!.groupValues
// Log.i("CsvTopFragment", "group: ${group}")
val year = group[1].toInt()
val month = group[2].toInt()
val date = group[3].toInt()
val hour = group[4].toInt()
val minute = group[5].toInt()
val calendarSleepDatetime = Calendar.getInstance()
calendarSleepDatetime.timeZone = TimeZone.getTimeZone("Asia/Tokyo")
calendarSleepDatetime.set(Calendar.YEAR, year)
calendarSleepDatetime.set(Calendar.MONTH, month - 1)
calendarSleepDatetime.set(Calendar.DATE, date)
calendarSleepDatetime.set(Calendar.HOUR_OF_DAY, hour)
calendarSleepDatetime.set(Calendar.MINUTE, minute)
calendarSleepDatetime.set(Calendar.SECOND, 0)
calendarSleepDatetime.set(Calendar.MILLISECOND, 0)
// Log.i("CsvTopFragment", "datetime: ${calendarSleepDatetime.time}")
return calendarSleepDatetime.time.time
}
private fun isDatetimeStringReadable(datetimeStr: String): Boolean {
val regex = Regex("^([0-9]{4})/([0-9]{2})/([0-9]{2}) ([0-9]{1,2}):([0-9]{1,2})$")
return regex.containsMatchIn(datetimeStr)
}
private fun dateStringToTimeStamp(dateStr: String): Long {
val regex = Regex("^([0-9]{4})/([0-9]{2})/([0-9]{2})")
val group = regex.find(dateStr)!!.groupValues
val year = group[1].toInt()
val month = group[2].toInt()
val date = group[3].toInt()
val calendarFromDateString = Calendar.getInstance()
calendarFromDateString.timeZone = TimeZone.getTimeZone("Asia/Tokyo")
calendarFromDateString.set(Calendar.YEAR, year)
calendarFromDateString.set(Calendar.MONTH, month - 1)
calendarFromDateString.set(Calendar.DATE, date)
calendarFromDateString.set(Calendar.HOUR_OF_DAY, 0)
calendarFromDateString.set(Calendar.MINUTE, 0)
calendarFromDateString.set(Calendar.SECOND, 0)
calendarFromDateString.set(Calendar.MILLISECOND, 0)
return calendarFromDateString.time.time
}
private fun isDateStringReadable(datetimeStr: String):Boolean {
val regex = Regex("^([0-9]{4})/([0-9]{2})/([0-9]{2})")
return regex.containsMatchIn(datetimeStr)
}
}
NavigationUI.onNavDestinationSelected(item, requireView().findNavController()) returns true in first navigation flow but returns false in second navigation flow's last navigation.
I was making Count-up Timer.
It works well when I press pause button or stop button, but the app freezes when I press start button.
I suspected that I was stuck in while loop but algorithm doesn't reach the codes before while loop.
What is wrong in my codes?
class MainActivity( var second : Int = 0 , var minute : Int = 0 , var hour : Int = 0 , var ongoing : Boolean = false ) : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initLayout()
}
private fun initLayout(): Unit {
val startButton: Button = findViewById(R.id.startButton)
val pauseButton: Button = findViewById(R.id.pauseButton)
val stopButton: Button = findViewById(R.id.stopButton)
var realTime : TextView = findViewById(R.id.Realtime)
startButton.setOnClickListener {
realTime.text = "0"//doesn't reach here
buttonOn()
countTime(realTime)
}
pauseButton.setOnClickListener {
buttonOff()
countTime(realTime)
}
stopButton.setOnClickListener {
buttonOff()
countTime(realTime)
}
}
private fun buttonOn(): Unit {
this.ongoing = true
}
private fun buttonOff(): Unit {
this.ongoing = false
}
private fun showTime(second: Int, minute: Int, hour: Int): String {
var secondShow: String = if (second < 10) "0$second" else second.toString()
var minuteShow: String = if (minute < 10) "0$minute" else minute.toString()
var hourShow: String = if (hour < 10) "0$hour" else hour.toString()
return "$hourShow:$minuteShow:$secondShow"
}
private fun countTime(textView: TextView): Unit {
var text = 0
textView.text = text.toString()
textView.text = showTime(this.second, this.minute, this.hour)
while (this.ongoing) {
Thread.sleep(1_000)
this.second++
if (this.second == 60) {
this.second = 0
this.minute++
if (this.minute == 60) {
this.minute = 0
this.hour++
}
}
textView.text = showTime(this.second, this.minute, this.hour)
//buttonOff reach here
}
}
}
Now i am writing my small molar mass calculator and i can't fix one bug. In MainActivity.kt i fill array from my .xml file, after that i use Regex to parse user input. BUT if i type, for example "C" (carbon) in my program it doesn't recognize it. WHY?
MainActivity.kt:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val adapter = MoleculeAdapter(this)
moleculeView.layoutManager = LinearLayoutManager(this)
moleculeView.setHasFixedSize(true)
moleculeView.adapter = adapter
val parser = resources.getXml(R.xml.elements)
val elements = Array(127) { Element() }
thread {
var i = 0
while (parser.eventType != END_DOCUMENT) {
if (parser.eventType == START_TAG && parser.name == "element") {
elements[i].number = parser.getAttributeIntValue(null, "number", 0)
elements[i].letter = parser.getAttributeValue(null, "letter")
elements[i].name = parser.getAttributeValue(null, "name")
val weight = parser.getAttributeValue(null, "weight")
elements[i].weight = if (weight.isNotEmpty()) weight.toFloat() else 0F
i++
}
parser.next()
}
parser.close()
}.join()
Log.i("elements:", elements.joinToString { it.toString() + "\n" })
val lowerCaseLetters = "abcdefghiklmnopqrstuy"
val elementsRegex = Regex("""[ABCDEFGHIKLMNOPRSTUVWXYZ]([$lowerCaseLetters]{2}|[$lowerCaseLetters]?)\d*""")
val digitsRegex = Regex("""\d+""")
formulaInput.doOnTextChanged { text, _, _, _ ->
lateinit var foundedElements: List<Element>
thread {
foundedElements = elementsRegex
.findAll(text ?: "")
.map {
elements.find { element ->
Log.i("value", it.value + " " + it.value)
if (it.value.filter { it.isLetter() } == element.letter) {
val number = digitsRegex.find(it.value)
if (number != null) {
try {
element.moleculeCount = number.value.toInt()
element.weight = element.weight * number.value.toInt()
} catch (e: NumberFormatException) { }
}
element.percentage = adapter.getTotalWeight(element.weight) * 100
true
} else false
}
}.filterNotNull().toList()
}.join()
adapter.insertElements(foundedElements)
}
}
}
Element.kt:
data class Element(var number: Int = -1,
var letter: String = "",
var name: String = "",
var weight: Float = 0F,
var percentage: Float = 100F,
var moleculeCount: Int = 1)
xml file item example:
<element
number="6"
letter="С"
name="Углерод"
weight="12.011" />
I can't believe it, in my xml file letter "С" was a cyrillic letter "C" (\u0421)! And because of this equals check "С" == "C" was failing.
Huge Thanks to Wiktor Stribiżew for his comment.