关于调用findFragmentById报null异常的解决方法,并由此引发对Fragment生命周期的思考
首先,在xml布局中写有一个fragment,在别处需要调用该fragment并调用该碎片中的一个方法,代码如下:
//这是郭大神《第一行代码》第四章中的习题
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.item_news,viewGroup,false);
final ViewHolder viewHolder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
News news = newsList.get(viewHolder.getAdapterPosition());
if (NewsListFragment.isTwopage){
//双页模式
NewsContentFragment newsContentFragment = (NewsContentFragment) new NewsContentFragment().getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(),news.getContent());
}else {
NewsContentActivity.actionStart(context,news.getTitle(),news.getContent());
}
}
});
return viewHolder;
}
运行后程序报空指针异常
解决方法为:
在NewsListFragment中定义静态的全局变量FragmentManager,并在onActivityCreated 生命周期回调方法中调用getFragmentManager()方法获取fragmentManager。在Adapter类中调用该FragmentManager对象即可。代码如下:
public class NewsListFragment extends Fragment {
public static boolean isTwopage;
public static FragmentManager fragmentManager;
private NewsListAdapter adapter;
private RecyclerView recyclerView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_list_frag,container,false);
recyclerView = view.findViewById(R.id.news_recyclerview);
adapter = new NewsListAdapter(getActivity(),getNews());
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
return view;
}
/*注意:!!!只有在fragment oncreateView方法调用后,并且在与之相关联的Activity成功oncreate之后才能获取到该fragment对象*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
fragmentManager = getFragmentManager();
super.onActivityCreated(savedInstanceState);
if (getActivity().findViewById(R.id.news_content_layout) != null){
//双页模式
isTwopage = true;
}else {
isTwopage = false;
}
}
private List<News> getNews(){
List<News> newsList = new ArrayList<>();
for (int i = 0;i < 50;i++){
News news = new News();
news.setTitle("News Title" + i);
news.setContent("this is content :!!" + i );
newsList.add(news);
}
return newsList;
}
}
Adapter中对应代码块改为:
NewsContentFragment newsContentFragment = (NewsContentFragment) NewsListFragment.fragmentManager.findFragmentById(R.id.news_content_fragment);
通过分析Fragment的生命周期就可以发现该问题产生的原因。
Fragment的生命周期图
若按这种写法来获取fragmentManager,显然是不行的,因为界面中并没有创建出NewsContentFragment,自然就获取不到其对应的fragmentManager了。
NewsContentFragment newsContentFragment = (NewsContentFragment) new NewsContentFragment().getFragmentManager().findFragmentById(R.id.news_content_fragment);
因此我们需要在已经创建了的NewsListFragment中获取fragmentManager。
同时,为了测试fragmentManager.findFragmentById(...)这个方法,我在fragment的生命周期回调方法onCreateView()加入以下代码:
Log.d(TAG, "onCreateView: " + getFragmentManager().findFragmentById(R.id.news_content_fragment));
并在onActivityCreated()加入:
Log.d(TAG, "onActivityCreated: " + fragmentManager.findFragmentById(R.id.news_content_fragment));
运行结果如图:
原因分析:
mainActivity托管着左右两个碎片,如图:
分析XML布局文件可知:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">
<!--平板模式下显示双页-->
<fragment
android:id="@+id/news_title_fragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.example.kais_.fragmentbestpractice.NewsListFragment"/>
<FrameLayout
android:id="@+id/news_content_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2">
<fragment
android:id="@+id/news_content_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.kais_.fragmentbestpractice.NewsContentFragment"/>
</FrameLayout>
</LinearLayout>
在加载Activity时候,会先创建左边的NewsListFragment,而右边的NewsContentFragment需要等待NewsListFragment创建完才开始创建。
通过分析NewsListFragment的生命周期,onActivityCreated()即为与之关联的Activity创建完成,而此时NewsContentFragment也已经创建完成,因此这就能解释,为什么在NewsListFragment的onCreateView()方法中获取不到NewsContentFragment的对象,而在onActivityCreated()方法中可以获取到了。
下一篇: mysql5.7安装