Android fragments 旋转Android应用程序时,recyclerView中clickListener上的NPE在片段中

Android fragments 旋转Android应用程序时,recyclerView中clickListener上的NPE在片段中,android-fragments,android-recyclerview,onitemclicklistener,Android Fragments,Android Recyclerview,Onitemclicklistener,我在一个片段中的RecyclerView上的clickListener上遇到了一个NPE问题。最初,它可以显示祝酒词,但当我旋转设备并触摸任何图标时: java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.android.newswiz.OnItemClickListener.onItemClick(java.lang.String)' on a null object refer

我在一个片段中的RecyclerView上的clickListener上遇到了一个NPE问题。最初,它可以显示祝酒词,但当我旋转设备并触摸任何图标时:

java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.android.newswiz.OnItemClickListener.onItemClick(java.lang.String)' on a null object reference
    at com.example.android.newswiz.Fragments.PublishersSlidePageFragment$PublishersAdapter$PublishersAdapterViewHolder$1.onClick(PublishersSlidePageFragment.java:86)
这是我的片段类。我已经对日志中存在NPE的部分进行了评论:

package com.example.android.newswiz.Fragments;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.android.newswiz.OnItemClickListener;
import com.example.android.newswiz.R;


public class PublishersSlidePageFragment extends Fragment {

public OnItemClickListener listener;

public PublishersSlidePageFragment(){}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
                         @Nullable ViewGroup container, @Nullable Bundle 
savedInstanceState) {

    ViewGroup rootView = (ViewGroup) 
inflater.inflate(R.layout.fragment_publishers_slide_page,
            container, false);

    RecyclerView mRecyclerView = 
rootView.findViewById(R.id.recyclerView_publishers);
    int numberOfColumns = 
Integer.parseInt(getContext().getResources().getString(R.string.no_of_cols));
    mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 
numberOfColumns));
    mRecyclerView.setHasFixedSize(true);
    PublishersAdapter mAdapter = new PublishersAdapter();
    mRecyclerView.setAdapter(mAdapter);
    return rootView;
}

public void setListener(OnItemClickListener onItemClickListener) {
        this.listener = onItemClickListener;
}

public  class PublishersAdapter extends RecyclerView.Adapter<PublishersAdapter.PublishersAdapterViewHolder>{


    @NonNull
    @Override
    public PublishersAdapter.PublishersAdapterViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.publisher_item, parent, false);
        return new PublishersAdapterViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull PublishersAdapter.PublishersAdapterViewHolder holder, int position) {
            holder.mPublisherImageView.setImageResource(mThumbIds[position]);
            holder.mPublisherTextView.setText(mPublishers[position]);
            holder.bind(mPublishers[position], listener);
    }

    @Override
    public int getItemCount() {
        return mThumbIds.length;
    }

    public class PublishersAdapterViewHolder extends RecyclerView.ViewHolder {

        private final ImageView mPublisherImageView;
        private final TextView mPublisherTextView;

        public PublishersAdapterViewHolder(View itemView) {
            super(itemView);
            mPublisherImageView = itemView.findViewById(R.id.publisher_icon);
            mPublisherTextView = itemView.findViewById(R.id.publisher_desc);
        }

        public void bind(final String mPublisher, final OnItemClickListener clickListener) {
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    clickListener.onItemClick(mPublisher); //NPE on this line
                }
            });
        }
    }
}

    // references to the images
    private Integer[] mThumbIds = {
            R.drawable.abc_news, R.drawable.abc_news_au,
            R.drawable.aftenposten, R.drawable.aljazeera_english,
            R.drawable.bbc, R.drawable.cbs_news,
            R.drawable.cnn_news, R.drawable.entertainment_weekly,
            R.drawable.espn, R.drawable.financial_post,
            R.drawable.financial_times, R.drawable.fox_news,
            R.drawable.fox_sports, R.drawable.ign,
            R.drawable.independent, R.drawable.lequipe,
            R.drawable.metro, R.drawable.msnbc,
            R.drawable.mtvnews, R.drawable.nat_geo,
            R.drawable.nbc_news, R.drawable.new_scientist,
            R.drawable.new_york_magazine, R.drawable.talk_sport,
            R.drawable.techradar, R.drawable.the_guardian,
            R.drawable.the_nyt, R.drawable.wsj
    };


    private String[] mPublishers = {"ABC News", "ABC News (AU)", "Aftenposten", "AlJazeera (ENG)"
            , "BBC", "CBS News", "CNN News", "Entertainment Weekly", "ESPN", "Financial Post",
            "Financial Times", "Fox News", "Fox Sports", "IGN", "Independent", "L'Equipe",
            "Metro", "MSNBC", "MTV News", "Nat. Geo.", "NBC News", "New Scientist",
            "NY Magazine", "Talk Sport", "TechRadar", "The Guardian", "NYT",
            "Wall Street Journal"};
}

