Creating a Fragment: constructor vs newInstance()
I recently grew tired of constantly having to know String keys to pass arguments into Bundles when creating my Fragments. So I decided to make constructors for my Fragments that would take the parameters I wanted to set, and put those variables into the Bundles with the correct String keys, therefore eliminating the need for other Fragments and Activities needing to know those keys.
public ImageRotatorFragment() {
super();
Log.v(TAG, "ImageRotatorFragment()");
}
public ImageRotatorFragment(int imageResourceId) {
Log.v(TAG, "ImageRotatorFragment(int imageResourceId)");
// Get arguments passed in, if any
Bundle args = getArguments();
if (args == null) {
args = new Bundle();
}
// Add parameters to the argument bundle
args.putInt(KEY_ARG_IMAGE_RES_ID, imageResourceId);
setArguments(args);
}
And then I pull out those arguments like normal.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "onCreate"); // Set incoming parameters
Bundle args = getArguments();
if (args != null) {
mImageResourceId = args.getInt(KEY_ARG_IMAGE_RES_ID, StaticData.getImageIds()[0]);
}
else {
// Default image resource to the first image
mImageResourceId = StaticData.getImageIds()[0];
}
}
However, Lint took issue with this, saying not to have subclasses of Fragment with constructors with other parameters, requiring me to use @SuppressLint("ValidFragment") to even run the app. The thing is, this code works perfectly fine. I can use ImageRotatorFragment(int imageResourceId) or the old school method ImageRotatorFragment() and call setArguments() manually on it. When Android needs to recreate the Fragment (orientation change or low memory), it calls the ImageRotatorFragment() constructor and then passes the same argument Bundle with my values, which get set correctly.
So I have been searching for the "suggested" approach and see a lot of examples using newInstance() to create Fragments with parameters, which seems to do the same thing my constructor is. So I made my own to test it, and it works just as flawlessly as before, minus Lint whining about it.
public static ImageRotatorFragment newInstance(int imageResourceId) {
Log.v(TAG, "newInstance(int imageResourceId)");
ImageRotatorFragment imageRotatorFragment = new ImageRotatorFragment();
// Get arguments passed in, if any
Bundle args = imageRotatorFragment.getArguments();
if (args == null) {
args = new Bundle();
}
// Add parameters to the argument bundle
args.putInt(KEY_ARG_IMAGE_RES_ID, imageResourceId);
imageRotatorFragment.setArguments(args);
return imageRotatorFragment;
}
I personally find that using constructors is a much more common practice than knowing to use newInstance() and passing parameters. I believe you can use this same constructor technique with Activities and Lint will not complain about it. So basically my question is, why does Google not want you to use constructors with parameters for Fragments?
My only guess is so you don't try to set an instance variable without using the Bundle, which won't get set when the Fragment gets recreated. By using a static newInstance() method, the compiler won't let you access an instance variable.
public ImageRotatorFragment(int imageResourceId) {
Log.v(TAG, "ImageRotatorFragment(int imageResourceId)");
mImageResourceId = imageResourceId;
}
I still don't feel like this is enough reason to disallow the use of parameters in constructors. Anyone else have insight into this?
1#
I personally find that using constructors is a much more common practice than knowing to use newInstance() and passing parameters.
The factory method pattern is used fairly frequently in modern software development.
So basically my question is, why does Google not want you to use constructors with parameters for Fragments?
You answered your own question:
My only guess is so you don't try to set an instance variable without using the Bundle, which won't get set when the Fragment gets recreated.
Correct.
I still don't feel like this is enough reason to disallow the use of parameters in constructors.
You are welcome to your opinion. You are welcome to disable this Lint check, either on a per-constructor or per-workspace fashion.
#2
EDIT: Although this works and can be useful, it is risky as private fields once set using this pattern maybe will not be retained when changing orientation and the references, usually from a previously viewed fragment, may be cleared by the system reclaiming memory. Using a bundle and making your objects parseable is advised.
I have come across a nice instantiation pattern using newInstance that means you don't have to call setArguments or getArguments which means you are not limited to just the data types you can set in a bundle.
Fragments need to have empty constructors to enable the system to re-instantiate and fragment constructors with parameters won't get called when re-instantiating the fragment. Hence using newInstance is a much better idea to actually add object references and data to your fragment instance.
I also don't really like using setArguments, getArguments. It is extra code and null checks and just seems restrictive as you can only set Parcelable's and primitive types. You also need extra boiler plate code to set up static key name strings etc etc...
So, you can achieve the same result but with viewer restrictions using newInstance like this:
private int resourceId;
private SomeView someView;
private OtherObject otherObject; // empty constructor to allow fragment re-instantiation
public ImageRotatorFragment() {} public static ImageRotatorFragment newInstance
(int resourceId, SomeView someView, OtherObject otherObject) {
ImageRotatorFragment fragment = new ImageRotatorFragment();
fragment.resourceId = resourceId;
fragment.someView = someView;
fragment.otherObject = otherObject;
return fragment;
}
If done this way, you can access the private member variables/fields of your fragment anywhere in your code with out have to call getArguments etc....
Hopefully this helps with writing cleaner more flexible code.
1.But... your private fields will be lost if the screen is rotated. Calling setArguments internally avoids that. The point of the question was what's the difference between using a constructor and a static newInstance. (Not much as far as I can see). Writing directly to private fields could equally be done using a constructor. – Stuart Whitehouse
2.I'm the wrong person to ask - that's why I found this question ;-) But AFAIK you'll need to bundle them somehow to either add them to the arguments or stored state of the fragment. In my case all I needed to actually do was store the ID of the object (a Guid which can be converted to/from a string) and that lets me recover the object from the data store if the activity is re-created. – Stuart Whitehouse Sep 10 '13 at 14:03
3.@domji84 while this code will work when you first instantiate the Fragment. If the screen rotates or the Fragment is destroyed for memory needs, when it's recreated it will have it's empty constructor called and it will not set those instance variables again. This solution would only work if you also saved all of this state in onSaveInstanceState() and restored it in onCreate(), which means you still would have to use a Bundle. – Steven Byle Sep 19 '13 at 13:32
public class TestFragment extends Fragment {
private static final String TAG = "TestFragment";
private String hello;// = "hello android";
private String defaultHello = "default value";
static TestFragment newInstance(String s) {
TestFragment newFragment = new TestFragment();
Bundle bundle = new Bundle();
bundle.putString("hello", s);
newFragment.setArguments(bundle);
return newFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "TestFragment-----onCreate");
Bundle args = getArguments();
hello = args != null ? args.getString("hello") : defaultHello;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
Log.d(TAG, "TestFragment-----onCreateView");
View view = inflater.inflate(R.layout.lay1, container, false);
TextView viewhello = (TextView) view.findViewById(R.id.tv_hello);
viewhello.setText(hello);
return view;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "TestFragment-----onDestroy");
}
}
Creating a Fragment: constructor vs newInstance()的更多相关文章
- listview,fragment结合起来
这是csdn上的以个demo,很适合初学者.来源:http://download.csdn.net/detail/endlife99/7274419,侵删. MainActiviy: package ...
- Android Fragment 详解(未完...)
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. 之前写过一篇关于 Fragment 生命周期的文章 ...
- fragment的介绍与使用
稍稍摘录一段Fragment.java中的说明文档. /** * A Fragment is a piece of an application's user interface or behavio ...
- 如何向一个Fragment传递参数---setArguments方法的介绍
在我们平常开发中经常会用到Fragment,当我们使用Fragment时一般是通过new Fragment的构造方法来实现,如果我问你怎么向一个Fragment传递参数,你是不是会首先想到通过构造方法 ...
- java中new关键字和newInstance()方法有什么区别?
1.new可以调用带参数的构造函数,newInstance不可以. 2.new 是 java 的关键字,用来构造一个类的实例.而 newInstance 是 Class 的一个方法,不过两个写法的效果 ...
- Fragment官方解析
由于fragment和activity的生命周期很类似,对activity不熟悉的可以参考–深入了解Activity-生命周期, 深入理解Activity-任务,回退栈,启动模式, 概要 A Frag ...
- ViewPager+Fragment的结合使用,实现QQ界面的理解
http://www.cssxt.com/html/2449/2449.html 效果如图: 实现代码解析:MainActivity.java1.引入布局文件2.4个标题控件的初始化以及点击事件的监听 ...
- Android Fragment详解(二):Fragment创建及其生命周期
Fragments的生命周期 每一个fragments 都有自己的一套生命周期回调方法和处理自己的用户输入事件. 对应生命周期可参考下图: 创建片元(Creating a Fragment) To c ...
- Java中创建对象的5种方式 &&new关键字和newInstance()方法的区别
转载:http://www.kuqin.com/shuoit/20160719/352659.html 用最简单的描述来区分new关键字和newInstance()方法的区别:newInstance: ...
随机推荐
- 72.2801 LOL-盖伦的蹲草计划(广搜)
时间限制: 1 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 众所周知,LOL这款伟大的游戏,有个叫盖伦的英雄.他的伟大之处在 ...
- [转]Android中常用适配器及定义自己的适配器
一,适配器. 顾名思义,就是把一些数据给弄得适当,适合以便于在View上显示.可以看作是界面数据绑定的一种理解.它所操纵的数据一般都是一些比较复杂的数据,如数组,链表, 数据库,集合等.适配器就像 ...
- [转] Spring MVC 4.1.3 + MyBatis 零基础搭建Web开发框架
首先感谢一下润和软件,指引我走上了Spring MVC Web开发的道路. 下面进入正题 搭建开发环境: Netbeans8.0.2 + MySql5.6 + JDK1.7 + tomcat8.0.1 ...
- HDU 5650 so easy 数学
so easy 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5650 Description Given an array with n integ ...
- PAT甲级1087. All Roads Lead to Rome
PAT甲级1087. All Roads Lead to Rome 题意: 确实有从我们这个城市到罗马的不同的旅游线路.您应该以最低的成本找到您的客户的路线,同时获得最大的幸福. 输入规格: 每个输入 ...
- 帝国cms安装在二级目录 构建中英文网站
帝国cms是很好的建站工具,一般都是安装在根目录,但是有时候租一个空间,要搭建两个网站,或者中英文双语的网站,肯定得用到二级目录 帝国cms安装二级目录步骤 1,讲安装包解压缩到要安装的二级目录下 2 ...
- IPC low/medium/high density 什么意思?
http://wiki.altium.com/pages/viewpage.action?pageId=3080344 Land Pattern Information Density Level A ...
- PHP正则表达式30分钟入门教程
正则表达式30分钟入门教程 三个常用的知识点: 1.惰性匹配:正则引擎默认是贪婪的,若要最少重复的话,需要用到惰性匹配符 “?” 懒惰限定符 代码/语法 说明 *? 重复任意次,但尽可能少重复 +? ...
- Linux文件目录的一点小结
转载:http://blog.chinaunix.net/uid-29171357-id-3889735.html 1. 相关指令: chgrp:改变文件所属用户组 点击(此处)折叠或打开 ...
- javascript转换日期字符串为Date对象
把一个日期字符串如“2007-2-28 10:18:30”转换为Date对象: 1: var strArray=str.split(" "); var strDate=strArr ...