网上的都是自己绘制的或者图片,我的需求是可以随意的自定义底部和顶部的布局。所以自己重写一个,原理就是直接继承 View 来实现一个刮层,让这个刮层和图片以及文字不产生任何依赖,再结合 FrameLayout 将刮层放置最上一层,刮层之下你想放多少图片文字,图片文字要怎么布局摆放都行。由于是FrameLayout ,刮层的上面想加内容都是可以的。如图:

原理:刮刮卡无非就是文本,或者图片,就是我们下边的布局,然后在其上绘制刮奖层,设置DST_OUT,然后把用户触摸绘制上去;这样消失以后就能看到背后的奖了。

布局

 <FrameLayout
        android:layout_width="350dp"
        android:layout_centerInParent="true"
        android:layout_height="150dp">
        <include
            layout="@layout/scratch_view_after"/>
        <coordemo.ly.com.myapplication.GuaGuaKaView
            android:layout_width="match_parent"
            android:id="@+id/gg1"
            android:layout_height="match_parent" />
    </FrameLayout>

  

刮一刮控件
public class GuaGuaKaView extends View {
    /**
     * 绘制线条的画笔
     */
    private Paint mOutterPaint = new Paint();
    /**
     * 遮层画笔
     */
    private Paint mMaskPaint = new Paint();
    /**
     * 最下面画笔
     */
    private Paint mBackPint = new Paint();
    /**
     * mCanvas绘制内容在其上
     */
    private Bitmap mBitmap;
    /**
     * 记录用户绘制的Path
     */
    private Path mPath = new Path();
    /**
     * 内存中创建的Canvas
     */
    private Canvas mCanvas;
    private boolean isComplete;
    private Rect mTextBound = new Rect();
    private String mText = "¥500,0000";
    private int mLastX;
    private int mLastY;
    private int measuredWidth;
    private int measuredHeight;
    public GuaGuaKaView(Context context) {
        this(context, null);
    }
    public GuaGuaKaView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public GuaGuaKaView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }
    private void init() {
        mPath = new Path();
        setUpOutPaint();
        setUpBackPaint();
    }
    /**
     * 初始化canvas的绘制用的画笔
     */
    private void setUpBackPaint() {
        mBackPint.setStyle(Style.FILL);
        mBackPint.setTextScaleX(2f);
        mBackPint.setColor(Color.DKGRAY);
        mBackPint.setTextSize(32);
        mBackPint.getTextBounds(mText, 0, mText.length(), mTextBound);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        if (!isComplete) {
            drawPath();
            canvas.drawBitmap(mBitmap, 0, 0, null);
        } else {
            this.setVisibility(GONE);
        }
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measuredWidth = getMeasuredWidth();//宽高和父view的相同
        measuredHeight = getMeasuredHeight();
        // 初始化bitmap
        mBitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
        mMaskPaint.setColor(Color.parseColor("#00000000"));//遮层透明
        mMaskPaint.setStyle(Style.FILL);
        mCanvas.drawRoundRect(new RectF(0, 0, measuredWidth, measuredHeight), 0, 0, mMaskPaint);
        mCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(),
                R.drawable.award_1), null, new RectF(0, 0, measuredWidth, measuredHeight), null);//遮层
    }
    /**
     * 设置画笔的一些参数
     */
    private void setUpOutPaint() {
        // 设置画笔
//         mOutterPaint.setAlpha(0);
        mOutterPaint.setColor(Color.parseColor("#c0c0c0"));
        mOutterPaint.setAntiAlias(true);
        mOutterPaint.setDither(true);
        mOutterPaint.setStyle(Style.STROKE);
        mOutterPaint.setStrokeJoin(Paint.Join.ROUND); // 圆角
        mOutterPaint.setStrokeCap(Paint.Cap.ROUND); // 圆角
        // 设置画笔宽度
        mOutterPaint.setStrokeWidth(50);
    }
    /**
     * 绘制线条
     */
    private void drawPath() {
        mOutterPaint.setStyle(Style.STROKE);
        mOutterPaint
                .setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));//取俩者的交集
        mCanvas.drawPath(mPath, mOutterPaint);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mPath.moveTo(mLastX, mLastY);
                break;
            case MotionEvent.ACTION_MOVE:
                int dx = Math.abs(x - mLastX);
                int dy = Math.abs(y - mLastY);
                if (dx > 3 || dy > 3)
                    mPath.lineTo(x, y);
                mLastX = x;
                mLastY = y;
                new Thread(mRunnable).start();
                break;
            case MotionEvent.ACTION_UP:
                new Thread(mRunnable).start();
                break;
        }
        invalidate();
        return true;
    }
    /**
     * 统计擦除区域任务
     */
    private Runnable mRunnable = new Runnable() {
        private int[] mPixels;
        @Override
        public void run() {
            int w = getWidth();
            int h = getHeight();
            float wipeArea = 0;
            float totalArea = w * h;
            Bitmap bitmap = mBitmap;
            mPixels = new int[w * h];
            /**
             * 拿到所有的像素信息
             */
            bitmap.getPixels(mPixels, 0, w, 0, 0, w, h);
            /**
             * 遍历统计擦除的区域
             */
            for (int i = 0; i < w; i++) {
                for (int j = 0; j < h; j++) {
                    int index = i + j * w;
                    if (mPixels[index] == 0) {
                        wipeArea++;
                    }
                }
            }
            /**
             * 根据所占百分比,进行一些操作
             */
            if (wipeArea > 0 && totalArea > 0) {
                int percent = (int) (wipeArea * 100 / totalArea);
//                Log.e("TAG", percent + "");
                if (percent > 50) {
//                    Log.e("TAG", "清除区域达到50%,下面自动清除");
                    isComplete = true;
                    postInvalidate();
                }
            }
        }
    };
    /**
     * 将布局转换成bitmap
     * @param addViewContent
     * @return
     */
    private Bitmap getViewBitmap(View addViewContent) {
        addViewContent.setDrawingCacheEnabled(true);
        addViewContent.measure(
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        addViewContent.layout(0, 0,
                addViewContent.getMeasuredWidth(),
                addViewContent.getMeasuredHeight());
        addViewContent.buildDrawingCache();
        Bitmap cacheBitmap = addViewContent.getDrawingCache();
        Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
        return bitmap;
    }
}

  

 

