How to add or draw a border around Android Custom Textview

In android application development, in some situations you will need to add or draw a border around android textview so knowing how to add or draw a border around android textview can make a difference.

Just like most things in programming, one single task can be achieved in different ways.

Android textview border is no exception. A simple google search on this topic will reveal different examples on how to achieve it.

Majority of these solutions create a shape drawable and set it as the background resource of a textview.

Shape drawable will indefinitely do the job in most scenarios but what if we want to change the thickness or color of the border at run-time then using a drawable background resource will not work.

Welcome to another solution. We can also solve the same problem by creating a custom textview with a border. This border is achieved by overriding the Textview class onDraw() method.

Depending on what you planned to accomplish, it is possible to expose methods that can be used to change the border width or color at run-time.

We will look into some of these options with a simple code example. You are free to choose whatever options that suits your project requirement.

App Demo Screenshot

1. CREATE A NEW ANDROID PROJECT

  • Open Android Studio
  • Go to file menu
  • Select  new
  • Enter project name 
  • Enter activity name
  • Keep other default settings
  • Click on finish button to create a new android project

2. Open default activity_main.xml file

We are going to add a TextView in our layout file. Drag and drop a TextView widget or you can as well write it in code.

At this point you only see the text content of the Textview. In order to add a border to the TextView, we are going to create a shape drawable.

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text Border "
        android:textColor="@color/colorPrimaryDark"
        android:textSize="20sp"
        android:padding="30dp"
        android:layout_marginBottom="24dp"
        android:background="@drawable/border"
        android:layout_gravity="center"/>

You will notice a red mark that shows our layout file has an error. This is because android studio could not found the value of the TextView background property we declare.

Next, we need to create the missing border.xml file.

3. Create a Shape Drawable

Go to res folder > drawable folder and right click. In the open file dialog, enter the name of your resource file. I used border.xml but free feel to choose a name of your choice.

Open the created file and add the code below to it

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
    <solid android:color="#ffffff" />
    <stroke android:width="2dp" android:color="#4fa5d5"/>
</shape>

The code above is self explanatory. Feel to customize it to suit your project requirements. 

If everything works for you, you will see a border around the TextView

4. Add Text Border With Shape Drawable

There are situations we might need to change the thickness, width or color of a TextView border at runtime.

We can achieve this by create a custom TextView. If you have not created a custom view in android before I will suggest you reach my post on how to create Android Custom Calendar View with Events

To create a custom TextView we will create a new Java file.

5. Create a new Java File

Go to your project package folder and right click,  select  New > Java class. Add the new of the class. I naxtBorderView.

Open the java file and let  the class inherits from the android TextView  widget.

I did two experiments. The first one is to use Android Paint classgetTextBounds() method to get the rectangular coordinates of the text.

In order to get the perfect position of the text, the padding values was factored in.

The second border is using the canvas coordinates to draw a border around the text since the text content leaves in the canvas.

Decide which option is best for your scenario.

Add the code below to TextBorderView.

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;

import com.inducesmile.textborder.R;

public class TextBorderView extends android.support.v7.widget.AppCompatTextView {

    private static final String TAG = TextBorderView.class.getSimpleName();

    private Context context;

    private Rect textBoundingRect;

    private String userText;

    private int thickness = 5;

    private Paint rectPaint;

    private float initialXPosition;
    private float initialYPosition;

    private float finalXPosition;
    private float finalYPosition;

    private int rectLeft;
    private int rectRight;
    private int rectTop;
    private int rectBottom;


    public TextBorderView(Context context) {
        super(context);
        initCustomView(context);
    }

    public TextBorderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initCustomView(context);
    }

    public TextBorderView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initCustomView(context);
    }

    private void initCustomView(Context context) {
        this.context = context;

        textBoundingRect = new Rect();

        rectPaint = new Paint();
        rectPaint.setAntiAlias(true);
        rectPaint.setStrokeWidth(thickness);
        rectPaint.setStyle(Paint.Style.STROKE);
        rectPaint.setTypeface(Typeface.DEFAULT);
        rectPaint.setTextSize(20);
        rectPaint.setColor(getResources().getColor(R.color.colorAccent));

        getRectBoundingSize();
    }

    private void getRectBoundingSize() {
        rectPaint.getTextBounds(getText().toString(), 0, getText().toString().length(), textBoundingRect);

        int pLeft = getPaddingLeft();
        int pRight = getPaddingRight();
        int pTop = getPaddingTop();
        int pBottom = getPaddingBottom();

        rectLeft = (int)convertDpToPixel(textBoundingRect.left, context) + pLeft;
        rectRight = (int)convertDpToPixel(textBoundingRect.right, context) + pRight;

        rectTop = getTop() + pTop + thickness;
        rectBottom = (int)(convertDpToPixel(textBoundingRect.bottom, context) + pBottom + getTextSize()) + thickness;

    }

    private boolean viewHasText() {
        if (getText().toString().equals("")) {
            return false;
        }
        return true;
    }

    private void drawRectangleTextBorder(Canvas canvas, Paint paint) {
        Rect rectToDraw = new Rect(rectLeft, rectTop, rectRight, rectBottom);
        canvas.drawRect(rectToDraw, paint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(0, 0, getWidth(), getHeight(), rectPaint);
        if (viewHasText()) {
            drawRectangleTextBorder(canvas, rectPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int touchAction  = event.getAction();

        switch (touchAction){
            case MotionEvent.ACTION_DOWN:
                actionToPerformOnDown(event);
                return true;

            case MotionEvent.ACTION_UP:
                initialXPosition = finalXPosition;
                initialYPosition = finalYPosition;
                return true;

            case MotionEvent.ACTION_MOVE:
                finalXPosition = event.getX();
                int changeInXPosition  =  (int)(finalXPosition - initialXPosition);
                finalYPosition = event.getY();
                int changeInYPosition = (int)(finalYPosition - initialYPosition);
                moveTextHorizontally(changeInXPosition, changeInYPosition);
                return true;
        }

        return false;
    }

    private void actionToPerformOnDown(MotionEvent event){
        initialXPosition = event.getX();
        initialYPosition = event.getY();
        Log.d(TAG, "Action down has been called");
    }

    private void moveTextHorizontally(int left, int top){
        rectLeft = rectLeft + left;
        rectRight = rectLeft + 300;
        rectTop = rectTop + top;
        rectBottom = rectTop + 40;
        this.setX(rectLeft);
        this.setY(rectTop);
    }

    public void setUserText(String userText) {
        this.userText = userText;
        invalidate();
    }


    public float convertDpToPixel(float dp, Context context){
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        return dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
    }

}

6. Add the Custom TextBorderView in xml layout file

Open our activity layout file and add the code below.

<com.inducesmile.textborder.custom.TextBorderView
        android:id="@+id/text_in_center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textSize="20sp"
        android:padding="30dp"
        android:gravity="center"
        android:textColor="@color/colorPrimaryDark"
        android:text="Custom Textview With Border"/>

We have come to the end of this tutorial. If everything work for you, you will get a similar screen like the demo screenshot above.

Free feel to download the source code from ourĀ Github account.

If you have questions or suggestions kindly use the comment box below.

Add a Comment