German ScrollView2D + Zoom

StephanP82

Member
Licensed User
Longtime User
Hallo zusammen,

ich nutze das ScrollView2D, auf dessen Panel ein Layout geladen wird. Dieses soll nun zoombar sein. Eigentlich mit der typischen Fingergeste, aber selbst mit dem GestureDetector hab ich das nicht hin bekommen. Nun also mit 2 Buttons (cmdZoomIn & cmdZoomOut). Bei Click wird ein ScaleView ausgelöst, dass das ganze ScrollView per Schleife nach und nach vergrößert, gewissermaßen zoomt. Leider ist dann jedoch das horizontale Scrollen nur fehlerhaft möglich, weil ich z.B. gar nicht nach ganz links gelange, obwohl ich die Width des ScrollView-Panels angepasst habe. Was mache ich falsch?

B4X:
    scvMainScroll.Initialize(100%x, 2000dip, "scvMainScroll")
    Activity.AddView(scvMainScroll, 0, 0, 100%x, 100%y)
    scvMainScroll.Panel.LoadLayout("main")

B4X:
Private Sub cmdZoomIn_Click
    scvMainScroll.Panel.Width = Activity.Width * (mfltLastMainScrollScale + 1.0)
    ScaleView(scvMainScroll, mfltLastMainScrollScale + 1.0)
End Sub

Private Sub cmdZoomOut_Click
    scvMainScroll.Panel.Width = Activity.Width * (mfltLastMainScrollScale - 1.0)
    ScaleView(scvMainScroll, mfltLastMainScrollScale - 1.0)
End Sub

B4X:
Sub ScaleView(v As View, Scale As Float)
    If mblnScalingInProgress Then Return
   
    If Scale >= 1.0 Then
        mblnScalingInProgress = True
        Dim jo As JavaObject = v
       
        Do Until mfltLastMainScrollScale = Scale
            Sleep(0)
            If Scale > mfltLastMainScrollScale Then
                Dim NewScale As Float = mfltLastMainScrollScale + 0.1
                If NewScale >= Scale Then
                    jo.RunMethod("setScaleX", Array(Scale))
                    jo.RunMethod("setScaleY", Array(Scale))
                    mfltLastMainScrollScale = Scale
                    Exit
                End If
                jo.RunMethod("setScaleX", Array(NewScale))
                jo.RunMethod("setScaleY", Array(NewScale))
                mfltLastMainScrollScale = NewScale
            Else If Scale < mfltLastMainScrollScale Then
                Dim NewScale As Float = mfltLastMainScrollScale - 0.1
                If NewScale <= Scale Then
                    jo.RunMethod("setScaleX", Array(Scale))
                    jo.RunMethod("setScaleY", Array(Scale))
                    mfltLastMainScrollScale = Scale
                    Exit
                End If
                jo.RunMethod("setScaleX", Array(NewScale))
                jo.RunMethod("setScaleY", Array(NewScale))
                mfltLastMainScrollScale = NewScale
            End If
        Loop
       
        mfltLastMainScrollScale = Scale
        mblnScalingInProgress = False
    End If
End Sub
 

klaus

Expert
Licensed User
Longtime User
Ich vermute dass diese Zeile, und die andere mit minus, falsch sind:
scvMainScroll.Panel.Width = Activity.Width * (mfltLastMainScrollScale + 1.0)
scvMainScroll.Panel.Width ist doch schon am Anfang grösser als Activity.Width, Du setzt sie aber immer gemäss Activity.Width.
Ich hab noch nie mit setScale gespielt, was genau macht diese Funktion, ändert die auch Left und Top von SscvMainScroll und SscvMainScroll.Panel?
Wenn ja, musst diese Werte wahrscheinlich ändern.
 

StephanP82

