电商活动中刮刮卡的实现
最后更新于:2022-04-01 14:32:22
**一、实现原理**
Paint.setXferMode();
a、绘制我们的圆形
b、setXferMode(SrcIn)
c、绘制矩形(正方形)图片
**二、主要思想:**
将一个view设计成多层:背景层,含中奖信息等;
遮盖层,用于刮奖,使用关联一个Bitmap的Canvas
在该Bitmap上,使用它的canvas.drawPath的api来处理 手势滑动(类似刮奖的动作)
使用paint.setXfermode 来进行消除手势滑动区域
当刮开90%的时候会全部消失。
**三、代码实现**
~~~
public class GuaGuaKa extends View
{
private Paint mOutterPaint;
private Path mPath;
private Canvas mCanvas;
private Bitmap mBitmap;
private int mLastX;
private int mLastY;
private Bitmap mOutterBitmap;
// -------------------------------
// private Bitmap bitmap;
private String mText;
private Paint mBackPaint;
/**
* 记录刮奖信息文本的宽和高
*/
private Rect mTextBound;
private int mTextSize;
private int mTextColor;
// 判断遮盖层区域是否消除达到阈值
private volatile boolean mComplete = false;
/**
* 刮刮卡刮完的回调
*
*
*/
public interface OnGuaGuaKaCompleteListener
{
void complete();
}
private OnGuaGuaKaCompleteListener mListener;
public void setOnGuaGuaKaCompleteListener(
OnGuaGuaKaCompleteListener mListener)
{
this.mListener = mListener;
}
public GuaGuaKa(Context context)
{
this(context, null);
}
public GuaGuaKa(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public GuaGuaKa(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init();
TypedArray a = null;
try
{
a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.GuaGuaKa, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++)
{
int attr = a.getIndex(i);
switch (attr)
{
case R.styleable.GuaGuaKa_text:
mText = a.getString(attr);
break;
case R.styleable.GuaGuaKa_textSize:
mTextSize = (int) a.getDimension(attr, TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_SP, 22,
getResources().getDisplayMetrics()));
break;
case R.styleable.GuaGuaKa_textColor:
mTextColor = a.getColor(attr, 0x000000);
break;
}
}
} finally
{
if (a != null)
a.recycle();
}
}
public void setText(String mText)
{
this.mText = mText;
// 获得当前画笔绘制文本的宽和高
mBackPaint.getTextBounds(mText, 0, mText.length(), mTextBound);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
// 初始化我们的bitmap
mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
// 设置绘制path画笔的一些属性
setupOutPaint();
setUpBackPaint();
// mCanvas.drawColor(Color.parseColor("#c0c0c0"));
mCanvas.drawRoundRect(new RectF(0, 0, width, height), 30, 30,
mOutterPaint);
mCanvas.drawBitmap(mOutterBitmap, null, new Rect(0, 0, width, height),
null);
}
/**
* 设置我们绘制获奖信息的画笔属性
*/
private void setUpBackPaint()
{
mBackPaint.setColor(mTextColor);
mBackPaint.setStyle(Style.FILL);
mBackPaint.setTextSize(mTextSize);
// 获得当前画笔绘制文本的宽和高
mBackPaint.getTextBounds(mText, 0, mText.length(), mTextBound);
}
/**
* 设置绘制path画笔的一些属性
*/
private void setupOutPaint()
{
mOutterPaint.setColor(Color.parseColor("#c0c0c0"));
mOutterPaint.setAntiAlias(true);
mOutterPaint.setDither(true);
mOutterPaint.setStrokeJoin(Paint.Join.ROUND);
mOutterPaint.setStrokeCap(Paint.Cap.ROUND);
mOutterPaint.setStyle(Style.FILL);
mOutterPaint.setStrokeWidth(20);
}
@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;
break;
case MotionEvent.ACTION_UP:
if (!mComplete)
new Thread(mRunnable).start();
break;
}
if (!mComplete)
invalidate();
return true;
}
~~~
~~~
</pre>我们在ACTION_UP的时候就行计算,首先我们还是给大家灌输下计算的原理,如果大家用心看了,应该知道我们所有的操作基本都在mBitmap,现在我们获得mBItmap上所有的像素点的数据,统计被清除的区域(被清除的像素为0);最后与我们图片的总像素数做个除法元算,就可以拿到我们清除的百分比了;不过,计算可能会是一个耗时的操作,具体速度跟图片大小有关,所以我们决定使用异步的方式去计算:<pre name="code" class="java">
~~~
~~~
private Runnable mRunnable = 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);
Log.e("TAG", percent + "");
if (percent > 90)
{
// 清除掉图层区域
mComplete = true;
postInvalidate();
}
}
}
};
@Override
protected void onDraw(Canvas canvas)
{
// canvas.drawBitmap(bitmap, 0 , 0, null);
canvas.drawText(mText, getWidth() / 2 - mTextBound.width() / 2,
getHeight() / 2 + mTextBound.height() / 2, mBackPaint);
if (!mComplete)
{
drawPath();
canvas.drawBitmap(mBitmap, 0, 0, null);
}
if (mComplete)
{
if (mListener != null)
{
mListener.complete();
}
}
}
private void drawPath()
{
mOutterPaint.setStyle(Style.STROKE);
mOutterPaint.setXfermode(new PorterDuffXfermode(Mode.DST_OUT));
mCanvas.drawPath(mPath, mOutterPaint);
}
/**
* 进行一些初始化操作
*/
private void init()
{
mOutterPaint = new Paint();
mPath = new Path();
mOutterBitmap = BitmapFactory.decodeResource(getResources(),
cn.zhilinghui.guaguaka.R.drawable.fg_guaguaka);
mText = "谢谢惠顾";
mTextBound = new Rect();
mBackPaint = new Paint();
mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
22, getResources().getDisplayMetrics());
}
}
~~~
view的自定义控件:
~~~
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:hyman="http://schemas.android.com/apk/res/cn.zhilinghui.guaguaka"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<cn.zhilinghui.guaguaka.view.GuaGuaKa
android:id="@+id/id_guaguaka"
android:layout_width="300dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
hyman:text="¥500,0000"
hyman:textColor="#ff00f0"
hyman:textSize="30sp" />
</RelativeLayout>
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-14_56e65d6d6731e.jpg)