'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:
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...
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:
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>
Create the base fragment
public class BaseFragment extends Fragment { public BaseFragment() { // Required empty public constructor } ... }
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); } ... }
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>
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 |