Activity inside a non-activity fragment class freezing the app - android

I am working on localizing my Android application using Kotlin and want to set the content to spanish everytime the radio button is pressed. For that, whenever the button is clicked I fire up a LanguageActivity which basically is supposed to set the locale value to espanol. However, I notice that when the LanguageActivity loads, the screen turns white and nothing happens unless I hit the back button. I don't see any error message too, so I'm not sure what's happening. I am loading the activity from within a non-activity NewsFragment class. I'm using Android API 30.
Also, I'm pretty much new to all to this and would greatly appreciate if you could provide feedback on the localization logic in LanguageActivity and ContextUtilsand suggest any better approach.
NewsFragment
internal fun showLanguagePopup(anchorView: View, gravity: Int) {
val popupBinding = DataBindingUtil.inflate<ItemNewsLanguagePopupBinding>(layoutInflater, R.layout.item_news_language_popup, null, false)
viewModel.availableLanguages
.forEachIndexed { index, language ->
val button = layoutInflater.inflate(R.layout.item_news_language_popup_button, popupBinding.gLanguages, false) as RadioButton
button.text = language.toNameString(activity!!)
button.id = index
button.isChecked = viewModel.getCurrentLanguage() == language
popupBinding.gLanguages.addView(button)
}
val popupWindow = PopupWindow(popupBinding.root, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
popupWindow.elevation = 10f
}
popupBinding.bCancel.setOnClickListener { popupWindow.dismiss() }
popupBinding.bOk.setOnClickListener {
val selectedLanguageOrdinal = popupBinding.gLanguages.checkedRadioButtonId
if (selectedLanguageOrdinal >= 0) {
viewModel.setupLanguage(viewModel.availableLanguages[selectedLanguageOrdinal])
val intent = Intent(activity, LanguageActivity::class.java)
//intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
drawerLogicView.onBackPressed()
}
popupWindow.dismiss()
}
popupWindow.showAsDropDown(anchorView, 0, -anchorView.height / 2, gravity)
}
LanguageActivity
package com.oigetit.oigetit.ui.news.list
import android.content.Context
import android.content.ContextWrapper
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.oigetit.oigetit.utility.ContextUtils
import java.util.*
class LanguageActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
val localeToSwitchTo = Locale("es")
val localeUpdatedContext: ContextWrapper? = ContextUtils.updateLocale(newBase, localeToSwitchTo)
super.attachBaseContext(localeUpdatedContext)
}
}
ContextUtils
package com.oigetit.oigetit.utility
import android.content.Context
import android.content.ContextWrapper
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import android.os.LocaleList
import java.util.*
class ContextUtils(base: Context?) : ContextWrapper(base) {
companion object {
fun updateLocale(context: Context, localeToSwitchTo: Locale?): ContextWrapper? {
var context = context
val resources: Resources = context.resources
val configuration: Configuration = resources.getConfiguration() // 1
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val localeList = LocaleList(localeToSwitchTo) // 2
LocaleList.setDefault(localeList) // 3
configuration.setLocales(localeList) // 4
} else {
configuration.setLocale(localeToSwitchTo) // 5
}
//val resources: Resources = context.resources
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
context = context.createConfigurationContext(configuration) // 6
} else
{
context = context.createConfigurationContext(configuration)
}
return ContextUtils(context) // 8
}
}
}

You didn't start your activity with that intent.
add startActivity(intent) below the val intent = Intent(activity, LanguageActivity::class.java) line.
I am loading the activity from within a non-activity NewsFragment class and don't want to use the MainActivity for loading
For this part of your question, fragments are completely depend on activities, therefore they can't start another activity inside themselves.

Related

All intent's extras are null while starting a new activity

