simple currency converter with Kotlin - android

i have two questions.(sorry if my questions seem simple to you but i'm new to kotlin)
The first I try to do a simple calculation with a value entered in one field which is multiplied by a defined value, to get the result in another field.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Convertisseur.setOnClickListener {
val idr = editTextNumber.text.toString().toDouble()
val dollardft = 0.00007
resultDollar.text = (idr * dollardft).toString()
}
following this code I have an error on the result = Type mismatch: inferred type is String but Editable! was expected.
My second question, is it possible to do without a calculate button to obtain the result instantly.
thank you very much for your advice

Use setText instead
resultDollar.setText((idr * dollardft).toString())
or
fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this)
resultDollar.text = (idr * dollardft).toString().toEditable()

Related

Kotlin Change ViewText with an ID provided by a String

Goal: To get a ViewText resource and edit it from an activity, using a mutable string (because then the string can be changed to alter other ViewTexts in the same function).
Context: I'm making a grid using TableRows and TextViews that can be altered to form a sort of map that can be generated from an array.
Issue: The binding command does not recognise strings. See my comment "PROBLEM HERE".
Tried: getResources.getIdentifier but I've been told that reduces performance drastically.
An excerpt from gridmap.xml
<TextView
android:id="#+id/cell1"/>
GridMap.kt
package com.example.arandomadventure
import android.R
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.arandomadventure.databinding.GridmapBinding
class GridMap : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//sets the binding and assigns it to view
val binding: GridmapBinding = GridmapBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//creates a string variable
var cellID = "cell1"
//uses binding to set the background colour to teal
binding.cellID.setBackgroundResource(R.color.teal_200) //<- PROBLEM HERE (Unresolved Reference)
//getResources.getIdentifier is not an option as it degrades performance on a larger scale
}
}
A binding object is just an autogenerated class, whose class members are defined by the views in your layout XML. You can't add or access a field on a class with the syntax you showed - binding classes are no different from any other class. If you wanted to be able to access them by name, you could load them into a map
val viewMap = mapOf(
"cell1" to binding.cell1,
"cell2" to binding.cell2,
"cell3" to binding.cell3
)
then you can use the map to access them by name
var cellID = "cell1"
viewMap[cellID].setBackgroundResource(R.color.teal_200)
If you want the map to be a class member, you can set it like this
private lateinit var viewMap: Map<String,View>
override fun onCreate(savedInstanceState: Bundle?) {
//...
viewMap = mapOf(
"cell1" to binding.cell1,
"cell2" to binding.cell2,
"cell3" to binding.cell3
)
}
If your layout has hundreds of views and this becomes cumbersome, you may want to consider adding the views programmatically instead.
Edit
If you want to do this a more ugly, but more automatic way you can use reflection. To do this you need to add this gradle dependency:
implementation "org.jetbrains.kotlin:kotlin-reflect:1.7.0"
then you can build up the map programmatically with all views in the binding.
val viewMap = mutableMapOf<String,View>()
GridmapBinding::class.members.forEach {
try {
val view = it.call(binding) as? View
view?.let { v ->
viewMap[it.name] = v
}
}
catch(e: Exception) {
// skip things that can't be called
}
}
Or you can use this to call a method (keep in mind this will throw if no such class member exists):
var cellID = "cell1"
val view = GridmapBinding::class.members.filter { it.name == cellID }[0].call(binding)

I am learning Android studio and doing a rectangle calculator, my kotlin has 19 errors and cannot figure it out

I am learning Android studio and doing a rectangle calculator. My Kotlin has 19 errors, and I cannot figure it out. I keep getting unresolved errors for btn, functions that cannot be called, and expecting an element. I am trying to do a calculator that takes height and width and then calculates area and perimeter. Just need guidance on what I am doing wrong and not looking for someone to give me new code.
MainActivity.kt
package com.example.calculator
import android.annotation.SuppressLint
import android.icu.text.DecimalFormat
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.*
class MainActivity : AppCompatActivity() {
#SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_calculate.setOnClickListener {
calculate()
btn_calculate.onEditorAction(EditorInfo.IME_ACTION_DONE)
}
btn_reset.setOnClickListener {
reset()
}
}
private fun calculate() {
val formatter = DecimalFormat("#.##")
val editNum1 = (EditText) editNum1.text.toString()
val editNum2 = (EditText) editNum2.text.toString()
val Area = DecimalFormat(editNum1.toDouble() * editNum2.toDouble())
val Perimeter = DecimalFormat(2* ( (editNum1.toDouble()) + (editNum2.toDouble()))
}}
If you want to access the views from your XML file directly in your activity you should use Kotlin Android Extension. But this plugin is deprecated. You should migrate to View Binding.
https://developer.android.com/topic/libraries/view-binding/migration

I keep getting "modifier override is not applicable to local function' when trying to use onclicklistener

