Android Question [solved] finding a view's cumulative position?

Dave O

Well-Known Member
Licensed User
Longtime User
I'm trying to calculate the cumulative position of a view inside a scrollview (or the activity, if there is no scrollview involved) - that is, the left and top offsets of the view and all its parent views up to the base container.

So, I'm getting the left and top coordinates of the view, it's parent view (e.g. a panel), the grandparent's view (e.g. another panel), until I hit the base container (e.g. a scrollview or the activity).

However, when I try to get the Left of a panel, I get a classCastException:

B4X:
java.lang.ClassCastException: android.widget.FrameLayout$LayoutParams cannot be cast to anywheresoftware.b4a.BALayout$LayoutParams
  at anywheresoftware.b4a.objects.ViewWrapper.getLeft(ViewWrapper.java:150)
...

Panels have a Left property, so why am I getting this exception?

Here's my code:
B4X:
'Calculate the position of the view relative to its base container (scrollview if any, otherwise activity)
'Returns an integer array where index 0 is left, index 1 is top
public Sub getCumulativePosition(viewArg As View) As Int()
   Dim tempLeft, tempTop As Int
   Dim tempView As View = viewArg
   Dim parentView As View = viewArg.Parent
   Do While True
     If tempView Is Panel Then               'a spinner tests as TRUE here (because it's based on a panel internally?)
       Dim tempPanel As Panel = tempView       'try an explicit cast to avoid classCast exception
       tempLeft = tempLeft + tempPanel.Left     'exception raised - cannot cast to BALayout
       tempTop = tempTop + tempPanel.Top
     Else
       tempLeft = tempLeft + tempView.Left
       tempTop = tempTop + tempView.Top
     End If
     If (parentView Is ScrollView) Or (parentView = tipActivity) Then Exit
     tempView = parentView
     parentView = parentView.Parent
   Loop
   If parentView = tipActivity Then
     tempTop = tempTop - (GetDeviceLayoutValues.Height - tipActivity.Height)     'adjust for title bar, if any
   End If
   Return Array As Int(tempLeft, tempTop)
End Sub

BTW, when I pass a view such as a spinner to this sub, the spinner (assigned to tempView) causes the "is Panel" expression to return TRUE. Is this because all views are internally based on panels? Is there a way to test if a view is actually a panel (in the B4A user's sense)?

Also BTW, I know that, for an activity with no scrollview, I can easily get this result using getLocationonScreen. However, I also need to handle the case where the view's ancestor is a scrollview, so I wanted to create a single method that would handle both. But I'm happy to hear about other solutions too. :)

Any help greatly appreciated. This one is boggling me...
 
Last edited:

Dave O

Well-Known Member
Licensed User
Longtime User
Aaaaaand, I finally realized what was going on.

It turns out that I was trying to get the Left property of the scrollview's panel, which doesn't have a layout, hence the exception.

For an activity with a scrollview, suppose that we load a layout consisting of a panel that contains a button. The view hierarchy would be:

B4X:
activity
  scrollview
    scrollview's panel (no Left or Top available)
      panel (has Left and Top)
        button (has Left and Top)
So, we just need to go up the tree, adding to the Left total and the Top total, until we get to the scrollview, then be careful not to ask for the Left and Top of the scrollview's panel (because it has no layout for some reason).

I also decided to rewrite this as a recursive function, but the solution is the same - don't ask a scrollview's panel for its layout. :)

B4X:
'Calculate the position of the view relative to its base container (scrollview if any, otherwise activity)
'Returns an integer array where index 0 is left, index 1 is top
Private Sub getCumulativePosition(viewArg As View) As Int()
   Dim position(2), parentPosition(2) As Int
   If viewArg.parent Is ScrollView Then
     'the scrollview's panel has no layout, so ignore
     Return position
   else if viewArg.parent = tipActivity Then
     position(0) = viewArg.Left
     position(1) = viewArg.Top
     Return position
   Else
     parentPosition = getCumulativePosition(viewArg.parent)
     position(0) = viewArg.Left + parentPosition(0)
     position(1) = viewArg.top + parentPosition(1)
     Return position
   End If
End Sub
 
Upvote 0
Top