【问题标题】:Cannot catch toolbar home button click event无法捕获工具栏主页按钮单击事件
【发布时间】:2014-12-22 07:23:10
【问题描述】:

我已经实现了最新的 appcompat 库并使用 Toolbar 作为操作栏。但问题是我无法捕捉到主页按钮/汉堡图标点击事件。我已经尝试并查看了所有内容,但似乎没有发现类似的问题。

这是我的Activity 课程:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

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

    // Set up the drawer.
    navDrawerFragment = 
        (NavigationDrawerFragment) getSupportFragmentManager()
        .findFragmentById(R.id.navigation_drawer);
    navDrawerFragment.setUp(
        R.id.navigation_drawer, 
        (DrawerLayout) findViewById(R.id.drawer_layout), 
        toolbar);
}

这是我的 NavigationDrawerFragment 类:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        currentSelectedPosition = savedInstanceState.getInt(
            STATE_SELECTED_POSITION);
        fromSavedInstanceState = true;
    }

    // Select either the default item (0) or the last selected item.
    selectItem(currentSelectedPosition);
}

@Override
public void onActivityCreated (Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    // Indicate that this fragment would like 
    // to influence the set of actions in the action bar.
    setHasOptionsMenu(true);
}

public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        drawerListView = (ListView) inflater.inflate(
            R.layout.fragment_navigation_drawer, container, false);
        drawerListView.setOnItemClickListener(
            new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, 
                View view, int position, long id) {
                selectItem(position);
            }
        });
        //mDrawerListView.setAdapter();
        //mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
        return drawerListView;
}

public void setUp(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
    fragmentContainerView = getActivity().findViewById(fragmentId);
    this.drawerLayout = drawerLayout;

    // set a custom shadow that overlays the main 
    // content when the drawer opens
    drawerLayout.setDrawerShadow(
        R.drawable.drawer_shadow, GravityCompat.START);
    // set up the drawer's list view 
    // with items and click listener

    ActionBar actionBar = getActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);
    actionBar.setHomeButtonEnabled(true);

    // ActionBarDrawerToggle ties together the the proper interactions
    // between the navigation drawer and the action bar app icon.
    drawerToggle = new ActionBarDrawerToggle(
        getActivity(), 
        drawerLayout, 
        toolbar, 
        R.string.navigation_drawer_open, 
        R.string.navigation_drawer_close) {
        public void onDrawerClosed(View view) {
            super.onDrawerClosed(view);
        }

        public void onDrawerOpened(View drawerView) {
            super.onDrawerOpened(drawerView);
        }
    };

    // If the user hasn't 'learned' about the drawer, 
    // open it to introduce them to the drawer,
    // per the navigation drawer design guidelines.
    if (!userLearnedDrawer && !fromSavedInstanceState) {
        drawerLayout.openDrawer(fragmentContainerView);
    }

    // Defer code dependent on restoration of previous instance state.
    drawerLayout.post(new Runnable() {
        @Override
        public void run() {
            drawerToggle.syncState();
        }
    });

    drawerLayout.setDrawerListener(drawerToggle);
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(STATE_SELECTED_POSITION, currentSelectedPosition);
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    // Forward the new configuration the drawer toggle component.
    drawerToggle.onConfigurationChanged(newConfig);
}

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

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Log.d("cek", "item selected");
    if (drawerToggle.onOptionsItemSelected(item)) {
        Log.d("cek", "home selected");
        return true;
    }

    return super.onOptionsItemSelected(item);
}

当我单击一个菜单项时,会调用日志“所选项目”。但是当我点击主页按钮时,它会打开导航抽屉,但日志“home selected”永远不会被调用。我也在我的Activity 中设置了onOptionsItemSelected 方法,但它仍然没有被调用。

【问题讨论】:

    标签: android navigation-drawer android-toolbar


    【解决方案1】:

    如果您想知道点击主页的时间是AppCompatActivity,那么您应该这样尝试:

    首先告诉 Android 你想使用你的Toolbar 作为你的ActionBar

    setSupportActionBar(toolbar);
    

    然后将 Home 设置为通过 setDisplayShowHomeEnabled 显示,如下所示:

    getSupportActionBar().setDisplayShowHomeEnabled(true);
    

    最后像往常一样监听android.R.id.home上的点击事件:

    @Override
    public boolean onOptionsItemSelected(MenuItem menuItem) {
        if (menuItem.getItemId() == android.R.id.home) {
            Timber.d("Home pressed");
        }
        return super.onOptionsItemSelected(menuItem);
    }
    

    如果您想知道导航按钮何时在AppCompatActivity 以外的类中单击Toolbar,您可以使用这些方法设置导航图标并监听其上的点击事件。导航图标将出现在您的Toolbar 的左侧,“主页”按钮曾经位于该位置。

    toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_nav_back));
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d("cek", "home selected");
        }
    });
    

    如果您想知道何时单击汉堡包以及何时打开抽屉,您已经通过onDrawerOpenedonDrawerClosed 监听这些事件,因此您需要查看这些回调是否符合您的要求。

    【讨论】:

    • 之前尝试过第一部分,但它不起作用。尝试了第二部分,它可以工作。但是如果我用导航抽屉注册工具栏,图标不会改变。还有一个问题,是否有任何替代 setDrawerIndicatorEnabled 的方法?我已经尝试过使用这个新的导航抽屉并出现错误。谢谢你
    • 第二种解决方案有效。但是我们如何在第二种解决方案中检测到单击主页按钮和抽屉按钮。当我单击抽屉图标时,它没有打开抽屉。
    • 当您为工具栏设置NavigationOnClickListener 时,您将失去“本机”抽屉行为:(
    • 所以现在我们需要一个单独的点击监听器,之前在片段中我们可以在 onOptionsItemSelected() 中检查 android.R.id.home?这真的很烦人
    • re:工具栏,如果您设置了一个新的 NavigationOnClickListener(使用 setNavigationOnClickListener),您可以稍后通过使用 ActionBarDrawerToggle 再次调用 setDrawerListener 来恢复 NavDrawer。
    【解决方案2】:
        mActionBarDrawerToggle = mNavigationDrawerFragment.getActionBarDrawerToggle();
        mActionBarDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // event when click home button
            }
        });
    

    在我的情况下,这段代码完美运行

    【讨论】:

    • 你真的很棒,它成功了,我从没想过我可以通过 DrawerToggle 处理工具栏后退按钮..
    • 要让 ActionBarDrawerToggle.setToolbarNavigationClickListener 工作,必须首先调用: mActionBarDrawerToggle.setHomeAsUpIndicator(R.drawable.menu_icon); mActionBarDrawerToggle.setDrawerIndicatorEnabled(false);并自己管理点击事件。 (点击打开/关闭抽屉)
    【解决方案3】:

    这就是我返回正确片段的方法,否则如果您在同一级别上有多个片段,如果您不覆盖工具栏后退按钮的行为,它将返回第一个片段。

    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
    

    【讨论】:

    • Kotlin 中是否有“覆盖”点击方法?
    • @AkhilaMadari 在 Kotlin 中尝试这样的操作: val toolbar = findViewById(R.id.toolbar) as Toolbar setSupportActionBar(toolbar) toolbar.setNavigationOnClickListener { /*做你想做的事*/ finish() }
    【解决方案4】:

    我认为支持库 21 的正确解决方案如下

    // action_bar is def resource of appcompat; 
    // if you have not provided your own toolbar I mean  
    Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
    if (toolbar != null) {
        // change home icon if you wish
        toolbar.setLogo(this.getResValues().homeIconDrawable());
        toolbar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //catch here title and home icon click                          
            }                       
        });                 
    }
    

    【讨论】:

    • this.getResValues().homeIconDrawable()this 是谁?
    • 这是一项活动。 Getresvalues 是我的方法,所以在这里不相关。 Setlogo 接受一个可绘制的资源 id。
    • 这将处理用户在整个工具栏的任何位置点击,我不认为这是他要问的
    【解决方案5】:

    我已经处理了导航抽屉中的返回和主页按钮,例如

    public class HomeActivity extends AppCompatActivity
            implements NavigationView.OnNavigationItemSelectedListener {
        private ActionBarDrawerToggle drawerToggle;
        private DrawerLayout drawerLayout;
        NavigationView navigationView;
        private Context context;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_home);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
            resetActionBar();
    
            navigationView = (NavigationView) findViewById(R.id.navigation_view);
            navigationView.setNavigationItemSelectedListener(this);
    
            //showing first fragment on Start
            getSupportFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).replace(R.id.content_fragment, new FirstFragment()).commit();
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            //listener for home
            if(id==android.R.id.home)
            {  
                if (getSupportFragmentManager().getBackStackEntryCount() > 0)
                    onBackPressed();
                else
                    drawerLayout.openDrawer(navigationView);
                return  true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    
        @Override
        public void onBackPressed() {
           if (drawerLayout.isDrawerOpen(GravityCompat.START)) 
                drawerLayout.closeDrawer(GravityCompat.START);
           else 
                super.onBackPressed();
        }
    
        @Override
        public boolean onNavigationItemSelected(MenuItem item) {
            // Begin the transaction
    
            Fragment fragment = null;
            // Handle navigation view item clicks here.
            int id = item.getItemId();
            DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
            if (id == R.id.nav_companies_list) {
                fragment = new FirstFragment();
                // Handle the action
            } 
    
    
            // Begin the transaction
            if(fragment!=null){
    
                if(item.isChecked()){
                    if(getSupportFragmentManager().getBackStackEntryCount()==0){
                        drawer.closeDrawers();
                }else{
                        removeAllFragments();
                        getSupportFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE).replace(R.id.WikiCompany, fragment).commit();
                        drawer.closeDrawer(GravityCompat.START);
                    }
    
                }else{
                    removeAllFragments();
                    getSupportFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE).replace(R.id.WikiCompany, fragment).commit();
                    drawer.closeDrawer(GravityCompat.START);
                }
            }
    
            return true;
        }
    
        public void removeAllFragments(){
            getSupportFragmentManager().popBackStackImmediate(null,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    
        public void replaceFragment(final Fragment fragment) {
            FragmentManager fragmentManager = getSupportFragmentManager();
            fragmentManager.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                    .replace(R.id.WikiCompany, fragment).addToBackStack("")
                    .commit();
        }
    
    
        public void updateDrawerIcon() {
            final Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    try {
                        Log.i("", "BackStackCount: " + getSupportFragmentManager().getBackStackEntryCount());
                        if (getSupportFragmentManager().getBackStackEntryCount() > 0)
                            drawerToggle.setDrawerIndicatorEnabled(false);
                        else
                            drawerToggle.setDrawerIndicatorEnabled(true);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }, 50);
        }
    
        public void resetActionBar()
        {
            //display home
            getSupportActionBar().setDisplayShowHomeEnabled(true);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setHomeButtonEnabled(true);
        }
    
        public void setActionBarTitle(String title) {
            getSupportActionBar().setTitle(title);
        }
    }
    

    并且在我调用的每个onViewCreated

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        ((HomeActivity)getActivity()).updateDrawerIcon();
        ((HomeActivity) getActivity()).setActionBarTitle("List");
    }
    

    【讨论】:

      【解决方案6】:

      这就是我在材料前设计中实现它的方式,现在我已经切换到新的Toolbar,它似乎仍然有效。在我的情况下,如果用户在注销时尝试打开侧导航,我想登录用户(并捕获事件,以便侧导航不会打开)。在你的情况下,你不能return true;

      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          if (!isLoggedIn() && item.getItemId() == android.R.id.home) {
              login();
              return true;
          }
          return mDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
      }
      

      【讨论】:

      • 啊,我忘了我发现我无法捕捉到片段中的主页按钮单击,我在这里提出了一个问题并建议了一种解决方法,即手动将事件传递到所有片段。 stackoverflow.com/q/21938419/1007151
      【解决方案7】:

      我稍微更改了 DrawerLayout 以获取事件并能够消费和事件,例如如果你想在详细视图中使用 actionToggle 作为返回:

      public class ListenableDrawerLayout extends DrawerLayout {
      
          private OnToggleButtonClickedListener mOnToggleButtonClickedListener;
          private boolean mManualCall;
      
          public ListenableDrawerLayout(Context context) {
              super(context);
          }
      
          public ListenableDrawerLayout(Context context, AttributeSet attrs) {
              super(context, attrs);
          }
      
          public ListenableDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
              super(context, attrs, defStyle);
          }
      
          /**
           * Sets the listener for the toggle button
           *
           * @param mOnToggleButtonClickedListener
           */
          public void setOnToggleButtonClickedListener(OnToggleButtonClickedListener mOnToggleButtonClickedListener) {
              this.mOnToggleButtonClickedListener = mOnToggleButtonClickedListener;
          }
      
          /**
           * Opens the navigation drawer manually from code<br>
           * <b>NOTE: </b>Use this function instead of the normal openDrawer method
           *
           * @param drawerView
           */
          public void openDrawerManual(View drawerView) {
              mManualCall = true;
              openDrawer(drawerView);
          }
      
          /**
           * Closes the navigation drawer manually from code<br>
           * <b>NOTE: </b>Use this function instead of the normal closeDrawer method
           *
           * @param drawerView
           */
          public void closeDrawerManual(View drawerView) {
              mManualCall = true;
              closeDrawer(drawerView);
          }
      
      
          @Override
          public void openDrawer(View drawerView) {
      
              // Check for listener and for not manual open
              if (!mManualCall && mOnToggleButtonClickedListener != null) {
      
                  // Notify the listener and behave on its reaction
                  if (mOnToggleButtonClickedListener.toggleOpenDrawer()) {
                      return;
                  }
      
              }
              // Manual call done
              mManualCall = false;
      
              // Let the drawer layout to its stuff
              super.openDrawer(drawerView);
          }
      
          @Override
          public void closeDrawer(View drawerView) {
      
              // Check for listener and for not manual close
              if (!mManualCall && mOnToggleButtonClickedListener != null) {
      
                  // Notify the listener and behave on its reaction
                  if (mOnToggleButtonClickedListener.toggleCloseDrawer()) {
                      return;
                  }
      
              }
              // Manual call done
              mManualCall = false;
      
              // Let the drawer layout to its stuff
              super.closeDrawer(drawerView);
          }
      
          /**
           * Interface for toggle button callbacks
           */
          public static interface OnToggleButtonClickedListener {
      
              /**
               * The ActionBarDrawerToggle has been pressed in order to open the drawer
               *
               * @return true if we want to consume the event, false if we want the normal behaviour
               */
              public boolean toggleOpenDrawer();
      
              /**
               * The ActionBarDrawerToggle has been pressed in order to close the drawer
               *
               * @return true if we want to consume the event, false if we want the normal behaviour
               */
              public boolean toggleCloseDrawer();
          }
      
      }
      

      【讨论】:

        【解决方案8】:

        我们可以做的最简单的方法是将主页图标更改为已知图标并比较可绘制对象(因为 android.R.id.home 图标可能因不同的 api 版本而异

        所以将工具栏设置为操作栏 SetSupportActionBar(_toolbar);

        _toolbar.NavigationIcon = your_known_drawable_here;
        
           for (int i = 0; i < _toolbar.ChildCount; i++)
                    {
                        View v = _toolbar.GetChildAt(i);
                        if (v is ImageButton)
                        {
                            ImageButton imageButton = v as ImageButton;
        
                            if (imageButton.Drawable.GetConstantState().Equals(_bookMarkIcon.GetConstantState()))
                            {
                               //here v is the widget that contains the home  icon you can add your click events here 
                            }
                        }
                    }
        

        【讨论】:

          【解决方案9】:

          在我的情况下,我不得不使用:

          toolbar.setNavigationIcon(R.drawable.ic_my_home);
          setSupportActionBar(toolbar);
          getSupportActionBar().setDisplayShowHomeEnabled(true);
          getSupportActionBar().setHomeButtonEnabled(true);
          

          然后用默认的 onOptionsItemSelected 和 android.R.id.home id 监听点击事件

          【讨论】:

          • 这行不通。 android.R.id.home 永不开火
          【解决方案10】:

          对于任何寻找 Xamarin 实现的人(因为事件在 C# 中的完成方式不同),我只是创建了这个 NavClickHandler 类,如下所示:

          public class NavClickHandler : Java.Lang.Object, View.IOnClickListener
          {
              private Activity mActivity;
              public NavClickHandler(Activity activity)
              {
                  this.mActivity = activity;
              }
              public void OnClick(View v)
              {
                  DrawerLayout drawer = (DrawerLayout)mActivity.FindViewById(Resource.Id.drawer_layout);
                  if (drawer.IsDrawerOpen(GravityCompat.Start))
                  {
                      drawer.CloseDrawer(GravityCompat.Start);
                  }
                  else
                  {
                      drawer.OpenDrawer(GravityCompat.Start);
                  }
              }
          }
          

          然后,分配一个自定义汉堡菜单按钮,如下所示:

                  SupportActionBar.SetDisplayHomeAsUpEnabled(true);
                  SupportActionBar.SetDefaultDisplayHomeAsUpEnabled(false);
                  this.drawerToggle.DrawerIndicatorEnabled = false;
                  this.drawerToggle.SetHomeAsUpIndicator(Resource.Drawable.MenuButton);
          

          最后,为抽屉式菜单切换器分配了我之前创建的类类型的 ToolbarNavigationClickListener:

                  this.drawerToggle.ToolbarNavigationClickListener = new NavClickHandler(this);
          

          然后你就有了一个自定义菜单按钮,处理了点击事件。

          【讨论】:

            【解决方案11】:

            试试这个代码

            @Override
            public boolean onOptionsItemSelected(MenuItem item) {
                int id = item.getItemId();
                if(id == android.R.id.home){
                    //You can get 
                }
                return super.onOptionsItemSelected(item);
            }
            

            将以下代码添加到您的 onCreate() 方法中

            ActionBar ab = getSupportActionBar();
                ab.setDisplayHomeAsUpEnabled(true);
            

            【讨论】:

              【解决方案12】:

              除了MrEngineer13提供的答案之外,还有另一个可能的原因是onOptionsSelected方法可能没有捕获到点击事件。您的DrawerLayout 可能在布局 XML 文件中覆盖了您的Toolbar 的界面组件。因此,每当您尝试单击主页按钮时,您只会单击DrawerLayout,而不是位于其下方的主页按钮。

              您现在要做的就是在相应的布局 XML 文件中重新排列您的 Toolbar,使其不会被任何其他 UI 组件阻止。

              以编程方式,我确实尝试在toolbar (toolbar.bringToFront()) 上调用bringToFront() 方法。但是,在我的应用程序的上下文中,它似乎不是解决方案。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-03-06
                相关资源
                最近更新 更多