วิธีการบังคับใช้เมนูโอเวอร์โฟลว์บนอุปกรณ์ที่มีปุ่มเมนู


159

ฉันต้องการที่จะมีทั้งหมดของรายการเมนูที่ไม่พอดีกับ ActionBar ไปในเมนูล้น (หนึ่งที่เข้าถึงได้จากแถบการทำงานไม่ได้ปุ่มเมนู) แม้ในอุปกรณ์ที่ทำมีปุ่มเมนู ดูเหมือนว่าจะทำให้ผู้ใช้เข้าใจได้ง่ายกว่าการโยนพวกเขาลงในรายการเมนูแยกต่างหากที่ต้องการให้ผู้ใช้ข้ามจากการโต้ตอบ (หน้าจอ) ไปยังการโต้ตอบแบบใช้ปุ่มเพียงเพราะเค้าโครงของ ActionBar ไม่สามารถปรับให้เข้ากับบาร์ได้

ในอีมูเลเตอร์ฉันสามารถตั้งค่า "ฮาร์ดแวร์แบ็ค / โฮมคีย์" เป็น "ไม่" และรับเอฟเฟกต์นี้ ฉันค้นหาวิธีการทำสิ่งนี้ในรหัสสำหรับอุปกรณ์จริงที่มีปุ่มเมนู แต่ไม่สามารถปรับได้ มีใครช่วยฉันบ้าง

คำตอบ:


54

แก้ไข: แก้ไขเพื่อตอบสำหรับสถานการณ์ของปุ่มเมนูทางกายภาพ

นี่คือการป้องกันโดยการออกแบบจริง ตามที่มาตราความเข้ากันได้ของ Android คู่มือการออกแบบ ,

"... แอคชั่นมากเกินพร้อมใช้งานจากคีย์ฮาร์ดแวร์ของเมนูป๊อปอัพการกระทำที่เป็นผลลัพธ์ ... จะปรากฏที่ด้านล่างของหน้าจอ"

คุณจะสังเกตเห็นในหน้าจอโทรศัพท์ที่มีปุ่มเมนูแบบกายภาพจะไม่มีเมนูล้นใน ActionBar วิธีนี้จะหลีกเลี่ยงความกำกวมของผู้ใช้โดยมีปุ่มสองปุ่มให้เปิดเมนูเดียวกัน

ในการแก้ไขปัญหาความสอดคล้องของอุปกรณ์: ในที่สุดสิ่งสำคัญคือประสบการณ์ของผู้ใช้ที่แอปของคุณจะทำงานอย่างต่อเนื่องกับแอพอื่น ๆ ทั้งหมดในอุปกรณ์เดียวกันมากกว่าที่จะทำงานอย่างสม่ำเสมอกับตัวเองในทุกอุปกรณ์


1
อเล็กซานเดอร์ - ไม่ฉันได้ลองทำตามค่าการกระทำทั้งหมดและชุดค่าผสมแล้วและไม่มีใครทำกลอุบาย (อย่างน้อยในโปรแกรมจำลอง) เมนูโอเวอร์โฟลว์บนแถบแอ็คชันจะปรากฏขึ้นต่อเมื่อฉันเลียนแบบอุปกรณ์โดยไม่มีปุ่มเมนู ฉันต้องการที่จะเห็นจุดไข่ปลาแนวตั้งนั้นและมีรายการล้นปรากฏบนอุปกรณ์ที่มีปุ่มเมนู ฉันได้แก้ไขคำถามของฉันเพื่อให้ชัดเจนขึ้น
PaulP

41
มาเริ่มการสนทนานี้กัน: ฉันรู้ว่ามันถูกป้องกันโดยการออกแบบ (ฉันอ่านแนวทางการออกแบบ) แต่นั่นคือ% $ /% # + ฉันคิดว่า ตัวอย่างเช่น: ผู้ใช้เปลี่ยนจาก Galaxy Nexus (-> w ล้น) เป็น Nexus One (กว้าง 4.0 / -> ไม่มีล้น) ฉันพนันได้เลยว่าผู้ใช้จะไม่พบรายการเมนูอีกต่อไป ด้วยเหตุนี้ฉันจึงต้องการการใช้งานเดียวกันสำหรับอุปกรณ์ทั้งหมด ดังนั้นฉันจะจบลงด้วยปัญหาเดียวกันกับพอล ไม่มีวิธีแก้ปัญหาใด ๆ ที่สะอาดหรือไม่
Sprigg

10
ฉันก็อ่านคู่มือการออกแบบก่อน สำหรับฉันนี่เป็นตัวเลือกที่ไม่ดีในการออกแบบในแพ็คเกจการสนับสนุน กลยุทธ์ที่ดีกว่าในการเปลี่ยนผู้ใช้ออกจากปุ่ม (วัตถุประสงค์ใช่ไหม) คือการทำให้มันซ้ำซ้อนวางฟังก์ชันการทำงานบนหน้าจอเช่นเดียวกับในปุ่ม เมื่อตอนนี้สิ่งต่าง ๆ ปุ่มไม่ได้แสดงรายการตัวเลือกเมนูทั้งหมดเฉพาะที่ไม่ได้อยู่ใน actionbar ดังนั้นการออกแบบนี้จึงไม่สนับสนุนการเปลี่ยนไปใช้ actionbar ที่ราบรื่นและไม่ทำสิ่งที่เคยทำก่อนที่จะเพิ่ม actionbar (แสดงเมนูทั้งหมดทางเลือก) ฉันสงสัยว่าคุณพูดถูกและไม่มีวิธีแก้ไขง่ายๆ
PaulP

18
Google ขัดแย้งกับสิ่งนี้ในแอป Google + ใหม่ของพวกเขา มันมีรายการมากเกินโดยไม่คำนึงถึงอุปกรณ์ .. แต่พวกเขาก็ยังป้องกันไม่ให้นักพัฒนาทำเช่นเดียวกันและให้คำแนะนำกับมันเพื่อความรู้ของฉัน
Karl

10
จริง ๆ แล้วฉันเคยเห็นผู้ใช้หลายคนมากเกินไปที่จะไม่ลองใช้เมนูคีย์เพื่อคาดหวังให้พวกเขาทำ หากการออกแบบแจ้งว่าผู้ใช้บนโทรศัพท์ใด ๆ ไม่ควรมีตัวบ่งชี้บนหน้าจอว่ามีตัวเลือกเมนูอยู่การออกแบบนั้นเป็นข้อมูลที่ไม่ดี
Lance Nanek

323

คุณสามารถใช้แฮ็คตัวเล็ก ๆ ได้ที่นี่:

try {
    ViewConfiguration config = ViewConfiguration.get(this);
    Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
    if (menuKeyField != null) {
        menuKeyField.setAccessible(true);
        menuKeyField.setBoolean(config, false);
    }
} catch (Exception ignored) {
}

สถานที่ที่ดีที่จะใส่มันจะเป็นonCreate-Method ของคลาสแอปพลิเคชันของคุณ

มันจะบังคับให้แอปแสดงเมนูโอเวอร์โฟลว์ ปุ่มเมนูยังคงใช้งานได้ แต่จะเปิดเมนูที่มุมขวาบน

[แก้ไข] เนื่องจากมันเกิดขึ้นหลายครั้งแล้วในตอนนี้: แฮ็คนี้ใช้ได้กับ ActionBar ดั้งเดิมที่ใช้ใน Android 3.0 เท่านั้นไม่ใช่ ActionBarSherlock หลังใช้ตรรกะภายในของตนเองเพื่อตัดสินใจว่าจะแสดงเมนูโอเวอร์โฟลว์หรือไม่ หากคุณใช้ระบบ ABS ABS ทุกแพลตฟอร์มจะได้รับการจัดการโดย ABS และอยู่ภายใต้ตรรกะของมัน แฮ็คจะยังคงใช้งานได้กับอุปกรณ์ทั้งหมดที่ใช้ Android 4.0 หรือสูงกว่า (คุณสามารถเพิกเฉยได้อย่างปลอดภัยกับ Android 3.x เนื่องจากไม่มีแท็บเล็ตใด ๆ อยู่ที่นั่นพร้อมปุ่มเมนู)

มีความพิเศษ ForceOverflow-ธีมที่จะบังคับเมนู ABS ที่มีอยู่ แต่ Apperently มันเป็นไปได้ที่จะถูกลบออกในรุ่นอนาคตเนื่องจากภาวะแทรกซ้อน


4
ฉันดูรหัสต้นฉบับ โกรฟสมบัติของคุณสมบัติที่ไม่มีเอกสาร :) เพียงให้แน่ใจว่าคุณมีทางเลือกที่ใช้การได้สำหรับสิ่งต่าง ๆ เช่นนั้น
Timo Ohr

4
ขอบคุณมาก! อันนี้ยอดเยี่ยมจริงๆ ฉันต้องการที่จะใส่ความคิดเห็นข้อผิดพลาดและแบ่งปันการกระทำลงในล้น แต่มันไม่ปรากฏบน Nexus S ผู้ใช้จะไม่แม้แต่จะคลิกปุ่มเมนู ด้วยผู้ใช้มากเกินเห็นว่าการกระทำพิเศษที่มีอยู่
Yuriy Kulikov

4
@Ewoks ฉันค่อนข้างช้าในการแสดงความคิดเห็นที่นี่ แต่ฉันเพิ่งลองมันกับ ActionBarCompat รุ่นล่าสุดและมันก็ทำงานได้ดี
jacobhyphenated

3
ใช้งานได้กับ Samsung Galaxy S3 และ S4 แต่ไม่ได้อยู่ใน LG G2 (สงสัยว่าพวกเขาเขียนโค้ดยากที่จะแสดงปุ่มเมนูล้น)
dvd

18
สวย! ถ้า Eclipse ให้คุณเลือกการนำเข้าที่แตกต่างกันถึง 6 แบบให้เลือกตัวFieldเลือกนี้java.lang.reflect.Field;)
Eugene van der Merwe

35

ฉันใช้เพื่อแก้ไขปัญหาโดยกำหนดเมนูของฉันเช่นนี้ (เช่นเดียวกับไอคอน ActionBarSherlock ที่ใช้ในตัวอย่างของฉัน):

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menu_overflow"
        android:icon="@drawable/abs__ic_menu_moreoverflow_normal_holo_light"
        android:orderInCategory="11111"
        android:showAsAction="always">
        <menu>
            <item
                android:id="@+id/menu_overflow_item1"
                android:showAsAction="never"
                android:title="@string/overflow_item1_title"/>
            <item
                android:id="@+id/menu_overflow_item2"
                android:showAsAction="never"
                android:title="@string/overflow_item2_title"/>
        </menu>
    </item>

</menu>

ฉันยอมรับว่านี่อาจต้องใช้ "การจัดการมากเกินไป" ใน xml ของคุณด้วยตนเอง แต่ฉันพบว่าโซลูชันนี้มีประโยชน์

คุณยังสามารถบังคับให้อุปกรณ์ใช้ปุ่ม HW เพื่อเปิดเมนูโอเวอร์โฟลว์ในกิจกรรมของคุณ:

private Menu mainMenu;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // TODO: init menu here...
    // then:
    mainMenu=menu;
    return true;
}

@Override
public boolean onKeyUp(int keycode, KeyEvent e) {
    switch(keycode) {
        case KeyEvent.KEYCODE_MENU:
            if (mainMenu !=null) {
                mainMenu.performIdentifierAction(R.id.menu_overflow, 0);
            }
    }

    return super.onKeyUp(keycode, e);
}

:-)


2
วิธีใช้ปุ่ม HW เพื่อเปิดเมนูโอเวอร์โฟลว์
bladefury

1
ดูคำตอบที่อัปเดตของฉันเพื่อเปิดเมนูโอเวอร์โฟลว์ด้วยปุ่ม HW :)
Berťák

11

หากคุณกำลังใช้แถบการกระทำจากไลบรารีการสนับสนุน ( android.support.v7.app.ActionBar) ใช้สิ่งต่อไปนี้:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:yorapp="http://schemas.android.com/apk/res-auto" >

    <item
        android:id="@+id/menu_overflow"
        android:icon="@drawable/icon"
        yourapp:showAsAction="always"
        android:title="">
        <menu>
            <item
                android:id="@+id/item1"
                android:title="item1"/>
            <item
                android:id="@+id/item2"
                android:title="item2"/>
        </menu>
    </item>

</menu>

เมื่อมีการใช้ห้องสมุดการสนับสนุนที่ผมดำเนินการนี้มากเกินไปและการทำงาน ok ทั้งก่อนและหลัง 3.0 อุปกรณ์
leafcutter

7

วิธีการนี้ถูกป้องกันโดย Android Developers Design System แต่ฉันพบวิธีที่จะผ่านมัน:

เพิ่มลงในไฟล์เมนู XML ของคุณ:

<item android:id="@+id/pick_action_provider"
    android:showAsAction="always"
    android:title="More"
    android:icon="@drawable/ic_action_overflow"
    android:actionProviderClass="com.example.AppPickActionProvider" />

ถัดไปสร้างคลาสที่ชื่อว่า 'AppPickActionProvider' และคัดลอกรหัสต่อไปนี้:

    package com.example;

import android.content.Context;
import android.util.Log;
import android.view.ActionProvider;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.SubMenu;
import android.view.View;

