'Create BaseFragment and extend this Fragment in ChildFragment

I don't know whether this is repeated question or not, But I didn't found any solution.

The problem is, there is a screen which has similar views in all the screens, that screens are in fragment.

So, I want to create a base fragment and want to extend this base fragment in all child fragments.

I found on google, for demo examples, But I didn't find any solution.

I don't know from where to start.

Please help me.

I found this links but it is not much clear:

  1. Fragments inheritance Android

  2. When do I need a base activity and base fragment?

BaseFragment.java

public class BaseFragment extends Fragment {

  public BaseFragment() {
    // Required empty public constructor
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    return inflater.inflate(R.layout.fragment_base, container, false);
  }
}

ChildFragment.Java

public class ChildFragment extends BaseFragment{

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_child, container, false);
  }
}

Now, there is a TextView in fragment_child layout. that is displayed when I run the app, but there are two other views in fragment_base, that is not showing...

enter image description here



Solution 1:[1]

ABSTRACT CLASS APPROACH ?

I understand what you are trying to do (inheritance) here. But as pointed out by @hobokent, just by inheriting the BaseClass will not include your childView layout on top of BaseClass Layout. There are many ways to conquer this problem

Let's look into this solution.

  • Create an abstract class which will extend the Fragment Class (This will be BaseClass).

  • Make an abstract method which will return a layout.

  • Provide implementation for your abstract method in the ChildClass.

Here is the code snippet.

BaseClass

public abstract class BasicFragment extends Fragment {

  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
  }

  public View onCreateView(LayoutInflater inflater,ViewGroup parent, Bundle savedInstanseState)
   {
      View view = provideYourFragmentView(inflater,parent,savedInstanseState);
      return view;
   }

   public abstract View provideYourFragmentView(LayoutInflater inflater,ViewGroup parent, Bundle savedInstanceState);

}

ChildClass

public class ImageFragment extends BasicFragment{

  @Override
  public View provideYourFragmentView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.image_fragment,parent,false);

    //Now specific components here (you can initialize Buttons etc)

    return view;
   }


}

For your specific requirement, if you want the same layout to appear in child class layout. You can make a method which will return a layout and maybe in your child layout you make a placeholder for BaseClass Layout and add the BaseClass layout in childLayout using child.add(base_layout). Again it is just another design solution.
You can also put a common layout in Activity layout and Add fragment in Activity placeholder for the fragment. There are so many possible solutions.

I don't have any code specific to your requirement but here is the example where I have implemented this approach for TabLayout, where each tab is a different Fragment with a different layout.
Github full code sample.

Solution 2:[2]

I've implemented this structure where you can have your BaseFragment layout appear in your ChildFragment layout. Basically, I've used ViewStub to implement the solution. VewStub is kinda placeholder layout. It will be created when xml is loaded and then you can replace the ViewStub with designated view hierarchy. I'm gonna use self-explanatory variable names to avoid additional explanation :). You can check out the sample project on GitHub.

fragment_core.xml

<?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:layout_width="match_parent"
    android:layout_height="match_parent">

    <ViewStub
        android:id="@+id/child_fragment_will_be_displayed_here"
        android:layout_width="match_parent"
        android:layout_height="100dp">
    </ViewStub>


    <TextView
        android:id="@+id/core_fragment_text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/child_fragment_content_holder"/>

</RelativeLayout>

FragmentCore.class

   public abstract class FragmentCore extends Fragment {

    public FragmentCore() {
        // Required empty public constructor
    }

     protected TextView mTextViewInCoreActivity;

     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
         View coreFragmentView = inflater.inflate(R.layout.fragment_core, container, false);
         mTextViewInCoreActivity = coreFragmentView.findViewById(R.id.core_fragment_text_view);
         loadLayoutFromResIdToViewStub(coreFragmentView, container);
         return coreFragmentView;
     }

     public abstract void loadLayoutFromResIdToViewStub(View rootView,ViewGroup container);

 }

fragment_child.xml

 <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="100dp"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/child_fragment_content_holder">

    <TextView
        android:id="@+id/child_fragment_text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</FrameLayout>

