Italian [RISOLTO] Movimento oggetto non fluido

Sagenut

Well-Known Member
Licensed User
Ciao!
Ho fatto un piccolo programma di test per vedere di riuscire a spostare una immagine trascinandola.
L'immagine l'ho caricata su un Panel per avere gli eventi Touch che mi pare non siano disponibili con la ImageView.
Fondamentalmente mi pare che funzioni solo che il movimento dell'oggetto è decisamente traballante, soprattutto se lo muovo molto lentamente.
La sub che si occupa del movimento è questa:
B4X:
Sub Panel1_Touch (Action As Int, X As Float, Y As Float)
    Select Action
        Case Panel1.ACTION_DOWN
            toccox = X
            toccoy = Y
        Case Panel1.ACTION_MOVE
            Panel1.Left = Panel1.Left + (X - toccox)
            Panel1.Top = Panel1.Top + (Y - toccoy)
    End Select
End Sub
Secondo voi è corretto il mio codice?
Come posso fare per ottenere un movimento un pò più fluido e preciso?
Grazie
 

Star-Dust

Expert
Licensed User
Metti un po' di olio nel panel :p
Il problema è legato al wrap dell'oggetto che fa b4a. Android legge da 3 a 5 punti (multi touch) e ti da quel senso di vibrazione

Ma c'è un modo per evitarlo,leggendo solo il primo punto usando il reflector
 

Sagenut

Well-Known Member
Licensed User
Adesso vedo di integrare la soluzione che mi avete indicato.
Mi rendo conto che 9 volte su 10 la soluzione è usare il Reflector.
Il problema di usare il Reflector è capire quale metodo Java bisogna andare a puntare di volta in volta.
E questo al momento non mi sembra troppo semplice, ma direi che è abbastanza normale visto che sono un benemerito principiante.
Conto di riuscire a capire come fare prima dell'arrivo dell'Alzheimer.
 

LucaMs

Expert
Licensed User
Mettiamo che il movimento che devi eseguire sia semplicemente in un'Activity; ciapa quelle 3 righe di codice e schiaffale nella Activity_Create, non devi fare altro.

Semmai, se vuoi farlo per più panel, crea una routine apposita, passandogli ogni volta un panel (o una View qualunque).

B4X:
Sub SetOnTouchListener(Vw As View, EventName As String)
    Dim r As Reflector
    r.Target = Vw
    r.SetOnTouchListener(EventName & "_Touch")
End Sub
Meglio ancora, potresti dichiarare una mReflector nella Process_Globals ed usare quella nella routine, al posto di r.
 

Sagenut

Well-Known Member
Licensed User
Aggiungendo il tuo codice in Activity_Create ottengo questo errore appena clicco l'immagine:
java.lang.Exception: Sub panel1_touch signature does not match expected signature.
Nella riga del r.setontouchlistener mi indica come dovrebbe essere la Sub (in neretto le differenze):
Sub Whatever (viewtag As Object, action As Int, X As Float, Y As Float, motionevent As Object)
ma non capisco esattamente come devo modificarla.
 

Sagenut

Well-Known Member
Licensed User
Sicuramente sbaglio qualcosa io......adesso addirittura crasha l'app.
B4X:
Sub Globals
    Dim lstContacts As ListView
    Public Panel1 As Panel
    Dim r As Reflector
End Sub

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime Then
        cu.Initialize
    End If
    Activity.LoadLayout("1")
    For Each c As cuContact In cu.FindAllContacts(True)
        lstContacts.AddSingleLine2(c.DisplayName, c)
    Next
    r.Target = Panel1
    r.SetOnTouchListener("Panel1_Touch")
End Sub

Sub Panel1_Touch (o As Object, Action As Int, X As Float, Y As Float, motion As Object)
    Select Action
        Case Panel1.ACTION_DOWN
            toccox = X
            toccoy = Y
        Case Panel1.ACTION_MOVE
            Panel1.Left = Panel1.Left + (X - toccox)
            Panel1.Top = Panel1.Top + (Y - toccoy)
    End Select
    Return True
End Sub
Il Reflector l'ho messo nelle Globals......ma avevo comunque l'errore anche mettendolo in Activity_Create.
L'immagine viene reperita dai contatti.
 

LucaMs

Expert
Licensed User
metti:
B4X:
If Panel1 IS Null Then
   Log("è null")
Else
   log("NON è null")
   r.Target = Panel1
   r.SetOnTouchListener("Panel1_Touch")
End If
Il seguito non c'entra con il crash, ma:
1) Panel1 è privato
2) r lo chiamerei mReflector e comunque lo dichiarerei nella Process_Globals
 

Sagenut

Well-Known Member
Licensed User
Mi dice che il Reflector non può essere dichiarato in Process_Globals e và messo in Globals.
Non la facevo così irta la cosa.
E non gradisce nemmeno
B4X:
If Panel1 IS Null Then
 

