Thursday, February 4, 2016

Understanding Widgets in Plaid App - Part 1

CutoutTextView.java

Launch Plaid app  and  Go to Menu -> About
You will be able to see the text "PLAID"  but look close you can actually see through it.
Today we will go into details of how this is implemented.



We can see that there is textview PLAID and we can actually see through it . After seeing that Portar Duff should be in your mind.

There is already a comment in the source code.
"A view which punches out some text from an opaque color block, allowing you to see through it."

Really important post before you do read further.

Lets see how this view is used in layout

1
2
3
4
5
6
<io.plaidapp.ui.widget.CutoutTextView
            android:layout_width="match_parent"
            android:layout_height="@dimen/about_header_height"
            android:text="@string/app_name"
            app:foregroundColor="?android:colorPrimary"
            app:font="roboto-mono-regular" />



Custom stylable attributes can be found in file   attrs_cutout_text_view.xml


1
2
3
4
5
6
7
<resources>
    <declare-styleable name="CutoutTextView">
        <attr name="foregroundColor" format="color" />
        <attr name="android:text" />
        <attr name="font" />
    </declare-styleable>
</resources>


Constructor is really self explanatory , where we just get the staylable attributes from the xml


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
 public CutoutTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);

        final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable
                .CutoutTextView, 0, 0);
        if (a.hasValue(R.styleable.CutoutTextView_font)) {
            textPaint.setTypeface(FontUtil.get(context, a.getString(R.styleable
                    .CutoutTextView_font)));
        }
        if (a.hasValue(R.styleable.CutoutTextView_foregroundColor)) {
            foregroundColor = a.getColor(R.styleable.CutoutTextView_foregroundColor,
                    foregroundColor);
        }
        if (a.hasValue(R.styleable.CutoutTextView_android_text)) {
            text = a.getString(R.styleable.CutoutTextView_android_text);
        }
        maxTextSize = context.getResources().getDimensionPixelSize(R.dimen.display_4_text_size);
        a.recycle();
    }

The main call to onSizeChanged() is done after the construction of your view but before the drawing. At this time the system will calculate the size of your view and notify you by calling onSizeChanged()


CalculateTextPosition : finds out the x,y co-ordinates within view so that the text is centered.
createBitmap : creates a canvas -> draws over it using the textPaint.

But while doing that it just punches a hole on the canvas using the Portar Duff 

// this is the magic – Clear mode punches out the bitmap
        textPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        cutoutCanvas.drawText(text, textX, textY, textPaint);


The overall effect is we are able to see through the text and we are able to see some content of the main recyclerview item that it in that position.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        calculateTextPosition();
        createBitmap();
    }

    private void calculateTextPosition() {
        float targetWidth = getWidth() / PHI;
        textSize = ViewUtils.getSingleLineTextSize(text, textPaint, targetWidth, 0f, maxTextSize,
                0.5f, getResources().getDisplayMetrics());
        textPaint.setTextSize(textSize);

        // measuring text is fun :] see: https://chris.banes.me/2014/03/27/measuring-text/
        textX = (getWidth() - textPaint.measureText(text)) / 2;
        Rect textBounds = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), textBounds);
        float textHeight = textBounds.height();
        textY = (getHeight() + textHeight) / 2;
    }

    private void createBitmap() {
        if (cutout != null && !cutout.isRecycled()) {
            cutout.recycle();
        }
        cutout = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        cutout.setHasAlpha(true);
        Canvas cutoutCanvas = new Canvas(cutout);
        cutoutCanvas.drawColor(foregroundColor);

        // this is the magic – Clear mode punches out the bitmap
        textPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        cutoutCanvas.drawText(text, textX, textY, textPaint);
    }

No comments:

Post a Comment