Search code examples
androidandroid-edittextandroid-canvasandroid-relativelayout

Clear a views canvas with a TextView


I'm trying to achieve a visual effect, that if I could make would look awesome! The login of the app that I'm doing looks like this:

enter image description here

Keep in mind that the image on the background is an animation, that makes a slightly transition from that image to another.

What I want is make the title of the app "Akrasia" be transparent, but transparent meaning that you can see the image in background through the title letters, this means that in some way I must override the onDraw method of the RelativeLayout that contains this form. I tried to do that, but the only thing that I got was errors. Maybe I'm wrong trying to override the onDraw method in boths, the TextView and the RelativeLayout, maybe there's an easiest way to do it. What do you think? Or maybe is impossible to achive this effect?

UPDATE:

This is how it should look like.

enter image description here

Also I tried to make a custom view extending from TextView wich has a method setBackgroundView wich stores a view instance into a field. Later on the onDraw method and I managed to get the bitmap from the background image. But I don't know how draw it using canvas.

UPDATE: I make it work! Now I only need change that blue-like background by the drawable of the background.

enter image description here

The view:

final public class SeeThroughTextView extends TextView
{
    Bitmap mMaskBitmap;
    Canvas mMaskCanvas;
    Paint mPaint;

    Drawable mBackground;
    Bitmap mBackgroundBitmap;
    Canvas mBackgroundCanvas;
    boolean mSetBoundsOnSizeAvailable = false;

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

    private void init() {
        mPaint = new Paint();
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
    }

    public SeeThroughTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public SeeThroughTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }



    @Override
    @Deprecated
    public void setBackgroundDrawable(Drawable bg)
    {
        mBackground = bg;
        int w = bg.getIntrinsicWidth();
        int h = bg.getIntrinsicHeight();

        // Drawable has no dimensions, retrieve View's dimensions
        if (w == -1 || h == -1)
        {
            w = getWidth();
            h = getHeight();
        }

        // Layout has not run
        if (w == 0 || h == 0)
        {
            mSetBoundsOnSizeAvailable = true;
            return;
        }

        mBackground.setBounds(0, 0, w, h);
        invalidate();
    }



    @Override
    public void setBackgroundColor(int color)
    {
        setBackgroundDrawable(new ColorDrawable(color));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);
        mBackgroundBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mBackgroundCanvas = new Canvas(mBackgroundBitmap);
        mMaskBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mMaskCanvas = new Canvas(mMaskBitmap);

        if (mSetBoundsOnSizeAvailable)
        {
            mBackground.setBounds(0, 0, w, h);
            mSetBoundsOnSizeAvailable = false;
        }
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        // Draw background
        mBackground.draw(mBackgroundCanvas);

        // Draw mask
        mMaskCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
        super.onDraw(mMaskCanvas);

        mBackgroundCanvas.drawBitmap(mMaskBitmap, 0.f, 0.f, mPaint);
        canvas.drawBitmap(mBackgroundBitmap, 0.f, 0.f, null);
    }
}

And in my fragment I have this because the animation in the background:

vBackground.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                vTitle.setBackgroundDrawable(new BitmapDrawable(vBackground.getDrawingCache()));
                vTitle.invalidate();
            }
        });

Solution

  • Nailed!

    enter image description here

    The view:

    final public class SeeThroughTextView extends TextView
    {
        Bitmap mMaskBitmap;
        Canvas mMaskCanvas;
        Paint mPaint;
    
        Drawable mBackground;
        Bitmap mBackgroundBitmap;
        Canvas mBackgroundCanvas;
        boolean mSetBoundsOnSizeAvailable = false;
    
        public SeeThroughTextView(Context context)
        {
            super(context);
            init();
        }
    
        private void init() {
            Typeface myTypeface = Typeface.createFromAsset(getContext().getAssets(), "fonts/gillsans.ttf");
            setTypeface(myTypeface);
            mPaint = new Paint();
            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        }
    
        public SeeThroughTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public SeeThroughTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
    
    
        @Override
        @Deprecated
        public void setBackgroundDrawable(Drawable bg)
        {
            mBackground = bg;
            int w = bg.getIntrinsicWidth();
            int h = bg.getIntrinsicHeight();
    
            // Drawable has no dimensions, retrieve View's dimensions
            if (w == -1 || h == -1)
            {
                w = getWidth();
                h = getHeight();
            }
    
            // Layout has not run
            if (w == 0 || h == 0)
            {
                mSetBoundsOnSizeAvailable = true;
                return;
            }
    
            mBackground.setBounds(0, 0, w, h);
            invalidate();
        }
    
    
    
        @Override
        public void setBackgroundColor(int color)
        {
            setBackgroundDrawable(new ColorDrawable(color));
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh)
        {
            super.onSizeChanged(w, h, oldw, oldh);
            mBackgroundBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            mBackgroundCanvas = new Canvas(mBackgroundBitmap);
            mMaskBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            mMaskCanvas = new Canvas(mMaskBitmap);
    
            if (mSetBoundsOnSizeAvailable)
            {
                mBackground.setBounds(0, 0, w, h);
                mSetBoundsOnSizeAvailable = false;
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas)
        {
            // Draw background
            mBackground.draw(mBackgroundCanvas);
    
            // Draw mask
            mMaskCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
            super.onDraw(mMaskCanvas);
    
            mBackgroundCanvas.drawBitmap(mMaskBitmap, 0.f, 0.f, mPaint);
            canvas.drawBitmap(mBackgroundBitmap, 0.f, 0.f, null);
        }
    }
    

    In my fragment:

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        vLoginBtn = (Button) view.findViewById(R.id.btn_login);
        vRegistrationBtn = (Button) view.findViewById(R.id.btn_registration);
        vForgotBtn = (Button) view.findViewById(R.id.btn_forgot);
        vBackground = (KenBurnsView) view.findViewById(R.id.login_background);
        vTitle = (SeeThroughTextView) view.findViewById(R.id.txt_view_login_title);
        vBackground.setResourceUrls(
                "http://www.youwall.com/papel/peaceful_place_wallpaper_4f3f3.jpg",
                "http://www.fwallpaper.net/wallpapers/P/E/Peaceful-Scenary_1920x1200.jpg",
                "http://p1.pichost.me/i/39/1620902.jpg"
        );
        vBackground.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                vTitle.setBackgroundDrawable(getResources().getDrawable(R.drawable.drawable_background_login_top));
                vTitle.invalidate();
                vBackground.removeOnLayoutChangeListener(this);
            }
        });
    
    }
    

    The drawables are just two shapes, one with the top-left corner and top-right corner with radius 10dp and the another one with the radius in the bottoms. The custom TextView with the top drawable shape is alligned above the RelativeLayout wich contains the EditTexts. No much rocket science. Thanks a lot to @Klotor for suggesting the idea!