LucaMs

Expert
Licensed User
Comunque è probabile che Panel1 sia Null quando cerchi di aggiungere il listener.

Va in crash... con quale messaggio d'errore?

Eventualmente, puoi provare ad impostare il listener in una routine, da chiamare usando CallSubDelayed.

B4X:
Sub Globals
    Dim lstContacts As ListView
    Private Panel1 As Panel
    Dim mReflector As Reflector
End Sub

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime Then
        cu.Initialize
    End If
    Activity.LoadLayout("1")
    For Each c As cuContact In cu.FindAllContacts(True)
        lstContacts.AddSingleLine2(c.DisplayName, c)
    Next
    CallSubDelayed(Me, "ImpostaListener")
End Sub

Sub ImpostaListener
    mReflector.Target = Panel1
    mReflector.SetOnTouchListener("Panel1_Touch")
End Sub

Sub Panel1_Touch (o As Object, Action As Int, X As Float, Y As Float, motion As Object)
    Select Action
        Case Panel1.ACTION_DOWN
            toccox = X
            toccoy = Y
        Case Panel1.ACTION_MOVE
            Panel1.Left = Panel1.Left + (X - toccox)
            Panel1.Top = Panel1.Top + (Y - toccoy)
    End Select
    Return True
End Sub
 

Sagenut

Well-Known Member
Licensed User
Ho applicato la tua correzione:
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean
at anywheresoftware.b4a.agraham.reflection.Reflection$7.onTouch(Reflection.java:1121)
at android.view.View.dispatchTouchEvent(View.java:9939)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2663)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2344)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2301)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2301)
at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:411)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1810)
at android.app.Activity.dispatchTouchEvent(Activity.java:3061)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:373)
at android.view.View.dispatchPointerEvent(View.java:10163)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4434)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4302)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3849)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3902)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3868)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3995)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3876)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4052)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3849)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3902)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3868)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3876)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3849)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6210)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6184)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6145)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6313)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:323)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Appena clicco sull'immagine.
Quindi il Reflector dovrebbe comunque averlo impostato correttamente, credo.
 

Sagenut

Well-Known Member
Licensed User
Parentesi: testo su Emulatore.......devo provare su dispositivo reale?

EDIT

Crasha anche su dispositivo reale.
 

LucaMs

Expert
Licensed User
Parentesi: testo su Emulatore.......devo provare su dispositivo reale?
Questo sarebbe meglio, sempre; ma non penso sia questo il problema, adesso.

Direi che, senza impiccarci troppo (anche perché io stesso sto scrivendo una domanda complessa alla quale spero mi rispondano :D), dovresti fare così:

1) crea una nuova classe e chiamala DraggableView
2) elimina il contenuto e sostituiscilo con questo:
B4X:
'DraggableView class module
Sub Class_Globals
   Private innerView As View
   Private panel1 As Panel
   Private downx, downy As Int
   Private ACTION_DOWN, ACTION_MOVE, ACTION_UP As Int
End Sub

Sub Initialize(Activity As Activity, v As View)
   innerView = v
   panel1.Initialize("")
   panel1.Color = Colors.Transparent
   Activity.AddView(panel1, v.Left, v.Top, v.Width, v.Height)
   ACTION_DOWN = Activity.ACTION_DOWN
   ACTION_MOVE = Activity.ACTION_MOVE
   ACTION_UP = Activity.ACTION_UP
   Dim r As Reflector
   r.Target = panel1
   r.SetOnTouchListener("Panel1_Touch") 'why reflection instead of the regular Panel_Touch event? Good question which deserves a forum thread of its own (not related to classes)...
End Sub

Private Sub Panel1_Touch (o As Object, ACTION As Int, x As Float, y As Float, motion As Object) As Boolean
   If ACTION = ACTION_DOWN Then
      downx = x
      downy = y
   Else
      innerView.Left = innerView.Left + x - downx
      innerView.Top = innerView.Top + y - downy     
      panel1.Left = innerView.Left
      panel1.Top = innerView.Top
   End If
   Return True
End Sub

poi nella Globals della tua Activity:
Private mDrag As DraggableView

nella Activity_Create:
mDrag.Initialize(Activity, Panel1)


Se va ancora in crash, significa che Panel1 non è stato ancora caricato (prova il codice del post #10)
 

Sagenut

Well-Known Member
Licensed User
Vi allego il progetto se vi capita di volerci guardare.
Per oggi penso di fare pausa.
Grazie dell'aiuto.
 

Attachments

Sagenut

Well-Known Member
Licensed User
Appena posso verifico.
L'unica differenza che ho notato al volo guardando la classe Draggable è che la Sub del Touch è Private....a parte il resto della implementazione.
Che possa essere quella la differenza importante?
In ogni caso vi farò sapere...... Anche se a voi esperti non serve. :oops:
Grazie del tuo tempo.
 
Top