Android - Adding Borders To Views Programmatically

The Approach

When acting as a sole developer, I rarely make use of WYSIWYG tools and prefer to build the UI by hand and through code, as opposed to external UI files such as XML, XAML, or HTML. It's true that the initial layout of the various controls/views on a form/activity is easier with a visual layout tool but going forward I find myself being much more productive when I can make use of inheritance and polymorphism. These two cornerstones of OOP, along with being able to encapsulate both data and its processes, allow me to reuse UI elements much more than any theme-based system. Of course we could make use of both approaches but I find the additional headache of having to maintain two sets of files, UI and logic, not really worth it. Anyway, not much point discussing this much more though as, in the end, it's a personal choice.

A quick Internet search for adding borders to views programmatically resulted in the usual XML based solutions, listed below. My programmatic approach differs in that it's, well, programmatic. Rather than rely on XML defined shapes I've chosen to create a Border class that extends the Drawable class. This way I'm not fighting with the limitations of XML resources when trying to get things to render exactly as I want. Additionally, I can take this Border class and reuse, extend, and encapsulate to my hearts content on future projects without having to cut-and-paste and/or manage additional XML.

The Code

The main Activity consists of a single LinearLayout, with a top-down orientation, which then contains two TextViews. Both TextViews and the LinearLayout have borders applied to them. The border around the LinearLayout should appear in blue, whilst the top TextView border should be red and the bottom TextView, green (See screen-shot).

The code for the main Activity is shown below and does not require any external layout files as everything is done programmatically. The Activity begins by creating the necessary LinearLayout (lines 13-16) and applying its border (line 16). Then the first TextView is created and added to the LinearLayout (lines 18-26) followed by the second TextView (lines 28-36). Both TextViews have borders applied at lines 21 and 31. Lastly, the LinearLayout is applied to the Activity (line 38).


1: public class MainActivity   
2: extends android.app.Activity  
3: {  
4:   @Override  
5:   public void onCreate(android.os.Bundle savedInstanceState)  
6:   {  
7:     android.widget.TextView text_view;  
8:     android.widget.LinearLayout layout;  
9:     android.widget.LinearLayout.LayoutParams params;  
10:    
11:    super.onCreate(savedInstanceState);  
12:    
13:    layout=new android.widget.LinearLayout(this);  
14:    layout.setOrientation(android.widget.LinearLayout.VERTICAL);  
15:    layout.setPadding(10, 10, 10, 10);  
16:    layout.setBackgroundDrawable(new Border(0xff0000ff, 10));  
17:    
18:    text_view=new android.widget.TextView(this);  
19:    text_view.setText("First Text View");  
20:    text_view.setGravity(android.view.Gravity.CENTER);  
21:    text_view.setBackgroundDrawable(new Border(0xffff0000, 10));  
22:    params=new android.widget.LinearLayout.LayoutParams(  
23:      android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 0, 1);  
24:    params.setMargins(10, 10, 10, 10);  
25:    params.weight=1;  
26:    layout.addView(text_view, params);  
27:    
28:    text_view=new android.widget.TextView(this);  
29:    text_view.setText("Second Text View");  
30:    text_view.setGravity(android.view.Gravity.CENTER);  
31:    text_view.setBackgroundDrawable(new Border(0xff00ff00, 10));  
32:    params=new android.widget.LinearLayout.LayoutParams(  
33:      android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 0, 1);  
34:    params.setMargins(10, 10, 10, 10);  
35:    params.weight=1;  
36:    layout.addView(text_view, params);  
37:    
38:    setContentView(layout);  
39:  }  
40:}  

The border class is very simple with only three functions containing code. The remainder are not implemented but have to be defined since we are extending the Drawable class. The constructor's only duty is to create a suitable paint object. That's followed by an override of the onBoundsChange function that allows us to keep track of changes to the size of the encapsulating view. Lastly, the draw function draws our border.


1:  public class Border  
2:  extends android.graphics.drawable.Drawable  
3:  {  
4:    public android.graphics.Paint paint;  
5:    public android.graphics.Rect bounds_rect;  
6:     
7:    public Border(int colour, int width)  
8:    {  
9:      this.paint = new android.graphics.Paint();  
10:     this.paint.setColor(colour);  
11:     this.paint.setStrokeWidth(width);  
12:     this.paint.setStyle(android.graphics.Paint.Style.STROKE);  
13:   }  
14:     
15:   @Override  
16:   public void onBoundsChange(android.graphics.Rect bounds)  
17:   {  
18:     this.bounds_rect = bounds;  
19:   }  
20:     
21:   public void draw(android.graphics.Canvas c)  
22:   {  
23:     c.drawRect(this.bounds_rect, this.paint);  
24:   }  
25:     
26:   public void setAlpha(int a)  
27:   {  
28:     // TODO: Implement this method  
29:   }  
30:     
31:   public void setColorFilter(android.graphics.ColorFilter cf)  
32:   {  
33:     // TODO: Implement this method  
34:   }  
35:     
36:   public int getOpacity()  
37:   {  
38:     // TODO: Implement this method  
39:     return 0;  
40:   }  
41: }     

As mentioned previously, this example was intentionally kept simple but obvious ways to expand on it would be to add functionality for optional borders, rounded corners, and background effects. By optional borders I'm referring to the ability to specify only a top border or only left and right borders, for example. Another nice feature would be functionality whereby one can supply a background image, colour, gradient, or other drawable.

Popular Posts