Подтвердить что ты не робот

Как получить текущее местоположение элемента ActionBar MenuItem?

Я хочу показать небольшую информационную метку под MenuItem в приложении ActionBar. Чтобы правильно установить это положение, мне нужно знать x-положение a MenuItem.

Google пока не принес мне никаких полезных результатов.

Возможно ли это?

4b9b3361

Ответ 1

Наконец-то я нашел ответ на этот вопрос! Это немного хаки, но не слишком много. Единственными минусами являются:

  • Он использует API 3.0+. Серьезно, хотя Google, какой смысл выпускать новые версии Android, если мы не сможем их использовать?/Декламация.
  • Вы должны заменить MenuItem на свой собственный View. ImageView довольно похож на стандартный, но он пропускает такие функции, как длительное нажатие, чтобы увидеть подсказку и, возможно, другие вещи.
  • Я не тестировал его много.

Хорошо, вот как мы это делаем. Измените Activity onCreateOptionsMenu() на следующее:

View mAddListingButton;

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    getMenuInflater().inflate(R.menu.activity_main, menu);

    // Find the menu item we are interested in.
    MenuItem it = menu.findItem(R.id.item_new_listing);

    // Create a new view to replace the standard one.
    // It would be nice if we could just *get* the standard one
    // but unfortunately it.getActionView() returns null.

    // actionButtonStyle makes the 'pressed' state work.
    ImageView button = new ImageView(this, null, android.R.attr.actionButtonStyle);
    button.setImageResource(R.drawable.ic_action_add_listing);
    // The onClick attributes in the menu resource no longer work (I assume since
    // we passed null as the attribute list when creating this button.
    button.setOnClickListener(this);

    // Ok, now listen for when layouting is finished and the button is in position.
    button.addOnLayoutChangeListener(new OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom,
                int oldLeft, int oldTop, int oldRight, int oldBottom)
        {
            // Apparently this can be called before layout is finished, so ignore it.
            // Remember also that it might never be called with sane values, for example
            // if your action button is pushed into the "more" menu thing.
            if (left == 0 && top == 0 && right == 0 && bottom == 0)
                return;

            // This is the only way to get the absolute position on the screen.
            // The parameters passed to this function are relative to the containing View
            // and v.getX/Y() return 0.
            int[] location = new int[2];
            v.getLocationOnScreen(location);

            // Ok, now we know the centre location of the button in screen coordinates!
            informButtonLocation(location[0] + (right-left)/2, location[1] + (bottom-top)/2);
        }
    });

    it.setActionView(button);
    mAddListingButton = it.getActionView();
    return true;
}

И затем у вас есть функция, которая обновляет ваш полезный вид инструкций с расположением кнопки и перерисовывает ее:

private void informButtonLocation(int cx, int cy)
{
    MyHelpView v = (MyHelpView)findViewById(R.id.instructions);
    v.setText("Click this button.");
            v.setTarget(cx, cy);
}

Наконец, сделайте свой необычный пользовательский вид, который рисует стрелку (рядом) с кнопкой. В вашем методе onDraw вы можете преобразовать координаты экрана обратно в координаты Canvas, используя ту же функцию getLocationOnScreen. Что-то вроде этого:

@Override
public void onDraw(Canvas canvas)
{
    int[] location = new int[2];
    getLocationOnScreen(location);

    canvas.drawColor(Color.WHITE);

    mPaint.setColor(Color.BLACK);
    mPaint.setTextSize(40);
    mPaint.setAntiAlias(true);
    mPaint.setTextAlign(Align.CENTER);
    canvas.drawText(mText, getWidth()/2, getHeight()/2, mPaint);

    // Crappy line that isn't positioned right at all and has no arrowhead.
    if (mTargetX != -1 && mTargetY != -1)
        canvas.drawLine(getWidth()/2, getHeight()/2,
                mTargetX - location[0], mTargetY - location[1], mPaint);
}

(Но, очевидно, вам нужно скопировать целевое местоположение с размером холста). Если вы идете по тому же методу, что и Google, на котором вы просматриваете представление по всему экрану, вам не придется беспокоиться об этом, но это более навязчиво. Метод Google определенно не использует какие-либо частные API (удивительно). Проверьте Cling.java в коде Launcher2.

Кстати, если вы посмотрите на источник аналогичной реализации Google для этой пусковой установки (они почему-то называют экран инструкций a Cling), вы можете видеть, что он еще более взломан. Они в основном используют абсолютные позиции экрана, определяемые использованием макета.

Надеюсь, это поможет людям!

Ответ 2

Я нашел другой способ получить крючок на кнопке панели действий, которая может быть немного проще, чем принятое решение. Вы можете просто вызвать findViewById, с идентификатором элемента меню!

Теперь жесткая часть , когда вы можете назвать это? В onCreate и onResume это слишком рано. Ну, один из способов сделать это - использовать ViewTreeObserver окна:

    final ViewTreeObserver viewTreeObserver = getWindow().getDecorView().getViewTreeObserver();
    viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            View menuButton = findViewById(R.id.menu_refresh);
            // This could be called when the button is not there yet, so we must test for null
            if (menuButton != null) {
                // Found it! Do what you need with the button
                int[] location = new int[2];
                menuButton.getLocationInWindow(location);
                Log.d(TAG, "x=" + location[0] + " y=" + location[1]);

                // Now you can get rid of this listener
                viewTreeObserver.removeGlobalOnLayoutListener(this);
            }
        }
    });

Это находится в onCreate.

Примечание: это было протестировано в проекте с помощью ActionBarSherlock, поэтому возможно, что он не работает с "реальной" панелью действий.

Ответ 3

Просто обрабатывайте пункт меню как обычный вид... например:

    View myActionView = findViewById(R.id.my_menu_item_id);
    if (myActionView != null) {
        int[] location = new int[2];
        myActionView.getLocationOnScreen(location);

        int x = location[0];
        int y = location[1];
        Log.d(TAG, "menu item location --> " + x + "," + y);
    }

Примечание. Я тестировал свою активность, настроенную с помощью ToolBar, а не непосредственно как ActionBar... но я думаю, что она должна работать аналогично.

Ответ 4

Этот ответ устарел, ищите принятый ответ выше

Я узнал, что (в это время) нет способа узнать значение x/y для элемента меню. См. Пункты меню, которые можно просто скрывать от экрана, пока пользователь не нажмет кнопку меню. В Android 3.0 и выше у вас есть возможность реализовать меню в приложении ActionBar. Вы можете добавить много элементов меню в панель действий, когда больше нет места для размещения элемента на панели, он просто создаст кнопку "Переполнение", которая будет содержать все пункты меню, которые нельзя было бы разместить на панели действий, потому что нехватки места или потому, что программист явно установил пункт меню в меню переполнения.

Все это, вероятно, связано с тем, что элементы меню не имеют своей позиции x/y, которую вы можете получить (или установить).

Как я в конечном итоге решил эту проблему, используйте функцию Paint.measureText(), чтобы определить, насколько широка будет каждая позиция меню на панели действий, и использовать ее для вычисления смещения с правой стороны экрана, где должна быть указана метка быть.

Это обходной путь, но если у кого-либо есть такая же проблема, как и я, знайте, что это работает отлично. Не забудьте установить соответствующий размер шрифта в объекте Paint, а также при измерении длины текста он, скорее всего, рассчитает ширину текста с использованием другого размера шрифта, чем размер шрифта вашего меню.