FragmentChild.class

    public class FragmentChild extends FragmentCore {


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public void loadLayoutFromResIdToViewStub(View coreFragmentView, ViewGroup container){

        setRetainInstance(true);

        if (container != null) {
            container.removeAllViews();
        }

        ViewStub stub = coreFragmentView.findViewById(R.id.child_fragment_will_be_displayed_here);
        stub.setLayoutResource(R.layout.fragment_child);
        stub.inflate();

        TextView childFragmentTextView = coreFragmentView.findViewById(R.id.child_fragment_text_view);

        mTextViewInCoreActivity.setText("Core (Parent) Fragment TextView is accessible");
        childFragmentTextView.setText("Child Fragment TextView is accessible");
    }

}

Solution 3:[3]

Extending the Fragment won't include the views, those views are from which layout you inflate, therefore you won't see the text views from fragment_base in your Child Fragment.

It might benefit you to instead create and use custom views/compound views that you can then reuse in your other fragments.

You can read up on Compound Views here:

https://medium.com/@Sserra90/android-writing-a-compound-view-1eacbf1957fc

Solution 4:[4]

BaseFragment extends Fragment class and then your other Fragments extends BaseFragment

public class BaseFragment extends Fragment 

public class AboutFragment extends BaseFragment

if you want other layouts in:

 View headView = LayoutInflater.from(getContext()).inflate(R.layout. - your Fragment layout - , null, false);

This is working for me.

Hope it helped.

Solution 5:[5]

You can achieve this by including a base layout for each child fragment. Then you need to bind the view in the base fragment for all the base view in the base layout. For the child view layout, you need to bind it in the child fragment.

Here the step:

  1. Create the base fragment layout (fragment_base.xml):

     <?xml version="1.0" encoding="utf-8"?>
       <RelativeLayout>
    
           <!-- Declare your view here -->
           <android.support.v7.widget.AppCompatTextView
               android:id="@+id/sample_tv"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="Sample"/>
    
     </RelativeLayout>
    
  2. Create the base fragment

    public class BaseFragment extends Fragment {
    
      public BaseFragment() {
        // Required empty public constructor
      }
    
      ...
    }
    
  3. Create a base method in the base fragment to bind the view. This will be overridden and reuse in your child fragment. Here we use bindView() name:

    public class BaseFragment extends Fragment {
    
      protected AppCompatTextView mTvSample;
    
      ...
    
      protected void bindView(View view) {
        mTvSample = view.findViewById(R.id.sample_tv);
      }
      ...
    
    }
    
  4. Create the child layout by reusing the base layout. You can use include. It should be something like this (fragment_child.xml):

     <?xml version="1.0" encoding="utf-8"?>
       <RelativeLayout>
    
           <include
                layout="@layout/fragment_base"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
           />
    
           <!-- Child view -->
           <android.support.v7.widget.AppCompatTextView
               android:id="@+id/child_tv"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="Child Sample"/>
    
     </RelativeLayout>
    
  5. Now you can create the child fragment by extending the base fragment. You need to remember to use and override the bindView(). So, you need to make something like this:

    public class ChildFragment extends BaseFragment {
      private AppCompatTextView mTvChild;
      public ChildFragment() {
        // Required empty public constructor
    
      }
    
    
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    
        View view = inflater.inflate(R.layout.fragment_child, container, false);
    
        bindView(view);
        return view;
      }
    
      @Ovverride
      protected void bindView(View view) {
        super.bindView(view); //call base method to bind the base view
        mTvChild = view.findViewById(R.id.child_tv);
      }
    }
    

Solution 6:[6]

You can do like this

abstract class BaseFragment : Fragment() {

override fun onCreateView(inflater: LayoutInflater, container:ViewGroup?, savedInstanceState: Bundle?): View {
    return inflater.inflate(getContentView(), container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

        onViewReady(savedInstanceState, arguments)
    }
// create below 2 methods
    abstract fun getContentView(): Int // this will be used to pass view
    abstract fun onViewReady(savedInstanceState: Bundle?, bundle: Bundle?) // this will be invoked in our fragment when view is ready
}

Fragment

