27 October 2015

Android custom view, interesting lessons


So here's an interesting one, I recently created a custom view in Android. Basically a specific shape that a client wanted to frame some text. To begin with the custom view was very simple and I used a relative layout to hold the view in place and the centred brand name inside.


The java comprised of a Paint with a fill in the view initializer and in the onDraw method a Path drawn on the canvas with drawPath().

Unfortunately vertical centre was of the whole view, I needed it to be centred by the left side (a). Remember the view is more than what you can see and in this case it includes the invisible part of the rectangle above the slope. My first impulse was to push it down with some marginTop, but this would of course not work consistently on smaller devices.
So what I needed to do was measure a and centre the text accordingly. I grabbed height and width in onMeasure using MeasureSpec.getSize. Then used the following code to I found on stack overflow:

int yPos = (int) ((mHeight / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
canvas.drawText("", 20, yPos, mTextPaint);

This allowed me to centre the text as required. I added a paint object with some formatting:

        //Text font
        mTextToShow = getContext().getResources().getString(R.string.some_text);
        Typeface ty = Typeface.createFromAsset(getContext().getAssets(), "fonts/some_font.otf");

        //Text size
        Resources resources = getContext().getResources();
        float scale = resources.getDisplayMetrics().density;

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize((int) (20 * scale));
        mTextPaint.setTypeface(ty);
        mTextPaint.setColor(getResources().getColor(R.color.primary_white));


I now needed to do one more thing, the view was to be a link. I'm a huge fan of using Android's state to change text colour when its clickable. This helps the user know they've clicked something and it looks good too. However I had no idea how to do this, if I'd made an xml layout for this view I could do it, but using drawText meant I had no xml. Hmmm.

A few Google searches and even the mighty stack overflow didn't get me very far. Then I started to think, don't all view's have a onPressed or an onStateChanged method? I hit Ctrl Space and scanned through my options. OnPressed was there but I needed more... drawableStateChanged. Sounds promising. A few minutes later I had a working guess and it was building. Hey preseto! It worked, check this out:
    @Override
    protected void drawableStateChanged() {
        if(isPressed()){
            mTextPaint.setColor(getResources().getColor(R.color.primary));
        }else{
            mTextPaint.setColor(getResources().getColor(R.color.primary_white));
        }
        invalidate();

        super.drawableStateChanged();
    }

Hope this helps, happy coding


No comments: