Created
January 11, 2014 23:50
-
-
Save dandc87/8378533 to your computer and use it in GitHub Desktop.
Useful View to create a scratch-able surface for your lottery apps.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.dandc87.scratchnsniff; | |
import android.content.Context; | |
import android.graphics.Bitmap; | |
import android.graphics.Canvas; | |
import android.graphics.Color; | |
import android.graphics.Paint; | |
import android.graphics.Path; | |
import android.graphics.PathMeasure; | |
import android.graphics.PorterDuff; | |
import android.graphics.PorterDuffXfermode; | |
import android.graphics.RectF; | |
import android.graphics.drawable.ColorDrawable; | |
import android.graphics.drawable.Drawable; | |
import android.util.AttributeSet; | |
import android.view.MotionEvent; | |
import android.view.View; | |
/** | |
* Created by dandc87 on 1/11/14. | |
*/ | |
public class ScratchView extends View { | |
public static interface OnScratchCallback { | |
/** | |
* Get stats for scratch coverage. | |
* <br/><br/> | |
* Be aware areaCovered uses path BOUNDS so if the user | |
* draws a square around the view, it will be near 100%. | |
* <br/><br/> | |
* A value of 1 is good for coverage. | |
* | |
* @param areaCovered [area of path bounds] / [view area] | |
* @param coverage [path length] * [scratch stroke width] / [area of path bounds] | |
*/ | |
public void onScratch(float areaCovered, float coverage); | |
} | |
private Bitmap mFillCache; | |
private Canvas mFillCanvas; | |
private Drawable mScratchSurface; | |
private final Path mNullPath = new Path(); | |
private final Paint mNullPaint = new Paint(); | |
private final RectF mPathBounds = new RectF(); | |
private float mViewArea = 0f; | |
private final PathMeasure measure = new PathMeasure(); | |
private OnScratchCallback mScratchCallback = null; | |
public ScratchView(Context context) { | |
super(context); | |
init(); | |
} | |
public ScratchView(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
init(); | |
} | |
public ScratchView(Context context, AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
init(); | |
} | |
private void init() { | |
//We need to be focusable if we want to scratch | |
setFocusableInTouchMode(true); | |
//Setup paint with ProterDuff Mode Clear | |
mNullPaint.setColor(Color.TRANSPARENT); | |
mNullPaint.setStyle(Paint.Style.STROKE); | |
mNullPaint.setStrokeWidth(64); | |
mNullPaint.setStrokeCap(Paint.Cap.ROUND); | |
mNullPaint.setStrokeJoin(Paint.Join.ROUND); | |
mNullPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); | |
//Initialize with a gray surface | |
setScratchSurfaceDrawable(new ColorDrawable(Color.GRAY)); | |
} | |
/** | |
* Set the scratching surface drawable | |
* @param d Drawable to use as the scratching surface. Cannot be null | |
*/ | |
public void setScratchSurfaceDrawable(Drawable d) { | |
if(d == null) throw new NullPointerException("Scratch Surface can not be null"); | |
mScratchSurface = d; | |
mScratchSurface.setBounds( | |
getPaddingLeft(), | |
getPaddingTop(), | |
getWidth() - getPaddingRight(), | |
getHeight() - getPaddingBottom() | |
); | |
mViewArea = getWidth() * getHeight() + 0.1f; | |
postInvalidate(); | |
} | |
/** | |
* | |
* @return The scratch width in pixels | |
*/ | |
public float getScratchWidth() { | |
return mNullPaint.getStrokeWidth(); | |
} | |
/** | |
* | |
* @param w The scratch width in pixels | |
*/ | |
public void setScratchWidth(float w) { | |
mNullPaint.setStrokeWidth(w); | |
} | |
/** | |
* Get stats for scratch coverage. | |
* <br/><br/> | |
* Be aware areaCovered uses path BOUNDS so if the user | |
* draws a square around the view, it will be near 1. | |
* <br/><br/> | |
* A value of 1 is good for coverage. | |
* | |
* @param cb OnScratchCallback instance, or null to clear. | |
*/ | |
public void setOnScratchCallback(OnScratchCallback cb) { | |
mScratchCallback = cb; | |
} | |
@Override | |
protected void onSizeChanged(int w, int h, int oldw, int oldh) { | |
super.onSizeChanged(w, h, oldw, oldh); | |
final Drawable surface = mScratchSurface; | |
if(surface != null) { | |
//Honor padding values | |
mScratchSurface.setBounds( | |
getPaddingLeft(), | |
getPaddingTop(), | |
w - getPaddingRight(), | |
h - getPaddingBottom() | |
); | |
} | |
//Cache view area calculation | |
mViewArea = w * h; | |
//Setup new Surface bitmap to clear with scratch path | |
if(mFillCanvas != null) mFillCache.recycle(); | |
mFillCache = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); | |
mFillCanvas = new Canvas(mFillCache); | |
} | |
@Override | |
public boolean onTouchEvent(MotionEvent event) { | |
//New scratch line | |
if(event.getActionMasked() == MotionEvent.ACTION_DOWN) { | |
mNullPath.moveTo(event.getX(), event.getY()); | |
mNullPath.lineTo(event.getX(), event.getY()); | |
} | |
//More of a scratch | |
else if(event.getActionMasked() == MotionEvent.ACTION_MOVE) { | |
mNullPath.lineTo(event.getX(), event.getY()); | |
} | |
//Do some stats for a callback, if there is one | |
final OnScratchCallback cb = mScratchCallback; | |
if(cb != null) { | |
mNullPath.computeBounds(mPathBounds, false); | |
float pathArea = mPathBounds.width() * mPathBounds.height(); | |
measure.setPath(mNullPath, false); | |
cb.onScratch(pathArea / mViewArea, measure.getLength() * mNullPaint.getStrokeWidth() / pathArea); | |
} | |
//More scratching, so we need to repaint | |
postInvalidate(); | |
return true; | |
} | |
@Override | |
protected void onDraw(Canvas canvas) { | |
super.onDraw(canvas); | |
//Clear out old cruft | |
mFillCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); | |
//Draw our surface, nice an pristine | |
final Drawable surface = mScratchSurface; | |
if(surface != null) { | |
surface.draw(mFillCanvas); | |
} | |
//Scratch the surface | |
mFillCanvas.drawPath(mNullPath, mNullPaint); | |
//Draw the scratched surface to the screen | |
canvas.drawBitmap(mFillCache, 0, 0, null); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment