碎片真的需要一个空的构造函数吗?

我有一个具有多个参数的构造函数的片段,在testing阶段一切正常,但现在约300用户下载应用程序后,我有一个exception的发生:

android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment make sure class name exists, is public, and has an empty constructor that is public 

我的意思是我可以提供一个不同的构造函数,但这并没有太多的意义,因为我将不得不调用另一种方法来真正设置片段。

我很好奇,为什么这是偶尔发生,并不总是,也许即时使用碎片ViewPager只是错误的,因为我实例化所有的片段我自己,并将其保存到活动内的列表。 我不使用FragmentManager交易的东西,因为关于碎片Viewpager的例子不是很清楚,最后一切正常。

是的,他们有。

无论如何,你不应该重写构造函数。 您应该定义一个newInstance()静态方法,并通过参数(bundle)传递任何参数

例如:

 public static final AlertFragment newInstance(int title, String message) { AlertFragment f = new AlertFragment(); Bundle bdl = new Bundle(2); bdl.putInt(EXTRA_TITLE, title); bdl.putString(EXTRA_MESSAGE, message); f.setArguments(bdl); return f; } 

当然也可以通过这种方式来获取参数:

 @Override public void onCreate(Bundle savedInstanceState) { title = getArguments().getInt(EXTRA_TITLE); message = getArguments().getString(EXTRA_MESSAGE); //... //etc //... } 

然后你会像你这样从你的片段pipe理器实例化:

 public onCreate(Bundle savedInstanceState) { if(savedInstanceState == null){ getSupportFragmentManager() .beginTransaction() .replace(R.id.content,AlertFragment.newInstance( R.string.alert_title, "Oh noes an error occured!") ) .commit(); } } 

这种方式如果分离和重新连接的对象状态可以通过参数存储。 很像捆绑意图。

原因 – 额外阅读

我想我会解释为什么人们想知道为什么。

如果您检查: https : //android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Fragment.java

你会看到Fragment类中的instantiate(..)方法调用newInstance方法。

如果链接中断,您也可以在这里看到: 在这里输入图像描述

http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#newInstance()解释为什么在实例化时检查访问者是;public ,并且该类加载器允许访问它。

总而言之这是一个非常讨厌的方法,但它允许FragmentManger杀死和重新创build与国家的Fragments 。 (Android子系统与Activities类似)。

示例类

我被问到了很多关于调用newInstance ,(不要把它与类方法混淆,整个类的例子应该显示这个用法。

 /** * Created by chris on 21/11/2013 */ public class StationInfoAccessibilityFragment extends BaseFragment implements JourneyProviderListener { public static final StationInfoAccessibilityFragment newInstance(String crsCode) { StationInfoAccessibilityFragment fragment = new StationInfoAccessibilityFragment(); final Bundle args = new Bundle(1); args.putString(EXTRA_CRS_CODE, crsCode); fragment.setArguments(args); return fragment; } // Views LinearLayout mLinearLayout; /** * Layout Inflater */ private LayoutInflater mInflater; /** * Station Crs Code */ private String mCrsCode; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCrsCode = getArguments().getString(EXTRA_CRS_CODE); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mInflater = inflater; return inflater.inflate(R.layout.fragment_station_accessibility, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mLinearLayout = (LinearLayout)view.findViewBy(R.id.station_info_accessibility_linear); //Do stuff } @Override public void onResume() { super.onResume(); getActivity().getSupportActionBar().setTitle(R.string.station_info_access_mobility_title); } // Other methods etc... } 

正如CommonsWare在这个问题中指出的那样,如果你正在创build一个片段的匿名子类,也会发生这个错误,因为匿名类不能有构造函数。

不要做匿名的片段的子类:-)

是的,正如你所看到的,support-package也会实例化碎片(当它们被破坏和重新打开时)。 你的Fragemnt子类需要一个公共的空构造函数,因为这是由框架调用的。

这是我简单的解决scheme:

1 – 定义你的片段

 public class MyFragment extends Fragment { private String parameter; public MyFragment() { } public void setParameter(String parameter) { this.parameter = parameter; } } 

2 – 创build你的新片段并填充参数

  myfragment = new MyFragment(); myfragment.setParameter("here the value of my parameter"); 

3 – 享受它!

显然你可以改变参数的types和数量。 快速简单。