GitHub地址:

 
 
 
 
 
 

Android刮刮卡自定义控件的更多相关文章

  1. Android 自定义控件实现刮刮卡效果 真的就只是刮刮卡么

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40162163 , 本文出自:[张鸿洋的博客] 很久以前也过一个html5的刮刮卡 ...

  2. Android 自定义View修炼-【2014年最后的分享啦】Android实现自定义刮刮卡效果View

    一.简介: 今天是2014年最后一天啦,首先在这里,我祝福大家在新的2015年都一个个的新健康,新收入,新顺利,新如意!!! 上一偏,我介绍了用Xfermode实现自定义圆角和椭圆图片view的博文& ...

  3. Android 自己定义控件实现刮刮卡效果 真的就仅仅是刮刮卡么

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40162163 , 本文出自:[张鸿洋的博客] 非常久以前也过一个html5的刮刮 ...

  4. 【Android - 自定义View】之自定义View实现“刮刮卡”效果

    首先来介绍一下这个自定义View: (1)这个自定义View的名字叫做 GuaguakaView ,继承自View类: (2)这个View实现了很多电商项目中的“刮刮卡”的效果,即用户可以刮开覆盖层, ...

  5. Android项目刮刮奖详解扩展篇——开源刮刮奖View的制作

    Android项目刮刮奖详解(四) 前言 我们已经成功实现了刮刮奖的功能了,本期是扩展篇,我们把这个View直接定义成开源控件,发布到JitPack上,以后有需要也可以直接使用,关于自定义控件的知识, ...

  6. 用c#开发微信 (16) 微活动 2 刮刮卡

    微信营销是一种新型的营销模式,由于微信更重视用户之间的互动,故而这种营销推广不不能盲目地套用微博营销的单纯大量广告推送方式.这种方式在微信营销中的效果非常差,会令用户反感,继而取消去企业或商家的微信公 ...

  7. Android项目刮刮奖详解(三)

    Android项目刮刮奖详解(二) 前言 上一期我们已经实现了一个简易的刮刮卡功能,这一期我们来将其完善一下 目标 将刮刮奖的宽高改为合适高度 将刮刮奖位置居中 将信息层的图片换成文字(重点) 实现 ...

  8. Android项目刮刮奖详解(四)

    Android项目刮刮奖详解(三) 前言 上一期我们已经是完成了刮刮卡的基本功能,本期就是给我们的项目增加个功能以及美化一番 目标 增加功能 用户刮卡刮到一定程度的时候,清除遮盖层 在遮盖层放张图片, ...

  9. Android打造完美的刮刮乐效果控件

    技术:Android+Java   概述 趁着元旦假期之际,首先在这里,我祝福大家在新的2019年都一个个的新健康,新收入,新顺利,新如意!!! 上一偏,我介绍了用Xfermode实现自定义圆角和椭圆 ...

