Gibt es eine gängige Methode, um ein großes Bild anzuzeigen und es dem Benutzer zu ermöglichen, das Bild zu vergrößern, zu verkleinern und zu schwenken?
Bisher habe ich zwei Wege gefunden:
Überschreiben von ImageView, das scheint ein bisschen zu viel für ein so häufiges Problem zu sein.
Verwenden einer Webansicht, aber mit weniger Kontrolle über das Gesamtlayout usw.
Es gibt eine ZOOM-STEUERUNG (Widget) und Sie können das OnTouch-Ereignis abhören, um das Schwenken zu handhaben.
– Tobrien
29. März 2010 um 13:33 Uhr
Eine ähnliche Frage, stackoverflow.com/questions/2537396/…, hat einen Link zu diesem Tutorial anddev.org/…. Sie finden das vielleicht nützlich, um Ihr Bild zu schwenken. Ich habe es nicht im Detail gelesen, aber es könnte Ihnen auch einige Ideen geben, wie Sie die Zoom-Funktion nutzen können.
– Steve Haley
29. März 2010 um 13:45 Uhr
Hat jemand versucht, das Bild beim Zoomen zu speichern? Ich möchte, dass das gespeicherte Bild einen Standardzustand anstelle des gezoomten Zustands hat. Siehe meine Frage: stackoverflow.com/questions/24730793/… Danke
– Blaze Tama
18. Juli 2014 um 5:00 Uhr
Mike Ortiz
AKTUALISIEREN
Ich habe gerade TouchImageView ein neues Update gegeben. Es enthält jetzt Double Tap Zoom und Fling zusätzlich zu Panning und Pinch Zoom. Der folgende Code ist sehr datiert. Sie können die überprüfen github-Projekt um den neuesten Code zu erhalten.
VERWENDUNGSZWECK
Platzieren Sie TouchImageView.java in Ihrem Projekt. Es kann dann genauso wie ImageView verwendet werden. Beispiel:
Wenn Sie TouchImageView in XML verwenden, müssen Sie den vollständigen Paketnamen angeben, da es sich um eine benutzerdefinierte Ansicht handelt. Beispiel:
Hinweis: Ich habe meine vorherige Antwort entfernt, die einen sehr alten Code enthielt, und verlinke jetzt direkt auf den aktuellsten Code auf GitHub.
ViewPager
Wenn Sie TouchImageView in einen ViewPager einfügen möchten, lesen Sie diese Antwort.
Paulo, ich hatte keine Leistungsprobleme, aber ich konnte es nicht auf einem Tablet testen. Meinst du mit langsam langsam? Ich habe zu Beginn von onScale einen maximalen Zoomfaktor von 1,05 eingestellt. Ist es das, wovon du sprichst? Wenn nicht, versuchen Sie Folgendes: 1. Befinden Sie sich im Debug-Modus? Dies würde es erheblich verlangsamen. 2. Welche Bildgröße stellen Sie ein? Ich habe nicht mit sehr großen (8 MP) Bildern getestet, aber das könnte es verlangsamen. 3. Haben Sie ein Telefon, auf dem Sie testen könnten? 4. Wenn alles andere fehlschlägt, prüfen Sie, ob die Multiplikation von mScaleFactor mit 2 (falls > 1) oder 0,5 (falls < 1) Ihrer Situation hilft.
– Mike Ortiz
4. Oktober 2011 um 0:51 Uhr
@Ahsan Ändern Sie den View-Konstruktor in: TouchImageView(Context context, AttributeSet attrs) und Ruf an super(context, attrs); Dies liegt daran, dass beim Aufblasen der benutzerdefinierten Ansicht diese mit zwei Parametern erstellt wird und nicht nur mit einem. Wenn ich dazu komme, werde ich TouchImageView reparieren, um die drei Ansichtskonstruktoren und Drawables zu unterstützen.
– Mike Ortiz
26. Oktober 2011 um 19:54 Uhr
@Ahsan Da es sich um eine benutzerdefinierte Ansicht handelt, müssen Sie den gesamten Namen in die XML-Datei schreiben, dh <com.example.TouchImageView android:id="@+id/img" />. Hast du das gemacht?
– Mike Ortiz
2. November 2011 um 5:56 Uhr
Das ist eine tolle Sache, suche schon seit Ewigkeiten danach. Verwenden Sie den Code von github, da er neuer ist und einfach besser funktioniert
– Alex
16. Juli 2012 um 18:20 Uhr
@Mike Ich habe diesen Code ausprobiert, aber die benutzerdefinierte Galerie funktioniert nicht. Gibt es einen Trick um dieses Problem zu umgehen.
– Umesh
4. Oktober 2012 um 6:36 Uhr
Ich habe etwas Code angepasst, um ein TouchImageView zu erstellen, das Multitouch unterstützt (>2.1). Es ist von dem Buch inspiriert Hallo Android! (3. Auflage)
Es ist in den folgenden 3 Dateien TouchImageView.java WrapMotionEvent.java EclairMotionEvent.java enthalten
TouchImageView.java
import se.robertfoss.ChanImageBrowser.Viewer;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
public class TouchImageView extends ImageView {
private static final String TAG = "Touch";
// These matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
Context context;
public TouchImageView(Context context) {
super(context);
super.setClickable(true);
this.context = context;
matrix.setTranslate(1f, 1f);
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent rawEvent) {
WrapMotionEvent event = WrapMotionEvent.wrap(rawEvent);
// Dump touch event to log
if (Viewer.isDebug == true){
dumpEvent(event);
}
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
break;
case MotionEvent.ACTION_UP:
int xDiff = (int) Math.abs(event.getX() - start.x);
int yDiff = (int) Math.abs(event.getY() - start.y);
if (xDiff < 8 && yDiff < 8){
performClick();
}
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
// ...
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);
} else if (mode == ZOOM) {
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
setImageMatrix(matrix);
return true; // indicate event was handled
}
});
}
public void setImage(Bitmap bm, int displayWidth, int displayHeight) {
super.setImageBitmap(bm);
//Fit to screen.
float scale;
if ((displayHeight / bm.getHeight()) >= (displayWidth / bm.getWidth())){
scale = (float)displayWidth / (float)bm.getWidth();
} else {
scale = (float)displayHeight / (float)bm.getHeight();
}
savedMatrix.set(matrix);
matrix.set(savedMatrix);
matrix.postScale(scale, scale, mid.x, mid.y);
setImageMatrix(matrix);
// Center the image
float redundantYSpace = (float)displayHeight - (scale * (float)bm.getHeight()) ;
float redundantXSpace = (float)displayWidth - (scale * (float)bm.getWidth());
redundantYSpace /= (float)2;
redundantXSpace /= (float)2;
savedMatrix.set(matrix);
matrix.set(savedMatrix);
matrix.postTranslate(redundantXSpace, redundantYSpace);
setImageMatrix(matrix);
}
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(WrapMotionEvent event) {
// ...
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
Log.d(TAG, sb.toString());
}
/** Determine the space between the first two fingers */
private float spacing(WrapMotionEvent event) {
// ...
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
/** Calculate the mid point of the first two fingers */
private void midPoint(PointF point, WrapMotionEvent event) {
// ...
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
}
WrapMotionEvent.java
import android.view.MotionEvent;
public class WrapMotionEvent {
protected MotionEvent event;
protected WrapMotionEvent(MotionEvent event) {
this.event = event;
}
static public WrapMotionEvent wrap(MotionEvent event) {
try {
return new EclairMotionEvent(event);
} catch (VerifyError e) {
return new WrapMotionEvent(event);
}
}
public int getAction() {
return event.getAction();
}
public float getX() {
return event.getX();
}
public float getX(int pointerIndex) {
verifyPointerIndex(pointerIndex);
return getX();
}
public float getY() {
return event.getY();
}
public float getY(int pointerIndex) {
verifyPointerIndex(pointerIndex);
return getY();
}
public int getPointerCount() {
return 1;
}
public int getPointerId(int pointerIndex) {
verifyPointerIndex(pointerIndex);
return 0;
}
private void verifyPointerIndex(int pointerIndex) {
if (pointerIndex > 0) {
throw new IllegalArgumentException(
"Invalid pointer index for Donut/Cupcake");
}
}
}
EclairMotionEvent.java
import android.view.MotionEvent;
public class EclairMotionEvent extends WrapMotionEvent {
protected EclairMotionEvent(MotionEvent event) {
super(event);
}
public float getX(int pointerIndex) {
return event.getX(pointerIndex);
}
public float getY(int pointerIndex) {
return event.getY(pointerIndex);
}
public int getPointerCount() {
return event.getPointerCount();
}
public int getPointerId(int pointerIndex) {
return event.getPointerId(pointerIndex);
}
}
Robert Foss, wenn dieser Grenzrichter hinzufügt, kann es besser fallen. Vielen Dank für Ihren Code
– pengwang
2. Dezember 2010 um 7:20 Uhr
Es funktioniert, aber ich sehe keinen Sinn darin WrapMotionEvent und EclairMotionEvent… jedenfalls +1.
– Cipi
3. Juni 2011 um 14:27 Uhr
Multitouch für Telefone, die dies unterstützen. Eine regelmäßige Berührung für Android <2.0
– Robert Foß
8. Juli 2011 um 10:04 Uhr
Schönes Beispiel, es funktioniert gut, aber ich habe nicht bekommen, was Viewer ist, wenn (Viewer.isDebug == true) { dumpEvent (event); }
– Tofeeq Ahmad
6. April 2012 um 6:09 Uhr
Was ist das? >> se.robertfoss.ChanImageBrowser.Viewer
– Smaragdhieu
14. Mai 2012 um 6:51 Uhr
Janusz
Ich habe ein WebView verwendet und das Bild aus dem Speicher geladen
webview.loadUrl("file://...")
Die WebView übernimmt das gesamte Schwenken, Zoomen und Scrollen. Wenn Sie wrap_content verwenden, wird die Webansicht nicht größer als das Bild und es werden keine weißen Bereiche angezeigt. Der WebView ist der bessere ImageView 😉
Ich verwende den gleichen Ansatz. Ich habe eine große U-Bahn-Karte, die der Benutzer zoomen und scrollen soll. Mir ist jedoch aufgefallen, dass bei einem ziemlich großen Bild (dh 1000 oder 3000 Pixel breit) das Bild verschwommen wird, sobald Sie hineinzoomen. Es scheint, dass Coliris ein großes gezoomtes Bild nicht sehr scharf anzeigen kann. Obwohl das Originalbild unkomprimiert und sehr scharf ist. Daher habe ich das eine große Bild in kleinere Scheiben geschnitten und per HTML wieder zusammengesetzt. So bleibt das Bild beim Hineinzoomen scharf. (Ich bin auf Nexus One, vorher 2.1update und jetzt auf 2.2)
– Mathias Conradt
15. Juni 2010 um 2:58 Uhr
@ Mathias Lin: Wenn ein großes Bild über die Leitung gesendet wird, habe ich gehört Träger Komprimieren Sie große Bilder. Passt dieser Anwendungsfall zu Ihnen oder haben Sie das Bild lokal geladen?
– Samuel
22. Februar 2011 um 4:42 Uhr
@Sam Quest: Laden Sie es lokal
– Mathias Conradt
22. Februar 2011 um 8:15 Uhr
Es ist viel besser, die integrierten Zoom-Schaltflächen von Webview und die Unterstützung für Pinch zum Vergrößern/Verkleinern zu verwenden, als einen völlig neuen Algo zu schreiben, der möglicherweise nicht auf verschiedenen Telefonen und zukünftigen Android-Plattformversionen funktioniert
– samisch
1. August 2011 um 5:52 Uhr
Diese Lösung kann nur angewendet werden, wenn Sie das Bild zufällig auf der Festplatte herumliegen haben oder das Bild klein genug ist, dass Sie es mit Base 64 codieren und den Zeichenfolgenwert an loadUrlWithData() übergeben können.
– Jeffrey Blattmann
10. September 2012 um 20:10 Uhr
Digiphd
Als Antwort auf die ursprüngliche Frage von Janusz gibt es mehrere Möglichkeiten, dies zu erreichen, die sich alle in ihrem Schwierigkeitsgrad unterscheiden und unten aufgeführt sind. Die Verwendung einer Webansicht ist gut, aber in Bezug auf Look and Feel und Steuerbarkeit sehr eingeschränkt. Wenn Sie eine Bitmap von einer Leinwand zeichnen, scheinen die vielseitigsten Lösungen, die vorgeschlagen wurden, die von MikeOrtiz, Robert Foss und/oder die von Jacob Nordfalk zu sein. Es gibt ein tolles Beispiel für die Einbindung des Android-Multitouch-Controllers von PaulBourkeund ist großartig für die Multi-Touch-Unterstützung und alle Arten von benutzerdefinierten Ansichten.
Persönlich finde ich die Lösung von MikeOrtiz am einfachsten, wenn Sie einfach eine Leinwand auf eine Bitmap zeichnen und sie dann in ImageView anzeigen und in der Lage sein möchten, mit Multi-Touch hineinzuzoomen und sich zu bewegen. Allerdings ist für meine Zwecke der Code aus der Git das er bereitgestellt hat, scheint nur zu funktionieren, wenn seine benutzerdefinierte ImageView-Klasse TouchImageView das einzige untergeordnete Element ist oder die Layoutparameter wie folgt bereitstellt:
Leider brauchte ich aufgrund meines Layoutdesigns “wrap_content” für “layout_height”. Als ich es so änderte, wurde das Bild unten beschnitten und ich konnte nicht in den beschnittenen Bereich scrollen oder zoomen. Also habe ich mir die angeschaut Quelle für ImageView, nur um zu sehen, wie Android “onMeasure” implementiert und MikeOrtiz angepasst hat.
Hier ist resolveSize(int,int) ein “Dienstprogramm zum Abgleichen einer gewünschten Größe mit Einschränkungen, die durch eine MeasureSpec auferlegt werden, wobei:
Parameter:
- size How big the view wants to be
- MeasureSpec Constraints imposed by the parent
Kehrt zurück:
- The size this view should be."
So wird im Wesentlichen ein Verhalten bereitgestellt, das der ursprünglichen ImageView-Klasse etwas ähnlicher ist, wenn das Bild geladen wird. Einige weitere Änderungen könnten vorgenommen werden, um eine größere Vielfalt von Bildschirmen zu unterstützen, die das Seitenverhältnis ändern. Aber im Moment hoffe ich, dass dies hilft. Danke an MikeOrtiz für seinen Originalcode, großartige Arbeit.
Die Bibliothek ist wirklich toll, wenn auch anfangs etwas schwer zu fassen.
zontar
Ich habe gerade TouchImageView von Robert Foss integriert: es funktionierte sofort tadellos! Danke!
Ich habe den Code nur ein wenig geändert, damit ich ihn aus meiner layout.xml instanziieren kann.
Fügen Sie einfach zwei Konstruktoren hinzu
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TouchImageView(Context context) {
super(context);
init(context);
}
und transformiere den alten Konstruktor in eine Init-Methode:
private void init(Context context){
//...old code ofconstructor of Robert Moss's code
}
Roman Truba
@Robert Foss, @Mike Ortiz, vielen Dank für Ihre Arbeit. Ich habe Ihre Arbeit zusammengeführt und die Robert-Klassen für Android > 2.0 mit Mikes zusätzlicher Arbeit abgeschlossen.
Als Ergebnis meiner Arbeit präsentiere ich Android Touch Gallery, basierend auf ViewPager und verwendet modifiziertes TouchImageView. Bilder werden per URL geladen und Sie können sie zoomen und ziehen. Sie finden es hier https://github.com/Dreddik/AndroidTouchGallery
9648200cookie-checkWie erhalte ich die Zoomfunktion für Bilder?yes
Es gibt eine ZOOM-STEUERUNG (Widget) und Sie können das OnTouch-Ereignis abhören, um das Schwenken zu handhaben.
– Tobrien
29. März 2010 um 13:33 Uhr
Eine ähnliche Frage, stackoverflow.com/questions/2537396/…, hat einen Link zu diesem Tutorial anddev.org/…. Sie finden das vielleicht nützlich, um Ihr Bild zu schwenken. Ich habe es nicht im Detail gelesen, aber es könnte Ihnen auch einige Ideen geben, wie Sie die Zoom-Funktion nutzen können.
– Steve Haley
29. März 2010 um 13:45 Uhr
Hat jemand versucht, das Bild beim Zoomen zu speichern? Ich möchte, dass das gespeicherte Bild einen Standardzustand anstelle des gezoomten Zustands hat. Siehe meine Frage: stackoverflow.com/questions/24730793/… Danke
– Blaze Tama
18. Juli 2014 um 5:00 Uhr