2012-05-01 14 views

Antwort

12

Ich habe endlich eine Antwort darauf gefunden! Es ist ein bisschen hacky, aber nicht zu viel. Die einzigen Nachteile sind:

  1. Es verwendet eine 3.0+ API. Ernsthaft, obwohl Google, was ist der Sinn der Veröffentlichung neuer Versionen von Android, wenn wir sie nicht verwenden können? /schimpfen.
  2. Sie müssen die MenuItem s durch Ihre eigene View ersetzen. Ein ImageView ist dem Standard ziemlich ähnlich, aber es fehlen Merkmale wie langes Drücken, um ein Tool-Tipp zu sehen, und wahrscheinlich andere Dinge.
  3. Ich habe es nicht viel getestet.

Ok, hier ist, wie wir es machen. Ändern Sie Ihre Activity ‚s onCreateOptionsMenu() etwas wie folgt aus:

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; 
} 

Und dann haben Sie eine Funktion, die aktualisiert Ihre hilfreiche Anweisungen mit dem Standort der Taste anzeigen und neu zeichnet es:

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

Schließlich machen Sie Ihre fancy schmancy custom view, die einen Pfeil in die Nähe des Buttons zeichnet. In Ihrer onDraw Methode können Sie die Bildschirmkoordinaten zurück in Canvas Koordinaten mit der gleichen getLocationOnScreen Funktion konvertieren.Etwas wie folgt aus:

@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); 
} 

(Aber natürlich müssen Sie auf die Leinwand Größe den Zielort Clip). Wenn Sie dieselbe Methode wie Google verwenden, bei der Sie eine Ansicht über den gesamten Bildschirm einblenden, müssen Sie sich keine Gedanken darüber machen, aber es ist aufdringlicher. Googles Methode verwendet definitiv keine privaten APIs (erstaunlich). Überprüfen Sie Cling.java im Launcher2-Code.

Übrigens, wenn Sie sich die Quelle für ähnliche Implementierung von Google für den Launcher ansehen (sie nennen die Anweisungen Bildschirm a Cling aus irgendeinem Grund), können Sie sehen, dass es noch hacky ist. Sie verwenden grundsätzlich absolute Bildschirmpositionen, die durch das verwendete Layout bestimmt werden.

Hoffe das hilft den Menschen!

+0

Das funktioniert eigentlich gut! Obwohl die Zeit bereits vorbei ist, um für mich nützlich zu sein (ich habe bereits meine eigene Lösung benutzt, siehe unten) und eine Änderung der App ist momentan keine Option. Aber ich hoffe, es wird anderen Menschen in der Zukunft helfen! Gute Antwort! – Pieter888

+0

Der addOnLayoutChangeListener wird nur von Honeycomb unterstützt. Es wird nicht mit alten APIs funktionieren. –

+0

Ich habe jetzt die Koordinaten - aber wie zeichne ich oben auf ein Fragment? – Skynet

0

Diese Antwort jetzt veraltet ist, Achten Sie auf die akzeptierte Antwort oben

ich, dass (zu diesem Zeitpunkt) fand heraus, gibt es keine Möglichkeit, die x/y-Wert eines Menüpunktes herauszufinden. Die Menüeinträge können genauso gut vom Bildschirm ausgeblendet werden, bis der Benutzer die Menütaste drückt. In Android 3.0 und höher haben Sie die Möglichkeit, das Menü in der App ActionBar zu implementieren. Sie können eine Menge Menüpunkte in der Aktionsleiste hinzufügen, wenn es keinen Platz mehr gibt, um einen Gegenstand auf der Leiste zu platzieren, wird einfach eine "Überlauf" -Schaltfläche erzeugt, die alle Menüpunkte enthält, die nicht in der Aktionsleiste platziert werden konnten von einem Mangel an Speicherplatz oder weil der Programmierer explizit den Menüeintrag im Überlaufmenü angezeigt.

All dieses Verhalten ist wahrscheinlich, warum Menüpunkte nicht ihre x/y-Position haben, die Sie abrufen können (oder festlegen).

Wie ich dieses Problem schließlich gelöst habe, indem ich die Paint.measureText() Funktion verwende, um zu bestimmen, wie breit jeder Menüeintrag in der Aktionsleiste sein sollte, und benutze es, um den Versatz von der rechten Seite des Bildschirms zu berechnen .

Es ist ein Workaround, aber wenn jemand jemals das gleiche Problem wie ich hat, wissen Sie, dass dies funktioniert gut. Denken Sie daran, die entsprechende Schriftgröße für das Objekt Paint festzulegen. Wenn Sie die Länge des Textes messen, wird die Textbreite höchstwahrscheinlich mit einer anderen Schriftgröße als der Schriftgröße Ihres Menüelements berechnet.

31

Ich habe einen anderen Weg gefunden, um einen Haken auf einem Aktionsleistenknopf zu bekommen, der ein bisschen einfacher als die akzeptierte Lösung sein kann. Sie können einfach findViewById anrufen, mit der ID des Menüpunktes!

Jetzt ist der schwierige Teil wenn können Sie dies nennen? In onCreate und onResume ist es zu früh. Nun ja, eine Möglichkeit, dies zu tun, ist die ViewTreeObserver des Fensters zu verwenden:

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); 
      } 
     } 
    }); 

Die in onCreate geht.

Hinweis: Dies wurde in einem Projekt mit ActionBarSherlock getestet, daher ist es möglich, dass es nicht mit einer "echten" Aktionsleiste funktioniert.

+7

Ich empfehle das Hinzufügen von 'if (viewTreeObserver.isAlive())' zu überprüfen, bevor Sie den Listener entfernen, da manchmal eine Ausnahme ausgelöst wird, wenn sie nicht aktiv ist. –

+0

Wunderbar! Danke für diese großartige Antwort. Dies sollte der richtige sein. – Cilenco

+0

findViewById - funktioniert für einen Menüpunkt? – Skynet

10

Griff einfach den Menüpunkt als normaler view..like dies:

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); 
    } 

Hinweis:ich mit meiner Aktivität getestet habe konfiguriert ToolBar verwenden, nicht direkt als ActionBar ... aber i denke, es sollte ähnlich funktionieren.

+0

Sie können dies auch innerhalb der onOptionsItemSelected aufrufen und es wird funktionieren. – Simon

Verwandte Themen