2014-11-06 11 views
6

Mam naprawdę ciężko próbować to rozgryźć. Otrzymuję NullPointerException podczas przełączania między Fragments a RecyclerView, ale tylko w określonej kolejności. Kod i wyjątek znajduje się poniżej. Po pierwsze wyjaśnienie:Wyjątek NullPointerException podczas przełączania między fragmentami zawierającymi RecyclerView

Mam fragment z tego zakładkami układ jak widać tutaj: enter image description here

3 z tych kart wykorzystywać ten sam fragment zawierający RecyclerView: Harmonogram, historia i Test. Każda z nich zadziała, jeśli najpierw je wybiorę. Problem polega na tym, że idę do jednej z pozostałych wyżej wymienionych zakładek po prawej stronie pierwszego wybranego! Jeśli jednak przejdę na lewą stronę pierwszego, który wybrałem, ładowanie będzie w porządku.

Jeśli wybiorę następnie test przejść do historii, a następnie zaplanować wszystko będzie działać dobrze

Jeśli wybiorę testowy następnie zaplanować te dwa będą działać, ale wtedy będzie Historii spowoduje wyjątek .

Jeśli wybiorę opcję Harmonogram, a następnie Historia, nastąpi wyjątek.

Jeśli wybiorę Historia następnie zaplanować te dwa będą działać, ale wtedy będzie testować wyjątek nastąpi

Jeśli wybiorę Historia następnie przetestować wyjątek nastąpi

I rozszerzył RecycleView i umieścić przerwę w onMeasure, aby sprawdzić, czy mogę dowiedzieć się, co się dzieje i zauważyłem, że mAdapter z RecyclerView był null. Sprawdziłem wewnątrz setAdapter() z RecyclerView i zawsze był przekazywany prawidłowy adapter. Wygląda na to, że w pewnym momencie mAdapter jest czyszczone. Po prostu nie mogę zrozumieć, dlaczego tak się dzieje tylko wtedy, gdy otwieram zakładki od lewej do prawej, a nie od prawej do lewej ...

Każda pomoc będzie doceniona, gdy docieram do końca mojego rozumu!

Oto NullPointerException:

Link to Exception trace (było umieścić go tutaj, aby uniknąć limit znaków)

EDIT: Po łapania NullPointerExceptionIllegalStateException wyskakuje też. Wygląda na to, że pochodzi z LayoutManager. Here is a link to that output

Oto fragment widoku z zakładkami:

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; 
     } 
    } 
} 

Tutaj fragment:

public class RVFKitSchedule extends Fragment 

realizuje 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(); 
    } 
}; 

}

Oto widok 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); 
     } 
    } 
} 
+0

Śledzenie stosu wygląda na obcięte (w środku "com.android.internal.policy.impl.PhoneWi") - czy masz sposób rejestrowania/uzyskiwania dostępu do pełnego śledzenia? –

+0

Mam pełny ślad. Zaktualizowałem go linkiem do niego, ponieważ pełny ślad wprowadził mnie do limitu znaków –

Odpowiedz

3

Należy robić swoje wyświetlania przypisań (i przypisanie LayoutManager) w onViewCreated() zamiast onActivityCreated() - onActivityCreated() nie zamierza pojawić się ponownie, gdy Fragment zostanie odłączony i ponownie podłączony przez ViewPager. Widok zostanie zniszczony (onDestroyView()) i odtworzony (onCreateView()), gdy zniknie i powróci, ale RecyclerView nigdy nie otrzyma w tym przypadku numeru LayoutManager.

EDYCJA: Wyciągnięta z komentarzy, innym problemem jest użycie rodzica Activity do rozwiązania RecyclerView zamiast używania View. Ponieważ findViewById() dokonuje pierwszego wyszukiwania w głąb, jeśli posiadasz wiele dołączonych Fragments, które zawierają widoki o tym samym ID, możesz otrzymać nieprawidłowy widok, ale wszystko będzie nadal kompilować i działać, tylko z nieoczekiwanymi rezultatami. Użyj numeru view zwrócony w onViewCreated(), aby ograniczyć wyszukiwanie widoku do układu, który nadałeś w .

+0

Przeniosłem moje widoki i przypisania menedżera układu do 'onViewCreated' ale nadal otrzymuję ten sam wynik –

+0

@MattSwanson Czy możesz zaktualizować ten kod w oryginalnym pytaniu ? – kcoppock

+0

Zaktualizowałem oryginalny –

Powiązane problemy