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.
Related
I mean the method from the class View that allows to put a tag to an object of that class like this:
view.setTag(1)
Which would be the equivalent in Swift?
You can set tag for view this way.
view.tag = 1
I may be late, but the actual answer given by #Muhammad Afzal is not exactly equivalent to .setTag() in Android.
Swift .tag only allows the use of Int values, while Android .setTag() allows the use of any Object value.
To get an equivalent functionality, one should create a simple custom class like the following:
public class UITagView:UIView{
var Tag:Any?
init() {
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setTag(_ tag:Any){
self.Tag = tag
}
func getTag()->Any{
return self.Tag!
}
}
If view is an instance of the class, then view.setTag() would be the the same in Android and Swift.
However, if the tags that you need are always Integers, it is easier to use view.tag, as #Muhammad Afzal said.
Note: As in Android, when recovering an object with the .getTag() method, it should be parsed to the class it belongs to (view.getTag() as! SomeClass).
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"
}
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 ?
Situation
I have a BaseActivity from which I extend other activities. In the BaseActivity I have a findCastedViewById which basicaly casts the view and then returns it.
I do this because I, personaly, find it ugly casting the view all the time.
Question
I was wondering if there is any problem or cons that I could get from using this approach that anybody else using this method had.
Here is BaseActivity:
BaseActivity.java
public class BaseActivity extends Activity{
//Other stuff
private <E extends View> E findCastedViewById(int id){
return (E) findViewById(id);
}
//Other stuff
}
Consider using ButterKnife, it solves the problem of having to continuously cast your views and it saves you a lot of time
Once you go butterknife, there's no way back
Basically you annotate the View variables with
#FindView annotation and it will find the correct view for you when ButterKnife.bind(this) is called
Here's a small snippet of code where ButterKnife is used from the GitHub Page
class ExampleActivity extends Activity {
#FindView(R.id.user) EditText username;
#FindView(R.id.pass) EditText password;
#OnClick(R.id.submit) void submit() {
// TODO call server...
}
#Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}
You can read more about ButterKnife from here
As lukaspp already noted, in SDK 26+ there is no need for vew casting.
They have implemented the same code as in the question, except it is now the default findViewById(). So yes OP, your code is good!
As explained in this answer, if you compileSdk is at least API 26, you don't need to cast the view anymore.
It's not only ugly but also an expensive operation if you repeat it constantly.
If you are sure you will always need that specific derived class, you can freely use it. However, I sometimes find I only need methods from the base class View, e.g. setVisibility(), in such occasions casting would be a waste.
Could someone explain the best way to achieve the following, I think it's a fairly simple question but I'd like to know the best way, as it can be achieved several different ways.
Let's say I wanted a class which updated a TextView's text to "Test". Of course this isn't what my real problem is, but I'm trying to make this question as simple as possible. Let's also say this will accept any text view.
The class it calls does not inherit Activity.
The ways I see to do this are as follows, please explain why and why not these methods should or shouldn't be used.
Pass the TextView as a reference and update the text.
public class Test
public void updateText(TextView tv)
{
tv.setText("Test");
}
The other option is to pass the Activity, and call findViewById, but the problem here is if the ID does not match that that the Test class expects, the view will return null and won't update the TextView.
public class Test
public void updateText(Activity act)
{
TextView tv = (TextView) act.findViewById(R.id.i_must_exist);
tv.setText("Test");
}
Another choice would be to use getters/setters.
public class Test
private TextView mTvToUpdate;
public void setTextView(TextView tv)
{
mTvToUpdate = tv;
}
public void updateText(Activity act)
{
mTvToUpdate.setText("Test");
}
I guess the real question is it wrong to pass an objects reference as a parameter, is Activity the preferred way? Why? It is more likely to experience memory leaks, are both solutions OK? Is it down to preference?
Please don't reply with "Why would you want to do this?" as in this example I obviously wouldn't want to do what I am asking, but the question behind it still applies.
Thanks in advance.
I wouldn't go with the last way without a good reason because when you hold a reference to an UI element, you may leak memory more easily. When you hold a reference to a Widget, you hold a reference to the Activity either, so you have to make sure that you don't hold the reference to the Widget if the Activity is destroyed (possibly with a WeakReference). This is described in the article Avoiding Memory Leaks.
Besides that there's no real difference.
The object which "owns" the textfield should be the one who actually calls the setText method. So in most cases, that would be the activity. It seems like in this case, your Test class should only provide a method which will return the text you want to display as a string.
So something like
public class MyActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(...);
Test t = new Test(...);
String testText = t.getTestString();
TextView tv = (TextView)findViewById(R.id.TestTextView);
tv.setText(testText)
}
}
public class Test
{
public String getTestString()
{
return "Test"; // Probably would be more dynamic in your case.
}
}
This will abstract the data knowledge out to the Test class. All your activity needs to know is that someone else is providing a data string, and it can set the text itself.