Search code examples
androidandroid-actionbarandroid-recyclerviewcontextual-action-barmultipleselection

Change ActionBar items depending on how many selected


How can I change what the ActionBar menu contains depending on how many items in a RecyclerView are selected? For instance, when one card is selected in 'selection mode,' I want an Edit icon (pencil) visible. As soon as more than one item is selected, I want the Edit icon to disappear from the options.

The code below: I tried to create a condition in which mDeletionMode creates a menu with an Edit icon present if the number of selected items was <= 1 , and create a menu without the pencil if the number was more than one. My approach was foolish, as I realize that the menu is only created after an item experiences a longClick, and of course only one item would have been selected at that point. I left the dumb-dumb code I used to try this just to show my approach, while what I am actually looking for is the following:

public class SubjectManagerFragment extends BaseFragment implements ActionMode.Callback {

public static ArrayList<SubjectInfo> subjectList = new ArrayList<SubjectInfo>();
public static FloatingActionButton fabCreateSubject;
private AlertDialog.Builder build;
private MultiSelector mMultiSelector = new MultiSelector();

public ActionMode actionMode;
public RecyclerView recList;
public CardView cardView;
public ItemClickSupport itemClick;

// currently an adaptation from:
// https://github.com/bignerdranch/recyclerview-multiselect#modal-multiselection-with-long-click

public ActionMode.Callback mDeleteMode = new ModalMultiSelectorCallback(mMultiSelector) {

    @Override
    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
        super.onCreateActionMode(actionMode, menu);

        if (mMultiSelector.getSelectedPositions().size() <= 1) {
            getActivity().getMenuInflater().inflate(R.menu.menu_subject_manager, menu);
        } else {
            getActivity().getMenuInflater().inflate(R.menu.menu_subject_manager_multiple, menu);
        }
        return true;
    }

    @Override
    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
        switch (menuItem.getItemId()) {
            case R.id.action_select_all:
                // Delete crimes from model

                //actually do nothing.
                mMultiSelector.clearSelections();
                return true;
            default:
                break;
        }
        return false;
    }
};
public static final String ARG_PARAM1 = "param1";
public static final String ARG_PARAM2 = "param2";

private String mParam1;
private String mParam2;

public static SubjectManagerFragment newInstance(String param1, String param2) {
    SubjectManagerFragment fragment = new SubjectManagerFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
}

public SubjectManagerFragment() {
    // Required empty public constructor
}

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

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

@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    return false;
}

@Override
public void onDestroyActionMode(ActionMode mode) {

}


//non graphical initialization
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(false);
    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
        mParam2 = getArguments().getString(ARG_PARAM2);
    }
    setRetainInstance(true);

}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View smFragmentView = inflater.inflate(R.layout.fragment_subject_manager, container, false);
    recList = (RecyclerView) smFragmentView.findViewById(R.id.subject_card_list);
    cardView = (CardView) smFragmentView.findViewById(R.id.subject_card);
    recList.setHasFixedSize(true);
    LinearLayoutManager llm = new LinearLayoutManager(getActivity());
    llm.setOrientation(LinearLayoutManager.VERTICAL);
    subjectList = getSubjectInfoArrayList();
    recList.setLayoutManager(llm);
    recList.setAdapter(new CrimeAdapter());

    fabCreateSubject = (FloatingActionButton) smFragmentView.findViewById(R.id.fab_create_subject);
    fabCreateSubject.setOnClickListener (new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            build = new AlertDialog.Builder(getActivity());
            LayoutInflater inflater1 = getActivity().getLayoutInflater();
            View alertview = inflater1.inflate(R.layout.create_subject_dialog, null);

            // Pass null as the parent view because its going in the dialog layout
            build.setView(alertview);
            final EditText inputSubjectName = (EditText) alertview.findViewById(R.id.dialog_edit_subject_card_name);
            final EditText inputSubjectGrade = (EditText) alertview.findViewById(R.id.dialog_edit_subject_card_grade);

            build.setTitle("Add Subject");
            build.setPositiveButton("Save", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int id) {
                    String enteredSubjectName = inputSubjectName.getText().toString();
                    int enteredSubjectGrade = Integer.parseInt("0" + inputSubjectGrade.getText().toString()); //was getting stupid error from null value going to int?
                    boolean enteredSubjectIsArchived = false;

                    if (subjectCanBeEntered(inputSubjectName, inputSubjectGrade, subjectList)) {
                        SubjectInfo si = new SubjectInfo(enteredSubjectName, "Assignments", enteredSubjectGrade, enteredSubjectIsArchived, true);
                        si.save();
                        subjectList.add(si);
                        getActivity().recreate();
                        sa.notifyDataSetChanged();
                        recList.smoothScrollToPosition(subjectList.size()-1);
                    } //will need to check if subject already exists, but YOLO for now.
                    dialog.cancel();
                }
            });
            build.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    dialog.cancel();
                }
            });
            AlertDialog alert = build.create();
            alert.show();
        }
    });
    // Inflate the layout for this fragment
    return smFragmentView;
}

