How to make your custom view focusable?

Viewed 1011

I am creating a custom view. This is basically a rectangle box with borders in it. I want the border to change color and make it focusable when a user clicks on it. I want the border to return to original color if the rectangle loses focus. I want to use this background as form background. I have tried android documentation and stack overflow answers and I am not able to do this. I have made it clickable but I am not able to proceed further than that.

public class FormBackgroundView extends View implements View.OnClickListener{

    private int _borderColor;
    Paint fillPaint, strokePaint;
    Canvas canvas;

    public FormBackgroundView(Context context) {
        super(context);
        init();
    }

    public FormBackgroundView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public FormBackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public FormBackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init(){
        super.setOnClickListener(this);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        fillPaint = new Paint();
        strokePaint = new Paint();

        fillPaint.setStyle(Paint.Style.FILL);
        fillPaint.setColor(Color.WHITE);

        strokePaint.setStyle(Paint.Style.FILL);
        strokePaint.setColor(Color.BLACK);
        strokePaint.setAntiAlias(true);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            canvas.drawRoundRect(0,0,getWidth(),getHeight(), 10, 10, strokePaint);
            canvas.drawRoundRect(0 + 3,0 + 3,getWidth() - 3,getHeight() - 3, 7, 7, fillPaint);
        } else {
            canvas.drawRect(0,0,getWidth(),getHeight(), strokePaint);
            canvas.drawRect(0 + 4,0 + 4,getWidth() -4 ,getHeight() -4, fillPaint);
        }

        this.canvas = canvas;
    }



    @Override
    public void onClick(View view) {

    }


}
2 Answers

Your view is not automatically focusable in touch mode. You must make a call to "setFocusableInTouchMode()` if you want your view to get the focus when it is clicked (touched.) Although dated, I found this explanation useful to describe touch mode.

So, in your init() add setFocusableInTouchMode(true).

Secondly, you don't do anything different in your onDraw() whether the view is focused or not. Change it to something like the following:

protected void onDraw(Canvas canvas) {
    // your paint code
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        if (isFocused()) { // draw the border
            canvas.drawRoundRect(0, 0, getWidth(), getHeight(), 10, 10, strokePaint);
            canvas.drawRoundRect(0 + 3, 0 + 3, getWidth() - 3, getHeight() - 3, 7, 7, fillPaint);
        } else { // don't draw the border
            canvas.drawRoundRect(0, 0, getWidth(), getHeight(), 7, 7, fillPaint);
        }
    } else {
        //similar here
    }
    // The rest of your code
}

Try this:

public class FormBackGroundView extends View {

    private int _borderColor;
    Paint fillPaint, strokePaint;
    Canvas canvas;
    private boolean isFocused;

    public FormBackGroundView(Context context) {
        super(context);
        init();
    }

    public FormBackGroundView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public FormBackGroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        fillPaint = new Paint();
        strokePaint = new Paint();
        fillPaint.setStyle(Paint.Style.FILL);
        strokePaint.setStyle(Paint.Style.FILL);
        strokePaint.setAntiAlias(true);
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN && !isFocused) {
                    isFocused = true;
                    invalidate();
                } else if (motionEvent.getAction() == MotionEvent.ACTION_UP && isFocused) {
                    isFocused = false;
                    invalidate();
                }
                return true;
            }
        });
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        fillPaint.setColor(Color.WHITE);

        strokePaint.setColor(isFocused? Color.RED:Color.BLACK);


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            canvas.drawRoundRect(0, 0, getWidth(), getHeight(), 10, 10, strokePaint);
            canvas.drawRoundRect(0 + 3, 0 + 3, getWidth() - 3, getHeight() - 3, 7, 7, fillPaint);
        } else {
            canvas.drawRect(0, 0, getWidth(), getHeight(), strokePaint);
            canvas.drawRect(0 + 4, 0 + 4, getWidth() - 4, getHeight() - 4, fillPaint);
        }
    }
}
Related