NullPointerException when switching between fragments containing a RecyclerView

It is very difficult for me to try to understand this. I get a NullPointerException when switching between Fragments with a RecyclerView , but only in a specific order. Code and exception below. First explanation:

I have a snippet with this tab as shown here: enter image description here

3 of these tabs use the same fragment containing RecyclerView: Schedule, History and Test. Any of them will work if I select them first. Now the problem is that I go to one of the other aforementioned tabs to the right of the first one I selected! If I go to the left of the first one I selected, it will be very easy to boot.

If I select "Test", go to "History" and then "Schedule" will work fine

If I select Test, then Schedule these two will work, but then go to History, an exception.

If you select Schedule and History, an exception will occur.

If I choose History, then the Schedule of the two will work, but then go to Testing.

If I choose History, then Test will happen.

I extended the RecycleView and put the gap in onMeasure to see if I could figure out what was happening, and I noticed that the mAdapter RecyclerView was null . I checked inside setAdapter() RecyclerView and it was always passed by a valid adapter. It seems that at some point the mAdapter is cleared. I just can't understand why this happens when I open tabs going from left to right, and not from right to left ...

Any help would be appreciated as I reached this goal!

Here is the NullPointerException:

Link to exception tracing (If this were done to avoid character restrictions)

EDIT: After you select NullPointerException and IllegalStateException . Looks from LayoutManager . Here is a link to this conclusion

Here is a snippet for tabbed browsing:

 public class FKitViewer extends Fragment implements OnTabChangeListener { public static final String TAB_INFO = "Info"; public static final String TAB_SCHEDULE = "Schedule"; public static final String TAB_HISTORY = "History"; public static final String TAB_TEST = "Test"; public static final String TAB_REQS = "Reqs"; private View mRoot; private TabHost mTabHost; private int mCurrentTab; @Override public void onAttach(Activity activity){ super.onAttach(activity); } @Override public View onCreateView(LayoutInflater li, ViewGroup container, Bundle savedInstanceState){ mRoot = li.inflate(R.layout.kit_view, container); mTabHost = (TabHost) mRoot.findViewById(android.R.id.tabhost); setupTabs(); return mRoot; } @Override public void onActivityCreated(Bundle savedInstanceState){ super.onActivityCreated(savedInstanceState); setRetainInstance(true); mTabHost.setOnTabChangedListener(this); mTabHost.setCurrentTab(mCurrentTab); updateTab(TAB_INFO, R.id.tab1); } private void setupTabs(){ mTabHost.setup(); mTabHost.addTab(newTab(TAB_INFO, "Info", R.id.tab1)); mTabHost.addTab(newTab(TAB_SCHEDULE, "Schedule", R.id.tab2)); mTabHost.addTab(newTab(TAB_HISTORY, "History", R.id.tab3)); mTabHost.addTab(newTab(TAB_REQS, "Reqs", R.id.tab4)); mTabHost.addTab(newTab(TAB_TEST, "Test", R.id.tab5)); } private TabSpec newTab(String tag, String label, int tabContentId){ TabSpec tabSpec = mTabHost.newTabSpec(tag); tabSpec.setIndicator(label); tabSpec.setContent(tabContentId); return tabSpec; } private void updateTab(String tabId, int placeholder){ FragmentManager fm = getFragmentManager(); if(fm.findFragmentByTag(tabId) == null){ if(tabId.equals(TAB_INFO)) fm.beginTransaction() .replace(placeholder, new FListiesView(), tabId) .commit(); if(tabId.equals(TAB_SCHEDULE)) fm.beginTransaction() .replace(placeholder, new FKitSchedule(false), tabId) .commit(); if(tabId.equals(TAB_HISTORY)) fm.beginTransaction() .replace(placeholder, new FKitSchedule(true), tabId) .commit(); if(tabId.equals(TAB_REQS)) fm.beginTransaction() .replace(placeholder, new KitRequirements(), tabId) .commit(); if(tabId.equals(TAB_TEST)) fm.beginTransaction() .replace(placeholder, new FKitSchedule(false), tabId) .commit(); } } @Override public void onTabChanged(String tabId){ Log.d("MNB", "Changing to tab: " + tabId); if(TAB_INFO.equals(tabId)){ updateTab(tabId, R.id.tab1); mCurrentTab = 0; return; } if(TAB_SCHEDULE.equals(tabId)){ getActivity().getIntent().getExtras().putBoolean("com.crummy.history", false); updateTab(tabId, R.id.tab2); mCurrentTab = 1; return; } if(TAB_HISTORY.equals(tabId)){ getActivity().getIntent().getExtras().putBoolean("com.crummy.history", true); updateTab(tabId, R.id.tab3); mCurrentTab = 2; return; } if(TAB_REQS.equals(tabId)){ updateTab(tabId, R.id.tab4); mCurrentTab = 3; return; } if(TAB_TEST.equals(tabId)){ getActivity().getIntent().getExtras().putBoolean("com.crummy.history", true); updateTab(tabId, R.id.tab5); mCurrentTab = 4; return; } } } 

