不规则视图点击变暗效果实现

本文介绍了一种自定义View点击态的实现方法,通过在View的onDraw方法中生成和应用蒙层Bitmap,实现不规则形状View的点击态效果,如变暗或变深,适用于高要求的设计需求。

拿聊天气泡举例,点击气泡时气泡视图出现点击态给人反馈是正常操作,如果没有设计师,自己重写View的setPressed方法直接改下View的alpha值就可以实现“点击变淡”的效果。

然鹅这种操作,对于要求高的设计师一般是不可能让你这样的。

变淡参数不好设置也就算了,变深怎么操作?

一般简单的办法就是让设计师切一个点击态视图,与原图构成selector。

有没有别的通用的方法?

气泡时不规则的,直接蒙个蒙层周围也变灰了,怎么办?

熟悉canvas的同学可能已经想出来了:

自己绘制一张蒙层Bitmap,与View绘制的背景Bitmap做混合,设置PorterDuff模式为SRC_IN,即可生成一个气泡形蒙层,然后再onDraw方法里加上这个蒙层就成功了。

可是如何拿到View的背景呢?

沙雕的我发现,即使onDraw方法什么也不执行,View照样会显示背景,那么背景是在哪里绘制的呢?

看View#draw方法,发现原来背景是在draw方法里面绘制的。

    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        int saveCount;

        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

那如果拿到背景的bitmap呢?

看到了View的这个方法,可惜已经废弃了。

    @Deprecated
    public Bitmap getDrawingCache() {
        return getDrawingCache(false);
    }

看来只能自己动手了。

既然我们只是想用背景和我们的蒙层做mulitiply,那么我们需要的只是背景的轮廓而已。

draw方法绘制了背景以及内容,那么draw出来的图形形状是与背景相同的,我们用它就好了。用下面代码生成蒙层,生成之后进行一次重绘:

    private void updateMaskBitmap(){
        Log.d(TAG, "updateMaskBitmap");
        maskBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas1 = new Canvas(maskBitmap);
        draw(canvas1);
        canvas1.drawColor(colorMask, PorterDuff.Mode.SRC_IN);

        invalidate();
    }

在draw中进行蒙层的生成,注意生成蒙层会触发draw,要防止死循环:

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        int w = getWidth();
        int h = getHeight();
        int lastW = drawSize.x;
        int lastH = drawSize.y;

        drawSize = new Point(getWidth(), getHeight());

        Log.d(TAG, "w = " + w + " h = " + h);
        if (w > 0 && h > 0){
            if (w != lastW || h != lastH){
                updateMaskBitmap();
            }
        }
    }

在onDraw中如果生成了蒙层并且是点击状态,则绘制蒙层:

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

        if (isPressed()){
            if (maskBitmap != null){
                canvas.drawBitmap(maskBitmap, 0, 0, paint);
            }
        }
    }

最后在点击状态改变时也要刷新:

    @Override
    public void setPressed(boolean pressed) {
        super.setPressed(pressed);
        invalidate();
    }

最后,如果要使用的是TextView,那就继承TextView,如果是别的View呢,抽象出一个delegate吧:

public class MaskTextView extends AppCompatTextView {

    private static final String TAG = MaskTextView.class.getSimpleName();
    private IrregularMaskDelegate delegate = new IrregularMaskDelegate(this);

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

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

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

    private void init(){
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        delegate.draw(canvas);
    }

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

    @Override
    public void setPressed(boolean pressed) {
        super.setPressed(pressed);
        delegate.setPressed(pressed);
    }

    private static class IrregularMaskDelegate{

        private Paint paint = new Paint();
        private Bitmap maskBitmap;
        private Point drawSize = new Point(0, 0);
        private int colorMask = Color.parseColor("#22000000");

        private View host;

        public IrregularMaskDelegate(View v){
            host = v;
        }

        public void draw(Canvas canvas) {
            int w = host.getWidth();
            int h = host.getHeight();
            int lastW = drawSize.x;
            int lastH = drawSize.y;

            drawSize = new Point(w, h);

            Log.d(TAG, "w = " + w + " h = " + h);
            if (w > 0 && h > 0){
                if (w != lastW || h != lastH){
                    updateMaskBitmap();
                }
            }
        }

        private void updateMaskBitmap(){
            Log.d(TAG, "updateMaskBitmap");
            maskBitmap = Bitmap.createBitmap(host.getWidth(), host.getHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvas1 = new Canvas(maskBitmap);
            host.draw(canvas1);
            canvas1.drawColor(colorMask, PorterDuff.Mode.MULTIPLY);

            host.invalidate();
        }


        public void onDraw(Canvas canvas) {
            if (host.isPressed()){
                if (maskBitmap != null){
                    canvas.drawBitmap(maskBitmap, 0, 0, paint);
                }
            }
        }

        public void setPressed(boolean pressed) {
            host.invalidate();
        }
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值