public class AppPickActionProvider extends ActionProvider implements
        OnMenuItemClickListener {

    static final int LIST_LENGTH = 3;

    Context mContext;

    public AppPickActionProvider(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public View onCreateActionView() {
        Log.d(this.getClass().getSimpleName(), "onCreateActionView");

        return null;
    }

    @Override
    public boolean onPerformDefaultAction() {
        Log.d(this.getClass().getSimpleName(), "onPerformDefaultAction");

        return super.onPerformDefaultAction();
    }

    @Override
    public boolean hasSubMenu() {
        Log.d(this.getClass().getSimpleName(), "hasSubMenu");

        return true;
    }

    @Override
    public void onPrepareSubMenu(SubMenu subMenu) {
        Log.d(this.getClass().getSimpleName(), "onPrepareSubMenu");

        subMenu.clear();

        subMenu.add(0, 1, 1, "Item1")
        .setIcon(R.drawable.ic_action_home).setOnMenuItemClickListener(this);

        subMenu.add(0, 2, 1, "Item2")
            .setIcon(R.drawable.ic_action_downloads).setOnMenuItemClickListener(this);
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        switch(item.getItemId())
        {
            case 1:

                // What will happen when the user presses the first menu item ( 'Item1' )

                break;
            case 2:

                // What will happen when the user presses the second menu item ( 'Item2' )

                break;

        }

        return true;
    }
}

สิ่งที่แตกต่างระหว่างการใช้วิธีการนี้และเพียงแค่ใช้เมนูย่อยที่แสดงที่นี่: stackoverflow.com/a/14634780/878126 ?
นักพัฒนาซอฟต์แวร์ Android

5

ฉันคิดว่าอเล็กซานเดอร์ลูคัสได้ให้คำตอบที่ถูกต้อง (น่าเสียดาย) ดังนั้นฉันจึงทำเครื่องหมายว่าเป็น "ถูกต้อง" คำตอบอื่นที่ฉันเพิ่มที่นี่คือเพียงชี้ผู้อ่านใหม่ ๆ ไปยังโพสต์นี้ในบล็อกของนักพัฒนา Androidเป็นการสนทนาที่ค่อนข้างสมบูรณ์ของหัวข้อพร้อมคำแนะนำเฉพาะเกี่ยวกับวิธีจัดการกับรหัสของคุณเมื่อเปลี่ยนจากระดับก่อนระดับ 11 ไปที่แถบการกระทำใหม่

ฉันยังเชื่อว่ามันเป็นความผิดพลาดในการออกแบบไม่มีปุ่มเมนูทำหน้าที่เป็นปุ่ม "Action Overflow" ซ้ำซ้อนในปุ่มเมนูที่เปิดใช้งานอุปกรณ์เป็นวิธีที่ดีกว่าในการเปลี่ยนประสบการณ์ผู้ใช้ แต่น้ำใต้สะพานในตอนนี้


ฉันเห็นด้วยอย่างสมบูรณ์กับคุณเกี่ยวกับข้อผิดพลาดในการออกแบบ - และฉันพบว่ามันน่าหงุดหงิด!
พอล Hunnisett

1
ฉันจะเห็นด้วยกับคุณในเรื่องนี้หากไม่ใช่เพราะ Google เองเริ่มละเมิดกฎนั้นในแอป G + ล่าสุด และถูกต้องดังนั้น ปุ่มเมนูไม่ได้เป็นแบบดั้งเดิมแม้แต่อุปกรณ์ใหม่อย่าง SGS3 ก็มี มันอยู่ที่นี่อยู่น่าเสียดาย และมันก็เป็นอุปสรรคต่อการใช้งานอย่างจริงจัง
Timo Ohr

4

ฉันไม่แน่ใจว่านี่คือสิ่งที่คุณกำลังมองหาหรือไม่ แต่ฉันสร้างเมนูย่อยภายในเมนูของแถบเครื่องมือและตั้งไอคอนให้ตรงกับไอคอนของเมนูโอเวอร์โฟลว์ แม้ว่ามันจะไม่มีรายการที่ถูกส่งไปโดยอัตโนมัติ (IE คุณต้องเลือกสิ่งที่มองเห็นได้ตลอดเวลาและอะไรที่ล้นอยู่เสมอ) ดูเหมือนว่าฉันว่าวิธีการนี้อาจช่วยคุณได้


2

ในแอพ gmail ที่ติดตั้ง ICS ไว้ล่วงหน้าปุ่มเมนูจะปิดใช้งานเมื่อคุณเลือกหลายรายการ เมนูโอเวอร์โฟลว์อยู่ที่นี่ "บังคับ" เพื่อให้เกิดการกระตุ้นโดยการใช้ปุ่มโอเวอร์โฟลแทนปุ่มเมนูฟิสิคัล มี lib บุคคลที่สามชื่อ ActionBarSherlock ซึ่งให้คุณ "บังคับ" เมนูโอเวอร์โฟลว์ แต่สิ่งนี้จะใช้ได้เฉพาะกับ API ระดับ 14 หรือต่ำกว่า (pre-ICS)


2

หากคุณใช้Toolbarคุณสามารถแสดงโอเวอร์โฟลว์ในทุกเวอร์ชั่นและทุกอุปกรณ์ฉันได้ลองใช้กับอุปกรณ์ 2.x บางตัวมันทำงานได้


1

ขออภัยถ้าปัญหานี้ตาย

นี่คือสิ่งที่ฉันทำเพื่อแก้ไขข้อผิดพลาด ฉันไปที่เค้าโครงและสร้างสองรายการที่มีแถบเครื่องมือ หนึ่งคือเลย์เอาต์สำหรับ sdk เวอร์ชัน 8 และอีกอันสำหรับ sdk เวอร์ชัน 21 ในเวอร์ชัน 8 ฉันใช้ android.support.v7.widget.Toolbar ในขณะที่ฉันใช้ android.widget.Toolbar ในเค้าโครง sdk 21

จากนั้นฉันขยายแถบเครื่องมือในกิจกรรมของฉัน ฉันตรวจสอบ sdk เพื่อดูว่ามันเป็น 21 หรือสูงกว่า จากนั้นฉันขยายรูปแบบที่สอดคล้องกัน สิ่งนี้บังคับให้ปุ่มฮาร์ดแวร์เพื่อแมปลงบนแถบเครื่องมือที่คุณออกแบบจริง


0

สำหรับผู้ใช้ใหม่Toolbar:

private Toolbar mToolbar;

@Override
protected void onCreate(Bundle bundle) {
    super.onCreate(bundle);

    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(mToolbar);

    ...
}


@Override
public boolean onKeyUp(int keycode, KeyEvent e) {
    switch(keycode) {
        case KeyEvent.KEYCODE_MENU:
            mToolbar.showOverflowMenu();
            return true;
        }

    return super.onKeyUp(keycode, e);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.