NullPointerException when switching between fragments containing RecyclerView

Mat*_*son 6 java android android-fragments android-support-library

I'm having a real hard time trying to figure this out. I'm getting a NullPointerException when switching between Fragments with RecyclerView but only in certain order. Code and Exception is below. First an explanation:

I have a fragment with this tabbed layout as seen here: 在此输入图像描述

3 of these tabs use the same Fragment containing a RecyclerView: Schedule, History and Test. Any of them will work if I select them first. Now the problem is when 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, though, that will load just fine.

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

If I select Test then Schedule those two will work, but then going to History will cause the exception.

If I select Schedule then History the exception will happen.

If I select History then Schedule those two will work, but then going to Test the exception will happen

If I select History then Test the exception will happen

I extended the RecycleView and put a break in the onMeasure to see if I could figure out what was going on and I noticed that the mAdapter of the RecyclerView was null. I checked inside the setAdapter() of the RecyclerView and it was always passed a valid adapter. So it seems like the mAdapter is getting cleared at some point. I just can't figure out why it only happens when I open the tabs going from left to right and not right to left...

任何帮助将不胜感激,因为我达到我的智慧结束这一个!

这是NullPointerException:

链接到异常跟踪(不得不把它放在这里以避免字符限制)

编辑: 后追赶NullPointerExceptionIllegalStateException太弹出.看起来是来自LayoutManager. 这是该输出的链接

这是选项卡视图的片段:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是片段:

public class RVFKitSchedule extends Fragment 
Run Code Online (Sandbox Code Playgroud)

实现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();
    }
};
Run Code Online (Sandbox Code Playgroud)

}

这是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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

kco*_*ock 3

您应该进行视图分配(和 LayoutManager 分配),而onViewCreated()不是-- 当onActivityCreated()您通过. 视图在消失和返回时将被销毁 ( ) 并重新创建 ( ),但在这种情况下永远不会得到它。onActivityCreated()FragmentViewPageronDestroyView()onCreateView()RecyclerViewLayoutManager

编辑:从评论中提取,另一个问题是使用父级Activity来解决RecyclerView而不是使用Fragment's View. 由于findViewById()执行深度优先搜索,如果您有多个Fragments包含具有相同 ID 的视图的附件,您最终可能会得到错误的视图,但所有内容仍会编译和运行,只是会出现意外的结果。使用view返回的 inonViewCreated()将视图搜索限制为在 中膨胀的布局onCreateView()