我觉得很奇怪,当屏幕旋转时,可以重新创建片段,但无法为片段创建新的clickListener。我搜索了StackOverflow,发现它很有用,但我认为在我的情况下,拯救听众不是一个好的做法。我想创建一个新的侦听器,并在片段中的Recycler视图上设置它。我可能遗漏了一些琐碎的东西,但有人能给我建议吗?

我没有足够的代表发表评论,所以我将提供我认为可能是答案的答案

首先,我注意到没有对“bind(final String mPublisher,final OnItemClickListener clickListener)”方法中传递的视图“itemView”的引用,而且我看不到它全局可用。不知道为什么它会在屏幕旋转后运行一次和NPE

因此,这意味着我不保存fragments实例。当你旋转你的设备时,有些东西被破坏了,如果日志是正确的,那么说明变量mPublisher是以null的形式传入的

MPPublisher在片段第一次创建时就存在,但看起来它在方向更改时被销毁/未被重新创建

我要尝试的第一件事是setRetainInstance(true)跨配置更改保存片段实例。我一直在onCreate()中设置它,但我认为没有理由不能在onCreateView()中设置它

如果不能自动执行此操作,请在重新创建片段时保存发布者列表。这是在片段的onSaveInstanceState()中完成的

@Override
public void onSaveInstanceState(@NonNull Bundle bundle) {
    if (DEBUG) Log.i(TAG, "onSaveInstanceState(Bundle)");
    // make sure to call the super method so that the states of our views are saved
    super.onSaveInstanceState(bundle);

    // save our own state variables
    bundle.putStringArray("publishers", mPublishers);
}
然后可以在onCreateView()方法中还原它们


对于其他变量,例如mThumbIds数组,您可能也必须这样做。

解决方案实际上是一个简单的修复-我实际上需要在Fragment类中定义一个McClickListener接口。然后重写onAttach方法,将侦听器设置为上下文。以前我只是做了一个单独的界面

所以在Fragment类中:

public class PublishersSlidePageFragment extends Fragment {

private OnItemClickListener listener;

public PublishersSlidePageFragment(){}

public interface OnItemClickListener{
    void onItemClick(String item);
}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    listener = (OnItemClickListener) context;
}
// ... rest of code
然后在MainActivity中,声明将实现PublishersSlidePageFragment.OnItemClickListener:

public class MainActivity extends AppCompatActivity implements PublishersSlidePageFragment.OnItemClickListener {
然后该类需要实现侦听器方法:

@Override
public void onItemClick(String item) {
    Toast.makeText(context, "You have clicked " + item, Toast.LENGTH_SHORT).show();
}
public class PublishersSlidePageFragment extends Fragment {

private OnItemClickListener listener;

public PublishersSlidePageFragment(){}

public interface OnItemClickListener{
    void onItemClick(String item);
}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    listener = (OnItemClickListener) context;
}
// ... rest of code
public class MainActivity extends AppCompatActivity implements PublishersSlidePageFragment.OnItemClickListener {
@Override
public void onItemClick(String item) {
    Toast.makeText(context, "You have clicked " + item, Toast.LENGTH_SHORT).show();
}