ตั้งค่ารายการที่เลือกใน Android BottomNavigationView


123

ฉันกำลังใช้สิ่งใหม่android.support.design.widget.BottomNavigationViewจากไลบรารีการสนับสนุน ฉันจะตั้งค่าการเลือกปัจจุบันจากรหัสได้อย่างไร? ฉันรู้ว่าการเลือกจะเปลี่ยนกลับไปเป็นรายการแรกหลังจากหมุนหน้าจอ แน่นอนว่ามันจะยังความช่วยเหลือถ้ามีคนสามารถบอกวิธีการ "บันทึก" สถานะปัจจุบันของBottomNavigationViewในฟังก์ชั่นและวิธีการเรียกคืนข้อมูลในonPauseonResume

ขอบคุณ!


1
ฉันเพิ่งดูแหล่งที่มาและฉันก็มีคำถามเหมือนกัน .. ดูเหมือนว่าจะไม่มีวิธีการตั้งค่ารายการที่เลือกและBottomNavigationViewไม่ได้ทำการบันทึกสถานะภายในใด ๆ อาจคาดว่าสิ่งนี้จะรวมอยู่ในการอัปเดตในอนาคต ทำสำเนา (พร้อมข้อมูลเพิ่มเติม) ที่นี่: stackoverflow.com/questions/40236786/…
Tim Malseed

คำตอบ:


171

จาก API 25.3.0 ได้เปิดตัววิธีการsetSelectedItemId(int id)ที่ช่วยให้คุณสามารถทำเครื่องหมายรายการที่เลือกราวกับว่ามีการแตะ

จากเอกสาร:

ตั้งค่า ID รายการเมนูที่เลือก การดำเนินการนี้จะเหมือนกับการแตะที่รายการ

ตัวอย่างโค้ด:

BottomNavigationView bottomNavigationView;
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(myNavigationItemListener);
bottomNavigationView.setSelectedItemId(R.id.my_menu_item_id);

สำคัญ

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


3
สิ่งนี้ไม่ได้ผลเพียงแค่อัปเดตแท็บเท่านั้น แต่จะไม่ทริกเกอร์เหตุการณ์
setOnNavigationItemSelectedListener

1
หากคุณไม่ได้กำหนดพฤติกรรมไว้onTabSelectedอย่างชัดเจนก็จะไม่ทำอะไรเลย อ่านเอกสาร -> ตั้งค่า ID รายการเมนูที่เลือก การดำเนินการนี้จะเหมือนกับการแตะที่รายการ มาจากนักพัฒนา Android
Ricardo

1
ฉันขอแนะนำให้คุณแก้ไขข้อบกพร่องและตรวจสอบสิ่งที่คุณทำผิด ฉันได้ติดตั้งแอปพลิเคชั่น BottomNavigationView สามตัวแล้วและไม่เคยมีปัญหากับวิธีนี้
Ricardo

@Ricardo เมื่อฉันเพิ่มสิ่งนี้ลงในส่วนของฉันแอปพลิเคชันก็ค้างในขณะที่เข้าสู่ส่วนนั้น
Subin Babu

1
ในกรณีของฉันใช้งานไม่ได้เพราะฉันกำลังสร้าง navigationMenu โดยใช้โปรแกรม ระหว่างการก่อสร้างคลิกไม่ทำงาน หลังจากเพิ่มรายการทั้งหมดลงในเมนูแล้วการเรียก setSelectedItemId () จะทำงาน
Pedro Romão

53

หากต้องการทางโปรแกรมคลิกที่รายการ BottomNavigationBar ที่คุณต้องการใช้:

View view = bottomNavigationView.findViewById(R.id.menu_action_item);
view.performClick();

สิ่งนี้จะจัดเรียงรายการทั้งหมดด้วยป้ายกำกับอย่างถูกต้อง


4
นี่คือการแฮ็กเกี่ยวกับปัญหา คุณมีวิธีการจากห้องสมุดซึ่งจะช่วยให้คุณดำเนินการตามที่คุณต้องการได้ ถ้าคุณทำไม่ได้นั่นเป็นเพราะคุณทำผิด
Ricardo

47

สำหรับผู้ที่ยังใช้ SupportLibrary <25.3.0

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

private void updateNavigationBarState(int actionId){
    Menu menu = bottomNavigationView.getMenu();

    for (int i = 0, size = menu.size(); i < size; i++) {
        MenuItem item = menu.getItem(i);
        item.setChecked(item.getItemId() == actionId);
    }
}

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

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
        case R.id.some_id_1:
            // process action
            break;
        case R.id.some_id_2:
            // process action
            break;
        ...
        default:
            return false;
    }

    updateNavigationBarState(item.getItemId());

    return true;
}

เกี่ยวกับการบันทึกสถานะอินสแตนซ์ฉันคิดว่าคุณสามารถเล่นกับaction idมุมมองการนำทางแบบเดียวกันและหาวิธีแก้ปัญหาที่เหมาะสมได้


ไม่Menu menu = bottomNavigationView.getMenu();ส่งคืนสำเนาของเมนูดังนั้นbottomNavigationViewวัตถุจึงไม่ได้รับผลกระทบ?
最白目

1
@dan ตามแหล่งที่มาจะส่งคืนแอตทริบิวต์คลาส mMenu ไม่ใช่สำเนาandroid.googlesource.com/platform/frameworks/support/+/master/…
Viacheslav

6
คุณไม่จำเป็นต้องยกเลิกการเลือกรายการอื่นอย่างน้อยในวันที่ 26.1.0 / 27.1.0
Jemshit Iskenderov

อย่ายกเลิกการเลือกมันจะเน้นพวกเขา
djdance


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

   bottomNavigationView.setOnNavigationItemSelectedListener(this);
   Menu menu = bottomNavigationView.getMenu();
   this.onNavigationItemSelected(menu.findItem(R.id.action_favorites));
}


5

เพิ่มandroid:enabled="true"ในรายการ BottomNavigationMenu

จากนั้นกำหนดbottomNavigationView.setOnNavigationItemSelectedListener(mListener)และตั้งค่าตามที่เลือกโดยทำbottomNavigationView.selectedItemId = R.id.your_menu_id


3

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

สร้างมุมมองแบบกำหนดเองที่ขยายจาก BottomNavigationView และเข้าถึงบางฟิลด์

public class SelectableBottomNavigationView extends BottomNavigationView {

    public SelectableBottomNavigationView(Context context) {
        super(context);
    }

    public SelectableBottomNavigationView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SelectableBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setSelected(int index) {
        try {
            Field f = BottomNavigationView.class.getDeclaredField("mMenuView");
            f.setAccessible(true);
            BottomNavigationMenuView menuView = (BottomNavigationMenuView) f.get(this);

            try {
                Method method = menuView.getClass().getDeclaredMethod("activateNewButton", Integer.TYPE);
                method.setAccessible(true);
                method.invoke(menuView, index);
            } catch (SecurityException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

}

จากนั้นใช้ในไฟล์เลย์เอาต์ xml ของคุณ

<com.your.app.SelectableBottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:itemBackground="@color/primary"
        app:itemIconTint="@drawable/nav_item_color_state"
        app:itemTextColor="@drawable/nav_item_color_state"
        app:menu="@menu/bottom_navigation_menu"/>

3
การสะท้อนคิดเป็นความคิดที่ไม่ดี!
Filipe Brito

PerformClick ดีกว่าการสะท้อน imho ในกรณีนี้
Lassi Kinnunen

3

เพียงเพิ่มวิธีอื่นในการดำเนินการเลือกตามโปรแกรม - นี่อาจเป็นความตั้งใจในตอนแรกหรืออาจจะเพิ่มในภายหลัง

Menu bottomNavigationMenu = myBottomNavigationMenu.getMenu();
bottomNavigationMenu.performIdentifierAction(selected_menu_item_id, 0);

performIdentifierActionใช้Menuรหัสรายการและธง

ดูเอกสารสำหรับข้อมูลเพิ่มเติม


ดูเหมือนว่าจะดำเนินการที่เป็นของรายการเมนู แต่ไม่ได้ทำให้รายการเมนูปรากฏขึ้น ฉันคิดว่าอย่างหลังคือสิ่งที่ OP ต้องการ แต่ฉันไม่คิดบวก
LarsH

3

ดูเหมือนว่าจะได้รับการแก้ไขแล้วใน SupportLibrary 25.1.0 :) แก้ไข: ดูเหมือนว่าจะได้รับการแก้ไขสถานะของการเลือกจะถูกบันทึกไว้เมื่อหมุนหน้าจอ


1
ฉันเพิ่งอัปเดตเป็น 25.1.1 ปัญหายังคงมีอยู่
Xi Wei

3

เหนือ API 25 คุณสามารถใช้ setSelectedItemId (menu_item_id) ได้ แต่ภายใต้ API 25 คุณต้องทำสิ่งที่แตกต่างออกไปเมนูผู้ใช้เพื่อจัดการจากนั้น setChecked เป็นรายการที่ตรวจสอบแล้ว


3

ใช้สิ่งนี้เพื่อตั้งค่ารายการเมนูการนำทางด้านล่างที่เลือกตามรหัสเมนู

MenuItem item = mBottomNavView.getMenu().findItem(menu_id);
item.setChecked(true);

2

ฉันไม่ต้องการแก้ไขรหัสของคุณ ถ้าเป็นเช่นนั้นฉันขอแนะนำให้คุณลองใช้BottomNavigationViewEx

คุณเพียงแค่ต้องแทนที่โทรวิธีsetCurrentItem(index);และgetCurrentItem()

คลิกที่นี่เพื่อดูภาพ



1

ฉันทำข้อผิดพลาดกับ Google เกี่ยวกับข้อเท็จจริงที่ว่าไม่มีวิธีที่เชื่อถือได้ในการเลือกหน้าบน BottomNavigationView: https://code.google.com/p/android/issues/detail?id=233697

เห็นได้ชัดว่า NavigationView มีปัญหาที่คล้ายกันซึ่งพวกเขาได้รับการแก้ไขโดยการเพิ่มเมธอด setCheckedItem () ใหม่



0

การสะท้อนคิดเป็นความคิดที่ไม่ดี

ไปที่ส่วนสำคัญนี้ มีวิธีที่ดำเนินการเลือก แต่ยังเรียกใช้การโทรกลับ:

  @CallSuper
    public void setSelectedItem(int position) {
        if (position >= getMenu().size() || position < 0) return;

        View menuItemView = getMenuItemView(position);
        if (menuItemView == null) return;
        MenuItemImpl itemData = ((MenuView.ItemView) menuItemView).getItemData();


        itemData.setChecked(true);

        boolean previousHapticFeedbackEnabled = menuItemView.isHapticFeedbackEnabled();
        menuItemView.setSoundEffectsEnabled(false);
        menuItemView.setHapticFeedbackEnabled(false); //avoid hearing click sounds, disable haptic and restore settings later of that view
        menuItemView.performClick();
        menuItemView.setHapticFeedbackEnabled(previousHapticFeedbackEnabled);
        menuItemView.setSoundEffectsEnabled(true);


        mLastSelection = position;

    }

ผมใช้callOnClick()แทนperformClick()วิธีนี้ผมไม่จำเป็นต้องปิดการใช้งานเสียงที่ไม่ได้ทำงานด้วยperformClick()ล่ะค่ะ
arekolek

0
private void setSelectedItem(int actionId) {
    Menu menu = viewBottom.getMenu();
    for (int i = 0, size = menu.size(); i < size; i++) {
        MenuItem menuItem = menu.getItem(i);
        ((MenuItemImpl) menuItem).setExclusiveCheckable(false);
        menuItem.setChecked(menuItem.getItemId() == actionId);
        ((MenuItemImpl) menuItem).setExclusiveCheckable(true);
    }
}

มีเพียง 'ลบ' ของโซลูชันที่ใช้MenuItemImplซึ่งเป็น 'ภายใน' ไปยังไลบรารี (แม้ว่าจะเป็นสาธารณะ)


0

หากคุณต้องการส่งผ่านข้อโต้แย้งที่ไม่พึงประสงค์ให้ทำเช่นนี้

มีมากมาย (ซ้ำส่วนใหญ่หรือล้าสมัย) ที่นี่มีคำตอบ แต่ไม่มีพวกเขาจะจัดการกับความจำเป็นที่พบบ่อยมาก: แบบไดนามิกที่ผ่านการขัดแย้งแตกต่างกันไปส่วนโหลดลงในแท็บ

คุณไม่สามารถแบบไดนามิกผ่านการขัดแย้งแตกต่างกันไปส่วนโหลดโดยใช้ซึ่งจะสิ้นสุดลงเรียกสถิตย์setSelectedItemId(R.id.second_tab) OnNavigationItemSelectedListenerเพื่อเอาชนะข้อ จำกัด นี้ฉันได้ทำสิ่งนี้ในMainActivityแท็บของฉัน:

fun loadArticleTab(articleId: String) {
    bottomNavigationView.menu.findItem(R.id.tab_article).isChecked = true // use setChecked() in Java
    supportFragmentManager
        .beginTransaction()
        .replace(R.id.main_fragment_container, ArticleFragment.newInstance(articleId))
        .commit()
}

ArticleFragment.newInstance()วิธีการที่จะดำเนินการตามปกติ:

private const val ARG_ARTICLE_ID = "ARG_ARTICLE_ID"

class ArticleFragment : Fragment() {

    companion object {
        /**
         * @return An [ArticleFragment] that shows the article with the given ID.
         */
        fun newInstance(articleId: String): ArticleFragment {
            val args = Bundle()
            args.putString(ARG_ARTICLE_ID, day)
            val fragment = ArticleFragment()
            fragment.arguments = args
            return fragment
        }
    }

}

0

ฉันหวังว่านี่จะช่วยได้

//Setting default selected menu item and fragment
        bottomNavigationView.setSelectedItemId(R.id.action_home);
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.fragment_container, new HomeFragment()).commit();

เป็นการกำหนดแฟรกเมนต์เริ่มต้นที่โหลดพร้อมกันด้วยรายการเมนูการนำทางด้านล่างที่เกี่ยวข้อง คุณสามารถรวมสิ่งเดียวกันนี้ไว้ในการโทรกลับ OnResume ของคุณ


0

วิธีนี้ใช้ได้ผลกับฉัน

private fun selectBottomNavigationViewMenuItem(bottomNavigationView : BottomNavigationView,@IdRes menuItemId: Int) {
            bottomNavigationView.setOnNavigationItemSelectedListener(null)
            bottomNavigationView.selectedItemId = menuItemId
            bottomNavigationView.setOnNavigationItemSelectedListener(this)
        }

ตัวอย่าง

 override fun onBackPressed() {
        replaceFragment(HomeFragment())
        selectBottomNavigationViewMenuItem(navView, R.id.navigation_home)
    }



private fun replaceFragment(fragment: Fragment) {
        val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
        transaction.replace(R.id.frame_container, fragment)
        transaction.commit()
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.