Here is a snippet:

 public class RVFKitSchedule extends Fragment 

implements LoaderManager.LoaderCallbacks {

 private String mGetURL = "/index.php/droid/kitreport/"; private String mHistoryURL = "/index.php/droid/kithistory/"; private String mUpdateStatusURL = "/index.php/order_status/update/"; private Boolean mIsHistory = false; private LinearLayout mPbl; private int mDialogStatusLevel = 0; private int mDialogLineId = -1; private List<ScheduleRowModel> mItems = null; private FKVRecyclerView mRecyclerView; private KitScheduleRVAdapter mRVAdapter; private int mItemCount; private RecyclerView.LayoutManager mLayoutManager; private FragmentActivity mParentActivity; public RVFKitSchedule(){ } public RVFKitSchedule(boolean isHistory){ mIsHistory = isHistory; if(mIsHistory) mGetURL = mHistoryURL; } @Override public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b){ super.onCreateView(li, vg, b); return li.inflate(R.layout.rv_main, vg, false); } @Override public void onActivityCreated(Bundle icicle){ super.onActivityCreated(icicle); final InputMethodManager imm = (InputMethodManager) mParentActivity.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(getView().getWindowToken(), 0); } @Override public void onViewCreated(View view, Bundle icicle){ super.onViewCreated(view, icicle); mParentActivity = getActivity(); mRecyclerView = (FKVRecyclerView)mParentActivity.findViewById(R.id.recyclerView); mLayoutManager = new LinearLayoutManager(mParentActivity); //mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); //mLayoutManager. mLayoutManager.scrollToPosition(0); mRecyclerView.setLayoutManager(mLayoutManager); RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(mParentActivity, DividerItemDecoration.VERTICAL_LIST); mRecyclerView.addItemDecoration(itemDecoration); mItems = new ArrayList<ScheduleRowModel>(); mRVAdapter = new KitScheduleRVAdapter(mItems, mIsHistory); //mRecyclerView.setAdapter(null); if(mRecyclerView.getAdapter() != null) mRecyclerView.swapAdapter(mRVAdapter, true); else mRecyclerView.setAdapter(mRVAdapter); mRecyclerView.addOnItemTouchListener( new RecyclerItemClickListener(mParentActivity, new RecyclerItemClickListener.OnItemClickListener(){ @Override public void onItemClick(View v, int position){ String status = ((TextView)v.findViewById(R.id.sr_status)).getText().toString(); if(status.equals("Open")) mDialogStatusLevel = 0; else if(status.equals("In Production")) mDialogStatusLevel = 1; else if(status.equals("Waiting")) mDialogStatusLevel = 2; else if(status.equals("Ready")) mDialogStatusLevel = 3; else if(status.equals("Shipped")) mDialogStatusLevel = 4; int lineId = Integer.parseInt(((TextView)v.findViewById(R.id.sr_order_line_id)).getText().toString()); mDialogLineId = lineId; OrderStatusDialog df = new OrderStatusDialog(mParentActivity, mDialogStatusLevel){ @Override protected void onDialogClick(DialogInterface di, final int which){ Runnable update = new Runnable(){ @Override public void run(){ updateStatus(mDialogLineId, which); } }; Thread t = new Thread(null, update, "MagentoBackground"); t.start(); try{ t.join(); }catch(InterruptedException e){ e.printStackTrace(); } restartLoading(); dismiss(); } }; df.show(getFragmentManager(), "dialog"); } }){ @Override public void onItemLongClick(View v, int pos){ String s = ((TextView)v.findViewById(R.id.sr_pn)).getText().toString(); String n = ((TextView)v.findViewById(R.id.sr_pn)).getText().toString(); CheckItemTypeTask task = new CheckItemTypeTask(s, null); task.execute(mParentActivity); int type = -1; try{ type = task.get(); }catch(Exception e){ e.printStackTrace(); } if(type == ItemType.PART){ openPartViewer(s); }else if(type == ItemType.KIT){ openKitViewer(s,n); }else if(type == ItemType.ERROR){ Toast.makeText(mParentActivity, "Error getting item type!", Toast.LENGTH_LONG); }else{ Toast.makeText(mParentActivity, "Invalid item type!", Toast.LENGTH_LONG).show(); } } } ); if(mParentActivity.getIntent().getExtras() != null){ Bundle b = mParentActivity.getIntent().getExtras(); if(!b.isEmpty()){ mGetURL += b.getString("com.crummy.kitNum"); } } mPbl = (LinearLayout)mParentActivity.findViewById(R.id.main_pbl); mPbl.setVisibility(View.VISIBLE); LoaderManager lm = getLoaderManager(); if(lm.getLoader(0) != null){ lm.initLoader(0, null, this); } startLoading(); } protected void startLoading(){ getLoaderManager().initLoader(0, null, this); } protected void restartLoading(){ getLoaderManager().restartLoader(0, null, this); } @Override public Loader<Void> onCreateLoader(int arg0, Bundle arg1){ Log.d("MNB", "FKitSchedule: onCreateLoader"); AsyncTaskLoader<Void> loader = new AsyncTaskLoader<Void>(mParentActivity){ @Override public Void loadInBackground(){ try{ getLines(); }catch(Exception e){ e.printStackTrace(); } return null; } }; loader.forceLoad(); return loader; } @Override public void onLoadFinished(Loader<Void> arg0, Void arg1){ Log.d("MNB", "FKitSchedule: onLoadFinished"); mPbl.setVisibility(View.GONE); } @Override public void onLoaderReset(Loader<Void> arg0){ } protected boolean openKitViewer(String kitNum, String kitName){ boolean result = false; Intent i = new Intent(mParentActivity, GenFragmentActivity.class); i.putExtra("com.crummy.frag_layout_id", R.layout.kit_view_frag); i.putExtra("com.crummy.kitNum", kitNum); i.putExtra("com.crummy.kitName", kitName); startActivity(i); return result; } protected boolean openPartViewer(String itemNum){ boolean result = false; Intent i = new Intent(mParentActivity, PartInfoFragmentActivity.class); i.putExtra("com.crummy.partnum", itemNum); startActivity(i); return result; } protected void getLines(){ Log.d("MNB", "FKitSchedule: getLines() "); try{ HttpEntity response = HttpHelper.tryHttp(mParentActivity, mGetURL, null); if(response == null) return; String res = EntityUtils.toString(response); if(res.equals("[]")){ mParentActivity.runOnUiThread(noKitsError); return; } JSONArray jsona = new JSONArray(res); mItems = new ArrayList<ScheduleRowModel>(); Log.d("MNB", "jsona.length() = " + jsona.length()); for(int i=0; i < jsona.length(); i++){ Log.d("MNB", "Reading json line " + i); JSONObject j = (JSONObject) jsona.get(i); ScheduleRowModel srm = new ScheduleRowModel(); srm.custPo = j.getString("custPo"); srm.dueDate = j.getString("dueDate"); srm.partNum = j.getString("KitNumber"); srm.qty = j.getInt("quantity"); srm.status = j.getString("status"); srm.lineId = j.getInt("id"); srm.company = j.getString("name"); srm.rev = j.getString("rev"); if(!mIsHistory){ if(!j.getString("note").equals("null")) srm.note = j.getString("note"); } mItems.add(srm); } }catch(Exception e){ e.printStackTrace(); } mParentActivity.runOnUiThread(returnRes); } private void addItemToList(ScheduleRowModel model){ mItemCount++; mRVAdapter.addData(model); } private HttpEntity updateStatus(int lineId, int statusId){ HttpEntity resEntityPost = null; try{ HttpClient client = new DefaultHttpClient(); String host = HttpHelper.getHost(mParentActivity); HttpPost post = new HttpPost(host + mUpdateStatusURL); List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); nameValuePairs.add(new BasicNameValuePair("line_id", Integer.toString(lineId))); nameValuePairs.add(new BasicNameValuePair("status", Integer.toString(statusId))); post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); HttpResponse res = client.execute(post); resEntityPost = res.getEntity(); }catch(Exception e){ e.printStackTrace(); } return resEntityPost; } protected Runnable returnRes = new Runnable(){ @Override public void run(){ Log.d("MNB", "FKitSchedule returnRes "); mItemCount = 0; mRVAdapter.clear(); if(mItems != null && mItems.size() > 0){ for(int i = 0; i < mItems.size(); i++){ addItemToList(mItems.get(i)); } } } }; private Runnable noKitsError = new Runnable() { public void run(){ Toast t = Toast.makeText(mParentActivity, "No orders for this kit.", Toast.LENGTH_SHORT); t.show(); } }; 

}

