您的位置:首页 > 移动开发 > Android开发

Android 实现刮刮卡效果

2015-08-16 00:19 253 查看
因为项目需求一个刮刮卡效果的抽奖页面,然后就自己写一个刮刮卡效果的自定义view,基本可以满足产品的需求吧,正好周末,就把这个干货分享给大家,主要是通过Paint和Canvas来实现,喜欢的小伙帮们,可以看下。

上效果图:



原理:自定义的View是继承TextView的,所以文字和背景图片我们可以直接设置,刮奖区这个图片是通过Canvas画上去的,手指移动刮开是通过Paint来实现的。然后每次刮完后,通过Bitmap的getPixels()方法来计算刮到什么程度了,判断是否已经把文字全部显示出来,是的话,就说明已经刮完了。

自定义View的代码:

[code]
/**
 * 刮刮卡
 * 
 * @author xiongwei
 * 
 */
@SuppressLint("HandlerLeak")
public class GGKView extends TextView {

    private static final int MV = 1;
    private static final int SW = 50;

    private int mWidth;
    private int mHeight;
    private int mStrokeWidth;
    private float mX;
    private float mY;
    private boolean mRun;
    private boolean caculate;
    //沿着手指的路径绘制图形
    private Path mPath;
    private Paint mPaint;
    //获取刮刮卡图片和计算刮到什么程度的一些需要
    private Paint mBitmapPaint;
    private Canvas mCanvas;
    private Bitmap mBitmap;
    private int[] mPixels;
    private Thread mThread;
    private onWipeListener mWipeListener;

    public boolean isScratchEnd = false;

    public GGKView(Context context) {
        super(context);
        scratchInit();
    }

    public GGKView(Context context, AttributeSet attrs) {
        super(context, attrs);
        scratchInit();
    }

    private final void init() {
        mStrokeWidth = SW;

        mPath = new Path();
        mBitmapPaint = new Paint();

        //对手指移动的画笔,进行设置
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(mStrokeWidth);
        //获取刮奖区图片。
        Bitmap maskBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.card_dis_scratch_area);
        mBitmap = Bitmap.createBitmap(maskBitmap.getWidth(), maskBitmap.getHeight(), Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);

        mCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.card_dis_scratch_area), new Matrix(), new Paint());
        mRun = true;
        mThread = new Thread(mRunnable);
        mThread.start();

        setGravity(Gravity.CENTER);

        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mCanvas.drawPath(mPath, mPaint);  //画笔,随着手指的移动进行刮卡
        canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);   //把刮刮卡这张图片画上去
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int w = MeasureSpec.getSize(widthMeasureSpec);
        int h = MeasureSpec.getSize(heightMeasureSpec);
        //获取控件的高宽
        if (w > 0 && h > 0) {
            mWidth = w;
            mHeight = h;
        }
    }

    //重置画笔
    public void reset() {
        mPath.reset();
        mCanvas.drawPaint(mPaint);
        invalidate();
    }

    public void setOnWipeListener(onWipeListener listerer) {
        this.mWipeListener = listerer;
    }

    public void setStrokeWidth(int width) {
        this.mStrokeWidth = width;
        mPaint.setStrokeWidth(width);
    }

    /*
     * public void setMaskColor(int color) { this.mMaskColor = color; reset(); }
     */

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isScratchEnd) {
            boolean invalidate = false;
            boolean consume = false;
            int action = event.getAction();
            switch (action) {
            case MotionEvent.ACTION_DOWN:
                consume = true;
                touchDown(event);
                break;
            case MotionEvent.ACTION_MOVE:
                consume = true;
                invalidate = touchMove(event);
                break;
            case MotionEvent.ACTION_UP:
                consume = true;
                touchUp(event);
                break;
            }

            if (invalidate) {
                invalidate();
            }

            if (consume) {
                return true;
            }
        }
        return super.onTouchEvent(event);
    }

    private void touchDown(MotionEvent event) {
        caculate = false;
        mPath.reset();
        float x = event.getX();
        float y = event.getY();

        mX = x;
        mY = y;
        mPath.moveTo(x, y);
    }

    private boolean touchMove(MotionEvent event) {
        caculate = false;
        final float x = event.getX();
        final float y = event.getY();

        final float previousX = mX;
        final float previousY = mY;

        float cX = (x + previousX) / 2;
        float cY = (y + previousY) / 2;

        final float dx = Math.abs(x - previousX);
        final float dy = Math.abs(y - previousY);

        boolean move = false;

        if (dx >= MV || dy >= MV) {
            mPath.quadTo(cX, cY, x, y);
            mX = x;
            mY = y;

            move = true;
        }
        return move;
    }

    private void touchUp(MotionEvent event) {
        caculate = true;
        mRun = true;
    }

    private Runnable mRunnable = new Runnable() {

        @Override
        public void run() {

            while (mRun && !isScratchEnd) {

                SystemClock.sleep(100);

                if (caculate) {

                    caculate = false;

                    int w = mWidth;
                    int h = mHeight;

                    float wipeArea = 0;
                    float totalArea = w * h;

                    Bitmap bitmap = mBitmap;

                    if (mPixels == null) {
                        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>30时,就说明把文字全部刮出来了。
                                wipeArea++;
                            }
                        }
                    }

                    if (wipeArea > 0 && totalArea > 0) {
                        int percent = (int) (wipeArea * 100 / totalArea);
                        Message msg = mHandler.obtainMessage();
                        msg.what = 0x1;
                        msg.arg1 = percent;
                        mHandler.sendMessage(msg);
                    }

                }

            }

        }
    };

    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {

            if (mWipeListener != null) {
                int percent = msg.arg1;
                mWipeListener.onWipe(percent);
            }

        };
    };

    public interface onWipeListener {
        public void onWipe(int percent);
    }

    @Override
    protected void onDetachedFromWindow() {
        scratchEnd();
        super.onDetachedFromWindow();
    }

    /**
     * 刮奖完毕
     */

    public void scratchEnd() {
        this.isScratchEnd = true;
    }

    /**
     *  开始刮奖
     */

    public void scratchStart() {
        this.isScratchEnd = false;
        init();
    }

    /**
     * 初始化刮奖
     */

    public void scratchInit() {
        scratchEnd();
        init();
    }
}