I'm trying to write a code for a small quiz app. When you click the plus button it takes you to a separate page where you input the question and answer. When you click the check button it takes you back to the previous page and a new button with the text set as the question is created. I'm running across an error when I try to put override under my buttonclick event. Is there anyway I can get around this?
Newbtn page:
package com.example.test
import android.app.Activity
import android.content.Intent
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.google.android.material.floatingactionbutton.FloatingActionButton
class Newbtn : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_newbtn)
val newbutton = Button(this#Newbtn)
val btn = findViewById<FloatingActionButton>(R.id.btn2)
val intent = getIntent()
val test = intent.getStringExtra("test")
newbutton.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
newbutton.width=1010
newbutton.height=300
newbutton.gravity = Gravity.CENTER
newbutton.translationX= 65F
newbutton.setTextColor(Color.parseColor("#FFFFFFFF"))
newbutton.setBackgroundColor(Color.parseColor("#250A43"))
newbutton.text = test
btn.setOnClickListener{
val activityCode = 2
startActivityForResult(
Intent(this#Newbtn, QuestionPage::class.java), activityCode
)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == activityCode && resultCode == Activity.RESULT_OK) {
val buttonText = data?.getStringExtra("test")
newbutton.text = buttonText
}
}
}
}}
QuestionPage:
package com.example.test
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.EditText
import com.google.android.material.floatingactionbutton.FloatingActionButton
class QuestionPage : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_question_page)
val btn3 = findViewById<FloatingActionButton>(R.id.btn3)
val intent = Intent(this#QuestionPage, Newbtn::class.java)
val question = findViewById<EditText>(R.id.question)
val answer = findViewById<EditText>(R.id.answer)
val questiontext = question.text.toString()
val answertext = answer.text.toString()
val returnIntent = Intent()
returnIntent.putExtra("test", questiontext)
setResult(Activity.RESULT_OK, returnIntent)
finish()
btn3.setOnClickListener{
startActivity(intent)
}
}
}
Your Newbtn activity needs an onCreatefunction, and an onActivityResult one, both on the same level (inside the activity). Right now you have one nested inside the other, and inside an unclosed click listener lambda too.
Look at the vertical lines inside the IDE:
See how whenever you open a scope, like with a function or lambda, there's a vertical line going from where you open that scope to where you close it, i.e. where the closing brace is? See how btn.setOnClickListener extends way further down than where it should end? How onActivityResult is inset inside it?
You can use these guides to help you clean things up when the bracing gets messed up, and make sure stuff is nested correctly. So onActivityResult should be on the same indented level as onCreate, which should be closed before onActivityResult's opening brace - two separate lines, one after the other, each ending in a closing brace.
You can't reference activityCode because you're defining it inside the click listener lambda, it's a local variable in there, you can't see it from outside. It's not complaining in your current code because you have onActivityResult nested inside that lambda, where it can see the variable - but that code is broken because the function shouldn't be there anyway.
Just stick the activityCode value somewhere in your class, or even outside it (constants like this are a good candidate for const val ACTIVITY_CODE = 2 declarations). Basically, your onActivityResult function needs to be able to handle any result that comes in, and refer to the possible identifying codes it's supposed to handle. Those shouldn't be defined secretly in the click listener, because then the activity gets the result back and says "what does 2 mean???"

findViewById error - Not enough information to infer type variable T. I copied java code and converted it online

