findViewById() from custom view return null - android

It seem similar to other questions but I doesn't found solutions. I have a layout defined by xml file:
main_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
... >
<package.CustomView
... >
</package.CustomView>
<TextView
android:id="#+id/text_view"
... >
</TextView>
</RelativeLayout>
where CustomView extends SurfaceView.
In onCreate I have setContentView(R.layout.main_layout). Now, If I want to inflate, for example, the TextView in the activity I have to add after setContentView(...)
TextView textView = (TextView) findViewbyId(R.id.text_view);
and all works fine. But if I put this line in a method of my CustomView I got a null pointer. Why?

findViewById() will traverse only the given view and its children. It will not find sibling views. Your textview is a sibling to the custom view, not a child view.
In an activity, the root view where findViewById() traversal starts is the activity window (Activity.findViewById()). In a view, it is the view itself (View.findViewById()).

Understanding helpful explanation above I would like to give an easy way to find a view by its ID from CustomViews:
(Activity)getContext()).findViewById(R.id.youViewToBeFound);
inside your CustomView class.
This code must be run AFTER constructor code e.g. inside onAttachedToWindow()

Related

What do you mean by root view of an activity?

I have a very trivial confusion regarding the what is the root view of an activity. I searched this throughout google and stack overflow. I got some knowledge about it but didn't come to a convincing solution as no one clearly said about that term. The question which seems to be alike my question tell about what a root view of a given layout is but to me they don't seem to specify the root view of an activity is.Though the answer to my question is quite intuitive, I want a definite answer with no confusion.So here is the code structure.
I have the following activity class :
package com.example.android.viewpager;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
And the XML code associated with it when I created the activity is (activity_main.xml) :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.android.viewpager.MainActivity">
</LinearLayout>
So according to me is this activity_main.xml the root view of the MainActivity?
The Root View of Your MainActivity is LinearLayout
The Root View of Your Activity means the Top most parent layout of Your XML Layout
The Root View may be a RelativeLayout, CoordinatorLayout, ConstraintLayout, LinearLayout,FrameLayout
For Example
Your activity_main.xml Contain LinearLayout as parent layout than LinearLayout is your Root View which hold all the child controlls in it like Buttons, Imageview, EditText, TextView, etc....
The activity_main.xml is the layout of your MainActivity. Inside the layout the root view is the top hierarchical view which in this case is a LinearLayout. It could also be a RelativeLayout, CoordinatorLayout, ConstraintLayout etc.
The first view that is capable of holding child view will be called your root view. In your case, Linear Layout will be your root layout
activity_main is not the root view. This is the XML file where the layout of your activity is defined.
The root view is the view in this layout, containing all the other views.
In your example, the root view is the LinearLayout
When you do setContentView(R.layout.activity_main); the system will parse the XML file and create all the views and subviews defined into it with the attibute and everything
For example if your XML is defined like that:
<RelativeLayout
...
>
<TextView
android:text="HelloWorld"
... />
</RelativeLayout>
When the view is inflated you'll end with a RelativeLayout object containing a TextView object containing "HelloWorld" in one of its fields.
In this example, the root view is the RelativeLayout

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.

findViewById not working when finding layout?

I'm trying to make the whole screen clickable so I'm trying to set an id for a layout and find it in java class. When I do this, it says incompatiable types (layout vs a view). I understand they are different types but upon googling this, several posts have suggested selecting the layout in an activity in this manner. They can make a call such as this -
RelativeLayout theLayout = (RelativeLayout) this.findViewById(R.id.Layout)
and they don't seem to be getting this error.
Another stackoverflow post with this - onTouchListener for entire screen
My code referencing this is below -
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:id="#+id/thelayout" >
<TextView android:text="This will change with speech" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="#+id/textview"/>
</RelativeLayout>
And in activity -
RelativeLayout theLayout = this.findViewById(R.id.thelayout);
Am I missing somethinng?
Instead of this,
RelativeLayout theLayout = this.findViewById(R.id.thelayout)
Try this in your Activity,
RelativeLayout theLayout = (RelativeLayout) this.findViewById(R.id.thelayout)
Instead of using setContentView(int layoutResID) with the layout id, inflate the layout get the view reference and then use the setContentView(View view).
View layout = View.inflate(context, R.layout.theLayout, null)
setContentView(layout)
You don't need to cast the layout to RelativeLayout, as you can add the click/touch listener to a View class.

NPE when inflating layout

I have to set a layout inside other layout's child (linearlayout). To do this, I write this code on the layout's activity that I want to set into the root layout:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
**setContentView(R.layout.main);**
/**Define root layout's child where I want to set the layout*/
LinearLayout inside_menu_view = (LinearLayout)findViewById(R.id.activitycontent);
/**Inflate this layout and add it to the root layout*/
LayoutInflater inflater = (LayoutInflater) getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View this_layout = inflater.inflate(R.layout.main, null);
inside_menu_view.addView(this_layout);
But I'm getting a NULLPOINTEREXCEPTION in this last line inside_menu_view.addView(this_layout);
UPDATE
-Added the setContentView() after super.onCreate
This line will return null because you have not yet called setContentView()
LinearLayout inside_menu_view = (LinearLayout)findViewById(R.id.activitycontent);
You need to call setContentView(R.layout.layout_with_activitycontent); first
From the Docs
Finds a view that was identified by the id attribute from the XML that was processed in onCreate(Bundle).
Returns The view if found or null otherwise.
You have not yet processed an xml layout file with setContentView() or by using a LayoutInflater so it will return null causing a NPE when you try to call a method on it like addView().
Edit
I'm not sure why you are doing it that way but you can't. As in my link above, you can only use findViewById() to find a View inside the inflated layout. You can't use it to find a View inside some other layout which hasn't been inflated.
Put that layout file in setContentView() if that is the one you want to use.
Different approach
You probably want to use Fragments then to achieve this.
See the docs on how to use these
Or you can use <include> to include the "menu" layout that you want to include in all of the Activities then you can switch Activities when you need to show the inner part of your layout.
See here about re-using layouts
Example of <include>. You have some layout that you want to re-use, say main.xml, then in an Activity in which you want to re-use it you simply do something like
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="30dp"
android:background="#drawable/blue">
<include layout="#layout/main"
android:id="#+id/main_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- other layouts -->
/<RelativeLayout>
yours will obviously be different but this is an example of one I have. Hope this helps.
Let's say your R.layout.main is a LinearLayout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/main_layout" >
</LinearLayout>
Now in onCreate():
LinearLayout layout = (LinearLayout) findViewById(R.id.main_layout);
LayoutInflater inflater = (LayoutInflater) getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View this_layout = inflater.inflate(R.layout.main_layout, layout, true);
And this_layout is automatically added to the activity's layout.

Android: draw on a view inside an inflated xml (another view)

I have a small xml file called 'xmlview.xml' which is something like:
<tablelayout>
<tablerow>
<view id="view1" />
I created a class which extends view, and I inflated that xml file and now i have a view obtained due to inflation.
xmlView = layoutInflater.inflate(R.layout.xmlview, this);
I am wondering is there a way to draw on the view 'view1' inside the view 'xmlview'
I tried something like:
View view1 = (View)xmlview.findById(R.Id.view1);
and then I tried in the overriden onDraw to draw on view1 but nothing worked,
any ideas ??
Seems to me maybe you should try something like this:
<TableLayout>
<TableRow>
<com.yourdomain.yourproject.ViewClassName>
Then you can create a class ViewClassName that extends View and, on that class, override the onDraw method. This is explained in detail here: http://developer.android.com/guide/topics/ui/custom-components.html. You can go directly the topic Modifying an Existing View Type
See, you don't need to inflate the view if the only reason to do this is to try and manipulate the view's drawing. And if it is the only reason, you really shouldn't.
hope I helped
I'm not sure what exactly you mean by "draw on the view", but it sounds like you should be looking into the Canvas. You should not arbitrarily override the onDraw method, this is what causes your widgets to appear on the screen.

Categories

Resources