Is there a way that I can replace all findviewbyid formulas by Butterknife #Inject(id) annotation automatically or I have to do it manually? I use Android Studio IDE
I have written some regular expressions to handle the search and replace task. It may not be worth it for you though, depending on how many statements you have.
I used SublimeText to run the queries, it supports newline which I need. IntelliJ didn't match newlines in the query I tried. I think sed could also be used (check sed manual for 'advanced' switch or something).
Search Pattern for Strings:
private (String) (\w+);((?:.|\n)*)\1 = \w*.getString\(R.string.(\w*)\);
Replace Pattern for Strings:
#BindString(R.string.$4)\n $1 $2;$3
Search Pattern for View ids
(?:private |public |protected |)(\w*) (\w+)(?:\s*=\s*null|);((?:.|\n)*)\s\2 = .*findViewById\(R.id.(\w*)\);
In the above I added the search for fields declared with other modifiers.
Replace Pattern for View ids
#Bind(R.id.$4)\n $1 $2;$3
You may wish to replace spaces with \s*, but it makes expressions more difficult to read, so I didn't do it in every location.
Example of what it does
private View map_center;
someMethod() {
...
map_center = activityOrView.findViewById(R.id.centerview);
...
}
becomes
#Bind(R.id.centerview)
View map_center;
someMethod() {
...
...
}
The regular expression uses the back reference \1 to locate the assignment of the field. If the field is not there but is a local variable, it'll wrap the local variable with #Bind and you'll manually have to make it a field. It won't match local variables with declaration in same line as assignment.
According to this tutorial I should say yes!:
The Butter Knife library, developer and maintained by Jake Wharton
(Square Inc.), has annotations that help developers to instantiate the
views from our activity or fragment. It also has annotations to handle
events like onClick(), onLongClick(), etc.
In every activity or fragment, you have to remove, or comment out,
every call of the findViewById() method and add the #InjectView
annotation before the declaration of the variable, indicating the
identifier of the view.
#InjectView(R.id.sample_textview)
TextView sample_textview;
Related
Whenever we want to inflate a view or get a resource we have to cast it in run-time. views, for example, are used like so:
In the past, we would have needed to cast it locally
(RelativeLayout) findViewById(R.id.my_relative_layout_view)
Now, we use generics
findViewById<RelativeLayout>(R.id.my_relative_layout_view)
my question is why doesn't the compiler(or whoever generates the R class) doesn't also keep some kind of a reference to the type of the element(doesn't matter if it's a string or an int or any other type) that way casting problems should not occur
We cannot really speculate on that, that would be a design choice.
It might be that they wanted to avoid bloating the APK. Every ID would need a full package name to the class. So would each ID in android.R too. Since R is packaged in every APK.
Solutions
However, if you are using Kotlin, you can even do away with the generics check. Kotlin will determine it automatically.
val view = findViewById(R.id.my_relative_layout_view)
view.method()
Or event simpler, if you use synthetics:
my_relative_layout_view.method()
Also, if you are using data bindings, you can just access it like this:
binding.my_relative_layout_view.method()
I've been wanting to make the code cleaner, but I do not give it away. I mean...
To name the ids of the views in the XML I use Hungarian notation like this:
<WHAT> <WHERE> <DESCRIPTION> <SIZE>
For example: tvExampleSectionEmptyBig,tvExampleSectionEmptySmall
Previously, using Butter Knife, I did not get too much coding because to do the bindings, I did things like this:
#BindView (R.id.tvExampleSectionEmptyBig) TextView tvEmptyBig;
#BindView (R.id.tvExampleSectionEmptySmall) TextView tvEmptySmall;
The code was much clearer and more reusable since the Hungarian notation used to avoid the confrontation between ids with the same name in different activities, fragments, etc. it was not present in practice more than in XML.
What's going on?
Kotlin has synthetic, which makes your life easier since with putting the id of the view, the binding is done directly, but with such long ids the code is very dirty ... Besides, makes sense that all the views I use in an activity called ExampleSectionActivity, contain within its variable nameExampleSection?
What would I like?
Surely there are better solutions that, but initially, what I feel is to implement a way to rename variables by removing a given String. As I follow a convention in all the names of the ids, it would be something internally in this way:
val tvEmptyBig = tvExampleSectionEmptyBig
val tvEmptySmall = tvExampleSectionEmptySmall
But of course, I would like to do it in an automated way.
On the other hand, I already tried naming the ids without the and to be careful with the imports, but for the moment synthetic fails very occasionally in this respect and I had to rebuild constantly. Especially if I open another instance of Android Studio, which I usually do quite often for consulting other projects I have.
Any idea? :-)
In my opinion, the easies and the most clean thing you can do is this:
private val myTextView: TextView
get() = f_layoyt_text_view
This way you don't have to use ridiculous, at least in 2018, ButterKnife and even more inconvenient findViewById.
For a few weeks, I already take for granted, that with the latest stable updates of Android Studio, there is no problem with repeating names of ids in different activities or fragments. Therefore, it is no longer necessary to put long variable names. Only there is to pay a little bit of attention to the imports, everything works like a charm, more readable and reusable. :-)
I used synthetic property in my code.But wondering for how and when it actually initialise each view in android.
We simply provide import and access each view by its id. When it allocate memory for view object?
This is easy enough to investigate by decompiling a Kotlin file where you use Kotlin Android Extensions. (You can do this by going to Tools -> Kotlin -> Show Kotlin Bytecode and then choosing Decompile in the pane that appears.) In short, it's nothing magical, it just uses findViewById and then casts the View to the concrete type for you.
If you use it inside an Activity or a Fragment, these get cached in a Map so that the lookup only occurs once. After that, you're only paying the costs of fetching a map entry by the ID as the key.
You can also use it on a ViewGroup to find a child with a given ID in it, in these cases, there's no caching, these calls are replaced by simple findViewById calls that will happen every time that line is reached. This second syntax looks something like this:
val view = inflater.inflate(...)
view.btnLogin.text = "Login"
And it will translate to something similar to this in the bytecode:
View view = inflater.inflate(...);
Button btnLogin = (Button) view.findViewById(R.id.btnLogin);
btnLogin.setText("Login");
Note that the actual View instances are still created when your layout is inflated. Kotlin Android Extensions is only syntactic sugar over findViewById calls.
Is there a way, perhaps using RxBinding, to bind an Observable<String> to a TextView object such that its .text property is kept up to date with the Observable? Obviously, you could subscribe() and manually update the text field, but a convenience method seems likely. I just can't find it, and the documentation hasn't yielded any answers for me.
A similar convenience method exists in RxSwift (or rather RxCocoa), in case that clarifies what I am asking for; it's called .bindTo() there.
Yes methods like this is presented in rx-binding library.
For example for TextView RxTextView.text(textView) creates action which can be used as subscriber.
See source code
Usage would be something like this
observable.subscribe(RxTextView.text(textView), Throwable::printStackTrace);
Be careful with memory and read docs:
Warning: The created observable keeps a strong reference to view. Unsubscribe to free this reference.
It is not the same as bindTo magic but doing what you need.
why Google calls variables with the prefix "m" for example:
private int mSectionResourceId;
private int mTextResourceId;
I see it in all examples. But i not understand why they do it?
And now i have some example where it practic very good. If a called variabels without prefix i need write
public SimpleSectionedRecyclerViewAdapter(Context context, int sectionResourceId, int textResourceId,
RecyclerView.Adapter baseAdapter) {
this.sectionResourceId = sectionResourceId;
this.textResourceId = textResourceId;
but if i use prefix i can write
public SimpleSectionedRecyclerViewAdapter(Context context, int sectionResourceId, int textResourceId,
RecyclerView.Adapter baseAdapter) {
mSectionResourceId = sectionResourceId;
mTextResourceId = textResourceId;
I think it more readable. Who can explain to me the pros and cons of a prefix?
The variables starting with m are telling you they are variables in the scope of your class. Member of the class.
Link to Android Code Style Guide
The m just stands for 'Member'. It is simply declared that your Variable is a Class-Member.
It is more readable Code, because you know where Class Members got declared, so you can find it pretty fast. You don't need to write this, even if you don't prefix your Variables with an m.
In your Example, this only makes it more readable when there is no prefix-m. Another developer knows that it is a instance variable (member variable) and so declared on top or bottom of the class.
It is a prefix for class member variables. It's just a naming convention.
Mostly sure, taken from Hungarian Notation where similar prefix: m_ stands for exactly the same).
Referring to pros & cons:
Pros:
it allows to type fewer chars during programming,
programmers that are used to use Hungarian Notation may found it easier to follow the code.
Cons:
as the code changes very often, it is easy to forget about changing prefixes every time, when variable changes it's purpose (especially during prototyping),
it makes the code starts to smell bad,
Generally, it is some kind of reinventing the wheel. Java has this keyword that should be more than enough for accessing proper variable. If it's not, the code requires refactoring, maybe because of naming glitches or using too wide variable scopes.
Personally, I do not recommend to use Hungarian Notation (even the part of Android Code Style). We have great IDEs that increases the readability of the code.
There is an exception. The code, where Hungarian Notation (or more general, specific code style) was already been used. It is a matter of consistency.
The m is just a member variable. A class member if you will. Useable with constructors like WebView M WebView then later on you would use something like mWebView.loadurl("example.com"); it's just a placeholder for the variable you created. You don't have to add the member class variable as an m but it's more organized if you do