简体   繁体   中英

get context after returning from adapter to fragment

I am using contextual action bar in my fragment("UnitsFragment.java") to delete and edit items of recyclerview. But when I come back from recyclerview adapter class("UnitsRv.java"). The context seems to be null. I tried returning context from adapter and it worked for function " prepareSelection ". However for " onActionItemClicked " under ActionMode.callback , I need to get context so that I can use alertdialog for editing the items.

The "requireContext()" throws this error: Fragment UnitsFragment{e3a36c8 (b4957397-055a-4b1c-8af2-fee89a3e9b35)} not attached to a context. Here are my codes. UnitsFragment.java

public class UnitsFragment extends Fragment {
    private static final String TAG = "UnitsFragment";

    private RecyclerView recyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager layoutManager;
    ArrayList<UnitsList> unitsLists = new ArrayList<>();
    Activity mcontext = getActivity();
    Context dcontext;

    ActionMode actionMode;
    public static ArrayList<UnitsList> selectionList = new ArrayList<>();
    public static boolean isInActionMode = false;

    List<String> list = DatabaseClient.getInstance(getContext())
            .getUserDatabase()
            .getUnitDao().findUnitNameList();

    public UnitsFragment() {
    }

    private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            mode.getMenuInflater().inflate(R.menu.menu_item_action, menu);
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
                case R.id.menu_item_edit:
                    if (selectionList.size() == 1) {
                        final EditText editText = new EditText(requireContext());
                        new AlertDialog.Builder(requireContext())
                                .setTitle("Rename unit name").setView(editText)
                                .setPositiveButton("Rename", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int i) {
                                        UnitsList unitsList = selectionList.get(0);
                                        unitsList.setUnit_name(editText.getText().toString().trim());
                                        isInActionMode = false;
                                        ((UnitsRv) mAdapter).changeDataItem(getCheckedLastPosition(), unitsList);
                                        actionMode.finish();
                                        selectionList.clear();
                                    }
                                })
                                .create()
                                .show();
                        Toast.makeText(getContext(), "Edit", Toast.LENGTH_SHORT).show();
                        mode.finish();
                        return true;
                    }
                case R.id.menu_item_delete:
                    isInActionMode = false;
                    ((UnitsRv) mAdapter).removeData(selectionList);
                    Toast.makeText(getContext(), "Delete", Toast.LENGTH_SHORT).show();
                    actionMode.finish();
                    selectionList.clear();
                    return true;
                default:
                    return false;

            }
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            actionMode = null;
        }
    };

    private int getCheckedLastPosition() {
        ArrayList<UnitsList> dataSet = UnitsRv.getDataSet();
        for (int i = 0; i < dataSet.size(); i++) {
            if (dataSet.get(i).equals(selectionList.get(0))) {
                return i;
            }
        }
        return 0;
    }


    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_units, container, false);
        setHasOptionsMenu(true);

        dcontext = rootView.getContext();
        Log.d(TAG, "onCreateView1: " + dcontext);
        recyclerView = rootView.findViewById(R.id.rv_units);
        recyclerView.setHasFixedSize(true);
        recyclerView.addItemDecoration(new DividerItemDecoration(getContext(),
                DividerItemDecoration.HORIZONTAL));
        recyclerView.addItemDecoration(new DividerItemDecoration(getContext(),
                DividerItemDecoration.VERTICAL));
        layoutManager = new GridLayoutManager(getContext(), 2);
        recyclerView.setLayoutManager(layoutManager);


        for (String string : list) {
            unitsLists.add(new UnitsList(string));
        }
        Log.d(TAG, "onCreateView: " + getContext());
        mAdapter = new UnitsRv(mcontext,unitsLists);
        recyclerView.setAdapter(mAdapter);

        return rootView;
    }

    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        inflater.inflate(R.menu.add, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_item_new:
                final View customLayout = getLayoutInflater().inflate(R.layout.add_unit_dialog, null);
                final EditText edt_unit_name = customLayout.findViewById(R.id.edt_new_unit_name);
                final AlertDialog dialog = new AlertDialog.Builder(getContext())
                        .setView(customLayout)
                        .setTitle("Unit name")
                        .setPositiveButton(android.R.string.ok, null) //Set to null. We override the onclick
                        .setNegativeButton(android.R.string.cancel, null)
                        .create();

                dialog.setOnShowListener(new DialogInterface.OnShowListener() {
                    @Override
                    public void onShow(DialogInterface dialogInterface) {
                        Button ok_btn = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
                        Button cancel_btn = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);

                        ok_btn.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                String unit_name = edt_unit_name.getText().toString().trim();
                                if (!TextUtils.isEmpty(unit_name)) {
                                    String old_unit_name = DatabaseClient.getInstance(getContext())
                                            .getUserDatabase()
                                            .getUnitDao()
                                            .findByUnitName(unit_name);
                                    if (old_unit_name == null) {
                                        DatabaseClient.getInstance(getContext())
                                                .getUserDatabase()
                                                .getUnitDao()
                                                .insertUnits(new UnitsList(unit_name));
                                        unitsLists.add(new UnitsList(unit_name));
                                        dialog.dismiss();
                                    } else {
                                        edt_unit_name.setError("Unit already exists");
                                    }

                                } else {
                                    edt_unit_name.setError("Can't be empty");
                                }
                            }
                        });

                        cancel_btn.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                dialog.dismiss();
                            }
                        });
                    }
                });
                dialog.show();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    public void prepareSelection(Context context,int position) {
        if(actionMode == null) {
            isInActionMode = true;

            for (String string : list) {
                unitsLists.add(new UnitsList(string));
            }
            mAdapter = new UnitsRv(context, unitsLists);
            Log.d(TAG, "prepareSelection: " + mAdapter);
            Log.d(TAG, "prepareSelection1: " + dcontext);
            mcontext = (Activity)context;
            actionMode = mcontext.startActionMode(actionModeCallback);

            mAdapter.notifyDataSetChanged();

            if (!selectionList.contains(unitsLists.get(position))) {
                selectionList.add(unitsLists.get(position));
            }
            updateViewCounter();
        }
    }

    private void updateViewCounter() {
        int counter = selectionList.size();
        if (counter == 1) {
            actionMode.setTitle(counter + "item selected");
        } else {
            actionMode.setTitle(counter + "items selected");
        }
    }
}