activity代码:

[code]

public class MainActivity extends Activity {
    private GGKView ggk_view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_1).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
            //重新初始化刮刮卡
                drawScratchcard();

            }
        });
        initScratch();
    }

    /**
     * 初始化刮奖区域
     * 
     * @author zhangbp
     */

    private void initScratch() {
        ggk_view = (GGKView) findViewById(R.id.ggk_view);
        ggk_view.scratchInit();
        //初始化刮刮卡
        drawScratchcard();
    }

    /**
     * 绘制刮奖按钮
     * 
     * @author zhangbp
     */

    private void drawScratchcard() {
        ggk_view.setText("10金币");
        ggk_view.scratchStart();
        //监听已经刮到什么程度
        ggk_view.setOnWipeListener(new onWipeListener() {

            @Override
            public void onWipe(int percent) {
                // TODO Auto-generated method stub
                //当刮的面积大于30时,默认已经刮完了。
                if (percent > 30) {
                    Toast.makeText(MainActivity.this, "您刮出了10个金币", Toast.LENGTH_SHORT).show();
                    ggk_view.reset();
                    ggk_view.scratchEnd();
                }
            }
        });

    }
}


activity_main代码:

[code]<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <com.example.xw_test_0511.GGKView
        android:id="@+id/ggk_view"
        android:layout_width="275.5dp"
        android:layout_height="112.5dp"
        android:background="@drawable/card_dis_scratch_area_bg"
        android:textColor="#ff0000"
        android:textSize="35sp"
        android:textStyle="bold" />

    <Button
        android:id="@+id/btn_1"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="再刮一次" />

</LinearLayout>


要重点说的一点:

因为要通过bitmap.getPixels(mPixels, 0, w, 0, 0, w, h);来计算刮了多少面积,bitmap是我们刮奖区图片的bitmap,所以我们的控件最大的高宽就是这个图片的高宽,只能比这个图片小,不能比图片大,不然会抛出一个异常:

java.lang.IllegalArgumentException: x + width must be <=

bitmap.width()

这个异常的意思x+ 控件的宽度,不能超过bitmap的宽度。

bitmap.getPixels()方法详解可以去这里,这里不做多的阐述。

所以这里的控件高宽是通过计算图片大小得来的。

到这里刮刮卡的控件,就基本已经结束,欢迎大神留言指点,喜欢的小伙帮记得留言哦!

附上demo下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: