My Problem:
I have a MainActivity along with its XML file activity_main.xml , which contains a NavigationDrawer view and that view calls a header.xml file to be displayed in my drawer.
The issue is that my header file contains a button so that when I click the header it opens a site. However since upgrading my Android studio build tools to 23.0.2 the onClick() for tht button inside my MainActivity crashes the app, giving me a NullPointerException.
I figure that its crashing because the header file is a totally different file from my activity_main.xml so my MainActivity would not have direct access to views inside of the header file.
How can I get access to the button that is inside the header.xml file with and also make it clickable?
Solved:
The NullPointerException was due to the fact that the layout(Header.xml) containing my Button was not readily available for the MainActivity's xml layout, therefore findViewById would always be null because to it, the button doesn't exist. This question was solved by including the header.xml with all its contents(specifically the button) into the NavigationView of the MainActivity.xml
Like so:
<android.support.v4.widget.DrawerLayout ....>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/toolbar"
layout="#layout/tool_bar" />
....
</RelativeLayout>
<android.support.design.widget.NavigationView
...
<include
layout="#layout/header" />
</android.support.v4.widget.DrawerLayout>
You can set the HeaderView of your NavigationView in your activity onCreate() method and set a listener to the header view like :
yourNavigationView.addHeaderView(yourHeaderView);
yourHeaderView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Handle the click event
}
});
Related
I'm seeing this strange behavior and couldn't find anything similar to this.
So I have a parent Activity and inside is a Fragment, which I'm including in parent via include element and then in parent's onCreate, create Fragment and replace it with this include layout (Tell me if this is a right way? I was using FrameLayout but then switched to include and defined an id to it).
Activity
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.sourcey.materiallogindemo.CustomerDetailActivity"
tools:ignore="MergeRootFrame">
<com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.appbar.MaterialToolbar />
</com.google.android.material.appbar.AppBarLayout>
<include
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/app_bar"
app:layout_constraintBottom_toTopOf="#id/layout_bottom_bar"
layout="#layout/fragment_customer_detail" />
<androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.bottomappbar.BottomAppBar>
</com.google.android.material.bottomappbar.BottomAppBar>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Fragment
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="com.sourcey.materiallogindemo.CustomerDetailFragment"
android:layout_height="match_parent"
android:layout_width="match_parent">
<!-- THIS IS THE CULPRIT -->
<com.google.android.material.button.MaterialButton
android:id="#+id/btn_update_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.material.button.MaterialButton />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/sku_list"
app:layoutManager="LinearLayoutManager"
tools:context="com.sourcey.materiallogindemo.CustomerDetailFragment"
tools:listitem="#layout/fragment_s_k_u_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
Fragment is inflated correctly but when I do this inside onCreateView
rootView.btn_update_position.setOnClickListener {
// ... log something
}
and press the Button, it doesn't do anything? Even though most findings were led to this suggestion that I should inflate the view and then set onClickListener.
I also tried doing these
rootView.findViewById<MaterialButton>(R.id.btn_update_position).setOnClickListener {
// ... log something
}
and
val button = rootView.findViewById<MaterialButton>(R.id.btn_update_position)
button.setOnClickListener {
// ... log something
}
but none of them works.
I also tried above approaches in onViewCreated to see if maybe I was not getting the reference but no errors were thrown and no reaction was coming.
Only thing that works is this
activity?.findViewById<MaterialButton>(R.id.btn_update_position)
?.setOnClickListener {
// ... log something
}
I'm trying to understand why this happens? Could this be the issue of using include the Fragment?
NOTE I'm not a pro in android just do hobby work in it so don't know very deeply about it.
EDIT As you can see I have a RecyclerView in Fragment layout, I'm inflating the layout and then setting its adapter items which seems to work fine opposed to button.
rootView.sku_list.adapter = Adapter()
I'm bit confused about what you want to do here
First,<include> doesn't create new view, it just include the xml into the parent xml file so basically it still on activity and you need activity to findViewById
Second, about your question what different between FrameLayout and <include>.
With <include> like i said above, it just add xml file to the parent file, the main usage is for re-use layout (you can include it anywhere) .
With FrameLayout, from official doc : "FrameLayout is designed to block out an area on the screen to display a single item". E.g : you want your layout have a header and footer for all screen, only the middle part change so place a frame layout at middle then load different view for each screen, because that flexibility frame layout usually use for display fragment (you can google how to use frame layout for more details)
I have one layout which includes other layout inside. I tried to set onClikeListener on ImageView inside the included layout, but it's not working. But when I set background drawable it works. I don't know why. Here is my code:
//custom header
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sticky_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/iv_header_close"
android:layout_width="60dp"
android:layout_height="55dp"
android:background="#drawable/big_cross_icon" />
</LinearLayout>
//activity_detail
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f1f2f6">
<include
android:id="#+id/header"
layout="#layout/custom_header"/>
</RelativeLayout>
//in Activity
View header = findViewById(R.id.header);
iv_header_close = (ImageView)header.findViewById(R.id.iv_header_close);
iv_header_close.setBackgroundDrawable(getResources().getDrawable(R.drawable.big_edit_icon));
iv_header_close.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish(); //not working
}
});
I wonder why I can access to child view inside included layout but can't set OnClickListner. Thank you so much :)
Create your iv_header_close like this:
ImageView iv_header_close = (ImageView) findViewById(R.id.iv_header_close);
Instead of calling finish() Try to call YourActivity.this.finish()
What's happening is that, inside the onClick of your imageview, you don't have access to your activity so you have to access your activity using YourActivity.this if you want to access the methods of your activity.
In case anybody is having the same issue five years later...
Using the onClick attribute on the view (ImageView, Button... mine was a button) inside the included layout file fixes the problem. However, note that the onClick attribute is now deprecated
Template that provide Android Studio
This is how I try to do. What is the R.id.x?. I tried using layout id in xml ActivityMain but not the fragment is displayed or overlaid with out activity information.
After selecting one of the items, the fragment will be displayed above the activity. The activity has several elements belonging to material design as the FAB or the Toolbar, Could this create problems with what I do?
In activity_main.xml try replacing:
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
With this:
<FrameLayout android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
And then use R.id.fragment_container as your R.id.x.
This will however replace the view with the FAB and coordinator layout. But you will probably want to add them to the fragments where you want them.
If not you could try replacing the xml found below in app_bar_main.xml, or wrap it in the FrameLayout.
<include layout="#layout/content_main"/>
Update:
Replacing in activity_main.xml will cause some issues as that removes the toolbar.
If you replace the include in app_bar_main.xml your fragments will most likely get overlapped by the toolbar at the top. To prevent this you could try adding app:layout_behavior="#string/appbar_scrolling_view_behavior" in the FrameLayout snippet above.
R.id.x is the id of the View that your fragment going to be replaced. So in your layout R.layout.xyz you have created a layout
<FrameLayout android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
and add/replace the fragment to that id. (R.id.fragment_container) such that your new fragment will be placed in that layout.
transaction.add(R.id.fragment_container, firstFragment)
At the end solved the problem using some of the recommendations made in the answers they provided me.
Change content_main.xml content. Using a FrameLayout with its correspodiente id.
Add fragment on onNavigationItemSelected(MenuItem item)
Result
When I press a menu item ...
...the fragment is displayed
Is there any possible way to share layout(part) between activities? For example, in my app, all activities have similar layout, the top part is long operation indicator (a progress bar, hidden when no operation is being executed), the bottom part is for showing errors. Only the middle part is different for all activities. See the picture below.
so my question is, is it possible to reuse the common layout(loading and error part) for all activities in my app? (currently I don't want to use fragment to do it for some reasons)
maybe the layout resources should like this:
layoutfolder
activity_common.xml
activity_one_content.xml
activity_two_content.xml
thanks
You can create an abstract 'base' activity that all your activities extend from, overriding setContentView to merge the base, and sub activity layouts.
This way you can handle all the loading/error code in the base activity, and simply toggle between hiding and showing the views in the sub activities.
The abstract activity:
public abstract class BaseActivity extends Activity {
protected RelativeLayout fullLayout;
protected FrameLayout subActivityContent;
#Override
public void setContentView(int layoutResID) {
fullLayout = (RelativeLayout) getLayoutInflater().inflate(R.layout.activity_base, null); // The base layout
subActivityContent = (FrameLayout) fullLayout.findViewById(R.id.content_frame); // The frame layout where the activity content is placed.
getLayoutInflater().inflate(layoutResID, subActivityContent, true); // Places the activity layout inside the activity content frame.
super.setContentView(fullLayout); // Sets the content view as the merged layouts.
}
}
the layout file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The main content view -->
<FrameLayout
android:id="#+id/loading_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- The main content view -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/error_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
You could use include in XML to, well.. include the re-useable part of your layout code.
As an example, here's my layout file for the Toolbar I used in my app:
// /res/layout/component_toolbar.xml
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:taggr="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/primary"
android:minHeight="?attr/actionBarSize"
taggr:popupTheme="#style/ThemeOverlay.AppCompat.Light"
taggr:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" />
Now, say if I want to use that Toolbar again in a different Activity, this is all I'd have to write:
// /res/layout/whatever_layout_this_might_be.xml
<include layout="#layout/component_toolbar" />
Bear in mind that this would only copy the layout - not the actual behavior of said widget/component.
If you want to actually copy all of the aspects (layout, behaviour) I'm afraid Fragment is the only way to go.
Although ActivityGroup is deprecated fro API 13 but if you don't wish to go with fragments then this can be your best choice.
According to documentation, an ActivityGroup is:
A screen that contains and runs multiple embedded activities.
You can find a tutorial here and here Although the mentioned tutorial uses a Tablayout you can replace that with your common layout in XML.
A second Approach could be Reuse the layout with include tag, in this approach you could just reuse your once created common layout everywhere in the app.
Basically I have a drawer with a button in it. When I click on the button I want to hide a view (another button) that is loaded in the FrameLayout.
Below I include the XML code for the drawer. The button is not included there because it is generated dynamically within the code.
I tried getting the frameLayout and invalidating the view, but it does not seem to work. I think it is somehow related to the fact that it is loaded in a Fragment.
Any clue is more than welcome! Thanks!
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myDrawer"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/home_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<include
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="#layout/home_layout" />
</FrameLayout>
</android.support.v4.widget.DrawerLayout>
Alright, got it working. I thought there was some hidden magic related to the fact of using FrameLayout, but not. The method is completely the same. Find the solution below:
View view;
if ((view = findViewById(R.id.bottomSet)) != null) {
view.setVisibility(View.VISIBLE);
view.invalidate();
}
Note that "bottomSet" is defined within the FrameLayout that is being imported.