How to add dividers between specific menu items?

后端 未结 7 1883
旧巷少年郎
旧巷少年郎 2020-12-03 01:35

Background

I have a menu item in the action bar (toolbar actually) that when clicked, shows a list of items to choose from, similar to radio-buttons:



        
相关标签:
7条回答
  • 2020-12-03 01:44

    now :

    group1[ item0 item1 item2 ] group1[item3];
    

    change to :

    group1[ item0 item1] group1[item2 item3]
    

    group has a divider; it likes that group can add a divder between item;

    if divider is unavailable,try background; i never user menu; its my guess;

    0 讨论(0)
  • 2020-12-03 01:47

    OK, I've found a nice workaround, but I'm not sure the styling should be this way. That's what I'm missing:

    1. background of items is on top of the background of the popup of the spinner, and I'm not sure if that's the correct way to put it.
    2. I used the white background of the support library for the popup of the spinner. I think there should be a better way to make it white.
    3. I need to know what is the correct style of the divider. for now I used a simple one
    4. Action bar item style is missing. I just used a simple ImageView, and I think it should be different.
    5. For some reason, on some Android versions (maybe Lollipop and below) the background of the items look black instead of white.
    6. The spinner might sometimes have issues with setOnItemSelectedListener , not sure when.

    MainActivity

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        final MenuItem item = menu.findItem(R.id.action_settings);
        final Spinner spinner = ((Spinner) MenuItemCompat.getActionView(item));
        SimpleImageArrayAdapter adapter = new SimpleImageArrayAdapter(this);
        spinner.setAdapter(adapter);
        return true;
    }
    
    public class SimpleImageArrayAdapter extends ArrayAdapter<String> {
        private final String[] items = {"item 1", "item 2", "item 3", "extra item"};
    
        public SimpleImageArrayAdapter(Context context) {
            super(context, 0);
        }
    
        @Override
        public int getCount() {
            return items.length;
        }
    
        @Override
        public String getItem(final int position) {
            return items[position];
        }
    
        @Override
        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            View rootView = convertView == null ? LayoutInflater.from(getContext()).inflate(R.layout.spinner_item, parent, false) : convertView;
            TextView tv = (TextView) rootView.findViewById(android.R.id.text1);
            tv.setTextColor(0xff000000);
            tv.setText(items[position]);
            boolean isLastItem = position == getCount() - 1;
            rootView.findViewById(R.id.action_divider).setVisibility(isLastItem ? View.VISIBLE : View.GONE);
            rootView.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            return rootView;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            //this is the view that's shown for the spinner when it's closed
            ImageView iv = new ImageView(getContext());
            iv.setImageResource(android.R.drawable.ic_menu_add);
            int viewSize = getDimensionFromAttribute(MainActivity.this, android.support.v7.appcompat.R.attr.actionBarSize);
            iv.setLayoutParams(new ViewGroup.LayoutParams(viewSize, viewSize));
            iv.setScaleType(ScaleType.CENTER_INSIDE);
            iv.setBackgroundResource(getResIdFromAttribute(MainActivity.this, R.attr.selectableItemBackground));
            return iv;
        }
    
    }
    
    public static int getResIdFromAttribute(final Activity activity, final int attr) {
        if (attr == 0)
            return 0;
        final TypedValue typedValue = new TypedValue();
        activity.getTheme().resolveAttribute(attr, typedValue, true);
        return typedValue.resourceId;
    }
    
    public static int getDimensionFromAttribute(final Context context, final int attr) {
        final TypedValue typedValue = new TypedValue();
        if (context.getTheme().resolveAttribute(attr, typedValue, true))
            return TypedValue.complexToDimensionPixelSize(typedValue.data, context.getResources().getDisplayMetrics());
        return 0;
    }
    

    res/menu/menu_main.xml

    <menu xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          xmlns:tools="http://schemas.android.com/tools"
          tools:context="com.example.user.myapplication.MainActivity">
        <item
            android:id="@+id/action_settings"
            android:actionLayout="@layout/spinner"
            android:title=""
            app:actionLayout="@layout/spinner"
            app:showAsAction="always"
            />
    </menu>
    

    res/layout/spinner_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <ImageView
            android:id="@+id/action_divider"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/divider"/>
    
        <TextView
            android:id="@android:id/text1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?android:attr/selectableItemBackground"
            android:gravity="center_vertical"
            android:minHeight="?attr/listPreferredItemHeightSmall"
            android:paddingEnd="?attr/listPreferredItemPaddingRight"
            android:paddingLeft="?attr/listPreferredItemPaddingLeft"
            android:paddingRight="?attr/listPreferredItemPaddingRight"
            android:paddingStart="?attr/listPreferredItemPaddingLeft"
            android:textAppearance="?attr/textAppearanceListItemSmall"/>
    
    </LinearLayout>
    

    res/layout/spinner.xml

    <?xml version="1.0" encoding="utf-8"?>
    <Spinner
        android:id="@+id/spinner"
        style="@style/SpinnerWithoutArrow"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    

    res/values/styles.xml

    <style name="SpinnerWithoutArrow" parent="@style/Widget.AppCompat.Spinner">
        <item name="android:background">@null</item>
        <item name="android:popupBackground">@drawable/abc_popup_background_mtrl_mult</item>
    </style>
    

    res/drawable/divider.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <size
            android:height="1dp"/>
        <solid android:color="#FFff0000" />
    </shape>
    
    0 讨论(0)
  • 2020-12-03 01:51

    As of SDK version 28, you can use menu.setGroupDividerEnabled(boolean). If you're using ContextMenu this is only supported on SDK 28+, but MenuCompat offers backwards compatibility when used in onCreateOptionsMenu().

    This will add a divider between the actions for each different groupId, shown as 0 and 1 below:

    menu.add(0, getAdapterPosition(), action1, R.string.action1);
    menu.add(1, getAdapterPosition(), action2, R.string.action2);
    menu.setGroupDividerEnabled(true); 
    
    // Or for MenuCompat < SDK 28:
    MenuCompat.setGroupDividerEnabled(menu, true);
    

    Documentation here: https://developer.android.com/reference/android/view/Menu#setGroupDividerEnabled(boolean)


    EDIT: Sample code as requested by asker:

    Here's the code I am currently using in my app, located in a RecyclerView Adapter. It should work with your menu implementation as well. Since you're defining the menu by XML, the below will also work for you as long as you reference the menu resource. Here's what the result looks like:

    Override onCreateContextMenu or your menu's relevant onCreate.. method like so within the:

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        menu.setHeaderTitle(getStr(R.string.actions_title));
    
        // Groups 0 and 1, first parameter for menu.add()
        menu.add(0, getAdapterPosition(), 0, R.string.homescreen);
        menu.add(0, getAdapterPosition(), 1, R.string.lockscreen);
        menu.add(0, getAdapterPosition(), 2, R.string.wpLocation_both);
        menu.add(1, getAdapterPosition(), 3, R.string.action_download);
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            menu.setGroupDividerEnabled(true);  // This adds the divider between groups 0 and 1, but only supported on Android 9.0 and up.
        }
    }
    
    0 讨论(0)
  • 2020-12-03 01:54

    You should use action layout

    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        tools:context=".LandingActivity">
        <item
            android:id="@+id/action_cart"
            android:title="cart"
            android:actionLayout="@layout/cart_update_count"
            android:icon="@drawable/shape_notification"
            app:showAsAction="always"/>
    </menu>
    

    and then the action layout can have the textview with divider.

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <View
            android:id="@+id/divider"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/divider"/>
    
        <TextView
            android:id="@android:id/text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?android:attr/selectableItemBackground"
            android:gravity="center_vertical"          
            android:textAppearance="?attr/textAppearanceListItemSmall"/>
    
    </LinearLayout>
    

    then you can add the click listener in code

    0 讨论(0)
  • 2020-12-03 02:02

    Super simple solution that worked out for me:

    Define a drawable for the background:

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
    
        <solid android:color="@android:color/white"/>
        <stroke
            android:width="3dp"
            android:color="@color/colorPrimary"/>
    
    </shape>
    

    then in Styles use the background:

    <style name="bluetooth_popup" parent="@android:style/Widget.DeviceDefault.Light.PopupMenu">
        <item name="android:textColor">@color/colorPrimary</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textAllCaps">true</item>
        <item name="android:background">@android:color/transparent</item>
        <item name="android:itemBackground">@drawable/bluetooth_popup_buttons</item>
    
    0 讨论(0)
  • 2020-12-03 02:03

    This can be done by using popup window and list view. In your list view, you can have different view types, such as menu item and divider.

    I list the code for popup window part:

        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.option_menu, null);
        ListView listView = (ListView) view.findViewById(R.id.listView);
        listView.setDivider(null);
    
        mAdapter = new OptionListAdapter(context, options);
        listView.setAdapter(mAdapter);
    
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //TODO: The code when item is clicked.
            }
        });
    
        mPopupWindow = new PopupWindow(context, null, R.attr.popupMenuStyle);
        mPopupWindow.setFocusable(true); // otherwise on android 4.1.x the onItemClickListener won't work.
        mPopupWindow.setContentView(view);
        mPopupWindow.setOutsideTouchable(true);
    
        int height = 0;
        int width = 0;
        float density = context.getResources().getDisplayMetrics().density;
        int minWidth = Math.round(196 * density); // min width 196dip, from abc_popup_menu_item_layout.xml
        int cellHeight = context.getResources().getDimensionPixelOffset(R.dimen.option_height);
        int dividerHeight = context.getResources().getDimensionPixelOffset(R.dimen.divider_height);
        final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        for (int i = 0; i < mAdapter.getCount(); i++) {
            Object item = mAdapter.getItem(i);
            if (item != null) {
                View childView = mAdapter.getView(i, null, listView);
                childView.measure(widthMeasureSpec, heightMeasureSpec);
                height += cellHeight;
                width = Math.max(width, childView.getMeasuredWidth());
            } else {
                height += dividerHeight; // divider
            }
        }
        width = Math.max(minWidth, width);
        Drawable background = mPopupWindow.getBackground(); // 9-pitch images
        if (background != null) {
            Rect padding = new Rect();
            background.getPadding(padding);
            height += padding.top + padding.bottom;
            width += padding.left + padding.right;
        }
        mPopupWindow.setWidth(width);
        mPopupWindow.setHeight(height);
        mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
    

    Then you can use following method to show the popup window:

    PopupWindowCompat.showAsDropDown(mPopupWindow, parent, x, y, gravity);
    

    In the adapter for list view, you can override getViewTypeCount() and getItemViewType() to support both menu item layout and divider layout, also you can add any view type that you need.

    Here is a snapshot in my app:

    0 讨论(0)
提交回复
热议问题