随机推荐

  1. 【java】对象序列化Serializable、transient

    package 对象序列化; import java.io.Serializable; @SuppressWarnings("serial") class A implements ...

  2. 33 款主宰 2017 iOS 开发的开源库

    推荐一篇文章 改文章汇聚了现在主流的一些三方框架,很值得一看 https://mp.weixin.qq.com/s/ICodliohtzbmA-eLKRFT-Q

  3. sourceTree git 忽略指定文件

    按照如下步骤执行(终端命令) 1. git status modified: LovegoMall.xcworkspace/xcuserdata/Tiny.xcuserdatad/xcdebugger ...

  4. iOS 环信集成项目应用

    环信iOS端3.0版本集成记录--聊天界面篇 环信离线推送证书... 1,环信处在后台的时候,消息的接收与推送 离线发推送 配置属性 EMCallOptions *options = [[EMClie ...

  5. 通过 Sublime Package Control 安装插件后不能用的解决办法

    最近使用 Sublime 写 SASS 的时候需要使用高亮功能,通过 Package Control 安装了相关插件,但是安装之后没有反应,再次打开 Package Control 的时候,已经搜索不 ...

  6. <tangmuchw>之新手vue项目小记--新建.vue文件,运行项目,出现error:This dependency was not found...

    错误码: This dependency was not found: * !!vue-style-loader!css-loader?{"minimize":false,&quo ...

  7. 安装lamp代码

    tar -zxvf mysqladdUser mysql -s /sbin/nologinmv mysql /usr/local/mysql (改目录下直接存储bin docs等目录)mkdir -p ...

  8. DNS查询的工作原理

    二.DNS查询的工作原理 1.DNS查询过程按两部分进行     1.名称查询从客户端计算机开始, 并传送给本机的DNS客户服务程序进行解析     2.如果不能再本机解析查询, 可根据设定的查询DN ...

  9. rm 命令详解

    rm  作用: 删除一个目录中的一个或多个文件或目录,也可以将某个目录及下属的所有文件及子目录均删除掉, 对于连接文件只是删除整个连接文件,而保持原有文件. 注意: 使用rm 命令要格外小心,因为一旦 ...

  10. volatile作用及相关集合类

    在工作一年多之后,java程序员都会了解到volatile 这个修饰符, 其在多线程环境下解决了long/double写操作的原子性.基本变量的可见性.通过建立内存屏障保证指令有序性 那么在哪些Jav ...