I am using a base activity as a parent of another activity "RecicpeActivity" as I have overridden the method setContentView in the base activity so that the child activity can use it and pass its layout to be inflated in the frame layout.
The Child activity "RecicpeActivity" uses DataBinding to set its views.
What I am doing is I am trying to inflate the layout of the child activity into a frame layout "container" in the base activity BUT the data binding is not being considered at all as I am having a white screen even though I have seen the layout of the child activity as a child of the frame layout while debugging.
I have tried two ways:
1- The first one I have tried to pass the layout of the child activity simply by calling setContentView and inflated the passed layout to the frame layout in the base activity.
2- The second on I have tried to use data binding in the base activity, But I don't think it would matter.
_ChildActivity
public class RecipeActivity extends BaseActivity {
private ActivityRecipeBinding mBinding;
private static final String RECIPE_INTENT_KEY = "recipe key";
private ScrollView mScrollView;
private RecipeViewModel recipeViewModel;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recipeViewModel =
ViewModelProviders.of(this).get(RecipeViewModel.class);
mBinding = DataBindingUtil.inflate(
getLayoutInflater(),
R.layout.activity_recipe, null, false);
setContentView(R.layout.activity_recipe);
// This method is implemented in the BaseActivity.
showProgressBar(true);
recipeViewModel.getRecipe().observe(this, new Observer<Recipe>() {
#Override
public void onChanged(Recipe recipe) {
if (recipe != null){
if (recipe.getRecipe_id().equals(
recipeViewModel.getRecipeId())){
mBinding.setRecipe(recipe);
mScrollView.setVisibility(View.VISIBLE);
showProgressBar(false);
}
}
}
});
recipeViewModel.getRecipeById(getIncomingIntentRecipeId());
}
private String getIncomingIntentRecipeId(){
if (getIntent().hasExtra(RECIPE_INTENT_KEY)){
String recipe_id = getIntent().getStringExtra(RECIPE_INTENT_KEY);
return recipe_id;
}
return null;
}
_BaseActivity
public abstract class BaseActivity extends AppCompatActivity {
public ProgressBar mProgressBar;
#Override
public void setContentView(int layoutResID) {
RelativeLayout mRelativeLayout =
(RelativeLayout) getLayoutInflater().inflate(
R.layout.activity_base, null);
FrameLayout frameLayout = mRelativeLayout.findViewById(
R.id.activity_content);
mProgressBar = mRelativeLayout.findViewById(R.id.progress_bar)
/**
* True means layoutResID should be inflated and made a part of
parent frameLayout
*/
getLayoutInflater().inflate(layoutResID, frameLayout, true);
super.setContentView(mRelativeLayout);
}
public void showProgressBar(boolean visibility){
mProgressBar.setVisibility(visibility ?
View.VISIBLE : View.INVISIBLE);
}
_ChildActivity Layout "activity_recipe"
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="recipe"
type="com.mustafa.foodapp.models.Recipe" />
<import type="com.mustafa.foodapp.util.StringUtils" />
</data>
<ScrollView
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/recipe_image"
android:layout_width="match_parent"
android:layout_height="#dimen/recipe_image_height"
android:scaleType="center"
app:imageUrl="#{recipe.image_url}" />
<TextView
android:id="#+id/recipe_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/recipe_image"
android:padding="7dp"
android:text="#{recipe.title}"
android:textColor="#000"
android:textSize="#dimen/recipe_title_text_size" />
<LinearLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/recipe_title"
android:orientation="horizontal"
android:padding="10dp"
android:weightSum="100">
<TextView
android:id="#+id/recipe_social_score"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="10"
android:gravity="center"
android:text="#{String.valueOf(
Math.round(recipe.social_rank))}"
android:textColor="#color/red"
android:textSize="#dimen/
recipe_publisher_text_size"/>
</LinearLayout>
<LinearLayout
android:id="#+id/ingredients_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/container"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{StringUtils.getStringIngredients(
recipe.ingredients)}" />
</LinearLayout>
</RelativeLayout>
</ScrollView>
</layout>
_BaseActivity Layout "activity_base"
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/base_relative_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/activity_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
<ProgressBar
android:id="#+id/progress_bar"
style="#style/Widget.AppCompat.ProgressBar"
android:layout_width="125dp"
android:layout_height="125dp"
android:layout_centerInParent="true"
android:visibility="gone" />
</RelativeLayout>
_BaseActivity with data-binding "2nd way"
public abstract class BaseActivity extends AppCompatActivity {
public ProgressBar mProgressBar;
public ActivityBaseBinding baseBinding;
#Override
public void setContentView(int layoutResID) {
baseBinding = DataBindingUtil.inflate(
getLayoutInflater(), R.layout.activity_base, null, false);
mProgressBar = baseBinding.progressBar;
/**
* True means layoutResID should be inflated and made a part of the
parent frameLayout
*/
getLayoutInflater().inflate(layoutResID, baseBinding.activityContent,
true);
super.setContentView(baseBinding.getRoot());
}
public void showProgressBar(boolean visibility){
mProgressBar.setVisibility(visibility ?
View.VISIBLE : View.INVISIBLE);
}
Call layout.requestLayout() after inflating so it can adjust to the changes made after inflating.
getLayoutInflater().inflate(layoutResID, baseBinding.activityContent, true);
mRelativeLayout.requestLayout();
super.setContentView(baseBinding.getRoot());
public void requestLayout ()
Call this when something has changed which has invalidated the layout of this view. This will schedule a layout pass of the view tree.
If you can see the child in view heirarchy while debugging, then this should fix the issue.
https://developer.android.com/reference/android/view/View.html#requestLayout()
PART 2: Binding Doesn't work
You are inflating view two times, once in your child activity, and once in your BaseActivity:
mBinding = DataBindingUtil.inflate(
getLayoutInflater(),
R.layout.activity_recipe, null, false);
setContentView(R.layout.activity_recipe);
mBinding You inflated once for databinding, then you passed layout id to setContentView where it inflated again:
getLayoutInflater().inflate(layoutResID, frameLayout, true);
So the one in Databinding is a different view from the one you added to base layout.
Create an Overloaded version of setContentView that accepts view instead of id.
Related
I am trying to create image move transition from recycle view to fragment. But the problem is that after the transition image is displayed in the new the move animation doesn't happen it just fades in with the rest of the content. What could I be doing wrong. I was following this example:
http://mikescamell.com/shared-element-transitions-part-4-recyclerview/
I have recycle view item layout with image view:
<?xml ...?>
<layout ...>
<data>
<variable name="model" type="..."/>
</data>
<LinearLayout ...>
<FrameLayout ...>
<ImageView android:id="#+id/image" .../>
</FrameLayout>
<LinearLayout ...>
...
</LinearLayout>
</LinearLayout>
</layout>
And then I have a fragment layout to which I want to move the image from recycle view.
<FrameLayout ...>
<TextView .../>
<ImageView android:id="#+id/logo" >
</FrameLayout>
Here's recycle my view onBindViewHolder method
#Override
public void onBindViewHolder(RecycleViewAdapter.ViewHolder holder, int position) {
final T object = getItem(position);
holder.getBinding().setVariable(BR.model, object);
holder.getBinding().executePendingBindings();
ViewCompat.setTransitionName(holder.getBaseView().findViewById(R.id.logo), "wb_logo");
holder.getBaseView().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClickListener.onItemClick(view, object);
}
});
}
Here's recycle view item click handler in my main activity
#Override
public void onFragmentItemClick(View view, Object object) {
PreviewFragment fragment = PreviewFragment.newInstance(Object.imageId, "");
getFragmentManager()
.beginTransaction()
.addSharedElement(view.getRootView().findViewById(R.id.logo), "wb_logo")
.addToBackStack(null)
.replace(R.id.fragment_container, fragment)
.commit();
}
In my fragment class in which I wan't to animate image I have following setup:
#Override
public void onCreate(Bundle savedInstanceState) {
...
setSharedElementEnterTransition(
TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.move));
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ImageView imageView = view.findViewById(R.id.logo);
imageView.setTransitionName("wb_logo");
imageView.setImageResource(...);
}
I tried statically adding transitionName attribute to image views but the result was same.
Edit
Maybe there is something wrong with my general layout. My fragment container is in a DrawerLayout
<android.support.v4.widget.DrawerLayout ...>
<android.support.design.widget.CoordinatorLayout ...>
<android.support.design.widget.AppBarLayout ...>
<android.support.v7.widget.Toolbar ... />
</android.support.design.widget.AppBarLayout>
<FrameLayout android:id="#+id/fragment_container" .../>
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView .../>
</android.support.v4.widget.DrawerLayout>
Or fragment class I am using I am using
android.app.Fragment
instead of
android.support.v4.app.Fragment
within
android.support.v7.app.AppCompatActivity
I figured out what was my problem: I had multiple items in recycle view which all had ImageView inside them with android:transitionName="wb_logo".
I somehow missed the point that each shared item in recycle view must have a unique transitionName. when I modified transitionName to include id suffix it started working as intended.
android:transitionName="wb_logo_1"
android:transitionName="wb_logo_2"
Try setting the transition name in xml rather than JAVA side. I think it might not be working because you are setting it when the view is already created.
<ImageView
android:id="#+id/ivItem"
android:transitionName="wb_logo"
android:layout_width="match_parent"
android:background="#android:color/holo_blue_bright"
android:scaleType="centerCrop"
android:layout_height="wrap_content" />
The above would be in your item in your recycler view.
<ImageView
android:id="#+id/wb_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:transitionName="wb_logo" />
The above would then be in your fragment
Your start fragment code is correct
I have a BaseActivity that contains this structure:
<!-- activity_base -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:id="#+id/container_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<include
layout="#layout/tool_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<include
layout="#layout/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/tool_bar"
android:layout_above="#id/bottom_navigation" />
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:menu="#menu/bottom_navigation_menu" />
</RelativeLayout>
In my BaseActivity I'm controlling the BottomNavigationView. And I created several Activities where I would like to take advantage of the include content and load only the contents of that activity.
Today the project is using butterknife Java. And the project is getting giant because it is replicating the top structure of the BaseActivity for each sub activity. I would like to refactor (in the best and quickest possible way) the project so that there is only one
activity_base.xml and other activities control only its content (content.xml).
I saw something using ViewStub and another describing <merge> but I did not understand how to apply the concepts easily in the project since many use activity and fragment, my project is only with activities.
I'm not completely happy with this solution but I ended up with something like that working on a similar case:
base_activity.xml
<...>
..... Whatever you want at the top
<.../>
<!-- Container where your child Activity layout will be inflated -->
<FrameLayout
layout="#+id/child_activity_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<...>
..... Whatever you want at the bottom
<.../>
BaseActivity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
ViewGroup parent = findViewById(R.id.child_activity_container);
View childView = inflateChildLayout(LayoutInflater.from(this), parent);
// The child Activity can have no layout (for some reason)
if (childView != null) {
parent.addView(childView);
}
ButterKnife.bind(BaseActivity.this);
// ... the rest of your onCreate
}
#Nullable
protected abstract View inflateChildLayout(LayoutInflater inflater, ViewGroup parent);
ChildActivity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(...); // Already managed by the base Activity so we don't need it
}
#Override
protected View inflateChildLayout(LayoutInflater inflater, ViewGroup parent) {
return inflater.inflate(R.layout.activity_child, parent, false);
}
The idea is the BaseActivity manage the inflation of the child layout inside its own layout and manage the call to ButterKnife. As the parent and the child are in reality the same instance (as ChildActivity extends BaseActivity) ButterKnife both the parent and the child Views. That's why it's called after the call to inflateChildLayout
I'm trying to replace one fragment with another following a button click (onClick) event. I'm new to fragments so if I'm going about this the wrong way please let me know.
At run time a frame layout (container) is loaded. A fragment is then added to it dynamically. This works fine. If I add in the setOnClickListener I get a NullPointerException. Here's the Java:
public class WelcomeActivity extends ActionBarActivity {
FrameLayout container;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
//setContentView(R.layout.welcome_fragment);
container = (FrameLayout)findViewById(R.id.mainContainer);
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.mainContainer) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
addWelcomeFragment();
}
//NPE at the below line
Button submitUser = (Button) findViewById(R.id.submitUser);
submitUser.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View view){
addExpedFragment();
}
});
}
Through researching fragments I'm using static class within the WelcomeActivity class to load the fragments. They are here:
public static class WelcomeFragment extends Fragment {
public static Fragment newInstance(){
Fragment wFrag = new WelcomeFragment();
return wFrag;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.welcome_fragment, null);
return v;
}
}
//-----------------------------------------------------------------------------------------------------//
public static class ExpedFragment extends Fragment {
public static Fragment secondInstance(){
Fragment eFrag = new ExpedFragment();
return eFrag;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.exped_fragment, null);
return v;
}
}
And the methods to invoke these are also with WelcomeActivity here:
private void addWelcomeFragment(){
Fragment welcome = WelcomeFragment.newInstance();
FragmentTransaction fTrans = getSupportFragmentManager().beginTransaction();
fTrans.add(R.id.mainContainer, welcome);
fTrans.addToBackStack(null);
fTrans.commit();
}
private void addExpedFragment(){
Fragment exped = ExpedFragment.secondInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.mainContainer, exped).commit();
}
I have a feeling that the problem is to do with trying to find the button view id "submitUser" from the first fragment. My xmls look like this.
The first empty FrameLayout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<FrameLayout
android:id="#+id/mainContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent" >
</FrameLayout>
</RelativeLayout>
The first fragment loaded in at runtime:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/welcome_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
tools:context=".WelcomeActivity" >
<TextView
android:id="#+id/welcome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/welcome1"
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="#+id/userName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/welcome"
android:layout_alignRight="#+id/welcome"
android:layout_below="#+id/welcome"
android:layout_marginTop="32dp"
android:ems="10"
android:hint="#string/userHint"
android:inputType="textPersonName" >
<requestFocus />
</EditText>
<Button
android:id="#+id/submitUser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/userName"
android:layout_alignRight="#+id/userName"
android:layout_below="#+id/userName"
android:layout_marginTop="16dp"
android:text="#string/Done" />
<FrameLayout
android:id="#+id/mainContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent" >
</FrameLayout>
</RelativeLayout>
The second fragment to replace the first with the onClick event:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/exped_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
tools:context=".WelcomeActivity" >
<TextView
android:id="#+id/expedText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/exped"
android:textAppearance="?android:attr/textAppearanceLarge">
</TextView>
<FrameLayout
android:id="#+id/mainContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent" >
</FrameLayout>
</RelativeLayout>
IF I forget loading the first fragment at runtime and setContentView(first fragment) there is no NullPointerException but the two fragments overlay each other when the button is pressed rather than replacing.
So my question is, how do I reference the active fragment to properly set the OnClickListener and avoid the NullPointerException?
Button submitUser = (Button) findViewById(R.id.submitUser);
gives the NPE
Problem:
Button submitUser = (Button) findViewById(R.id.submitUser);
The submitUser is located in your WelcomeFragment's layout not in your Activity's layout that is why it is null.
solution:
Instead of initializing it in your activity class you need to initialize it within your Welcome fragment view.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.welcome_fragment, null);
Button submitUser = (Button) v.findViewById(R.id.submitUser);
submitUser.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View view){
addExpedFragment();
}
});
return v;
}
EDIT:
private void addExpedFragment(){
Fragment exped = ExpedFragment.secondInstance();
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.mainContainer, exped).commit();
}
I have a ViewPager which holds multiple base fragments, each base fragment have four more nested fragments and each nested fragment is a combination of 5 imageViews and textView.
(This is what I intended to do)
I have created a sample application but I am not able to implement that properly. I cannot see any view in the nested fragment or the base fragment.
Here's the code for the sample application
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<fragment android:name="com.example.nestedfragments.BaseFragment"
android:id="#+id/left_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</LinearLayout>
MainActivity.java
package com.example.nestedfragments;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
base_fragment.xml
<?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:gravity="center">
<Button
android:id="#+id/button1"
android:text="Launch Nested Fragment"
android:gravity="center"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingBottom="10dp"/>
</RelativeLayout>
BaseFragment.java
public class BaseFragment extends Fragment {
Button doNestingButton;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// set the view
View root = inflater.inflate(R.layout.base_fragment, container, false);
doNestingButton = (Button) root.findViewById(R.id.button1);
doNestingButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Fragment videoFragment = new NestedFragment();
// we get the 'childFragmentManager' for our transaction
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
// make the back button return to the main screen
// and supply the tag 'left' to the backstack
transaction.addToBackStack("left");
// add our new nested fragment
transaction.add(getId(), videoFragment, "left");
// commit the transaction
transaction.commit();
}
});
return root;
}
}
nested_fragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="154dp"
android:layout_height="154dp"
android:id="#+id/imageView" android:layout_gravity="left|center_vertical"
android:src="#drawable/ic_splash"/>
</LinearLayout>
NestedFragment.java
public class NestedFragment extends Fragment {
public NestedFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.nested_fragment, container, false);
ImageView doNestingButton = (ImageView) root.findViewById(R.id.imageView);
return root;
}
Once again this is a sample application, please guide me.
From your above code, you have not used the fragment container layout such as FrameLayout in base_fragment.xml .So add the frame layout for nested fragment inbase_fragment.xml
I tried changing the background color of a fragment, but a small problem occurred.
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
So, shown above is the code I had for my main class that calls the XML file for the fragment.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<fragment
android:id="#+id/fragment1"
android:name="com.northreal.practice.FirstFragment"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#CBA" />
</LinearLayout>
Above is the main.xml layout that is called by the main class (MainActivity).
public class FirstFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.main, parent, false);
}
}
Above the XML file with the fragment calls this class.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<TextView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BLAHHHH"
android:layout_gravity="center_vertical" />
</LinearLayout>
This layout above is inflated by the class FirstFragment
So, why doesn't this actually change the color of the background of my fragment?
Fragments don't inherit from View and thus don't have a set background method.
Any easy fix is to just grab the root view of fragment and set its background
fragment.getView().setBackgroundColor(Color.WHITE);
The reason why that doesn't work is because fragment is treated similar to an activity. It is a container that is the child of the main activity, and is used to display other items.
You need to put the android:background="#CBA" in the actual layout that holds the TextView and NOT the fragment itself.
Like so:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CBA"
android:orientation="horizontal" >
<TextView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="BLAHHHH" />
</LinearLayout>
Get the fragment object like:
Fragment fragment = (Fragment) getFragmentManager().findFragmentById(R.id.fragmentId);
Change it's background like:
fragment.getView().setBackgroundColor(Color.WHITE);
In AndroidX you can use a FragmentContainerView instead of a Fragment to set a background:
<androidx.fragment.app.FragmentContainerView
...
android:background="#FFFFFF"/>
I have faced the same problem but my requirement was to set or change background drawable image of fragment. As Adam answered the approach is right and I would like to show the connection from fragment to activity.
The best practice is to use an interface named 'Connector'(or any name).Then from your fragment:
Connector connector = (Connector) getActivity();
connector.setIt(this);
Then in your activity which will implement 'Connector' interface and have the method.
#Override
public void setIt(Fragment fragment){
FirstFragment firstFrag = (FirstFragment) getSupportFragmentManager().findFragmentByTag("first");
firstFrag.getView().setBackgroundDrawable(getResources().getDrawable(R.drawable.app_background));
//or for color set
firstFrag.getView().setBackgroundColor(Color.WHITE);
}