    class SoundFragment : BaseFragment() {
    override fun getContentView() = R.layout.fragment_sound

    override fun onViewReady(savedInstanceState: Bundle?, bundle: Bundle?) {
       // write your code here...
    }
}

Solution 7:[7]

Follow this simple and clean approach

public abstract class BaseFragment extends Fragment { 
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(getLayoutId(), null);

    return root;
}

@LayoutRes
protected abstract int getLayoutId();
}

that's it for base fragment

Then for child fragment

public class ChildFragment extends BaseFragment {

@Override
protected int getLayoutId() {
    return R.layout.child_fragment;
}

}

Make seprate methods for do your stuffs

Solution 8:[8]

The most simple and clean solution is to keep the onCreateView as it is and inject a function in it. But you need the fragment name for this to work. So two abstracts are needed, an view create injector and a name getter and you end up with a clean and very workable solution. I used it for one Activity with 10 replaceable fragments in it:

/**
 * base class for the application
 */
public abstract class AppFragment extends Fragment {

    // use shared view model for exchanging information between fragments.
    SharedViewModel sharedViewModel;

    /**
     * create view
     * @param inflater - inflater creates the view
     * @param container - container with views
     * @param savedInstanceState - for saving state
     * @return - return view
     */
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        // call base class first
        super.onCreateView(inflater, container, savedInstanceState);

        // then create view
        View fragView = inflater.inflate(getFragment(), container, false);

        // get shared view model.
        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);

        // inject fragment depended code
        createView(fragView);

        // return created view
        return fragView;
    }

    /**
     * get fragment
     */
    protected abstract int getFragment();

    /**
     * inject fragment depended code while creating the view
     */
    protected abstract void createView(View fragView);


then use it like this:

/**
 * make mot or tp empty
 */
public class EmptyFragment extends AppFragment {

    // parameter from load fragment
    private static final String ARG_PARAM1 = "paramSubject";
    private String paramSubject;

    // empty view
    View emptyView;

    /**
     * factory method for creating new instance of this fragment
     * @param paramSubject - subject from load fragment
     * @return - new instance of this fragment
     */
    public static EmptyFragment newInstance(String paramSubject) {
        EmptyFragment fragment = new EmptyFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, paramSubject);
        fragment.setArguments(args);
        return fragment;
    }

    /**
     * get param subject from load fragment when creating this fragment
     * @param savedInstanceState - save state data
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null)
            paramSubject = getArguments().getString(ARG_PARAM1);
    }

    /**
     * set fragment for view
     * @return - return used fragment
     */
    @Override
    protected int getFragment() {
        return R.layout.empty_fragment;
    }

    /**
     * create view
     * @param emptyView - created view by base class
     */
    @Override
    protected void createView(View emptyView) {

        // subject edit
        EditText editTextFrom = emptyView.findViewById(R.id.editTextFrom);
        editTextFrom.setText(paramSubject);

        // set headers
        sharedViewModel.getSubject().observe(getViewLifecycleOwner(), subject -> {

            // set header
            TextView textViewHeader = emptyView.findViewById(R.id.textViewHeader);
            String headerString = String.format(
                requireActivity().getString(R.string.message_makeempty),
                requireActivity().getString(subject));
            textViewHeader.setText(headerString);

            // subject header
            TextView textViewSubject = emptyView.findViewById(R.id.textViewSubject);
            textViewSubject.setText(subject);

            // only needed when its a tp
            if (subject == R.string.subject_mot) {
                SwitchMaterial switchBIC = emptyView.findViewById(R.id.switchBICCode);
                switchBIC.setVisibility(View.GONE);
            }

            // listener when empty button is pressed
            emptyMessageListener(emptyView, subject);
        });
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Farid
Solution 3 hobokent
Solution 4
Solution 5 Community
Solution 6 Aklesh Singh
Solution 7 Alok Singh
Solution 8