Member
Licensed User
Longtime User
Danke für deine Antwort. Das ScaleView vergrößert oder verkleinert Views entsprechend dem übergeben Float (1.0 = normal, 2.0 = doppel so groß, usw.), so erreiche ich einen Zoom-Effekt. Das Problem ist nun aber, dass ich das vergrößerte ScrollView, zwar nach rechts, nicht aber nach links bewegen kann. Die Width des ScrollView-Panels wird vergrößert, dadurch geht das, allerdings vergrößert es sich dann nur auf der rechten seite und ich kann das Sichtfeld quasi ganz außerhalb des ScrollViews bzw. Panels erreichen, nicht aber den linken Rand. Left und Top des ScrollViews bleiben 0, habe ich gerade getestet.

Gibt es denn noch andere Möglichkeiten das ScrollView zu zoomen, vielleicht sogar mit Fingergesten (zwei Finger auseinerander oder zusammen ziehen)?
 

StephanP82

Member
Licensed User
Longtime User
Das könnte ich, ich denke inzwischen aber, dass ich den falschen Ansatz habe. Ich habe mir zwischenzeitlich sämtliche Posts zu den Libs GestureDetector, dessen Vorgänger Gesture und auch zu PinchZoomAndMove durchgelesen und bin trotzdem nicht wirklich weiter. Sämtliche Zoom-Möglichkeiten beziehen sich auf ein ImageView und durch die Änderung von dessen Left, Top, Width und Height-Werten wird ein Zoom-Effekt erzielt, weil sich das Bild natürlich an die Dimensionen anpasst. Anders ist es aber wenn ein ScrollView gezoomt werden soll. Die Änderung der Werte verschiebt es letzlich nur innerhalb der Activity, aber die Inhalte des Panels werden nicht größer. ScaleView scheint zwar einen Zoom zu bewirken, schneidet aber die vor dem Zoom noch sichtbare linke Seite ab. Das View wird also innerhalb seines Parents nur vergrößert, behält aber seine Dimensionen.

Lange Rede, kurzer Sinn: Ich bin keinen Schritt weiter um das Panel des ScrollViews zu zoomen, also alle darin befindlichen Views optisch zu vergrößern. Man müsste das ScrollView in ein ImageView laden können, dann würde das vermutlich funktionieren. Ich habe im Forum auch nichts dergleichen gefunden, also dass jemand versucht ein ScrollView zu zoomen, was ich nicht nachvollziehen kann weil das doch eigentlich schon "zum guten Ton" bei Apps gehört, Ansichten zoomen zu können, oder?

Wie auch immer, hast du Klaus oder irgendjemand anders hier noch eine Idee wie ich das ganze technisch umsetzen kann? Bin mit meinem Latein am Ende.
 

MaFu

Well-Known Member
Licensed User
Longtime User
... weil das doch eigentlich schon "zum guten Ton" bei Apps gehört, Ansichten zoomen zu können, oder?
Bei Bildern, Karten und ähnliches ja. Aber ich interpretiere das so, dass Du Eingabeelemente (Button, EditBox, CheckBox, ...) in Deinem ScrollView hast. Und mir wäre jetzt keine App bekannt, bei der diese zoombar wären (ausgenommen Webansichten).
 

StephanP82

Member
Licensed User
Longtime User
Das funktioniert tatsächlich, vielen Dank Klaus :)

Die Idee, einfach alle einzelnen Views innerhalb des ScrollViews zu skalieren, ist so simpel wie genial :) Hätte ich eig. auch selbst drauf kommen können.

Ich habe jetzt im ScrollView viele Labels mit Border aneinander gereiht, so dass es optisch eine Tabelle ergibt. Nachdem ein- und wieder auszoomen scheinen die sich leicht zu verschieben, es entstehen Zwischenräume. Ist es möglich, dass das skalieren leichte Abweichungen zur Folge hat?
 

StephanP82

Member
Licensed User
Longtime User
Die Tabelle, also die Labels werden zur Laufzeit erzeugt. Ich habe dein Beispielprojekt mal damit erweitert und hänge es hier an. Wenn man 2-3 mal hinein zoomt und dann wieder hinaus, verändern sich die Zwischenräume, die Labels driften quasi auseinander. (Wieso denk ich dabei an die Dinos? Ach ja, Pangea :D )
 

Attachments

  • ScrollView2DZoom.zip
    253.5 KB · Views: 315

klaus

Expert
Licensed User
Longtime User
Ja, das sind Rundungsfehler.
Um das Problem zu lösen sehe Ich nur zwei Möglichkeiten.
Ähnlich wie Du es beim Aufbau der Tabelle machst musst Du die Breiten un Höhen als ganze Zahlen rechnen und dann die Left und Top Parameter anpassen.
Oder das Ganze per Grafik, mit Canvas, machen. Die Linien zeichnen und dann den Text reinzeichnen.
 

StephanP82

Member
Licensed User
Longtime User
Sorry, aber ich kann dir gerade nicht ganz folgen. Welche Breiten und Höhen sollen als ganze Zahlen gerechnet werden? Die neuen Werte beim skalieren, also z.B:

B4X:
v.Width = v.Width * Scale
v.Height = v.Height * Scale

?

Sind die nicht sowieso Int, also Ganzzahlen? Und wie fern sollen dann Left und Top angepasst werden? Die sind eig. auch bereits Int, oder hab ich da jetzt was falsch verstanden?
 

MaFu

Well-Known Member
Licensed User
Longtime User
Eigentlich sind die Int ja das Hauptproblem. Bei der Skalierung wird aufgrund der Int das Ergebnis immer gerundet, daher entsteht ein immer größerer Drift. Aber auch bei reiner Fließkommaberechnung wäre eine Ungenauigkeit bei häufigem Zoomen vorhanden.

Für eine korrekte Berechnung solltest die ursprünglichen Werte aller Labels speichern (also Position und Größe) und beim skalieren immer die aktuellen Werte anhand dieser Ursprungswerte berechnen. Dann kannst Du hundert mal rein- und rauszoomen ohne das sich etwas verschiebt.
 

klaus

Expert
Licensed User
Longtime User
Was ich meinte ist:
- Die verschiedenen Width und Height Werte rechnen mit Scale
- Left und Top nicht mit Scale rechnen sondern mit xx2.Left = xx1.Left + xx1.Width yy2.Top = yy1.Top + yy1.Top.Height, ähnlich wie Du es in der PaintTablle machst.
Ich würde folgendes vorschlagen:
Die PaintTable Routine in drei Routinen teilen.
- Die Erste zum Erstellen der Tabelle mit den Views.
- Die Zweite zum Ausrichten ja nach Zoomtiefe, mit %Werten von pnlMain in X und Y Richtung (X Richtung existiert ja schon).
mit Dim intActWidth As Int = pnlMain.Width - 20dip anstatt Dim intActWidth As Int = Activity.Width - 20dip
Und ähnlich für die Y-Richtung.
- Die Dritte zum Füllen mit den Daten.

Das Scalen wäre dann nur die Grösse von pnlMain mit Scale ermitteln, scvTest.Panel Grösse anpassen und dann die Routine zum Ausrichten aufrufen.
 

StephanP82

Member
Licensed User
Longtime User
Ich habe zwar die Routinen noch nicht aufgeteilt, aber die Zoom-Funktionen entsprechend angepasst, so dass die Top- und Left werte nach dem Scalen angepasst werden. Trotzdem habe ich nach mehrfachem Ein- und Auszoomen immernoch heftige Verschiebungen, besonders was die Zeilenhöhen angeht, die schrumfen immer mehr in sich zusammen.

Wo liegt der Fehler?

B4X:
Private Sub cmdZoomIn_Click
    Private    Scale As Double
    
    If mintZoomStufe = 5 Then Return
        
    If CurrentScale * 1.2 < 1.0 Then
        NewScale = 1.0
        mintZoomStufe = 0
    Else If CurrentScale * 1.2 > 5.0 Then
        NewScale = 5.0
        mintZoomStufe = 5
    Else
        NewScale = CurrentScale * 1.2
        mintZoomStufe = mintZoomStufe + 1   
    End If
        
    Scale = NewScale / CurrentScale
        
    ActionBar.Left = ActionBar.Left * Scale
    ActionBar.Top = ActionBar.Top * Scale
    ActionBar.Width = ActionBar.Width * Scale
            
    For i = 0 To 2
        lblKopfzeile1(i).Width = lblKopfzeile1(i).Width * Scale
        lblKopfzeile1(i).Height = lblKopfzeile1(i).Height * Scale
        lblKopfzeile1(i).TextSize = lblKopfzeile1(i).TextSize * Scale
    Next
    
    For i = 0 To 6
        lblKopfzeile2(i).Width = lblKopfzeile2(i).Width * Scale
        lblKopfzeile2(i).Height = lblKopfzeile2(i).Height * Scale
        lblKopfzeile2(i).TextSize = lblKopfzeile2(i).TextSize * Scale
        lblKopfzeile2(i).Top = lblKopfzeile1(0).Top + lblKopfzeile1(0).Height
    Next
    
    For i = 0 To 1
        lblKopfzeile3(i).Width = lblKopfzeile3(i).Width * Scale
        lblKopfzeile3(i).Height = lblKopfzeile3(i).Height * Scale
        lblKopfzeile3(i).TextSize = lblKopfzeile3(i).TextSize * Scale
        lblKopfzeile3(i).Top = lblKopfzeile2(1).Top + lblKopfzeile2(1).Height - 1dip
    Next
        
    For i = 0 To 30
        pnlHauptzeile(i).Width = pnlHauptzeile(i).Width * Scale
        pnlHauptzeile(i).Height = pnlHauptzeile(i).Height * Scale
        
        If i > 0 Then
            pnlHauptzeile(i).Top = pnlHauptzeile(i-1).Top + pnlHauptzeile(i-1).Height   
        Else
            pnlHauptzeile(i).Top = lblKopfzeile3(0).Top + lblKopfzeile3(0).Height
        End If
        
        Dim intWidth As Int = 0
        
        For x = 0 To 8
            lblHauptzeile(i,x).Width = lblHauptzeile(i,x).Width * Scale
            lblHauptzeile(i,x).Height = lblHauptzeile(i,x).Height * Scale
            lblHauptzeile(i,x).TextSize = lblHauptzeile(i,x).TextSize * Scale
            If i > 0 Then
                lblHauptzeile(i,x).Top = lblHauptzeile(i-1,x).Top + lblHauptzeile(i-1,x).Height
            Else
                lblHauptzeile(i,x).Top = lblKopfzeile3(0).Top + lblKopfzeile3(0).Height
            End If
                
            If x > 0 Then
                lblHauptzeile(i,x).Left = lblHauptzeile(i,x-1).Left + lblHauptzeile(i,x-1).Width
                lblHauptzeile(i,x).Top = lblHauptzeile(i,x-1).Top
            Else
                lblHauptzeile(i,x).Left = 10dip
            End If
            
            intWidth = intWidth + lblHauptzeile(i,x).Width
        Next
        pnlHauptzeile(i).Width = intWidth
    Next
    
    lblKopfzeile1(1).Left = lblHauptzeile(0, 6).Left
    lblKopfzeile1(2).Left = lblHauptzeile(0, 8).Left
    lblKopfzeile1(0).Width = lblHauptzeile(0, 5).Left + lblHauptzeile(0, 5).Width
    lblKopfzeile1(1).Width = lblKopfzeile1(1).Width + 1dip
    
    lblKopfzeile2(1).Left = lblHauptzeile(0, 2).Left
    lblKopfzeile2(2).Left = lblHauptzeile(0, 4).Left
    lblKopfzeile2(3).Left = lblHauptzeile(0, 5).Left
    lblKopfzeile2(4).Left = lblHauptzeile(0, 6).Left
    lblKopfzeile2(5).Left = lblHauptzeile(0, 7).Left
    lblKopfzeile2(6).Left = lblHauptzeile(0, 8).Left
    lblKopfzeile2(0).Width = lblKopfzeile2(0).Width + 1dip
    lblKopfzeile2(1).Width = lblKopfzeile2(1).Width + 1dip
    
    lblKopfzeile3(0).Left = lblHauptzeile(0, 2).Left
    lblKopfzeile3(1).Left = lblHauptzeile(0, 3).Left
    lblKopfzeile3(1).Width = lblHauptzeile(0,3).Width
        
    CurrentScale = NewScale

    If Activity.Height < pnlHauptzeile(30).Top + pnlHauptzeile(30).Height + ActionBar.Height + 20dip Then
        scvMainScroll.Panel.Height = pnlHauptzeile(30).Top + pnlHauptzeile(30).Height + ActionBar.Height + 20dip
    Else
        scvMainScroll.Panel.Height = Activity.Height
    End If
    
    pnlMain.Width = ActionBar.Width
    scvMainScroll.Panel.Width = ActionBar.Width
End Sub

Private Sub cmdZoomOut_Click
   Private   Scale As Double
   
   If mintZoomStufe = 0 Then Return
   
   If CurrentScale / 1.2 < 1.0 Then
       NewScale = 1.0
       mintZoomStufe = 0
   Else If CurrentScale / 1.2 > 5.0 Then
       NewScale = 5.0
       mintZoomStufe = 5
   Else
       NewScale = CurrentScale / 1.2
       mintZoomStufe = mintZoomStufe - 1
   End If
   
   If NewScale = 1.0 Or NewScale = 5.0 Then Return

   Scale = NewScale / CurrentScale

   ActionBar.Left = ActionBar.Left * Scale
   ActionBar.Top = ActionBar.Top * Scale
   ActionBar.Width = ActionBar.Width * Scale
           
   For i = 0 To 2
       lblKopfzeile1(i).Width = lblKopfzeile1(i).Width * Scale
       lblKopfzeile1(i).Height = lblKopfzeile1(i).Height * Scale
       lblKopfzeile1(i).TextSize = lblKopfzeile1(i).TextSize * Scale
   Next
   
   For i = 0 To 6
       lblKopfzeile2(i).Width = lblKopfzeile2(i).Width * Scale
       lblKopfzeile2(i).Height = lblKopfzeile2(i).Height * Scale
       lblKopfzeile2(i).TextSize = lblKopfzeile2(i).TextSize * Scale
       lblKopfzeile2(i).Top = lblKopfzeile1(0).Top + lblKopfzeile1(0).Height
   Next
   
   For i = 0 To 1
       lblKopfzeile3(i).Width = lblKopfzeile3(i).Width * Scale
       lblKopfzeile3(i).Height = lblKopfzeile3(i).Height * Scale
       lblKopfzeile3(i).TextSize = lblKopfzeile3(i).TextSize * Scale
       lblKopfzeile3(i).Top = lblKopfzeile2(1).Top + lblKopfzeile2(1).Height - 1dip
   Next
       
   For i = 0 To 30
       pnlHauptzeile(i).Width = pnlHauptzeile(i).Width * Scale
       pnlHauptzeile(i).Height = pnlHauptzeile(i).Height * Scale
       
       If i > 0 Then
           pnlHauptzeile(i).Top = pnlHauptzeile(i-1).Top + pnlHauptzeile(i-1).Height
       Else
           pnlHauptzeile(i).Top = lblKopfzeile3(0).Top + lblKopfzeile3(0).Height
       End If
       
       Dim intWidth As Int = 0
       
       For x = 0 To 8
           lblHauptzeile(i,x).Width = lblHauptzeile(i,x).Width * Scale
           lblHauptzeile(i,x).Height = lblHauptzeile(i,x).Height * Scale
           lblHauptzeile(i,x).TextSize = lblHauptzeile(i,x).TextSize * Scale
           If i > 0 Then
               lblHauptzeile(i,x).Top = lblHauptzeile(i-1,x).Top + lblHauptzeile(i-1,x).Height
           Else
               lblHauptzeile(i,x).Top = lblKopfzeile3(0).Top + lblKopfzeile3(0).Height
           End If
               
           If x > 0 Then
               lblHauptzeile(i,x).Left = lblHauptzeile(i,x-1).Left + lblHauptzeile(i,x-1).Width
               lblHauptzeile(i,x).Top = lblHauptzeile(i,x-1).Top
           Else
               lblHauptzeile(i,x).Left = 10dip
           End If
           
           intWidth = intWidth + lblHauptzeile(i,x).Width
       Next
       pnlHauptzeile(i).Width = intWidth
   Next
   
   lblKopfzeile1(1).Left = lblHauptzeile(0, 6).Left
   lblKopfzeile1(2).Left = lblHauptzeile(0, 8).Left
   lblKopfzeile1(0).Width = lblHauptzeile(0, 5).Left + lblHauptzeile(0, 5).Width
   lblKopfzeile1(1).Width = lblKopfzeile1(1).Width + 1dip
   
   lblKopfzeile2(1).Left = lblHauptzeile(0, 2).Left
   lblKopfzeile2(2).Left = lblHauptzeile(0, 4).Left
   lblKopfzeile2(3).Left = lblHauptzeile(0, 5).Left
   lblKopfzeile2(4).Left = lblHauptzeile(0, 6).Left
   lblKopfzeile2(5).Left = lblHauptzeile(0, 7).Left
   lblKopfzeile2(6).Left = lblHauptzeile(0, 8).Left
   lblKopfzeile2(0).Width = lblKopfzeile2(0).Width + 1dip
   lblKopfzeile2(1).Width = lblKopfzeile2(1).Width + 1dip
   
   lblKopfzeile3(0).Left = lblHauptzeile(0, 2).Left
   lblKopfzeile3(1).Left = lblHauptzeile(0, 3).Left
   lblKopfzeile3(1).Width = lblHauptzeile(0,3).Width
       
   CurrentScale = NewScale

   If Activity.Height < pnlHauptzeile(30).Top + pnlHauptzeile(30).Height + ActionBar.Height + 20dip Then
       scvMainScroll.Panel.Height = pnlHauptzeile(30).Top + pnlHauptzeile(30).Height + ActionBar.Height + 20dip
   Else
       scvMainScroll.Panel.Height = Activity.Height
   End If
   
   pnlMain.Width = ActionBar.Width
   scvMainScroll.Panel.Width = ActionBar.Width
End Sub

Sub GestDetector_onPinchOpen(NewDistance As Float, PreviousDistance As Float, MotionEvent As Object)
   cmdZoomIn_Click
End Sub

Sub GestDetector_onPinchClose(NewDistance As Float, PreviousDistance As Float, MotionEvent As Object)
   cmdZoomOut_Click
End Sub

Sub GestDetector_onScroll(distanceX As Float, distanceY As Float, MotionEvent1 As Object, MotionEvent2 As Object)
   scvMainScroll.VerticalScrollPosition = scvMainScroll.VerticalScrollPosition + distanceY
   scvMainScroll.HorizontalScrollPosition = scvMainScroll.HorizontalScrollPosition + distanceX
End Sub

Hinzu kommt das Problem, dass das Scrollen in beide Richtungen irgendwie zuerst ein Stück zurück "springt" und dann erst richtig mit dem Finger mit geht.
Das scheint sich langsam zur Never Ending Story zu entwickeln, diese Zoom-Funktion :(
 

StephanP82

Member
Licensed User
Longtime User
Hallo zusammen, wünsche ein frohes neues Jahr. Sorry dass so lange keine Antwort kam, habe jetzt erst wieder die Gelegenheit mich der App zu widmen.

Wegen den andauernden Schwierigkeiten mit dem Zoom, die mich langsam so richtig nerven, habe ich mir zwei neue Ansätze überlegt:

1. Tabelle im Designer bauen und per Skript anpassen - Ist es möglich Labels mit Border oder Rechtecke (Canvas?) per Designer zu erschaffen?

2. TabellenView benutzen und das Design an die Anforderungen anpassen - So müsste nur ein View beim Zoom geändert werden, aber gibt es überhaupt ein TabellenView, dass die dann nötigen Funktionen wie z.B. das Verbinden von Zellen unterstützt?
 
Top