我有一个活动A,片段为frag2。在片段内部,我有一个RecyclerView和Adapter来显示自定义类对象的列表。将对象添加到适配器是通过编程方式处理的。我在TwoFragment内部有一个打开FragmentDialog的按钮。我想通过确认此对话框将一个对象添加到我的适配器,但是当从FragmentDialog调用时,适配器似乎为空。

相同的适配器不为null,并且如果我从片段OnClick调用它也可以使用。

此外,适配器仅在屏幕旋转后才为空,在旋转之前工作正常。

为了在两个片段之间进行通信,我在活动A中实现了一个通信器类。

活动A

public void respond(String type) {
        frag2.addSupport(type);
    }


碎片2

public RecyclerView rv;
public ArrayList<support> supports;
public myAdapter adapter;

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supports = new ArrayList<>();
        adapter = new myAdapter(supports);
    }

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View layout = inflater.inflate( R.layout.fragment_two, container, false);
        layout.setId(R.id.frag2);


        if (savedInstanceState!=null)
        {
             supports = savedInstanceState.getParcelableArrayList("supports");
        }

        rv = (RecyclerView) layout.findViewById(R.id.rv);
        adapter = new myAdapter(supports);
        rv.setAdapter(myAdapter);
        rv.setLayoutManager(new LinearLayoutManager(getActivity()));
        rv.setItemAnimator(new DefaultItemAnimator());

@Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id){
            case R.id.button1:
                addSupport(type); // THIS WORKS ALWAYS, even after screen rotate
                break;

            case R.id.button2:
                showDialog();
                break;
        }

    }

public void showDialog(){

        FragmentManager manager = getFragmentManager();
        myDialog dialog = new myDialog();
        dialog.show(manager, "dialog");
    }

public void addSupport(String type){

    adapter.addItem(new support(type));  // this line gives null pointer on adapter, but only if called after screen rotate and only if called from the dialog
            }


对话

communicator comm;

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {


        View view = inflater.inflate(R.layout.dialog, null);
        comm = (myCommunicator) getActivity();
        create = (Button) view.findViewById(R.id.button_ok);
        create.setOnClickListener(this);

        return view;
    }



@Override
    public void onClick(View v) {

        if(v.getId()==R.id.button_ok)
        {
            // some controls to set type
            comm.respond(type)
            dismiss();
        }
        else {

            dismiss();
        }


myAdapter

public class myAdapter extends RecyclerView.Adapter<myAdapter.VH> {
    private LayoutInflater inflater;
    private ArrayList<support> data = new ArrayList<>();

// settings for viewholder

public myAdapter (ArrayList<support> data)
    {
        this.data=data;
    }

    public void addItem(support dataObj) {

        data.add(dataObj);
        notifyItemInserted(data.size());
    }

}


日志猫

FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference


希望没有错误,为了简化理解,我缩短了代码。请记住,如果我从不旋转屏幕,一切都会正常。

我是android的初学者,现在已经坚持了好几天。请帮忙。

最佳答案

要理解这个问题,就像您说的那样:


  ..如果我从不旋转屏幕,一切都会正常


因此,首先要了解旋转发生了什么,这是来自Android Developer website的引号:


  警告:每次用户旋转屏幕时,您的活动都会被破坏并重新创建。当屏幕改变方向时,由于屏幕配置已更改并且您的活动可能需要加载替代资源(例如布局),因此系统会销毁并重新创建前台活动。


好的,现在了解错误:

FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference


本质上,在您的dialog类中,您通过声明了以下内容来创建了强大的依赖关系:

comm = (myCommunicator) getActivity();

因为comm引用旋转时会销毁的对象,因此NullPointerException

为了进一步了解运行时更改,例如方向更改,建议您通过Handling Runtime Changes进行操作。

更新资料


  感谢您的回答,您会推荐什么而不是comm =(myCommunicator)getActivity();。 ?


该解决方案分为三个部分:


确保onCreateActivity A具有以下内容:




    @Override
    public void onCreate(Bundle savedInstanceState) {
        ......

        // find the retained fragment on activity restarts
        FragmentManager fm = getFragmentManager();
        frag2 = (Frag2) fm.findFragmentByTag(“frag2”);

        // create frag2 only for the first time
        if (frag2 == null) {
            // add the fragment
            frag2 = new Frag2();
            fm.beginTransaction().add(frag2 , “frag2”).commit();
        }
        ......

    }




setRetainInstance(true)添加到onCreatefrag2中。
删除隐式引用,即comm = (myCommunicator) getActivity();,并为dialog实现一些松散耦合。


对话



    public interface Communicator {

        void respond(String type);
    }

    Communicator comm;

    ....

    public void addCommunicator(Communicator communicator) {

        comm = communicator;
    }

    public void removeCommunicator() {

        comm = null;
    }

    @Override
    public void onClick(View v) {
        if((v.getId()==R.id.button_ok) && (comm!=null))
        {
            // some controls to set type
            comm.respond(type);
        }
        // Regardless of what button is pressed, the dialog will dismiss
        dismiss();
    }



这使您可以在frag2(或与此相关的任何其他类)中执行以下操作:

碎片2

<pre><code>
public class Frag2 extends Fragment implements dialog.Communicator {

    ........

    public void showDialog() {

        FragmentManager manager = getFragmentManager();
        myDialog dialog = new myDialog();
        dialog.addCommunicator(this);
        dialog.show(manager, "dialog");
    }

    @Override
    public void respond(String type){

        adapter.addItem(new support(type));
    }

}

10-04 19:17