Here is the RecyclerView.Adapter:

 import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class KitScheduleRVAdapter extends RecyclerView.Adapter<KitScheduleRVAdapter.ScheduleRowViewHolder>{ private List<ScheduleRowModel> items; private boolean mIsHistory = false; KitScheduleRVAdapter(List<ScheduleRowModel> modelData, boolean isHistory){ Log.d("MNB", "Constructing KitScheduleRVAdapter"); if(modelData == null){ throw new IllegalArgumentException( "modelData must not be null"); } this.items = modelData; mIsHistory = isHistory; } @Override public ScheduleRowViewHolder onCreateViewHolder(ViewGroup vg, int viewType){ Log.d("MNB", "KitScheduleRVAdapter.onCreateViewHolder()"); View itemView = LayoutInflater .from(vg.getContext()) .inflate(R.layout.schedule_row, vg, false); return new ScheduleRowViewHolder(itemView); } @Override public void onBindViewHolder(ScheduleRowViewHolder vh, int pos){ Log.d("MNB", "Binding Viewholder at pos "+pos); ScheduleRowModel model = items.get(pos); vh.date.setText(model.dueDate); vh.po.setText(model.custPo); vh.partnum.setText(model.partNum); vh.qty.setText(""+model.qty); vh.status.setText(model.status); vh.id.setText(""+model.lineId); vh.company.setText(model.company); vh.rev.setText(model.rev); vh.note.setText(model.note); Calendar cutoff = Calendar.getInstance(); cutoff.add(Calendar.DAY_OF_MONTH, 14); Date co = cutoff.getTime(); SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd", Locale.US); Date d = new Date(); try { d = s.parse(model.dueDate); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } View statusBar = vh.statusBar; if(!mIsHistory){ if(model.status.equals("Ready")){ statusBar.setBackgroundResource(R.color.sh_ready); }else if(model.status.equals("Waiting")){ statusBar.setBackgroundResource(R.color.sh_waiting); }else if(model.status.equals("In Production")){ statusBar.setBackgroundResource(R.color.sh_inprod); } else if(d.before(co)){ statusBar.setBackgroundResource(R.color.sh_soon); }else{ statusBar.setBackgroundResource(R.color.sh_open); } } else { statusBar.setBackgroundResource(R.color.sh_ready); } } @Override public int getItemCount(){ return items.size(); } public void addData(ScheduleRowModel data){ items.add(data); this.notifyItemInserted(items.size() - 1); } public void clear(){ Log.d("MNB", "KitScheduleRVAdapter.clear()"); int itemsCleared = items.size(); items.clear(); notifyItemRangeRemoved(0, itemsCleared); } public final static class ScheduleRowViewHolder extends RecyclerView.ViewHolder{ TextView date; TextView po; TextView qty; TextView partnum; TextView status; TextView id; TextView company; TextView rev; TextView note; View statusBar; public ScheduleRowViewHolder(View itemView){ super(itemView); date = (TextView) itemView.findViewById(R.id.sr_date); po = (TextView) itemView.findViewById(R.id.sr_po); qty = (TextView) itemView.findViewById(R.id.sr_qty); partnum = (TextView) itemView.findViewById(R.id.sr_pn); status = (TextView) itemView.findViewById(R.id.sr_status); id = (TextView) itemView.findViewById(R.id.sr_order_line_id); company = (TextView) itemView.findViewById(R.id.sr_company); rev = (TextView) itemView.findViewById(R.id.sr_rev); note = (TextView) itemView.findViewById(R.id.sr_note); statusBar = itemView.findViewById(R.id.sr_status_bar); } } } 
+6
source share
1 answer

You have to do view assignments (and LayoutManager assignments) in onViewCreated() instead of onActivityCreated() - onActivityCreated() will no longer occur when your Fragment is disconnected and reconnected through the ViewPager . The view will be destroyed ( onDestroyView() ) and recreated ( onCreateView() ) when it disappears and returns, but the RecyclerView will never get its LayoutManager in this case.

EDIT: As a result of the comments, another problem is to use the parent Activity to solve the RecyclerView instead of using Fragment's View . Since findViewById() does a depth search, if you have several linked Fragments that contain views with the same identifier, you may get the wrong look, but everything will compile and run, only with unexpected results. Use the View returned in onViewCreated() to limit the search to just the layout you onCreateView() in onCreateView() .

+3
source

Source: https://habr.com/ru/post/977854/


All Articles