I know I can use an id attribute with Anko to identify a view:
class MainActivityUI : AnkoComponent<MainActivity> {
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
frameLayout {
textView {
id = R.id.text
}
}
}
}
Then obtain it in the Activity using the find() function (or by using Kotlin Android Extensions):
class MainActivity : AppCompatActivity() {
private val textView by lazy {
find<TextView>(R.id.text)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MainActivityUI().setContentView(this)
textView.text = "Hello World"
}
}
But I feel like I am missing something; the only place the README mentions the find function or Kotlin Android Extensions is in the section titled Supporting Existing Code:
You don't have to rewrite all your UI with Anko. You can keep your old
classes written in Java. Moreover, if you still want (or have) to
write a Kotlin activity class and inflate an XML layout for some
reason, you can use View properties, which would make things easier:
// Same as findViewById(), simpler to use
val name = find<TextView>(R.id.name)
name.hint = "Enter your name"
name.onClick { /*do something*/ }
You can make your code even more compact by using Kotlin Android
Extensions.
Which makes it seem like the find function is only meant for supporting "old" XML code.
So my question is this; is using an id along with the find function the correct way of accessing a View from the Activity using Anko? Is there a more "Anko" way of handling this? Or am I missing some other benefit of Anko that makes accessing the View from the Activity irrelevant?
And a second related question; if this is the correct way of accessing a View from the Activity, is there a way of creating an id resource (i.e. "#+id/") from within an AnkoComponent? Rather than creating each id in the ids.xml file.
So, why still use XML id to locate the View? since we already use the Anko instead of the XML.
In my opinion, we can store the view elements inside the AnkoComponent instead of the find view's id method. Check the code blow:
class MainActivityUI : AnkoComponent<MainActivity> {
lateinit var txtView: TextView
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
frameLayout {
txtView = textView {
id = R.id.text // the id here is useless, we can delete this line.
}
}
}
}
class MainActivity : AppCompatActivity() {
lateinit var mainUI : MainActivityUI
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainUI = MainActivityUI()
mainUI.setContentView(this)
mainUI.txtView.text = "Hello World"
}
}
Do not use id to identify views with Anko DSL! It is unnecessary and useless because Anko was designed to get rid off XML layouts. Instead use this pattern:
class ActivityMain : AppCompatActivity() {
var mTextView: TextView // put it here to be accessible everywhere
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityMainUI().setContentView(this)
}
fun yourClassMethod() {
// So this is am example how to get the textView
// defined in your Anko DSL class (not by id!):
mTextView.text = "bla-bla-bla"
}
}
class ActivityMainUI : AnkoComponent<ActivityMain> {
override fun createView(ui: AnkoContext<ActivityMain>) = with(ui) {
// your fancy interface with Anko DSL:
verticalLayout {
owner.mTextView = textView
}
}
}
Please note the UI class definition:
class ActivityMainUI : AnkoComponent<ActivityMain> {
If you put there your activity class name in brackets then all its public variables become accessible via owner in UI class body so you can assing them there.
But you may put AppCompatActivity easily and make some universal class which might be cloned. In this case use lateinit var mTextView :
TextView in the body of UI class as described in Jacob's answer here.
I believe that, as you can add behavior to your Anko files, you don't have to instantiate your views in the activity at all.
That can be really cool, because you can separate the view layer even more. All the code that acts in your views can be inserted in the Anko files. So all you have to do is to call your activity's methods from the Anko and not instantiate any view.
But if you need to instantiate any view... you can use Kotlin Android Extensions in your activity.
Exemple:
Code in your activity:
seekBar.setOnSeekBarChangeListener(object: OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
// Something
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
// Just an empty method
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
// Another empty method
}
})
Code in Anko:
seekBar {
onSeekBarChangeListener {
onProgressChanged { seekBar, progress, fromUser ->
// Something
}
}
}
Now the code is in AnkoComponent. No need to instantiate the view.
Conclusion:
It's a more 'Anko' way to program if you put all your view logic in the AnkoComponents, not in your activities.
Edit:
As an exemple of a code where you don't have to instantiate a view:
In your Anko:
var networkFeedback : TextView = null
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
frameLayout {
textView {
id = R.id.text2
networkFeedback = this
onClick {
ui.owner.doSomething(2, this)
}
}
}
}
fun networkFeedback(text: String){
networkFeedback.text = text
}
In your activity:
class MainActivity : AppCompatActivity() {
overriding fun onCreate{
[...]
val mainUi = AnkoUi()
// some dynamic task...
mainUi.networkFeedback("lalala")
}
fun doSomething(number: Int, callback: TextView){
//Some network or database task goes here!
//And then, if the operation was successful
callback.text = "Something has changed..."
}
This is a very different approach. I'm not so sure if I like it or not, but this is a whole different discussion...
To generalize the question a bit: How can one make an AnkoComponent that is encapsulated, can be used from the DSL and can have its data programmatically set after creation?
Here is how I did it using the View.tag:
class MyComponent: AnkoComponent<Context> {
lateinit var innerds: TextView
override fun createView(ui: AnkoContext<Context>): View {
val field = with(ui) {
linearLayout {
innerds = complexView("hi")
}
}
field.setTag(this) // store the component in the View
return field
}
fun setData(o:SomeObject) { innerds.setStuff(o.getStuff()) }
}
inline fun ViewManager.myComponent(theme: Int = 0) = myComponent(theme) {}
inline fun ViewManager.myComponent(theme: Int = 0, init: MyComponent.() -> Unit) =
ankoView({ MyComponent(it) }, theme, init)
// And then you can use it anywhere the Anko DSL is used.
class SomeUser : AnkoComponent<Context>
{
lateinit var instance:View
override fun createView(ui: AnkoContext<Context>): View {
linearLayout {
instance = myComponent {}
}
}
fun onDataChange(o:SomeObject) {
(instance.Tag as MyComponent).setData(o)
}
}
}
Related
So basically I'm making an activity in which in button click one alert dialog displays on screen and it contain recyclerview with cardview and one close button. now, by clicking on cardview I have successfully toast a message but now there is requirement to get the image of cardview and then set it into imageview of different xml file (that cardview is also in different kt file). In short the main problem is i can not find that imageview into that different kt file.
So, I have tried few approaches mentioned below
First Approach -> view.findViewById (in this case the error is view.findViewById(R.id.custom_image_view) must not be null)
Second Approach -> by using view.find (in this case the error is null cannot be cast to non-null type android.widget.ImageView)
Third Approach -> accessing using className().imageview (in this case the error is lateinit property is not been initialized) method but the problem is still there.
So, below code contains button for displaying alert dialog.code file named as CreateCustomImageActivity.kt
class CreateCustomImageActivity : AppCompatActivity(), View.OnTouchListener {
/* some code */
lateinit var imageView: ImageView
/* some code */
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_create_custom_image)
/* below is the imageView in which I want to set image */
imageView = findViewById(R.id.custom_image_view)
/* below is the imageButton on which by clicking alert dialog with recyclerView
with cardView is displaying */
val image = findViewById<ImageButton>(R.id.bg_image)
image.setOnClickListener {
val selectImageDialog = LayoutInflater.from(this)
.inflate(R.layout.activity_choose_background_image_list, null)
val builder = AlertDialog.Builder(this).setView(selectImageDialog)
val showDialog = builder.show()
showDialog.window?.setGravity(Gravity.BOTTOM)
val close = selectImageDialog.findViewById<Button>(R.id.choose_image)
val recyclerImageList = selectImageDialog.findViewById<RecyclerView>(R.id.image_list_recycler_view)
val imageCard = ArrayList<backgroundImageListDatabase>()
imageCard.add(backgroundImageListDatabase(R.drawable.o))
imageCard.add(backgroundImageListDatabase(R.drawable.t))
imageCard.add(backgroundImageListDatabase(R.drawable.th))
imageCard.add(backgroundImageListDatabase(R.drawable.f))
imageCard.add(backgroundImageListDatabase(R.drawable.fi))
imageCard.add(backgroundImageListDatabase(R.drawable.s))
imageCard.add(backgroundImageListDatabase(R.drawable.se))
recyclerImageList.setLayoutManager(
GridLayoutManager(this,2,
GridLayoutManager.VERTICAL,false)
)
val adapter = BackgroundImageCardAdapter(imageCard)
recyclerImageList.setAdapter(adapter)
close.setOnClickListener {
showDialog.dismiss()
}
}
}
}
And below code contains the Adapter class for binding cardView to recyclerView. Code file named as ChooseBackgroundImageListActivity.kt
class BackgroundImageCardAdapter(val backgroundImageCardDetails: ArrayList<backgroundImageListDatabase>) :
RecyclerView.Adapter<BackgroundImageCardAdapter.ViewHolder>() {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
// var bgImageView: ImageView = view.find(R.id.custom_image_view)
fun bindItems(details: backgroundImageListDatabase) {
val imageView = itemView.findViewById<ImageView>(R.id.background_image_card_image)
imageView.setImageResource(details.image)
imageView.setOnClickListener {
// bgImageView.setImageResource(details.image)
Toast.makeText(imageView.context,details.image.toString(),Toast.LENGTH_SHORT).show()
Log.d(" - CARD CLICK EVENT - ","Clicked")
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView: View = LayoutInflater.from(parent.context).inflate(R.layout.choose_background_image_card,parent,false)
return ViewHolder(cardView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItems(backgroundImageCardDetails[position])
// holder.itemView.setOnClickListener {
// Toast.makeText(holder.itemView.context,"CARD CLICKED",Toast.LENGTH_SHORT).show()
// Log.d(" - CARD CLICK EVENT - ","Clicked")
// }
}
override fun getItemCount(): Int {
return backgroundImageCardDetails.size
}
}
Thanks in Advance.
Its not about putting them in the same or different kt file, You cant access any view until you have its reference.
I mean you have only access to your imageView when your dialog is on the screen and you can access its value with selectImageDialog.findViewById
The best option here is using Callbacks(when you click on a image in your adapter the parent of your adapter should get noticed that new image is clicked)
first create your callback:
interface CardAdapterCallback {
fun onImageClicked(resId : Int)
}
then change your adapter constructor and add your callback:
class BackgroundImageCardAdapter(
val backgroundImageCardDetails: ArrayList<backgroundImageListDatabase>,
val callback : CardAdapterCallback
) :RecyclerView.Adapter<BackgroundImageCardAdapter.ViewHolder>() {...}
then when you click on a image in your adapter bindItems function send your callback to your activity:
fun bindItems(details: backgroundImageListDatabase) {
val imageView = itemView.findViewById<ImageView>(R.id.background_image_card_image)
imageView.setImageResource(details.image)
imageView.setOnClickListener {
callback.onImageClicked(details.image)
}
}
Updated
change your adapter creating in your activity to this:
val adapter = BackgroundImageCardAdapter(
imageCard,
object : CardAdapterCallback {
override fun onImageClicked(resId : Int){
imageView.setImageResource(resId)
}
})
(object:CardAdapterCallback{} is for fixing your parameter error)
And to fix your callback variable error in bindItems function change this line in your BackgroundImageCardAdapter:
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bindItems(details: backgroundImageListDatabase) {
val imageView = itemView.findViewById<ImageView>(R.id.background_image_card_image)
imageView.setImageResource(details.image)
imageView.setOnClickListener {
callback.onImageClicked(details.image)
}
}
}
with that inner keyword, you will be able to call callback.onImageClicked(details.image) in your bindItems function
Hello i have a recyclerview that is filled with some data coming from a Webservice, using Retrofit.
Now I want to implement a onClickListener, so when i click each row of the Recycler View, i can see more data from that object, and tried to work with some examples, but i got stucked
Here is my adapter. I know that in the onCreateViewHolder, i should put in the Return AnunciosViewHolder a second parameter, of the type cellClickListener, but i have no idea what i have to put. I tried this#CellCLickListener and this#cellCLickListener and it gave me error that is is unresolved
class AnuncioAdapter(val anuncios: List<Anuncio>): RecyclerView.Adapter<AnunciosViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnunciosViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recyclerline, parent, false)
return AnunciosViewHolder(view)
}
override fun getItemCount(): Int {
return anuncios.size
}
override fun onBindViewHolder(holder: AnunciosViewHolder, position: Int) {
return holder.bind(anuncios[position])
}
}
class AnunciosViewHolder(itemView : View, private val cellClickListener: CellClickListener): RecyclerView.ViewHolder(itemView){
private val morada: TextView = itemView.findViewById(R.id.morada)
private val telemovel: TextView = itemView.findViewById(R.id.number)
private val fotografia: ImageView = itemView.findViewById(R.id.image)
fun bind(anuncio: Anuncio) {
morada.text = anuncio.morada
telemovel.text = anuncio.telemovel
itemView.setOnClickListener {
cellClickListener.onCellClickListener(anuncio)
}
I also tried creating an interface
interface CellClickListener {
fun onCellClickListener (data: Anuncio)
}
and in my Activity i put this method and it gives me an error that «overrides nothing»
override fun onCellClickListener(data: Anuncio) {
val intent = Intent(this#ListaAnuncios, DetalhesActivity::class.java)
intent.putExtra(PARAM_ID, data.id.toString())
intent.putExtra(PARAM_MORADA, data.morada)
intent.putExtra(PARAM_TELEMOVEL, data.telemovel)
startActivityForResult(intent, newAnuncioActivityRequestCode1)
Log.e("***ID", data.id.toString())
}
UPDATE
After using the suggestions made by Praveen i was able to clean my Adapter from errors, however i am struggling in the activity part
if it put
val anuncioAdapter = AnuncioAdapter(anuncios, this)
on the beggining of my On Create, it doesn't recognize «anuncios»
However i am declaring my adapter inside the call.enqueue
recyclerView.apply {
setHasFixedSize(true)
layoutManager =
LinearLayoutManager(this#ListaAnuncios)
adapter = AnuncioAdapter(response.body()!!)
}
And it is asking to pass an instance of cellClickListener here, but if i use «this» in here, it is stated that i am trying to pass an instance of the recycler view instead of the CellClickListener
NEW UPDATE
Forgot to put all the call.enqueue method
call.enqueue(object : Callback<List<Anuncio>> {
override fun onResponse(call: Call<List<Anuncio>>, response: Response<List<Anuncio>>) {
if (response.isSuccessful){
recyclerView.apply {
setHasFixedSize(true)
layoutManager =
LinearLayoutManager(this#ListaAnuncios)
adapter = AnuncioAdapter(response.body()!!)
}
}
}
override fun onFailure(call: Call<List<Anuncio>>, t: Throwable) {
Toast.makeText(this#ListaAnuncios, "${t.message}", Toast.LENGTH_LONG).show()
}
}) }
i tried both approaches of #Praveen and #aligur, but still struggling with asking me to put the instance of Clicklistener as the 2nd parameter, but using «this» is putting the instance of the Recycler View and not of the ClickListener
Thank You in advance
and in my Activity i put this method and it gives me an error that
«overrides nothing»
You are not implementing CellClickListener in your activity. Add CellClickListener after your activity's class name declaration
class MainActivity : AppCompatActivity(), CellClickListener {
}
I know that in the onCreateViewHolder, i should put in the Return
AnunciosViewHolder a second parameter, of the type cellClickListener,
but i have no idea what i have to put. I tried this#CellCLickListener
and this#cellCLickListener and it gave me error that is is unresolved
You've to add the private val cellClickListener: CellClickListener parameter to the constructor of AnuncioAdapter, not the ViewHolder. Only then you will be able to pass it from your activity.
Change constructor of AnuncioAdapter to accept a CellClickListener and remove the same from the constructor of AnunciosViewHolder
class AnuncioAdapter(
private val anuncios: List<Anuncio>,
private val cellClickListener: CellClickListener
): RecyclerView.Adapter<AnunciosViewHolder>() {
}
To access this cellClickListener inside AnunciosViewHolder you've to make it an inner class of AnuncioAdapter, which you can make, as it's already tightly coupled with the adapter.
inner class AnunciosViewHolder(itemView : View): RecyclerView.ViewHolder(itemView){
}
Now, on creating an object of AnuncioAdapter inside activity, just pass an instance of cellClickListener using this, as it's already implementing it.
val anuncioAdapter = AnuncioAdapter(anuncios, this)
I think the easiest way is passing function as parameter to RecyclerViewAdapter.
for instance:
RecyclerViewAdapter(val clickListener : () -> Unit)
onCreateViewHolder(){
clickListener.invoke()
}
in your view
adapter = ReceylerViewAdapter({
//do your stuff here
})
Was finally able to find a solution. By Using #Praveen suggestion, and by finding this example https://github.com/velmurugan-murugesan/Android-Example/tree/master/RetrofitWithRecyclerviewKotlin/app/src/main/java/app/com/retrofitwithrecyclerviewkotlin
On the activity i added a new val, before the OnCreate method
lateinit var anuncioAdapter: AnuncioAdapter
Added this on the onCreate (so i could use the first sugestion)
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
anuncioAdapter = AnuncioAdapter(this,this)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = anuncioAdapter
And changed the recyclerview.apply {} on the call.enqeue just to
anuncioAdapter.Anuncios(response.body()!!);
And finally created the Anuncios Method on the Adapter
fun Anuncios(anuncio: List<Anuncio>){
this.anuncios = anuncio;
notifyDataSetChanged()
}
With this it works like how i wanted id. Thanks for the help
What I am trying to do is restart the CountdownTimer object when it is finished. Therefor I put it inside a procedure 'goCountdown', so onFinish() it calls itself. However this gives the following problems:
A procedure goCountdown cannot be called from within the same class
B activity text cannot be updated with time from outside the MainActivity class.
See references '!!! nr !!!' in code below:
the 'goCountdown() function is found (recognized/doesnt give an
error) at place 1 even though the procedure is placed outside the class.
when goCountdown is placed inside the class at nr 2 it is not found at place nr 1 (gives error).
As it only works outside of the class, it is now impossible to update the text on the activity on every tick because the MainActivity isnt accessible.
Questions:
Why does Kotlin / Android Studio not recognize the 'goCountdown()' function when it is placed WITHIN
the same activity class?
Even if that will work, is there a way to acces the texts on the MainActivity from a top level procedure?
Besides my goal to make the timer loop I am trying to understand why it is not working. Thanks for explaining or pointing me to the explanation.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
tapAction.setOnClickListener{
boTimeRunning = !boTimeRunning
if(boTimeRunning ){
goCountdown() //!!! 1 !!! its fine to call it from here when its outside the class
}
}
//!!! 2 !!!but if fun goCountdown() block is placed here, it is not seen at !!! 1 !!! place
}
}
fun goCountdown(){
object : CountDownTimer1(timeSettings_set * 1000, 1000){
override fun onTick(p0: Long) {
MainActivity.txtBig.text = "sometext" //!!! 3 !!!this doesnt work, also when MainActivity is declared as a variable object.
}
override fun onFinish() {
goCountdown() //primary goal: restart the timer when its done
}
}.start()
}
Is txtBig kotlin synthetic view or a local variable or global variable?
if txtBig is kotlin synthetic view, you should be able to call it.
if txtBig isn't kotlin synthetic veiw, try this code.
class MainActivity : AppCompatActivity() {
private lateinit var txtBigcopy: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
txtBig = findViewById<TextView>(R.id.txt_big) // Your id in xml
txtBigcopy = txtBig
tapAction.setOnClickListener{
boTimeRunning = !boTimeRunning
if(boTimeRunning ){
goCountdown()
}
}
}
fun goCountdown(){
object : CountDownTimer1(timeSettings_set * 1000, 1000){
override fun onTick(p0: Long) {
txtBigcopy.text = "sometext"
}
override fun onFinish() {
goCountdown()
}
}.start()
}
}
As for the title, I am a beginner of Android. When running github's sceneform-sample, I found that when the model appeared on the screen, a white circle would appear under the model. What methods can I use to remove the white circle at the bottom?
I have uploaded a picture so that you can understand my question more intuitively.
Just don't call node.select(), it should be fine.
I think you may have found an answer to your question in GitHub but for completeness here are some examples in Kotlin, setting the SelectionVisualiser, which allows you determine what is shown or not shown under a selected TransformableNode.
Example 1
Changing the SelectionVisualiser for the entire activity:
class StartViewMainActivity : AppCompatActivity() {
//included in the class variables
private lateinit var defaultSelectionVisualiser:SelectionVisualizer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//As part of OnCreate, set the selection visualiser
arFragment.transformationSystem.selectionVisualizer = CustomSelectionVisualiser()
}
private class CustomSelectionVisualiser(): SelectionVisualizer {
//Class providing a custom TransformableNode SelectionVisualiser
//This particular custom visualiser shows no image
override fun applySelectionVisual(node:BaseTransformableNode){}
override fun removeSelectionVisual(node:BaseTransformableNode){}
}
}
Example 2
Changing the SelectionVisualiser programatically during part of your program flow:
class StartViewMainActivity : AppCompatActivity() {
//included in the class variables
private lateinit var defaultSelectionVisualiser:SelectionVisualizer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//As part of OnCreate, get the default selection visualiser
defaultSelectionVisualiser = arFragment.transformationSystem.selectionVisualizer
}
fun toggelSelectionVisulaisers() {
//Switch between the default and the custom selectionVisualisers
if (arFragment.transformationSystem.selectionVisualizer == defaultSelectionVisualiser) {
arFragment.transformationSystem.selectionVisualizer = CustomSelectionVisualiser) else
} else {
arFragment.transformationSystem.selectionVisualizer = defaultSelectionVisualiser arFragment.transformationSystem.selectionVisualizer = CustomSelectionVisualiser) else
}
private class CustomSelectionVisualiser(): SelectionVisualizer {
//Class providing a custom TransformableNode SelectionVisualiser
//This particular custom visualiser shows no image
override fun applySelectionVisual(node:BaseTransformableNode){}
override fun removeSelectionVisual(node:BaseTransformableNode){}
}
}
Yes, you can,
there is two way one is don't select the model,
like don't call bellow method
node.select()
Second one is aplly transperent selection visualiser
First Create a Transparent Visualiser class like bellow
class BlanckSelectionVisualizer : SelectionVisualizer {
override fun applySelectionVisual(var1: BaseTransformableNode) {}
override fun removeSelectionVisual(var1: BaseTransformableNode) {}
}
Then after applying it on ArFragmnet like bellow
arFragment.transformationSystem.selectionVisualizer = BlanckSelectionVisualizer()
I suggest use second one
How to update the text of Textview in kotlin. Set Text on-create function works but when I tried outside main fun it says unresolved ref.
How can I declare widget for reuse TextView for update the Text Value?
I don't have exp with kotlin. Can somebody help me?
class MediaPickedActivity : AppCompatActivity() {
val fullName = "Test User"
var score = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_media_picked)
val Tv_test = findViewById(R.id.tv_text) as TextView
Tv_test.setText("$fullName :: $score ")
if (score in 0..300) {
score = 5
setText()
}
}
private fun setText() {
// Error is here. I can't set text.
Tv_test.setText("$fullName :: $score ")
}
}
You should declare your Views as class level properties, and then you can access them from anywhere within the class. A variable declared inside a function is only accessible in that function.
class MediaPickedActivity : AppCompatActivity() {
lateinit var Tv_test: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_media_picked)
Tv_test = findViewById(R.id.tv_text) as TextView
}
private fun setText() {
Tv_test.setText("$fullName :: $score ")
}
}
I used lateinit in this example, but see a detailed discussion about different ways to declare the property here.
On top of what #zsmb13 said, you can also use the Kotlin Android Extensions plugin (since you included it as one of the topics) which is super convenient for minimizing potential findViewById() bugs, excluding the use of View fields/member variables, and etc. as follows:
First apply the plugin in your local build.gradle file:
apply plugin: 'kotlin-android-extensions'
Next, import the widget properties for a certain layout in your Activity class:
import kotlinx.android.synthetic.main.<layout>.*
... and then your Activity would be as follows:
import kotlinx.android.synthetic.main.activity_media_picked.*
class MediaPickedActivity : AppCompatActivity() {
val fullName = "Test User"
var score = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_media_picked)
// Simply references via the TextView's ID, and sets its text.
tv_text.setText("$fullName :: $score ")
if (score in 0..300) {
score = 5
setText()
}
}
private fun setText() {
tv_text.setText("$fullName :: $score ")
}
}
you can also using a lazy property which will initializing the property once in needed and separate initialization from your logic, for example:
class MediaPickedActivity : AppCompatActivity() {
val Tv_test by lazy { findViewById(R.id.tv_text) as TextView }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_media_picked)
Tv_test.setText("$fullName :: $score ")
// ...
}
private fun setText() {
Tv_test.setText("$fullName :: $score ")
}
}
OR write an inline function with a reified type parameter T to makes the code more readable.
class MediaPickedActivity : AppCompatActivity() {
val nonNull by find<TextView>(R.id.tv_text)
val nullable by find<TextView?>(R.id.tv_text)
// ...
}
inline fun <reified T : View?> Activity.find(id: Int): Lazy<T> {
return lazy { findViewById(id) as T }
}