Android 刮刮卡实现

作者: 轻云时解被占用了 | 来源:发表于2016-07-27 21:53 被阅读358次

    转载请注明原作者,如果你觉得这篇文章对你有帮助或启发,可以关注打赏。

    前段时间在项目使用原生api实现了刮刮卡效果,今天来分享给大家,如果需要的话,拿来主义,没必要重复造轮子了。

    下面是效果图:

    scratchcard_anim.gif

    ** 下面是核心代码:**

    /**
     * 自定义刮刮卡效果View
     */
    public class ScratchView extends View {
        private static Bitmap mOutterBitmap;
        private static Bitmap mBitmap;
        public final int DEFAULT_SWIPE_PAINT_WIDTH = 40;
        public final int DEFAULT_SWIPE_COMPLETE_PERCENTAGE = 70;
        private final int mOutterBgId;
        private Paint mOutterPaint;
        private Path mPath;
        private Canvas mCanvas;
        private int mLastX;
        private int mLastY;
        private Paint mOutBmpPaint;
        private int mSwipePaintWidth;
        private int mSwipeCompletePercentage;
        private OnCompleteListener mOnCompleteListener;
        private OnScratchListener mOnScratchListener;
        private int mWidth;
        private int mHeight;
        //判断遮盖层区域是否达到消除的比例
        private volatile boolean mCompleted = false;
        /**
         * 起一个线程来计算已经扫的面积及占总区域的比例
         * 根据区域来判断是否完成
         */
        private Runnable mTask = new Runnable() {
            @Override
            public void run() {
                int w = getWidth();
                int h = getHeight();
    
                float wipeArea = 0;
                float totalArea = w * h;
    
                Bitmap bitmap = mBitmap;
                int[] mPixels = new int[w * h];
                //获取bitmap的所有像素信息
                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);
                    if (percent > mSwipeCompletePercentage) {
                        //清除图层区域
                        mCompleted = true;
                        postInvalidate();
                    }
                }
            }
        };
        private Bitmap bitmap;
    
        public ScratchView(Context context) {
            this(context, null);
        }
    
        public ScratchView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public ScratchView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.scratchView);
            mOutterBgId = typedArray.getResourceId(R.styleable.scratchView_outter_bg, 0);
            init();
        }
    
        public static int calculateInSampleSize(
                BitmapFactory.Options options, int reqWidth, int reqHeight) {
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
    
            if (height > reqHeight || width > reqWidth) {
                if (width > height) {
                    inSampleSize = (int) Math.ceil(height / reqHeight);
                } else {
                    inSampleSize = (int) Math.ceil(width / reqWidth);
                }
            }
            return inSampleSize;
        }
    
        public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                             int reqWidth, int reqHeight) {
    
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(res, resId, options);
    
            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeResource(res, resId, options);
        }
    
        /**
         * 设置刮卡完成比例
         *
         * @param per
         */
        public void setCompletePercentage(int per) {
            this.mSwipeCompletePercentage = per;
        }
    
        /**
         * 设置刮卡完成监听
         *
         * @param mOnCompleteListener
         */
        public void setOnCompleteListener(OnCompleteListener mOnCompleteListener) {
            this.mOnCompleteListener = mOnCompleteListener;
        }
    
        /**
         * 设置刮卡开始监听
         *
         * @param mOnScratchListener
         */
        public void setOnScratchListener(OnScratchListener mOnScratchListener) {
            this.mOnScratchListener = mOnScratchListener;
        }
    
        /**
         * 进行初始化操作
         */
        private void init() {
            mOutterPaint = new Paint();
            mOutBmpPaint = new Paint();
            mPath = new Path();
            mSwipePaintWidth = DEFAULT_SWIPE_PAINT_WIDTH;
            mSwipeCompletePercentage = DEFAULT_SWIPE_COMPLETE_PERCENTAGE;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            mWidth = getMeasuredWidth();
            mHeight = getMeasuredHeight();
            drawView();
        }
    
        private void drawView() {
            if (mOutterBitmap == null)
                mOutterBitmap = decodeSampledBitmapFromResource(getResources(), mOutterBgId, mWidth, mHeight);
            //初始化bitmap
            if (mBitmap == null)
                mBitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_4444);
            //初始化canvas
            if (mCanvas == null)
                mCanvas = new Canvas(mBitmap);
            //设置画笔的一些属性
            setOutterPaint();
            setOutBmpPaint();
            Rect rect = new Rect(0, 0, mWidth, mHeight);
            mCanvas.drawBitmap(mOutterBitmap, null, rect, mOutBmpPaint);
        }
    
        /**
         * 设置刮扫画笔的属性
         */
        private void setOutterPaint() {
            mOutterPaint.setColor(Color.parseColor("#c3c3c3"));
            mOutterPaint.setAntiAlias(true);
            mOutterPaint.setDither(true);
            mOutterPaint.setStrokeJoin(Join.ROUND);//设置圆角
            mOutterPaint.setStrokeCap(Cap.ROUND);
            mOutterPaint.setStyle(Style.STROKE);
            mOutterPaint.setStrokeWidth(mSwipePaintWidth);
        }
    
        /**
         * 设置绘制刮刮卡圆角背景的画笔属性
         */
        private void setOutBmpPaint() {
            mOutBmpPaint.setColor(Color.parseColor("#c3c3c3"));
            mOutBmpPaint.setAntiAlias(true);
            mOutBmpPaint.setDither(true);
            mOutBmpPaint.setStrokeJoin(Join.ROUND);//设置圆角
            mOutBmpPaint.setStrokeCap(Cap.ROUND);
            mOutBmpPaint.setStyle(Style.FILL);
            mOutBmpPaint.setStrokeWidth(20);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            //刮扫完成回调
            if (mCompleted) {
                if (null != mOnCompleteListener) {
                    mOnCompleteListener.complete();
                }
            }
            //判断是否完成,如果完成了就不绘制遮盖层
            if (!mCompleted) {
                drawPath();
                canvas.drawBitmap(mBitmap, 0, 0, null);
            }
        }
    
        /**
         * 设置Xfermode模式为DST_OUT,并绘制扫的路径
         */
        private void drawPath() {
            mOutterPaint.setXfermode(new PorterDuffXfermode(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);
                        if (null != mOnScratchListener)
                            mOnScratchListener.onScratch();
                    }
                    mLastX = x;
                    mLastY = y;
                    post(mTask);
                    break;
                case MotionEvent.ACTION_UP:
                    break;
                default:
                    break;
            }
            invalidate();
            return true;
        }
    
        public void recycle() {
            if (mBitmap != null)
                mBitmap.recycle();
            if (mOutterBitmap != null)
                mOutterBitmap.recycle();
        }
    
        /**
         * 刮刮卡开始刮时回调
         */
        public interface OnScratchListener {
            void onScratch();
        }
    
        /**
         * 刮刮卡刮完之后的回调接口
         */
        public interface OnCompleteListener {
            void complete();
        }
    }
    

    在组件中使用:

     public class MainActivity extends Activity {
        private ScratchView mScratchView;
        private TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initViews();
        }
    
        private void initViews() {
            mScratchView = (ScratchView) findViewById(R.id.scrachView);
            mTextView = (TextView) findViewById(R.id.tv);
            mScratchView.setOnScratchListener(new ScratchView.OnScratchListener() {
                @Override
                public void onScratch() {
                    //do your things here
    
                    //如果希望onScratch只被调用一次
                    //可在此处mScratchView.setOnScratchListener(null);
                }
            });
            mScratchView.setOnCompleteListener(new ScratchView.OnCompleteListener() {
                @Override
                public void complete() {
                    //do your things here
                    //设置刮刮卡不可见 否则下面的view无法获得touch事件
                    mScratchView.setVisibility(View.GONE);
                }
            });
            mTextView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this, mTextView.getText().toString(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    相关文章

      网友评论

      • Raiden:为什么要在onMeasure中绘制 drawView();

      本文标题:Android 刮刮卡实现

      本文链接:https://www.haomeiwen.com/subject/nqgijttx.html