转载请注明原作者,如果你觉得这篇文章对你有帮助或启发,可以关注打赏。
前段时间在项目使用原生api实现了刮刮卡效果,今天来分享给大家,如果需要的话,拿来主义,没必要重复造轮子了。
下面是效果图:
** 下面是核心代码:**
/**
* 自定义刮刮卡效果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();
}
});
}
}