public ArrayList<SubjectInfo> getSubjectInfoArrayList(){
    ArrayList<SubjectInfo> sial= new ArrayList<SubjectInfo>();
    List<SubjectInfo> sil = SubjectInfo.listAll(SubjectInfo.class);
    sial.addAll(sil);
    for (int go = 0; go <sial.size(); go++) {
        if (sial.get(go).itemHeaderTitle.equals("Archived")) {
            sial.remove(go);
        }
    }
    return sial;
}

public boolean subjectCanBeEntered (EditText inputName, EditText inputGrade, ArrayList<SubjectInfo> aList) {
    boolean enterable = true;
    if ((inputName.getText().toString().equals(""))) {
        enterable = false;
        Toast.makeText(
                getActivity().getApplicationContext(), "Enter a class name.", Toast.LENGTH_SHORT).show();
    }
    if ((inputGrade.getText().toString().equals(""))) { // I don't think hint is picked up
        enterable = false;
        Toast.makeText(
                getActivity().getApplicationContext(), "Enter a grade.", Toast.LENGTH_SHORT).show();
    }
    for (int go = 0; go < aList.size(); go++) {
        if (inputName.getText().toString().equals(aList.get(go).subjectName)) {
            enterable = false;
            Toast.makeText(
                    getActivity().getApplicationContext(), "You've already saved a class with that name.", Toast.LENGTH_LONG).show();
        }
    }
    return enterable;
}

private class CrimeHolder extends SwappingHolder
        implements View.OnClickListener, View.OnLongClickListener {
    protected TextView vSubjectName;
    protected EditText vSubjectGrade;
    protected RelativeLayout vSubjectLayout;
    private SubjectInfo sInfo;

    public CrimeHolder(View itemView) {
        super(itemView, mMultiSelector);

        vSubjectName = (TextView) itemView.findViewById(R.id.subject_card_name_textView);
        vSubjectGrade = (EditText) itemView.findViewById(R.id.subject_card_grade_editText);
        vSubjectLayout = (RelativeLayout) itemView.findViewById(R.id.subject_card_relative_layout);
        itemView.setOnClickListener(this);
        itemView.setOnLongClickListener(this);
        itemView.setLongClickable(true);
    }

    public void bindCrime(SubjectInfo si) {
        sInfo = si;
        vSubjectName.setText(sInfo.subjectName);
        vSubjectGrade.setText(Integer.toString(si.subjectGrade));
        vSubjectLayout.setBackgroundColor(Color.parseColor(SubjectAdapter.giveSubjectHexValue((double) si.subjectGrade)));
    }

    @Override
    public void onClick(View v) {
        if (sInfo == null) {
            return;
        }
        if (!mMultiSelector.tapSelection(this)) {
            // This condition is the same as, if not in ActionMode, handle the click normally:
            //getActionBar().startActionMode(mDeleteMode);
        }
    }

    @Override
    public boolean onLongClick(View v) {
        //ActionBarActivity activity = (ActionBarActivity)getActivity();
        getActionBar().startActionMode(mDeleteMode);
        mMultiSelector.setSelected(this, true);
        return true;
    }
}

private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> {
    @Override
    public CrimeHolder onCreateViewHolder(ViewGroup parent, int pos) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.subject_card_layout, parent, false);
        return new CrimeHolder(view);
    }

    @Override
    public void onBindViewHolder(CrimeHolder holder, int pos) {
        SubjectInfo sInfo = subjectList.get(pos);
        holder.bindCrime(sInfo);
    }

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

SO my question is: how to programmatically either remove/add the Edit icon OR switch to the other menu (with one fewer option) depending on how may items are selected.


Solution

  • Use an ActionMode instance to manipulate the ActionBar menu.

    @Override
    public boolean onLongClick(View v) {
        this.actionMode = getActionBar().startActionMode(mDeleteMode);
        mMultiSelector.setSelected(this, true);
        return true;
    }
    

    Below is where your menuItem gets initialized. Keep in mind that onCreateActionMode resides in the creation of the new ModalMultiSelectorCallback.

        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
            super.onCreateActionMode(actionMode, menu);getActivity().getMenuInflater().inflate(R.menu.menu_subject_manager, menu);
            mEditItem = actionMode.getMenu().findItem(R.id.action_edit);
            mActionMenu = actionMode.getMenu();
            return true;
        }
    

    Now, mEditItem can be removed or replaced with ease, as mEditItem is an instance variable. I'll provide my implementation in my onClick()

    @Override
        public void onClick(View v) {
    
            if (sInfo == null) {
                return;
            }
            if (!mMultiSelector.tapSelection(this)) {
                // if not in selection mode, this is handled
            }
            if (mMultiSelector.tapSelection(this)) {
                mMultiSelector.tapSelection(this);
    
                switch (mMultiSelector.getSelectedPositions().size()) {
                    case 0:
                        actionMode.finish();
                        break;
                    case 1:
                        mEditItem.setVisible(true);
                        break;
                    case 2:
                        mEditItem.setVisible(false);
                        break;
                    default:
                        break;
                }
            }
        }
    

    And there I have it. For me, I just needed an item to disappear/reappear according to how many items were selected at any given moment. The .setVisible() method took care of this for me. I hope this helps someone with a similar issue!