Issue in Display view dynamically code failed - android

I am very new to Android development and actually stuck with code in tutorial for Kotlin programming for android. The code below is not working and I have tried to find alternative but no luck.
Will appreciate if somebody can help we with the alternative code
below is a code:
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addSomeViews(count = 5)
}
fun addSomeViews(count: Int) {
for (i in 1..count) {
val textView = TextView(this)
textView.text = "Hey, learner # $i"
textView.textSize = 20f
my_layout.addView(textView)
}
val button = Button(this)
button.text = "Click me!"
my_layout.addView(button)
}
}

That kotlinx.synthetic stuff is deprecated - it doesn't work anymore. Instead of just referencing my_layout directly (the synthetics are supposed to look it up for you and create that variable) you need to find it yourself:
fun addSomeViews(count: Int) {
// lookup the layout viewgroup and create a variable for it
val my_layout = findViewById<ViewGroup>(R.layout.my_layout)
for (i in 1..count) {
val textView = TextView(this)
textView.text = "Hey, learner # $i"
textView.textSize = 20f
// now this variable exists
my_layout.addView(textView)
}
}
Aside from that... this isn't how a beginner should be learning Android imo. Creating views like this is kind of an advanced thing, mostly you never have to do it, and if you do there's a bunch of configuration you need to do on the views to make them display correctly (like the appropriate LayoutParams)
You can do it, but mostly you create your layouts in XML using the layout editor. And Compose is the new thing for writing UI in code, which is probably worth learning. It's up to you obviously, I just wanted to warn you that it's a strange thing for a beginner to be learning, and you might be better trying the Codelabs stuff instead

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

How does Kotlin Android Extensions replacement for findViewById prevent null views?

I know that Kotlin's Android Extensions creates synthetic properties + caching function to replace any need to call findViewById:
https://stackoverflow.com/a/46482618/1650674
https://www.raywenderlich.com/84-kotlin-android-extensions
https://antonioleiva.com/kotlin-android-extensions/
All of these examples show that the similar java code would look like
private HashMap _$_findViewCache;
...
public View _$_findCachedViewById(int var1) {
if(this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}
View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
if(var2 == null) {
var2 = this.findViewById(var1);
this._$_findViewCache.put(Integer.valueOf(var1), var2);
}
return var2;
}
public void _$_clearFindViewByIdCache() {
if(this._$_findViewCache != null) {
this._$_findViewCache.clear();
}
}
What I don't understand is how this prevents potential NPEs? var2 = this.findViewById(var1); may still return null.
Using the example from that last link:
<TextView
android:id="#+id/welcomeMessage"
...
android:text="Hello World!"/>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
welcomeMessage.text = "Hello Kotlin!"
}
What type is welcomeMessage? TextView or TextView?
What I don't understand is how this prevents potential NPEs?
It doesn't. If you try referencing a widget that does not exist, you crash.
So long as your import statements are only for the relevant layout for your Kotlin code, you should not wind up referencing a widget that does not exist. Where the problem comes in is if you accidentally import the synthetic properties from another layout.
For example, suppose you have a project with activity_main.xml and scrap.xml layouts, and your activity is:
package com.commonsware.android.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.scrap.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
scrapView.visibility = View.GONE
}
}
Here, we are referencing a scrapView view in the scrap layout. We have not inflated that layout, and so this crashes with an IllegalStateException:
Caused by: java.lang.IllegalStateException: scrapView must not be null
at com.commonsware.android.myapplication.MainActivity.onCreate(MainActivity.kt:14)
What type is welcomeMessage? TextView or TextView?
Technically, it is TextView!, where ! means "it's a platform type, so we don't know if it can be null or not". Practically, TextView! is used the same as TextView, which is why you crash if it winds up being null.
While #CommonsWare's answer is correct, I also wanted to spare my 2 cents on this subject.
As #CommonsWare pointed out, you have to import relevant layout in order to be able to use Kotlin Extensions. The tricky part here is, it's not only about importing relevant layout but also inflating the layout before you call it with Kotlin Extensions.
So, if you have something like below
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
welcomeMessage.text = "Hello Kotlin!"
setContentView(R.layout.activity_main)
}
}
You will still get
java.lang.IllegalStateException: welcomeMessage must not be null
And your app will crash.
I got crash with NPE also in listener.
I made ZoomRecyclerView, which call onZoom listener method onZoom when zoomed.
This event is then propagated to activity, where i call synthetic imagebuttonview method, to set image resource to set zoom button image if item is zoomed or not.
It simply crash on synthetic call.
Example :
zoomRecycler.onZoom {
// exception here : zoomButton must be not null
zoomButton.setImageDrawable(...)
}
zoomButton.click {
// calling zoom which raise onZoom inside zoomRecycler
zoomRecycler.toggleZoom();
}

Make a webview search bar that searches Google and URL's

I'm 16 and trying to learn Kotlin, I'm very new. I want to create a search bar which searches both Google and can open any URL you type in. I was trying to use an if else statement, for example:
if the first three letters were "www." then use the string url which is equal to "https://",
else use the string start_url which is equal to "google.com/search?q=";.
I just do not know how to do that and I have tried looking for help across the internet I just couldn't.
The URLUtil.isValidUrl(url) will not work because it still only loads Google Search
I am open to any comments to try and help me further learn and improve my code, even if it is not related to my question. Point out any errors or things that could be improved in my code, I know it's not perfect, thank you!
package com.example.corie.quicklinks.mainpages
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.asynclayoutinflater.R.id.text
import android.webkit.WebChromeClient
import android.webkit.WebViewClient
import com.example.corie.quicklinks.R
import com.example.corie.quicklinks.R.string.start_url
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//------------------WEBVIEW-----------------//
webViewOne.webChromeClient = WebChromeClient()
webViewOne.isVerticalScrollBarEnabled = false
webViewOne.run{
webViewOne.loadUrl("https://" + getString(start_url))
}
goBtn.setOnClickListener{
webViewOne.loadUrl("https://www.google.com/search?q=" + editText.text.toString())
}
backBtn.setOnClickListener {
if (webViewOne.canGoBack())
webViewOne.goBack()
}
nextBtn.setOnClickListener {
if (webViewOne.canGoForward())
webViewOne.goForward()
}
//------------------WEBVIEW-----------------//
}
}
Simple with built-in regex pattern:
import android.util.Patterns
val isAddress = Patterns.WEB_URL.matcher(address).matches()
if (isAddress) {
this#WebpageFragment.mBinding.webpageWebView
.loadUrl(address)
} else {
this#WebpageFragment.mBinding.webpageWebView.loadUrl(
"https://www.google.com/search?q=$address"
)
}
Explain: First you need to check if the string is an URL. If it's an URL, you'll use the webview to load that URL; Otherwise, you will need to use that value as google search query.

Calling REST api on android emulator causes app to crash

I am relatively new to android studio. I am trying to build a very simple app that fetches the price of cryptocurrencies using the Bittrex exchanges api. However every time I try to get the info from the URL, my app crashes. I am using Kotlin by the way. I'm having trouble solving this because I don't know how to run the emulator in debug mode, just the compiler. Here is my code:
package com.example.sebastian.cryptoapp
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import java.net.URL
import java.net.MalformedURLException
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
fun search(): String {
//read in value
var market = searchBar.getText().toString()
//output text from URL query
val result = URL("https://bittrex.com/api/v1.1/public/getticker?market="
+ market).readText()
return result
}
fun getPrice(): String {
//calling search function
var info = search()
//split the string into a list
var list: List<String> = info.split(":", "}")
//access 7th index of list for last traded price
return list[6]
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
text_output.setText(getPrice())
}
}
}
This line here:
val result = URL("https://bittrex.com/api/v1.1/public/getticker?market=" + market).readText() looks like it is being run on the main thread. This will cause the app to crash with a a NetworkOnMainThreadException.
You can read more on this exception here.
Also, make sure you have the following in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Check out these answers for more information on how to run this in the background:
How to fix android.os.NetworkOnMainThreadException?
Android - android.os.NetworkOnMainThreadException
Alternatively in Kotlin you could also Anko or Coroutines.

Categories

Resources