This is an application with a list of dogs and information about the dog.
I'm trying to run a DetailActivity that contains information about a dog. It is launched after clicking the Show Details button, which has a setOnClickListener and runs the Intent, passing the name, age, hobby and other parameters of the dog to the running DetailActivity. But when I try to take those parameters in the DetailActivity code, they all equal null.
It's actually very simple and I've done the same thing in google course codelab before (now I decided to practice a bit) and I repeat everything as it's written there, but I don't understand what I'm doing wrong.
I'll insert the DetailActivity and DogCardAdapter code below. You can also see all the code at this link on GitHub: https://github.com/theMagusDev/DogglersApp
DetailActivity.kt:
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.example.dogglers.databinding.ActivityDetailBinding
class DetailActivity() : AppCompatActivity() {
private lateinit var binding: ActivityDetailBinding
private val TAG = "DetailActivity"
companion object {
const val DOG_IMAGE = "dogImage"
const val DOG_NAME = "dogName"
const val DOG_AGE = "dogAge"
const val DOG_HOBBIES = "dogHobbies"
const val DOG_SEX = "dogSex"
}
val dogImageResourceId = intent?.extras?.getString(DOG_IMAGE)
val dogName = intent?.extras?.getString(DOG_NAME)
val dogAge = intent?.extras?.getString(DOG_AGE)
val dogHobbies = intent?.extras?.getString(DOG_HOBBIES)
val dogSex = intent?.extras?.getString(DOG_SEX)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Setup view binding
binding = ActivityDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
val heOrShe: String = when(dogSex){
"male" -> "He"
else -> "She"
}
binding.dogName.text = dogName
Log.d(TAG, "${dogAge.toString()}, ${dogName.toString()}, $dogHobbies, $heOrShe, $dogImageResourceId")
binding.dogDescription.text = getString(R.string.dog_description, dogName, dogAge, dogSex, dogHobbies)
//binding.dogImage.setImageResource(dogImageResourceId!!.toInt())
binding.dogImage.setImageResource(R.drawable.bella)
Log.d(TAG, "dogDescription and dogImage were set")
title = getString(R.string.details_about, dogName)
}
Logcat:
2022-10-02 08:32:25.545 9660-9660/com.example.dogglers D/DetailActivity: null, null, null, She, null
2022-10-02 08:32:25.558 9660-9660/com.example.dogglers D/DetailActivity: dogDescription and dogImage were set
DogCardAdapter.kt:
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.dogglers.DetailActivity
import com.example.dogglers.DetailActivity.Companion.DOG_AGE
import com.example.dogglers.DetailActivity.Companion.DOG_HOBBIES
import com.example.dogglers.DetailActivity.Companion.DOG_IMAGE
import com.example.dogglers.DetailActivity.Companion.DOG_NAME
import com.example.dogglers.DetailActivity.Companion.DOG_SEX
import com.example.dogglers.R
import com.example.dogglers.const.Layout.GRID
import com.example.dogglers.data.DataSource
/**
* Adapter to inflate the appropriate list item layout and populate the view with information
* from the appropriate data source
*/
class DogCardAdapter(
private val context: Context?,
private val layout: Int
): RecyclerView.Adapter<DogCardAdapter.DogCardViewHolder>() {
// Initialize the data using the List found in data/DataSource
val data = DataSource.dogs
/**
* Initialize view elements
*/
class DogCardViewHolder(view: View?): RecyclerView.ViewHolder(view!!) {
// Declare and initialize all of the list item UI components
val imageView: ImageView = view!!.findViewById(R.id.dog_image)
val dogName: TextView = view!!.findViewById(R.id.dog_name)
val dogAge: TextView = view!!.findViewById(R.id.dog_age)
val dogHobbies: TextView = view!!.findViewById(R.id.dog_hobbies)
var dogSex = "n/a"
val showDetailsButton: Button = view!!.findViewById(R.id.details_button)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DogCardViewHolder {
// Use a conditional to determine the layout type and set it accordingly.
// if the layout variable is Layout.GRID the grid list item should be used. Otherwise the
// the vertical/horizontal list item should be used.
val layoutType = when(layout) {
GRID -> R.layout.grid_list_item
else -> R.layout.vertical_horizontal_list_item
}
// Inflate the layout
val adapterLayout =LayoutInflater.from(parent.context)
.inflate(layoutType, parent, false)
// Null should not be passed into the view holder. This should be updated to reflect
// the inflated layout.
return DogCardViewHolder(adapterLayout)
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(holder: DogCardViewHolder, position: Int) {
val resources = context?.resources
// Get the data at the current position
val item = data[position]
// Set the image resource for the current dog
holder.imageView.setImageResource(item.imageResourceId)
// Set the text for the current dog's name
holder.dogName.text = item.name
// Set the text for the current dog's age
holder.dogAge.text = resources?.getString(R.string.dog_age, item.age)
// Set the text for the current dog's hobbies by passing the hobbies to the
// R.string.dog_hobbies string constant.
holder.dogHobbies.text = resources?.getString(R.string.dog_hobbies, item.hobbies)
// Passing an argument to the string resource looks like:
// resources?.getString(R.string.dog_hobbies, dog.hobbies)
// Set the dog's sex variable
holder.dogSex = item.sex
// Declare context var
val context = holder.itemView.context
// Setting up OnClickListener
holder.showDetailsButton.setOnClickListener {
val intent = Intent(context, DetailActivity::class.java)
intent.putExtra(DOG_IMAGE, item.imageResourceId.toInt())
intent.putExtra(DOG_NAME, holder.dogName.toString())
intent.putExtra(DOG_AGE, holder.dogAge.toString())
intent.putExtra(DOG_HOBBIES, holder.dogHobbies.toString())
intent.putExtra(DOG_SEX, holder.dogSex.toString())
context.startActivity(intent)
}
}
}

How to Parcelize a MutableList in kotlin?

I have an App that lets users drag and draw boxes on a custom view. I want to persist the state of these boxes(list of boxes) across orientation change using onSavedInstanceState(): Parcelable and onRestoreInstanceState(state: Parcelable). However, I don't know how to store a MutableList because the only available function is putParcelableArrayList. Please how do I parcelize a Mutable List to persist the boxes across rotation? I know the docs said its possible but I don't know how to. Here is the code.
#Parcelize
class Box(private val start: PointF) : Parcelable {
// When a user touches BoxDrawingView, a new box will be created and added to the list of existing boxes.
var end: PointF = start
val left: Float
get() = start.x.coerceAtMost(end.x)
val right: Float
get() = start.x.coerceAtLeast(end.x)
val top: Float
get() = start.y.coerceAtMost(end.y)
val bottom: Float
get() = start.y.coerceAtLeast(end.y)
}
My custom View
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PointF
import android.os.Bundle
import android.os.Parcelable
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
/** This Class is where we setup our custom View and write the Implementation for listening to touch events from the USER and draw boxes on the Screen.**/
private const val TAG = "BoxDrawingView"
private const val BOX_STATE = "box"
private const val VIEW_STATE = "view"
class BoxDrawingView(context: Context, attrs: AttributeSet? = null) :
View(context, attrs) {
private var currentBox: Box? = null
private var boxen = mutableListOf<Box>() // list of boxes to be drawn out on the screen
private val boxPaint = Paint().apply {
color = 0x22ff0000
}
private val backGroundPaint = Paint().apply {
color = 0xfff8efe0.toInt()
}
init {
isSaveEnabled = true
}
override fun onSaveInstanceState(): Parcelable {
val bundle = Bundle()
bundle.putParcelableArrayList(BOX_STATE, boxen) // type mismatch error because of mutableList passed to ArrayList
bundle.putParcelable(VIEW_STATE, super.onSaveInstanceState())
return bundle
}
override fun onRestoreInstanceState(state: Parcelable?) {
var viewState = state
if (viewState is Bundle) {
boxen = viewState.getParcelableArrayList<Box>(BOX_STATE)?.toMutableList() ?: mutableListOf()
viewState = viewState.getParcelable(VIEW_STATE)
}
super.onRestoreInstanceState(state)
}
override fun onDraw(canvas: Canvas) {
// Fill in the background
canvas.drawPaint(backGroundPaint)
boxen.forEach { box ->
canvas.drawRect(box.left, box.top, box.right, box.bottom, boxPaint)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
val current = PointF(event.x, event.y)
var action = ""
when(event.action) {
MotionEvent.ACTION_DOWN -> {
action = "ACTION_DOWN"
// Reset drawing state
currentBox = Box(current).also {
boxen.add(it)
}
}
MotionEvent.ACTION_MOVE -> {
action = "ACTION_MOVE"
// update the currentBox.end as the user moves his/her finger around the screen
updateCurrentBox(current)
}
MotionEvent.ACTION_UP -> {
action = "ACTION_UP"
// tells the last report of the currentBox as the user's finger leaves the screen
updateCurrentBox(current)
currentBox = null
}
MotionEvent.ACTION_CANCEL -> {
action = "ACTION_CANCEL"
currentBox = null
}
}
// this is a log message for each of the 4 Event actions
Log.i(TAG, "$action at x=${current.x}, y=${current.y}")
return true
}
Changing the boxen type to arrayList() worked. Turns out arrayList works as a mutable list under the hood. Also David wasser's answer in the comments also worked.

Why does my app, written in Kotlin, stop when I press the 'balls' button?

My app has 2 screens and works correctly in development so far. It already has 2 buttons ('toss' and 'startGame'which work correctly. Now I have added a further button called 'balls', designed to change the background colours of 4 TextViews ('ball1' to 'ball4'). However, pressing it once does nothing, and pressing it again causes the app to stop, with the displayed message "GC Clicker has stopped" or "GC Clicker keeps stopping".
Here is the first section of my MainActivity.kt:
package com.example.golfclicker
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.EditText
import android.widget.TextView
import android.graphics.Color
import android.widget.Button
import android.content.Intent
import android.media.AudioManager
import android.media.SoundPool
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val soundPool:SoundPool?
// Link variables to xml views
val player1Name = findViewById<EditText>(R.id.PersonName1)
val handicap1 = findViewById<EditText>(R.id.hpInput1)
val player2Name = findViewById<EditText>(R.id.PersonName2)
val handicap2 = findViewById<EditText>(R.id.hpInput2)
val startGame = findViewById<Button>(R.id.startGame)
val toss = findViewById<Button>(R.id.toss)
val bkgnd1 = findViewById<TextView>(R.id.Background1)
val bkgnd1a = findViewById<TextView>(R.id.Background1a)
val bkgnd1b = findViewById<TextView>(R.id.Background1b)
val bkgnd2 = findViewById<TextView>(R.id.Background2)
val bkgnd2a = findViewById<TextView>(R.id.Background2a)
val bkgnd2b = findViewById<TextView>(R.id.Background2b)
val balls = findViewById<Button>(R.id.balls)
val ball1 = findViewById<TextView>(R.id.ball1)
val ball2 = findViewById<TextView>(R.id.ball2)
val ball3 = findViewById<TextView>(R.id.ball3)
val ball4 = findViewById<TextView>(R.id.ball4)
var priSec = true // True:Primaries False:Secondaries
var tossedP1 = "" //String values to pass to Intent
var tossedHp1 =""
var tossedP2 = ""
var tossedHp2 =""
var tossed = false //has Toss button been used?
//Set up soundpool and load the sound file
soundPool = SoundPool(2,AudioManager.STREAM_MUSIC,0)
val sound1 = soundPool.load(baseContext,R.raw.computer_keyboard,1)
val sound2 = soundPool.load(baseContext,R.raw.coins,1)
//Handle balls button click
balls.setOnClickListener {
if(priSec==true) {
with(ball1) { setBackgroundColor(Color.parseColor("button_blue")) }
with(ball2) { setBackgroundColor(Color.parseColor("red")) }
with(ball3) { setBackgroundColor(Color.parseColor("black")) }
with(ball4) { setBackgroundColor(Color.parseColor("yellow")) }
} else {
with(ball1) { setBackgroundColor(Color.parseColor("green")) }
with(ball2) {setBackgroundColor(Color.parseColor("pink")) }
with(ball3) {setBackgroundColor(Color.parseColor("brown")) }
with(ball4) {setBackgroundColor(Color.parseColor("white")) }
}
priSec = !priSec //toggle primary/secondary
}
//Handle 'Toss' button click
toss.setOnClickListener {
var ran = (0..1).random() //Toss coin
// val ran = 1 //checking name-swapping
Any suggestions would be welcome.
Color.parseColor(...) works in different way than you think.
You can parse color like this:
Color.parseColor("#ff77bb")
In other words, you must pass hex code of color
this line is wrong .. because you didn't define the color
setBackgroundColor(Color.parseColor("button_blue")
try
setBackgroundColor(Color.parseColor("#39b8db")
or
context.getResources().getColor(android.R.color.white)
I finally found the following works to use colours defined in colors.xml.
I didn't want to include hex colours in my main code, as I may want to edit them at a later date.
ball1.setBackgroundColor(getResources().getColor(R.color.button_blue))
Thanks Niaj; this is similar to your last suggestion.

I can't change a public variable Kotlin

I try to change a public variable in kotlin but i can't. When I receive the variable in another script, it has not changed. I print the variable in the second script but then it says zero. I have no idea why. Please help me. I'm stuck. Here are my code:
package com.backal.bingolooooooooooooto
import android.content.Intent
import android.os.Bundle
import android.util.Log.d
import android.view.View
import androidx.appcompat.app.AppCompatActivity
class AddFavourActivity: AppCompatActivity() {
public var clicked = 0;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.add_favour)
val alt1: View = findViewById(R.id.alt1)
val alt2: View = findViewById(R.id.alt2)
val alt3: View = findViewById(R.id.alt3)
val alt4: View = findViewById(R.id.alt4)
val alt5: View = findViewById(R.id.alt5)
val alt6: View = findViewById(R.id.alt6)
val alt7: View = findViewById(R.id.alt7)
alt1.setOnClickListener {
var clicked = 50
startActivity(Intent(this, EnterPasswordActivity::class.java))
}
alt2.setOnClickListener {
var clicked = 80
d("Alexander", "Clicked is: $clicked")
startActivity(Intent(this, EnterPasswordActivity::class.java))
}
alt3.setOnClickListener {
startActivity(Intent(this, EnterPasswordActivity::class.java))
clicked = 100
}
alt4.setOnClickListener {
startActivity(Intent(this, EnterPasswordActivity::class.java))
clicked = 300
}
alt5.setOnClickListener {
startActivity(Intent(this, EnterPasswordActivity::class.java))
clicked = 500
}
alt6.setOnClickListener {
startActivity(Intent(this, EnterPasswordActivity::class.java))
clicked = 100
}
alt7.setOnClickListener {
startActivity(Intent(this, EnterPasswordActivity::class.java))
clicked = 1000
}
}
}
every time you are doing var clicked = 50 you're declaring a new variable (even if it's the same name) so just remove the var :
clicked = 50
something else you might want to consider is to rather make use of a companion object :
companion object {
var clicked = 0
}
then you can use this variable everywhere, by just using: AddFavourActivity.clicked
or AddFavourActivity.clicked = 50
A companion object will ensure the same instance of the variable is used everywhere
You keep creating a new variable called clicked when you do:
var clicked = 80
You should simply do:
clicked = 80
Notice I removed the var as if you don't it simply creates a new variable withing the scope of the block called clicked and it doesn't modify the global clicked variable that you created.

How to use KeyEvent in Kotlin

This my first kotlin/java code. I want learn from this.
When I push "OK" button of Android keyboard, I want my app do same thing as when I push my button with android:onClick="onAnswerClick"
I know my code is not very good. You can't help me to know how to optimize it
I don't know if its a good idea to learn to code on android with kotlin.
import android.annotation.SuppressLint
import android.app.Activity
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.*
import android.widget.AdapterView
import android.widget.Toast
import android.widget.Spinner
import kotlinx.android.synthetic.main.activity_main.*
import android.widget.EditText
class MainActivity : Activity() {
class Calculateurs{
fun aire(r: Double): Double{
return 3.141592 * r * r
}
fun circonference(r: Double): Double{
return 2.0 * 3.141592 * r
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val spinner = findViewById<Spinner>(R.id.spinner) as Spinner
// Create an ArrayAdapter using the string array and a default spinner layout
val adapter = ArrayAdapter.createFromResource(this,
R.array.mesure_array, android.R.layout.simple_spinner_item)
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Apply the adapter to the spinner
spinner.adapter = adapter
}
#SuppressLint("SetTextI18n")
fun onAnswerClick(view: View) {
val answer = (findViewById<EditText>(R.id.editText2) as EditText).text.toString()
val spinner = findViewById<View>(R.id.spinner) as Spinner
var rayon = java.lang.Double.parseDouble(answer)
if (spinner.getSelectedItem() == "Diamètre"){
rayon = rayon *2
}
if (rayon < 0) {
val toasty = Toast.makeText(applicationContext, "Ce nombre est négatif !", Toast.LENGTH_SHORT)
toasty.show()
} else if (rayon > 0) {
val c = Calculateurs()
val tc = findViewById<TextView>(R.id.cire) as TextView
tc.text = "" + c.circonference(rayon)
val ta = findViewById<TextView>(R.id.aire) as TextView
ta.text = "" + c.aire(rayon)
} else {
val toasty = Toast.makeText(applicationContext, "Ce nombre est nul !", Toast.LENGTH_SHORT)
toasty.show()
}
}
}
I think your question is how do you get your app to respond to the enter or done key when you press it in an EditText.
Since you did not show your layout, I will make a few assumptions.
Lets say you have and EditText that has the following attributes (plus others)
<EditText
android:id="#+id/field"
android:imeOptions="actionDone"
android:inputType="text"
...
app:layout_constraintTop_toTopOf="parent" />
Here is an example activity that will respond to hitting the enter key.
class MainActivity : AppCompatActivity() {
lateinit var field: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
field = findViewById(R.id.field) as EditText
}
override fun onResume() {
super.onResume()
field.setOnEditorActionListener( { textView, action, event ->
var handled = false
if (action == EditorInfo.IME_ACTION_DONE) {
println("call your method here")
handled = true
}
handled
})
}
You can call your onAnswerClick() in the location of the println("call your method here") statement, passing in textView if you like.
This example makes use of Kotlin's SAM Conversion which is covered in this section of the Kotlin documentation, specifically it uses the SAM conversion to create a TextView.OnEditorActionListener from a lambda expression
As Kamil Kulig, points out, there is definitely more samples and examples using Java than Kotlin, but I don't think you have to start with Java. You will need to learn enough Java to be able to understand what's going on in examples and documentation however.

Categories

Resources