Android Code Snippet Drawing Borders around Buttons and Spinners

Discussion in 'Code Snippets' started by Gary Milne, Mar 3, 2015.

  1. Gary Milne

    Gary Milne Active Member Licensed User

    Here is an easy way to give the appearance of a border around a button or spinner.

    Quite simply you pass the name of a panel and the code adds a panel of a given color behind the view in question giving the illusion of a border.

    This is the function.
    Code:
    'Draws a border around all the given Buttons, Spinners and Imageviews on a Panel
        'PanelName = Panel to process
        'Border = border width, i.e. 3dip or 1%x
        'BorderColor = color of the border.  This is actually the color of the panel that is being used
        'Exclusions = a text list of views to not process using the Tag value i.e. "btnStart,btnIgnore".  All other views will be processed
        Sub Draw_Borders(PanelName As Panel, Border As Int, BorderColor As Int, Exclusions As String)
         
              
    Dim matcher1 As Matcher
            
              
    For Each v As View In PanelName.GetAllViewsRecursive
                
    If v Is Spinner OR v Is Button OR v Is ImageView Then
                   
                    matcher1=
    Regex.Matcher(v.Tag,Exclusions)
                    
    If matcher1.find = False OR Exclusions.Length = 0 Then
                        
    Dim pnlBack As Panel
                        pnlBack.Initialize(
    "")
                       
                        PanelName.Addview(pnlBack, v.Left - Border, v.Top - Border, v.Width + Border*
    2, v.Height + Border*2)
                        pnlBack.SendToBack
                        pnlBack.Color = BorderColor
                    
    End If   
                
    End If
            
    Next

        
    End Sub
    Call the function with this kind of syntax
    Code:
    Draw_Borders(pnlSessionStart, 2dipColors.White,"tbServe")
    You will get something that looks like this with white borders around the spinners and buttons.
    Screenshot_2014-10-27-10-37-41.png
     
    cupid, DroidLyon, shashkiranr and 4 others like this.
  2. Mahares

    Mahares Well Known Member Licensed User

    That is great Gary. But, are you aware that the new version of B4A 4.30 has a new member to colordrawable.initialize2 that can also very easily draw a border around any view. If what you are doing is different, then that your code is one more choice we have.
    http://www.b4x.com/android/help/drawing.html#colordrawable
     
  3. Gary Milne

    Gary Milne Active Member Licensed User

    No, I did not know that. I just installed 4.3 2 days ago as I was gone for a while. I will take a closer look at it. I'll always use the built in solution if I have that option.
     
  4. Guardian17

    Guardian17 Active Member Licensed User

    Thank you, Gary, for providing this method of adding a border to views. Your method allows more options -- you can use gradients on the buttons, as opposed to a gradient button which on its own does not currently allow for a border.

    I've gone a step further and allowed the background panel to be a ColorDrawable, which will allow for putting a radius on the border:

    Code:
    Sub Draw_Borders(PanelName As Panel, PanelColor As Int, Border As Int, BorderColor As Int, Exclusions As String)
        
    'Draws a border around all the given Buttons, Spinners and Imageviews on a Panel
          'PanelName = Panel to process
          'PanelColor = Color of panel inside its border
          'Border = border width, i.e. 3dip or 1%x
          'BorderColor = color of the border of the Panel.
          'Exclusions = a text list of views to not process using the Tag value i.e. "btnStart,btnIgnore".  All other views will be processed
      Dim matcher1 As Matcher
        
    Dim cd As ColorDrawable
        
    'cd.Initialize2(PanelColor as Int, CornerRadius as Int, BorderWidth as Int, BorderColor as Int)
        cd.Initialize2(PanelColor,5dip,5dip,BorderColor) '<<<<

      
    For Each v As View In PanelName.GetAllViewsRecursive
        
    'If v Is Spinner OR v Is Button OR v Is ImageView Then
        If v Is Button Then
          matcher1=
    Regex.Matcher(v.Tag,Exclusions)
          
    If matcher1.find = False OR Exclusions.Length = 0 Then
            
    Dim pnlBack As Panel
            pnlBack.Initialize(
    "")
            PanelName.Addview(pnlBack, v.Left - Border, v.Top - Border, v.Width + Border*
    2, v.Height + Border*2)
                    pnlBack.SendToBack
            
    'pnlBack.Color = BorderColor
            pnlBack.Background = cd
          
    End If
        
    End If
      
    Next
    End Sub
    I've uploaded a sample app, TestBorder.zip, that shows how this works. And this is how the buttons appear in that app:

    upload_2015-4-5_8-4-43.png


    Note that I added a "PanelColor" to the argument list. And since I've married the ColorDrawable.initialize2 in this code, if you were to change the call to your Sub from

    Code:
    Draw_Borders(Panel1, Colors.Yellow, 5dipColors.Yellow, "")
    to be:

    Code:
    Draw_Borders(Panel1, Colors.Blue, 10dipColors.Yellow, "")

    this is the result for the buttons, having two colors for the border:

    upload_2015-4-5_8-24-59.png


    I then went an additional step further and made the background panel a GradientDrawable (see TestGradientColorBorder.zip):

    Code:
    Sub Draw_Borders(PanelName As Panel, Border As Int, BorderColor As Int, Exclusions As String)
        
    'Draws a border around all the given Buttons, Spinners and Imageviews on a Panel
          'PanelName = Panel to process
          'Border = border width, i.e. 3dip or 1%x
          'BorderColor = color of the border of the Panel.
          'Exclusions = a text list of views to not process using the Tag value i.e. "btnStart,btnIgnore".  All other views will be processed
      Dim matcher1 As Matcher
      
    Dim gd As GradientDrawable
      
    Dim clrs(2As Int
      clrs(
    0) = BorderColor
      clrs(
    1) = Colors.RGB(50,50,50)
      gd.Initialize(
    "BR_TL",clrs)
      gd.CornerRadius = Border

      
    For Each v As View In PanelName.GetAllViewsRecursive
        
    'If v Is Spinner OR v Is Button OR v Is ImageView Then
        If v Is Button Then
          matcher1=
    Regex.Matcher(v.Tag,Exclusions)
          
    If matcher1.find = False OR Exclusions.Length = 0 Then
            
    Dim pnlBack As Panel
            pnlBack.Initialize(
    "")
            PanelName.Addview(pnlBack, v.Left - Border, v.Top - Border, v.Width + Border*
    2, v.Height + Border*2)
                    pnlBack.SendToBack
            
    'pnlBack.Color = BorderColor
            pnlBack.Background = gd
          
    End If
        
    End If
      
    Next
    End Sub

    This removes my added "PanelColor" from the argument list since it's not used for the gradient background.
    I really like this version because it makes the border look more like a 3D bevel, and makes the button itself appear like it's much more in the foreground:

    upload_2015-4-5_8-44-10.png

    Hope you find this useful
     

    Attached Files:

    Last edited: Jun 5, 2015
  5. Guardian17

    Guardian17 Active Member Licensed User

    Hmmmm... Something that I don't have an answer for ...

    If the buttons have different sizes (I changed the sizes of the buttons in Layout1), the following can happen:

    upload_2015-4-5_11-58-37.png

    Does anyone understand why this happens and what would fix it?
     
  6. Guardian17

    Guardian17 Active Member Licensed User

    Ah, silly old me ... the GradientDrawable has to be determined inside the For Loop for EACH view:

    Code:
    Sub Draw_Borders(PanelName As Panel, Border As Int, BorderColor As Int, Exclusions As String)
        
    'Draws a border around all the given Buttons, Spinners and Imageviews on a Panel
          'PanelName = Panel to process
          'Border = border width, i.e. 3dip or 1%x
          'BorderColor = color of the border of the Panel.
          'Exclusions = a text list of views to not process using the Tag value i.e. "btnStart,btnIgnore".  All other views will be processed
        Dim matcher1 As Matcher

      
    For Each v As View In PanelName.GetAllViewsRecursive
        
    'If v Is Spinner OR v Is Button OR v Is ImageView Then
        If v Is Button Then
          matcher1=
    Regex.Matcher(v.Tag,Exclusions)
          
    If matcher1.find = False OR Exclusions.Length = 0 Then
            
    Dim pnlBack As Panel
            pnlBack.Initialize(
    "")
            PanelName.Addview(pnlBack, v.Left - Border, v.Top - Border, v.Width + Border*
    2, v.Height + Border*2)
                    pnlBack.SendToBack
            
    'pnlBack.Color = BorderColor
            Dim gd As GradientDrawable
            
    Dim clrs(2As Int
            clrs(
    0) = BorderColor
            clrs(
    1) = Colors.RGB(50,50,50)
            gd.Initialize(
    "BR_TL",clrs)
            gd.CornerRadius = Border
            pnlBack.Background = gd
          
    End If
        
    End If
      
    Next
    End Sub
    Now we get:

    upload_2015-4-5_12-13-19.png

    Although I'm not quite sure why the ZERO button did not have a very large panel behind it before making this correction.
     
    Last edited: Jun 5, 2015
    cupid and DroidLyon like this.
  7. Gary Milne

    Gary Milne Active Member Licensed User

    Nice add Guardian17, thanks for sharing. I will try that out. I think B4A is a great RAD tool but it's hard to get a really polished looking app. I think this is quite a simple solution that is very helpful in achieving that.
     
  8. Beja

    Beja Expert Licensed User

    Gary and Guardian17, thanks for sharing this important additions..
    @Guardian17: Sorry I didn't try this, but may I ask about the light source of the border? Is the current implementation built-in or can be changed like in any gradient property?
    because now it is coming from bottom-right instead of top-left.. The panel light source is ok and it is coming from the top-left.
     
  9. Gary Milne

    Gary Milne Active Member Licensed User

    The "light source" is controlled by the direction of the gradient.
    gd.Initialize("BR_TL",clrs)
    In this case it is BR_TL, or bottom right to top left.
     
  10. Beja

    Beja Expert Licensed User

    Thanks Gary for the clarification..
    The Windows standards is Top-Left.. but it's not important if the inside button was the same, but the button followed Windows, so now we have two sources of the light.
     
  11. Guardian17

    Guardian17 Active Member Licensed User

    Thanks for answering for me, Gary.

    Yes, the light source is as Gary says, and that can be changed to any of the eight possible directional gradients.

    Also, the gradients of the faces of the buttons are handled currently in the Designer Layout, and for that, I reverse the gradient between pressed and enabled to also give the buttons a "shadowed press" visual effect.

    And the "brightness" of the lit bezel works best when you specify a bright color for BorderColor. Darker BorderColors will not be as vibrant.

    Glad you all like these effects, and thanks again Gary for the initial post.
     
    Last edited: May 28, 2015
  12. Guardian17

    Guardian17 Active Member Licensed User

    I don't see this as being two sources of light. If you take the source of light as ONLY TOP-LEFT, then this makes the button face a curved face with the light hitting the top left, and the border is then an angled bevel "into the screen", and as such, the "bottom right" of the bevel receives more light than the "top left" due to the bevel angle.

    The application of the reverse gradients on the button face between "enabled" and "pressed" makes the button itself act like a "rubberized button", in that when you press the rubber face, it gets pressed in enough to cause the bottom right to receive more light.
     
    cupid likes this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice