Alternate and fast replacement of findViewById in android - android

How I can avoid using findViewById in my app. Layout is very complicated and findViewById traverses its tree to find view which takes time and it is used several times.

First, you must edit your application’s build.gradle file and add the following into the android block:
android {
…
dataBinding.enabled = true
}
The next thing is to change the layout file by making the outer tag instead of whatever ViewGroup you use:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context=".MainActivity">
<TextView
android:id="#+id/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
</layout>
The layout tag tells Android Studio that this layout should take the extra processing during compilation time to find all the interesting Views and note them for the next step. All layouts without that outer layout tag will not get the extra processing step, so you can sprinkle these into new projects wherever you like without changing anything in the rest of your application.
The next thing you have to do is to tell it to load your layout file differently at runtime. Because this works all the way back to the Eclair release, there is no reliance on new framework changes to load these preprocessed layout files. Therefore, you do have to make a slight change to your loading procedure.
From an Activity, instead of:
setContentView(R.layout.hello_world);
TextView hello = (TextView) findViewById(R.id.hello);
hello.setText("Hello World"); // for example, but you'd use resources, right?
You load it like this:
HelloWorldBinding binding = DataBindingUtil.setContentView(this, R.layout.hello_world);
binding.hello.setText("Hello World"); // you should use resources!
Here you can see that a class, HelloWorldBinding, was generated for the hello_world.xml layout file, and the View with the ID #+id/hello was assigned to a final field hello that you can use. No casting, no findViewById.
It turns out that this mechanism for accessing views is not only much easier than findViewById, but can also be faster! The binding process makes a single pass on all Views in the layout to assign the views to the fields. When you run findViewById, the view hierarchy is walked each time to find it.
One thing you will see is that it transforms your variable names into camel case (just like hello_world.xml becomes the class HelloWorldBinding), so if you gave it the ID #+id/hello_text, then the field name would be helloText.
When you’re inflating your layouts for RecyclerView, ViewPager, or other things that aren’t setting the Activity contents, you’ll want to use the generated type-safe methods on the generated class. There are several versions that match the LayoutInflater, so use the one that is most appropriate for your use. For example:
HelloWorldBinding binding = HelloWorldBinding.inflate(
getLayoutInflater(), container, attachToContainer);
If you aren’t attaching the inflated View to the containing ViewGroup, you’ll have to get access to the inflated View hierarchy. You can do this from the getRoot() method of the binding:
linearLayout.addView(binding.getRoot());

Another choice is Kotlin's Android extensions.
// Using R.layout.activity_main from the main source set
import kotlinx.android.synthetic.main.activity_main.*
class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Instead of findView(R.id.textView) as TextView
textView.setText("Hello, world!")
}
}
https://kotlinlang.org/docs/tutorials/android-plugin.html

you can directly import views from xml:-
add kotlin extentions in app.gradle
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
see the below eg.

You have a few options, the two main ones being:
Android Data Binding:
https://developer.android.com/topic/libraries/data-binding/index.html
OR
Butterknife:
http://jakewharton.github.io/butterknife/
Personally I am a huge fan of Butterknife and use it everywhere where possible, worth noting, this just makes your code nicer to look at. I find that Androids data binding is far more complicated than Butter Knifes (That's more person opinion though...)
To my knowledge you can either use view binding or findViewByID. The clear thing here is that they do the same thing. Data/View binding will just write the findViewByID at compile time.
View Binding just makes your code easier to read. I'm not aware of any changes to android other than soon you will no longer have to cast your findViewByIDs.

You can use viewbinding to replace findviewbyid. It will help you to avoid null pointer exception and type cast exception. You just need to enable view binding in modules gradle file. Then create instance for binding class. then use that instance to reference views in your code.
You will get much clear information from these links.
View binding video tutorial : https://youtu.be/ILUf3Zf0ocI
View binding documentation : https://developer.android.com/topic/libraries/view-binding

How I can avoid using findViewById in my app
You can use getChildAt as alternative for findViewById for founding a view inside a ViewGroup and its derivative (like RelativeLayout, LinearLayout, etc). It is super fast because it's only try to find the view from the views array by its index (link to the source code):
// Child views of this ViewGroup
private View[] mChildren;
// Number of valid children in the mChildren array, the rest should be null or not
// considered as children
private int mChildrenCount;
...
public View getChildAt(int index) {
if (index < 0 || index >= mChildrenCount) {
return null;
}
return mChildren[index];
}
The following is the sample how using getChildAt. Assume you have the following Layout:
<RelativeLayout>
<TextView/>
<Button/>
<EditText/>
</RelativeLayout>
you can get all the views with the following:
// assume we have inflate the layout
TextView textView = (TextView) getChildAt(0);
Button button = (Button) getChildAt(1);
EditText editText = (EditText) getChildAt(2);
But then it is started to become tedious if you have complex layout like the following:
<RelativeLayout>
<TextView/>
<Button/>
<LinearLayout>
<TextView/>
<Button/>
<EditText/>
</LinearLayout>
<LinearLayout>
<TextView/>
<Button/>
<EditText/>
</LinearLayout>
</RelativeLayout>
The major problem of using getChildAt is you can't easily change your layout position without adapting your code for the change.

Related

How to loop on data while inflating layouts?

Do I have to rely on ListView/Recycler view each time I need to loop over data to repeat a layout ?
I totally understand it for long lists of data where scroll/performance is involved, but let's say I am sure i'll only have 0...3max items and need to display in very simple single-line-layout for each (1 image, 1 textview + button).. isn't there a simplier pattern than using adapters ?
Seems like overkill (and a pain to deal with for every little part of my screen where I need to loop overs small lists).
What are the other options while using Components architecture (databinding) ?
Manually inflating my layout ? In viewmodel ? fragment? Do I need to create another viewModel specially for this child layout ?
Thanks
I recently have a similar issue recently, but my problem was that of nested lists i.e. I needed to inflate another list inside a recycler view. Here is a minimal example of how I went about it.
Add a LinearLayout to your layout XML file:
<LinearLayout
android:id="#+id/smallList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:list="#{viewModel.yourList}" />
Create a binding adapter where you inflate the items. Like so:
#BindingAdapter("app:list")
fun setList(layout: LinearLayout, yourList: List<ListItemModel>) {
layout.removeAllViews() // Remove previous items if your list does change
for (listItem in yourList) {
ListItemBinding.inflate( // inflate your list item
LayoutInflater.from(layout.context),
layout, // pass your LinearLayout as root
true // attachToRoot is true so that the inflated view is added to the LinearLayout
).apply {
// set your binding variables
this.listItem = listItem
}
}
}
Note: This is a minimal example to solve the issue since actual data and functionality is unknown. You may want to:
Add a click listener variable to your list item XML file and set that similarly.
Create a custom view for the view if it is to be reused and write the binding adapter there.

Is it bad proactive to create backing subclasses for your layouts if they don't need customization?

I'm new to Android and have recently started adopting the pattern to create an auto-layout-loading custom view based on a layout file. In the layout you use the 'merge' tag as the root, then in the constructor of your view, you inflate that layout into yourself so you are essentially the root of the merged-in controls. Right after you inflate, still in the constructor, you can find the child controls from the layout and assign them to private fields in the custom class so they will always be available, even when using recycler views.
Contrast that with including a layout. In that case, the layout has to define a root element (which can be your custom control, but in that case, you would not inflate the layout into yourself and would have to move the control lookup to onFinishInflate) but otherwise it ends up with the same view hierarchy.
In short, instead of this...
<include layout="#layout/layout_myCustomControl" />
You can now do this...
<com.mydomain.MyCustomControl />
However I find the auto-loading custom control version to be much more flexible and maintainable. The advantages of the second one are not only that you can use custom attributes in the referencing XML layouts (which you can't with the 'include' version) but it also gives you a central place to manage the code as well as layout management/control lookup.
So now I can do this...
<com.mydomain.MyCustomControl
app:myCustomAttribute="Foo" />
And if the control has to have code backing up its behavior, it's nicely encapsulated in the custom view.
Here's an example layout for the auto-loading version...
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is static text in the header" />
<TextView android:id="#+id/dateTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</merge>
And here's the class that uses it...
public class MainHeader extends LinearLayout
{
TextView dateTextView;
public MainHeader(Context context, #Nullable AttributeSet attrs)
{
super(context, attrs);
setOrientation(VERTICAL);
LayoutInflater.from(context).inflate(R.layout.header_main, this);
dateTextView = (TextView)findViewById(R.id.dateTextView);
dateTextView.setText(<<Code for setting date goes here>>);
}
}
But what I'm wondering is if the layout is purely static in nature--say holding a static image and fixed text--is it still considered good practice to use a custom view to represent it? I'd say yes for consistency, plus should I want to extend it in the future, you're already prepared for it and have to do so in just one place regardless of how many places it's used. In contrast, if you included the layout directly in say 20 places, you may have to update all 20 (depending on what's actually changed/needed.)
Pros for the Custom View approach:
Central location for managing layout loading and control lookup
Can support backing code implicitly/internally for updating the view.
Can use attributes when being referenced in other layout files
Better encapsulation (you can hide the layout itself from the outside world exposing behaviors via direct methods)
Can simply be 'new'd' up in code and the layout will automatically load. No inflating or casting needed.
Cons for Custom View approach
When used in Layout files, you now have to also specify width and height making usage a little more verbose.
May add extra classes to your project (not always, but sometimes.)
Again, it seems to me like a no-brainer, but I'm wondering if there's a preferred 'Androidy' way, or is it just a preference up to each developer?
Such approach will add additional level in your view trees. So you only made the UI tree more complex for no good reason. From other side, you can follow next steps to get rid of that side-effect:
<merge /> can only be used as the root tag of an XML layout
when inflating a layout starting with a <merge />, you must specify a parent
ViewGroup and you must set attachToRoot to true (see the
documentation of the inflate() method)
That's it.
Well apparently what I came up with wasn't that unique after all! Here's an article explaining in detail this exact scenario, along with the pros and cons of each. Looks like they too came to the same conclusion.
TrickyAndroid: Protip. Inflating layout for your custom view

Newbie questtion : Inflating xml files with and without using the actually layout

This always confused me. Let's say I have these two scenarios :
First scenario :
my_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout ..>
<CardView id="#+id/myCardView" ../> // or any other view
</RelativeLayout>
In code :
.. onCreate() {
View view = inflater.inflate(R.layout.my_layout, container, false);
myCardView = (CardView) view.findViewById(R.id.myCardView);
}
Second scenario :
my_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<CardView ../>
In code :
.. onCreate() {
myCardView = (CardView) inflater.inflate(R.layout.my_layout, container, false);
}
To inflate and get a CardView object, when should one use first scenario and when the second ?
Note that in first scenario, I never do anything with the RelativeLayout object. It's merely used to get the CardView object.
The reason I ask is I see the first scenario in so many tutorials but it is never explained why the extra encapsulating Layout is actually there.
Is there any scenario where the first example above does make more sense than the second ?
Post Edit: Could you please evaluate my question with RecyclerView.ViewHolder pattern in mind ?
Post Edit 2:
I use the first scenario in ViewHolder pattern.
public class EntryViewHolder extends RecyclerView.ViewHolder {
public CardView cv;
public EntryViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.entries_card_view);
}
}
Still, even in this scenario I never make use of the encapsulating RelativeLayout. I only use it to call findViewById(..) to get the CardView object.
The first scenario would be used whenever you have an XML layout with multiple child views inside of them. A couple TextViews, EditTexts, Spinners, etc, the things you would need to put together a presentable page for the user. The <RelativeLayout and the other layout tags help organize the child views inside of them, in this case "Relative" to each other. I would stick to this practice instead of your second scenario.
I'm not sure I have ever seen anything like your second scenario before. Inflating the layout and then casting the entire layout to a CardView seems odd. The closest thing I can relate it to is when you make an XML for a custom adapter view, where you might be making a list of card objects.
Here is a helpful link on Layouts.

building flexible GUI

I have a Layout that I want to populate with items consisting of 2 textviews and one button. I do not know before hand how many items that will populate my Layout.
Since I don't know when writing the layout.xml how many items I want to add, thats means that I have to add the items in the java instead of the xml. But I do not like to build GUI in java because it looks ugly.
Does anyone know if I can create an xml file for my item and then add new items to my layout during execution?
I have written some pseudo code to try to demonstrate what I want to accomplish:
MainLayout.xml
//My empty Layout
<Layout myMainLayout >
</RelativeLayout>
Fragment_post.xml
//one post
<TextView/>
<TextView/>
<Button/>
In the code somewhere
setContentView(R.layout.MainLayout);
MyMainLayout.addFragment(R.layout.Fragment_post);
You can add your fragment_post.xml wherever you want:
LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout view=(LinearLayout)inflater.inflate(R.layout.yourfragment, null);
yourLayout.addView(view);
Please don't confuse a Fragment with a piece of the GUI. See here for details: http://developer.android.com/guide/components/fragments.html
Sure you can do this. Just set an initial empty layout to your activity.
onCreate()
{
setContentView(R.layout.initial_layout);
}
Then get and keep a reference to main layout.
LayoutInflater inflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
RelativeLayout mainLayout=(RelativeLayout)inflater.inflate(R.layout.initial_layout, null);
Next, add new views to your layout as and when you need them.
LinearLayout view=(LinearLayout)inflater.inflate(R.layout.fragment_post, null);
mainLayout.addView(view);
But note that what you refer to as fragments here are not what android refers to as fragments. Learn about actual android fragments here:
http://developer.android.com/guide/components/fragments.html

custom view declared in xml

When declaring custom view in xml, what is the difference between declaring a View of a custom class, or declaring a completely custom view:
<LinearLayout>
<view class="packageName.MyView" android:id="#+id/myView" />
</LinearLayout>
and
<LinearLayout>
<packageName.myView android:id="#+id/myView" />
</LinearLayout>
?
I've created a subclass of EditText, and when instatiating it as View class=".." my Activity crashes with ClassCastException when trying to access MyView:
(MyView) myView = (MyView) findViewById(R.id.myView);
When declared as second option, everything works as expected.
I'm not 100% sure on this, but let me give it a go. A couple of things could be happening. The parser might not understand the class attribute correctly (e.g. it thinks it is part of a stylesheet). I am not sure how the parser handles the class attribute, since I have never seen or used it (in fact, I've never seen the <View> tag used either). A better explanation, though, might be this: the parser is trying to down-cast your View into packageName.myView class and fails (down-casting is always risky; up-casting is always safe).
Regardless of what is happening, I would always use the second option you listed, <packageName.myView android:id...>, instead of using the <View> tag. Reason being, it's redundant to use the <View> tag. Everything in this xml file must be a view (LinearLayout, Button, TextView, etc. are all descendants of the View class).
Hope that helps. If you're really, really curious, you could always download the source code for the parser...

Categories

Resources