Copied java code from diolor/swipecards (GITHUB). Converted it into kotlin with help pf online tools. It had some errors which were corrected but this last one still appears (findViewById) in the OnScroll function.
package com.example.fanatic
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.ArrayAdapter
import android.widget.Toast
import com.lorentzos.flingswipe.SwipeFlingAdapterView
class Swipe_card_activity : AppCompatActivity() {
private var al:ArrayList<String> = TODO()
private lateinit var arrayAdapter:ArrayAdapter<String>
private var i:Int = 0
override fun onCreate(savedInstanceState:Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_swipe_card_activity)
al = ArrayList()
al.add("php")
al.add("c")
al.add("python")
al.add("java")
al.add("html")
al.add("c++")
al.add("css")
al.add("javascript")
arrayAdapter = ArrayAdapter(this, R.layout.item, R.id.helloText, al)
val flingContainer : SwipeFlingAdapterView = findViewById<SwipeFlingAdapterView>(R.id.frame)
flingContainer.setAdapter(arrayAdapter)
flingContainer.setFlingListener(object: SwipeFlingAdapterView.onFlingListener {
override fun removeFirstObjectInAdapter() {
// this is the simplest way to delete an object from the Adapter (/AdapterView)
Log.d("LIST", "removed object!")
al.removeAt(0)
arrayAdapter.notifyDataSetChanged()
}
override fun onLeftCardExit(dataObject:Any) {
//Do something on the left!
//You also have access to the original object.
//If you want to use it just cast it (String) dataObject
makeToast(this#Swipe_card_activity, "Left!")
}
override fun onRightCardExit(dataObject:Any) {
makeToast(this#Swipe_card_activity, "Right!")
}
override fun onAdapterAboutToEmpty(itemsInAdapter:Int) {
// Ask for more data here
al.add("XML " + (i).toString())
arrayAdapter.notifyDataSetChanged()
Log.d("LIST", "notified")
i++
}
THE ERROR IS PRESENT HERE ON FINDVIEWBYID
IT SAYS : NOT ENOUGH INFORMATION TO INFER TYPE T.
**override fun onScroll(scrollProgressPercent:Float) {
strong textval viuw = flingContainer.selectedView
viuw.run {
findViewById(R.id.item_swipe_right_indicator).setAlpha(if (scrollProgressPercent < 0) -scrollProgressPercent else 0)
findViewById(R.id.item_swipe_left_indicator).setAlpha(if (scrollProgressPercent > 0) scrollProgressPercent else 0)**
}
}
})
// Optionally add an OnItemClickListener
flingContainer.setOnItemClickListener { itemPosition, dataObject -> makeToast(this#Swipe_card_activity, "Clicked!") }
}
fun makeToast(ctx: Context, s:String) {
Toast.makeText(ctx, s, Toast.LENGTH_SHORT).show()
}
#OnClick(R.id.right)
fun right() {
/**
* Trigger the right event manually.
*/
val flingContainer : SwipeFlingAdapterView = findViewById<SwipeFlingAdapterView>(R.id.frame)
flingContainer.getTopCardListener().selectRight()
}
#OnClick(R.id.left)
fun left() {
val flingContainer : SwipeFlingAdapterView = findViewById<SwipeFlingAdapterView>(R.id.frame)
flingContainer.getTopCardListener().selectLeft()
}
}
annotation class OnClick(val right: Int)
I believe the problem you are facing due to code conversion. Java doesn't require you to cast the view explicitly whereas Kotlin requires you to specify the type of view. You need to set the view within angular brackets like this
findViewById<View>(R.id.item_swipe_right_indicator).setAlpha(...)
The type you are getting with findViewById is potentially unconstrained so the type cannot be inferred and in Kotlin needs to be explicitly stated/casted.
If you are targeting API 26 or higher in your app you can do:
findViewById<THE_VIEW_TYPE>(R.id.item_swipe_right_indicator).setAlpha(...)
Otherwise for API 25 and lower you can do:
(findViewById(R.id.item_swipe_right_indicator) as THE_VIEW_TYPE).setAlpha(...)

Button is not reacting as expected, I am trying to see if the function is not working or if there some other error

The function should use user input to do math and output into a text box for the user. On clicking the button, absolutely nothing happens. LogCat isn't showing me anything, so I'm not sure how to Troubleshoot this issue. I've got two similiar activities in the same project that are working fine, so I suspect I may not being doing the math correctly but can't find any other information. Any advice is appreciated.
package com.example.awcc
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main3.*
class Setup : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
editTextNumber120.text.toString().toInt()
}
}
class ThirdActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main3)
val button2 = findViewById< Button >(R.id.button2)
button2.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
val button3 = findViewById< Button >(R.id.button3)
button3.setOnClickListener {
val intent2 = Intent(this, MainActivity::class.java)
startActivity(intent2)
val button7 = findViewById< Button >(R.id.button35)
button7.setOnClickListener {
var value1 = editTextNumber120.text.toString().toInt()
fun accessory(): Int {
return when {
value1 > 10 -> value1 * 0
value1 in 10..20 -> value1 * 1
value1 in 21..40 -> value1 * 2
value1 in 40..50 -> value1 * 3
value1 in 51..75 -> value1 * 4
value1 < 75 -> value1 * 5
else -> value1
}
}
val complete = accessory().toString()
try {
editTextNumber19?.setText(complete)
} catch (e: NumberFormatException) {
Toast.makeText(
applicationContext,
"Please enter a 0 in the blank field",
Toast.LENGTH_LONG
).show()
}
}
}
}
}
where is editTextNumber120 defined? what is this text being used for?
If you're expecting the value to carry over from the setup activity to activity3, that's not how it works. Each activity uses it's own data and if you need data to be shared across Activities and Fragments, then you need to create a data model for that information.
Example:
public class MyDataModel {
protected MutableLiveData<Int> editTextData;
public MyDataModel() {
editTextData = new MutableLiveData<>();
}
// setter/getter - returns a LiveData object that allows
// you to observe the value for any changes
// If you don't need to observe changes, then just keep it as an int/string
}
This way your other Activity can access the same data being used in the Setup activity. Also note, this doesn't persist across reboots, so if you want the setup to contain the previous data used in the last boot of the app, i'd look into SharedPreferences.
Also I don't know the structure of your app, but I would question why you need so many Activities? My app I'm working on is fairly robust but even I only have one activity (with a couple fragments), and another activity for the settings, and that's it.
I found the answer if anyone runs into this issue behind me. I was using Var instead of Val so it wasn't actually multiplying or changing the var at all. Changed to Val and it works like a charm!

Categories

Resources