android-fragments 创建BaseFragment并在ChildFragment中扩展此片段

b1payxdu  于 2022-11-14  发布在  Android
关注(0)|答案(8)|浏览(131)

我不知道这是否是重复的问题,但我没有找到任何解决办法。
问题是,有一个屏幕在所有屏幕中有相似的视图,屏幕是碎片。
所以,我想创建一个基本片段,并在所有子片段中扩展这个基本片段。
我发现在谷歌上,演示的例子,但我没有找到任何解决方案.
我不知道该从何说起。
请帮帮我。
我发现了这个环节但并不是很清楚:

  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);
  }
}

现在,fragment_child布局中有一个TextView。当我运行应用程序时会显示该视图,但fragment_base中有两个其他视图,未显示...

yiytaume

yiytaume1#

遵循这种简单明了的方法

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();
}

这就是碱基片段
然后对于子片段

public class ChildFragment extends BaseFragment {

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

}

为你的工作制定单独的方法

ppcbkaq5

ppcbkaq52#

最简单和干净的解决方案是保持onCreateView不变,并在其中注入一个函数。但是,您需要片段名称才能工作。因此,需要两个抽象,一个视图创建注入器和一个名称获取器,这样您就得到了一个干净和非常可行的解决方案。我在一个Activity中使用了它,其中有10个可替换的片段:

/**
 * 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);

然后按如下方式使用它:

/**
 * 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);
        });
    }
egmofgnx

egmofgnx3#

抽象类方法

我理解你在这里试图做什么(继承)。但是正如@hobokent所指出的,仅仅通过继承BaseClass并不会在BaseClass布局之上包含你的childView布局。有很多方法可以克服这个问题
让我们来看看这个解决方案。

  • 创建一个将扩展Fragment类的抽象类(这将是BaseClass)。
  • 创建一个将返回布局的抽象方法。
  • 为ChildClass中的抽象方法提供实现。

下面是代码片段。
基本类别

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);

}

子类

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;
   }

}

对于您的特定需求,如果您希望相同的布局出现在子类布局中。您可以创建一个方法,该方法将返回一个布局,并且可能在您的子类布局中为BaseClass布局创建一个占位符,并使用child.add(base_layout)在childLayout中添加BaseClass布局。这也只是另一种设计解决方案。
您还可以在Activity布局中放置一个通用布局,并在Activity占位符中为片段添加片段。
我没有任何特定于您的需求的代码,但这里是我为TabLayout实现这种方法的示例,其中每个选项卡都是具有不同布局的不同片段。
Github full code sample

ukxgm1gy

ukxgm1gy4#

我已经实现了这个结构,你可以让你的BaseFragment布局出现在你的ChildFragment布局中。基本上,我已经使用ViewStub来实现这个解决方案。VewStub是一种占位符布局。它将在加载xml时创建,然后你可以用指定的视图层次结构替换ViewStub。我将使用自解释变量名来避免额外的解释:)。你可以在GitHub上查看sample project
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");
    }

}
ig9co6j1

ig9co6j15#

扩展Fragment不会包含视图,这些视图是从您膨胀的布局中得到的,因此您不会在子Fragment中看到fragment_base中的文本视图。
相反,创建和使用自定义视图/复合视图可能会对您有所帮助,您可以在其他片段中重复使用这些视图。
您可以在此处阅读有关复合视图的内容:
https://medium.com/@Sserra90/android-writing-a-compound-view-1eacbf1957fc

lmvvr0a8

lmvvr0a86#

BaseFragment扩展Fragment类,然后其他Fragment扩展BaseFragment

public class BaseFragment extends Fragment 

public class AboutFragment extends BaseFragment

如果要在中使用其他布局:

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

这对我有用
希望能帮上忙。

8cdiaqws

8cdiaqws7#

您可以通过为每个子片段包含一个基本布局来实现这一点。然后,您需要为基本布局中的所有基本视图绑定基本片段中的视图。对于子视图布局,您需要在子片段中绑定它。
这里的步骤:
1.创建基本片段布局(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>

1.创建基本片段

public class BaseFragment extends Fragment {

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

  ...
}

1.在基本片段中创建一个基本方法来绑定视图。它将被覆盖并在子片段中重用。这里我们使用bindView() name:

public class BaseFragment extends Fragment {

  protected AppCompatTextView mTvSample;

  ...

  protected void bindView(View view) {
    mTvSample = view.findViewById(R.id.sample_tv);
  }
  ...

}

1.通过重用基本布局来创建子布局。可以使用include。它应该类似于(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>

1.现在,您可以通过扩展基本片段来创建子片段。您需要记住使用并覆盖bindView()。因此,您需要创建如下内容:

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);
  }
}
rsaldnfx

rsaldnfx8#

你可以这样做

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
}

碎片

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

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

相关问题