How to update textview declared in OnViewCreated from another function - android

I am trying to update the TextView of my fragment in Kotlin. How would I be able to update it while having the TextView declared in the onViewCreated of my fragment?
Inside onViewCreated function:
val txt = view.findViewById<View>(R.id.txt) as TextView
In another function:
fun update(){
txt.text= "Hello"
}

You can't access it if its declared inside the onCreateView()
instead, try this in class level.
class MyFragment : Fragment(){
lateinit var txt: TextView
}
then assign your textView to the variable.
Although I have 2 suggestions for you:
Do not instantiate views inside onCreateView() try using onViewCreated() to instantiate.
use kotlin extensions or databinding to access views it saves a whole lot of trouble and actually easier to use.

I fixed this problem by using Manzur Alahi's suggestion. Instead of using findViewById to get the TextView, I added the plugin that directly accesses the TextView without using findViewById. You can learn more about it here: https://antonioleiva.com/kotlin-android-extensions/
Updated code (No need to declare anything in OnViewCreated) :
fun update(){
Textview.txt="It works"
}

Related

Kotlin Android - Is there a way to define the Views only one time in a class?

In my code I make use of the following Views in XML:
val googleButton: Button = findViewById<View>(R.id.google_login) as Button
val loginWithEmailText: TextView = findViewById(R.id.login_with_email_text)
val emailLoginButton: Button = findViewById(R.id.email_login_button)
val createAccountButton: Button = findViewById(R.id.email_create_account_button)
This code is extracted from a function inside my Kotlin class. Whenever I have to access these views, I need to write this code all over again.
Is there any way that I can access them from only one place in my class code? I tried putting them outside but the app won't start.
Thank you
You need to define these fields as a part of your class and initialize them once you set the layout resource for your Activity/Fragment. If you put these lines 1:1 in the class body, the initialization will fail, since the layout has not been inflated yet.
Please get familiar with the concept of lifecycle, so that you can understand how to approach View related topics: https://developer.android.com/guide/components/activities/activity-lifecycle
Please check out this snippet for a sample code:
class MyActivity: Activity() {
lateinit var textView: TextView
lateinit var button: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
// initialize your views here
textView = findViewById(R.id.text_view_id)
button = findViewById(R.id.button_id)
}
fun someOtherFunction(){
// you can reference your views here like normal properties
button.setOnClickListener { v -> callAnotherFunction() }
// ...
}
}
Since you are on Android, you might be interested in using Kotlin synthetic properties for referencing views without the whole boilerplate of finding them: https://antonioleiva.com/kotlin-android-extensions/. It's no longer a recommended practice to make use of it, but it's handy in some cases anyway.

How to create variable in Kotlin without initialization?

This is simple question. In Java you can create String variable or couple of variables without adding any value to it. This is used at start of the class before onCreate() is called in Activity. I've used lateinit property in Kotlin to achieve that, but now I have a problem with changing visibility of RecyclerView. It will throw exception "lateinit property recyclerView has not been initialized".
Is there any way how to know if property is initialized? This is called at start of the parent activity in Fragment (hide recyclerView and show ProgressBar till data are binded to recyclerView).
In Java you can create String variable or couple of variables without adding any value to it
Actually in that case it is implicitly declared null. Kotlin does not do that, because of its nullability mechanism. You must explicitly declare a variable nullable to allow null:
var str: String // does not work
var str: String? // does not work
var str: String? = null // works
Also see this answer.
Your other option indeed is to mark it lateinit:
lateinit var str: String // works
If you need to make a check to see if it is initialized before using it, you use
if (::str.isInitialized)
But really you should avoid this check and just make sure it is initialized before using it.
If you need to get your UI element in Kotlin, you do not need to create variable and initialise it by using findViewById anymore (though you can). Use kotlin view binding, which works pretty well.
https://kotlinlang.org/docs/tutorials/android-plugin.html#view-binding

How to cast view elements in kotlin? [duplicate]

I'm trying to build android application using Kotlin for the first time.
I want to declare on some buttons outside the OnCreate method and i can initialize them only Inside this function with findViewById.
Can i declare in simple and clean code like in java?
private Button btnProceed;
Because when converting it to Kotlin it look like:
private var btnProceed: Button? = null
And then when initialize OnClick function need to add ! sign:
btnProceed!!.setOnClickListener
What is the right and cleanest way?
This is a good use case for lateinit. Marking a property lateinit allows you to make it non nullable, but not assign it a value at the time that your Activity's constructor is called. It's there precisely for classes like Activities, when initialization happens in a separate initializer method, later than the constructor being run (in this case, onCreate).
private lateinit var btnProceed: Button
If the property is read before a real value is assigned to it, it will throw an exception at runtime - by using lateinit, you're taking the responsibility for initializing it before you access it for the first time.
Otherwise, if you want the compiler to guarantee safe access for you, you can make the Button nullable as the converter does by default. Instead of the unsafe !! operator though, which the converter often uses, you should use the safe call operator where you access the property:
btnProceed?.setOnClickListener { ... }
This will make a regular call if btnProceed is a non-null value, and do nothing otherwise.
On a final note, you can check out Kotlin Android Extensions, which eliminates the need to create properties for your Views altogether, if it works for your project.
Last edit (for now): you should also look at using lazy as described in the other answers. Being lazy is cool.
Instead of using lateinit, you can also do lazy initialization:
private val button by lazy {
findViewById(R.id.button) as Button
}
The first time you access the button property, it will execute the block once and use the result for future calls. In onCreate for example, you can now directly access it:
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(bundle)
setContentView(R.layout.my_view)
button.setOnClickListener { ... }
}
You can do it with lateinit as #zsmb13 suggest BUT this has the disadvantage that your views will be variable instead of final. If you want them to be final you can use the lazy property delegation
By using lazy you can declare how the value will be initialized when you first try to access it so by declaring
private val btnProceed: Button by lazy {
findViewById(R.id.yourID)
}
Whenever you access your btnProceed you will have your activity (this example assume you're using an activity) loaded so you can use that method

Accessing to a Textview from a class (not an activity)

I have a main activity that extends AppCompatActivity, this activity can easily access to the TextView, than I have another java class that I need to access to the same TextView... I couldn't find it out!
Check the code below:-
In your Activity :-
TextView txtview = (TextView)findviewbyId(R.id.tv);
MyJavaClass jav = new MyJavaClass();
jav.setTextView(txtview);
Now in your Java Class :-
class MyJavaClass {
TextView tv ;
public void setTextView(TextView view){
this.tv = view;
}
}
I think that it would be better to keep Android framework objects (especially those that extend Context (Views, Activities, Fragments, etc.) isolated from the rest of your code whenever possible.
Instead of exposing the TextView to your "plain" java object, it would be better to define an interface, devoid of Android Context, and pass that to your 'plain' java object.
Another effective strategy is to use a message bus (or RxJava subject acting as a bus), to communicate between the Activity containing the TextView and the plain java object. e.g. Let the plain java object tell the Activity to change the TextView's content via a message.
At first , why do you want to access a TextView from another class directly?
Is that class an Activity , Fragment or just a java class?
Although,
You can create a static TextView and you can access it in another class.
But be careful of activity lifecycle, if activity stopped so TextView's reference will be null.
Finally don't use static.
According to OOAD it's better to Don't pass TextView to another class.
Why my answer got -1 ?

Why to use casting to instantiate views in android

Does anyone know the reason to cast when instantiating a view?
e.g.: TextView textView = (TextView) findViewById(R.id.textView);
That's because all views in android are subclasses of View class and findViewById does not know anything about the subclass, for instance you might have your own custom view. There is no way android would want to know about your view class, hence it just returns the superclass and you would have to manually typecast it to more specific implementation.
The reason for casting is because return type of findViewById() function in Android is View.
However, contrary to what #Bajji stated in his (currently accepted) answer, a function does have a mean to know about the type of View you are expecting to get and perform automatic casting to that class. Unfortunately, Android does not leverage this functionality...
The following function does that:
protected <T extends View> T findViewById(#IdRes int id) {
return (T) getRootView().findViewById(id);
}
If you would have this function defined, then you could do it this way:
TextView textView = findViewById(R.id.textView);
You can get a bit more info in this blog post.

Categories

Resources