Android 实现刮刮卡效果
2015-08-16 00:19
253 查看
因为项目需求一个刮刮卡效果的抽奖页面,然后就自己写一个刮刮卡效果的自定义view,基本可以满足产品的需求吧,正好周末,就把这个干货分享给大家,主要是通过Paint和Canvas来实现,喜欢的小伙帮们,可以看下。
上效果图:
原理:自定义的View是继承TextView的,所以文字和背景图片我们可以直接设置,刮奖区这个图片是通过Canvas画上去的,手指移动刮开是通过Paint来实现的。然后每次刮完后,通过Bitmap的getPixels()方法来计算刮到什么程度了,判断是否已经把文字全部显示出来,是的话,就说明已经刮完了。
自定义View的代码:
activity代码:
activity_main代码:
要重点说的一点:
因为要通过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下载
上效果图:
原理:自定义的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下载
相关文章推荐
- Android中startService的使用及Service生命周期
- Android 登录界面的实现
- 简单的输入框右侧下拉实现,基于AutoCompleteTextView
- Android下发送邮件功能总结
- Android:ServiceDemo
- Android 用户注册界面的实现
- Android PopupWindow
- Android开发PopupWindow showAtLocation用法
- 导入一个AndroidStudio工程作为一个Library Module
- Android之——利用系统权限实现手机重启(获取系统权限签名详解)
- Android Activity的生命周期
- Android-用户登陆的例子(服务器端开发到客户端实现)
- Android LayoutInflater详解
- 居中显示并旋转 android Button 里的属性drawableLeft
- android开发 drawtext的开始坐标位置
- AndroidStudio中代码混淆以及打包操作
- HDOJ 5099 Comparison of Android versions(字符串模拟)
- <Android Framework 之路>Android5.1 Camera Framework(一)
- Android5.1 Camera Framework(一)
- Android基础(数据操作)