This is my Adapter class. UnitsRv.java

public class UnitsRv extends RecyclerView.Adapter<UnitsRv.ViewHolder> {
    private static final String TAG = "UnitsRv";

    private static ArrayList<UnitsList> munitsLists = new ArrayList<>();
    UnitsFragment unitsFragment = new UnitsFragment();
    Context mcontext;

    public UnitsRv(Context context,ArrayList<UnitsList> unitsLists) {
        mcontext = context;
        munitsLists = unitsLists;
    }

    class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
        TextView unit_name;

        public ViewHolder(View v) {
            super(v);
            unit_name = v.findViewById(R.id.unit_name);

            v.setOnLongClickListener(this);
        }


        @Override
        public void onClick(View view) {
            if (UnitsFragment.isInActionMode){
                unitsFragment.prepareSelection(mcontext,getAdapterPosition());
                notifyItemChanged(getAdapterPosition());
            }
        }

        @Override
        public boolean onLongClick(View view) {
            Log.d(TAG, "onLongClick: " + getAdapterPosition());
            unitsFragment.prepareSelection(view.getContext(),getAdapterPosition());
            return true;
        }
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.units_item, parent, false);
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
        holder.unit_name.setText(munitsLists.get(position).getUnit_name());
        if (UnitsFragment.isInActionMode){
            if (UnitsFragment.selectionList.contains(munitsLists.get(position))){
                holder.itemView.setBackgroundResource(R.color.colorSelected);
            }
        }

    }

    @Override
    public int getItemCount() {
        return munitsLists.size();
    }

    public static ArrayList<UnitsList> getDataSet() {
        return munitsLists;
    }

    public void changeDataItem(int position, UnitsList unitsList) {
        munitsLists.set(position, unitsList);
        notifyDataSetChanged();
    }

    public void removeData(ArrayList<UnitsList> list) {
        for (UnitsList unitsList : list) {
            munitsLists.remove(unitsList);
        }
        notifyDataSetChanged();
    }
}

