/* Copyright (C) 2014,2015 Björn Stelter * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/> */ package de.hu_berlin.informatik.spws2014.mapever.largeimageview; import android.graphics.Canvas; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; public abstract class OverlayIcon { // LargeImageView, an die das Overlay gebunden ist private LargeImageView parentLIV; // das Bild des Icons als Drawable private Drawable drawable; // ist das Icon gerade sichtbar? private boolean visible = true; // Transparenz: Alpha-Wert private int overlayAlpha = 255; // Drag and Drop: ID des Pointers (Fingers), mit dem das Icon gedraggt wird. -1 wenn nicht gedraggt. private int dragPointerID = -1; // //////////////////////////////////////////////////////////////////////// // //////////// CONSTRUCTORS AND LAYOUT STUFF // //////////////////////////////////////////////////////////////////////// public OverlayIcon(LargeImageView liv) { // Referenz auf die LargeImageView merken, zu der das Icon gehört this.parentLIV = liv; // Icon bei der LIV registrieren liv.attachOverlayIcon(this); } // ////// LAYOUT STUFF /** * Gibt die LargeImageView zurück, zu der das Icon gehört. */ public LargeImageView getParentLIV() { return parentLIV; } /** * Deregistriert das Icon von der LargeImageView (löscht es aus der OverlayIconList). Sollte nach dem Löschen eines * Icons aufgerufen werden. */ public void detach() { parentLIV.detachOverlayIcon(this); } // //////////////////////////////////////////////////////////////////////// // //////////// PROPERTIES // //////////////////////////////////////////////////////////////////////// // ////// DIMENSIONEN /** * Gibt die Breite des Icons zurück. */ public int getWidth() { return getDrawable() == null ? 0 : getDrawable().getIntrinsicWidth(); } /** * Gibt die Höhe des Icons zurück. */ public int getHeight() { return getDrawable() == null ? 0 : getDrawable().getIntrinsicHeight(); } // ////// POSITION AND OFFSET /** * X-Koordinate der Bildposition. Muss von Subklassen überschrieben werden, um die Position zu setzen. * * @return 0, wenn nicht überschrieben. */ protected int getImagePositionX() { return 0; } /** * Y-Koordinate der Bildposition. Muss von Subklassen überschrieben werden, um die Position zu setzen. * * @return 0, wenn nicht überschrieben. */ protected int getImagePositionY() { return 0; } /** * Bildoffset in X-Richtung. Muss von Subklassen überschrieben werden, wenn ein Offset erwünscht ist. * * @return 0, wenn nicht überschrieben. */ protected int getImageOffsetX() { return 0; } /** * Bildoffset in Y-Richtung. Muss von Subklassen überschrieben werden, wenn ein Offset erwünscht ist. * * @return 0, wenn nicht überschrieben. */ protected int getImageOffsetY() { return 0; } /** * Gibt ein Rechteck zurück, das relativ zur Bildposition angibt, welcher Bereich des Icons anklickbar ist. * Default-Implementation gibt die Dimensionen des Bildes verschoben um den ImageOffset zurück. * Kann überschrieben werden und darf null zurückgeben. null wird als "Icon ist nicht klickbar" interpretiert. */ public Rect getTouchHitbox() { return new Rect( getImageOffsetX(), getImageOffsetY(), getWidth() + getImageOffsetX(), getHeight() + getImageOffsetY()); } /** * Gibt mittels LargeImageView.imageToScreenPosition() die momentane Bildschirmposition (statt Bildposition) des * Icons zurück. */ public PointF getScreenPosition() { return parentLIV.imageToScreenPosition(getImagePositionX(), getImagePositionY()); } // ////// ICON DRAWABLE /** * Setze das Iconbild in Form eines Drawables. */ public void setDrawable(Drawable _drawable) { drawable = _drawable; // Boundaries/Bildgröße setzen drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); } /** * Gibt das Iconbild-Drawable zurück. */ public Drawable getDrawable() { return drawable; } // ////// APPEARANCE /** * True, wenn Icon gerade angezeigt wird (siehe {@link #hide()} und {@link #show()}). */ public boolean isVisible() { return visible; } /** * Setzt Sichtbarkeit des Icons. */ public void setVisibility(boolean vis) { visible = vis; update(); } /** * Versteckt das Icon. Kann mit {@link #show()} wieder angezeigt werden. */ public void hide() { setVisibility(false); } /** * Zeigt ein vorher mit {@link #hide()} verstecktes Icon wieder an. */ public void show() { setVisibility(true); } /** * Setze Transparenz des Icons. * * @param newAlpha Wert von 0 (vollkommen transparent) bis 255 (undurchsichtig). */ public void setOverlayAlpha(int newAlpha) { overlayAlpha = newAlpha; // Darstellung aktualisieren update(); } /** * Gibt Transparenz des Icons zurück. * * @return Wert von 0 (vollkommen transparent) bis 255 (undurchsichtig). */ public int getOverlayAlpha() { return overlayAlpha; } // //////////////////////////////////////////////////////////////////////// // //////////// EVENT HANDLERS // //////////////////////////////////////////////////////////////////////// // ////// CLICKS /** * Führt einen Klick auf das Icon aus. Macht normalerweise nichts, kann überschrieben werden. Wurde das Event * behandelt muss false zurückgegeben werden, damit keine andere View das Event erhält. * * @param screenX X-Koordinate des Klicks relativ zum Bildschirm * @param screenY Y-Koordinate des Klicks relativ zum Bildschirm * @return Immer false, falls nicht überschrieben. */ public boolean onClick(float screenX, float screenY) { // return false: Event wurde nicht behandelt. return false; } // ////// DRAG AND DROP /** * Markiere Icon als "wird jetzt mit Pointer pointerID gedraggt". Muss von onDragDown() aufgerufen werden, damit * onDragMove() getriggert wird. getDragPointerID() gibt dann (bis stopDrag()) pointerID zurück. * * @param pointerID Pointer-ID von 0 bis n */ protected void startDrag(int pointerID) { dragPointerID = pointerID; } /** * Beende Drag-Aktion. Muss von onDragUp() aufgerufen werden, damit onDragMove() nicht mehr getriggert wird. */ protected void stopDrag() { dragPointerID = -1; } /** * Wenn das Icon gerade gedraggt wird, dann gibt diese Funktion die PointerID des draggenden Fingers zurück. * Ansonsten -1, wenn das Icon nicht gedraggt wird. */ public int getDragPointerID() { return dragPointerID; } /** * Wird von LargeImageView.onTouchEvent_dragAndDrop() getriggert, wenn ein Finger das Icon berührt. * Soll kein Drag and Drop implementiert werden, reicht die Standard-Implementierung, die false zurückgibt. * Damit nun auch onDragMove() getriggert wird, muss startDrag(pointerID) aufgerufen werden. * Wurde das Event behandelt, sollte true zurückgegeben werden, damit das Event nicht mehr von anderen Objekten * behandelt wird (und z.B. das Panning auslöst). * * @param pointerID ID des Pointers (Fingers), der das Icon berührt * @param screenX X-Koordinate des Klicks relativ zum Bildschirm * @param screenY Y-Koordinate des Klicks relativ zum Bildschirm * @return Immer false, falls nicht überschrieben. */ public boolean onDragDown(int pointerID, float screenX, float screenY) { // return false: Event wurde nicht behandelt. return false; } /** * Wird von LargeImageView.onTouchEvent_dragAndDrop() getriggert, wenn getDragPointerID() einen Pointer > -1 * zurückgibt und eben dieser Pointer für ein ACTION_MOVE gesorgt hat. * Wurde das Event behandelt, sollte true zurückgegeben werden, damit das Event nicht mehr von anderen Objekten * behandelt wird (und z.B. das Panning auslöst). * * @param screenX X-Koordinate des Klicks relativ zum Bildschirm * @param screenY Y-Koordinate des Klicks relativ zum Bildschirm * @return Immer false, falls nicht überschrieben. */ public boolean onDragMove(float screenX, float screenY) { // return false: Event wurde nicht behandelt. return false; } /** * Wird von LargeImageView.onTouchEvent_dragAndDrop() getriggert, wenn getDragPointerID() einen Pointer > -1 * zurückgibt und eben dieser Pointer für ein ACTION_(POINTER_)UP gesorgt hat. * Muss stopDrag() aufrufen, damit keine weiteren onDragMove()s mehr getriggert werden! * Werden Float.NaN als Parameter übergeben, gilt der Dragvorgang als "abgebrochen" (Handler kann sich z.B. um * das Zurücksetzen einer Drag-Verschiebung kümmern.) * * @param screenX X-Koordinate des Klicks relativ zum Bildschirm * @param screenY Y-Koordinate des Klicks relativ zum Bildschirm * @return Immer false, falls nicht überschrieben. */ public void onDragUp(float screenX, float screenY) { // (Ein Rückgabewert macht hier wenig Sinn. Wenn das Icon gedraggt wurde und der Dragvorgang beendet werden // soll, MUSS dieses Event behandelt werden. Andernfalls wird es gar nicht getriggert.) stopDrag(); return; } // //////////////////////////////////////////////////////////////////////// // //////////// DARSTELLUNG // //////////////////////////////////////////////////////////////////////// /** * Zeichnet das Drawable (mit Alpha-Wert, und nur falls nicht hidden) auf einem (vorher translatierten) Canvas. */ public void draw(Canvas canvas) { // Canvas wurde schon entsprechend verschoben, sodass (0,0) nun der Iconposition (inkl. Offset) entspricht if (getDrawable() != null && visible) { // Transparenz anwenden und Drawable zeichnen getDrawable().setAlpha(overlayAlpha); getDrawable().draw(canvas); } } /** * Aktualisiert die Darstellung des Icons (ruft invalidate() auf). * Sollte immer aufgerufen werden, wenn z.B. die Position oder die Transparenz verändert wurde. */ public void update() { parentLIV.invalidate(); } // ////// ANIMATIONS /** * Starte Fade-Animation. * * @param from Start-Alphawert * @param to End-Alphawert * @param duration Dauer der Animation */ protected void startFading(float from, float to, long duration) { // Erstelle Animation... selbsterklärende Parameter // Animation fadeAnim = new AlphaAnimation(from, to); // fadeAnim.setInterpolator(new AccelerateInterpolator()); // fadeAnim.setDuration(duration); // fadeAnim.setFillEnabled(true); // fadeAnim.setFillAfter(true); // // this.startAnimation(fadeAnim); // TODO Animationen reimplementieren! setOverlayAlpha((int) (to * 255)); } }