碎片真的需要一个空的构造函数吗?
我有一个具有多个参数的构造函数的片段,在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和数量。 快速简单。