First, you should not create instance of your UnitsFragment inside your adapter.

You can use EventBus to communicate between Activities, Fragments, Adapters, etc.

Or You can do your task using interface . like below.

Create an interface like this

public interface AdapterCallback {
       void prepareSelection(Context context,int position);
}

In your UnitsFragment implement the above interface . like the following

public class UnitsFragment extends Fragment implements AdapterCallback{ 

    // your other codes

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_units, container, false);
        setHasOptionsMenu(true);

        // .... your other codes

        Log.d(TAG, "onCreateView: " + getContext());

        // modify below line like this
        mAdapter = new UnitsRv(mcontext, unitsLists, this); // here you have to pass an extra parameter that will implement your callback method from adapter.

        recyclerView.setAdapter(mAdapter);

        return rootView;
    }

    // ... you other codes

    @Override
    public void prepareSelection(Context context,int position) {
        if(actionMode == null) {
            isInActionMode = true;

            for (String string : list) {
                unitsLists.add(new UnitsList(string));
            }
            mAdapter = new UnitsRv(context, unitsLists,this); // add this as parameter.
            Log.d(TAG, "prepareSelection: " + mAdapter);
            Log.d(TAG, "prepareSelection1: " + dcontext);
            mcontext = (Activity)context;
            actionMode = mcontext.startActionMode(actionModeCallback);

            mAdapter.notifyDataSetChanged();

            if (!selectionList.contains(unitsLists.get(position))) {
                selectionList.add(unitsLists.get(position));
            }
            updateViewCounter();
        }
    }
    // other codes
}

Now, inside your Adapter you need to add an extra argument in constructor of UnitsRv and call your interface method from adapter ussing mAdapterCallback .

public class UnitsRv extends RecyclerView.Adapter<UnitsRv.ViewHolder> {
    private static final String TAG = "UnitsRv";

    private static ArrayList<UnitsList> munitsLists = new ArrayList<>();
    UnitsFragment unitsFragment = new UnitsFragment(); // remove this line
    private AdapterCallback mAdapterCallback; // add this line
    Context mcontext;

    public UnitsRv(Context context,ArrayList<UnitsList> unitsLists, AdapterCallback callback) {
        mcontext = context;
        munitsLists = unitsLists;
        this.mAdapterCallback = callback; // add this line
    }

    class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
        TextView unit_name;

        public ViewHolder(View v) {
            super(v);
            unit_name = v.findViewById(R.id.unit_name);

            v.setOnLongClickListener(this);
        }


        @Override
        public void onClick(View view) {
            if (UnitsFragment.isInActionMode){
                mAdapterCallback.prepareSelection(mcontext,getAdapterPosition()); // modify this line
                notifyItemChanged(getAdapterPosition());
            }
        }

        @Override
        public boolean onLongClick(View view) {
            Log.d(TAG, "onLongClick: " + getAdapterPosition());
            mAdapterCallback.prepareSelection(view.getContext(),getAdapterPosition()); // modify this line
            return true;
        }
    }
    // your other codes....

}

You should check for your fragment is attached or not with isAdded()

place if(!isAdded()) return in your onActionItemClicked . and replace requireContext() with getContext() because requireContext() always throws IllegalStateException if fragment is not attached.

override onAttach method to save context in your fragment.

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    this.context= context;
}

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM