Android刮刮卡:实现单次刮擦及解决状态恢复问题
解决方案:持久化刮擦状态
要实现刮擦状态在页面跳转或应用重启后不恢复,核心是把刮擦的状态持久化存储,下面分两种场景给出具体实现方案:
1. 基础方案:记录刮擦完成状态
如果你的需求是用户刮完后就永久保持已刮开状态,用SharedPreferences来记录状态是最简单的方式:
修改ScratchImageView代码
- 在初始化方法里添加状态读取逻辑,启动时检查是否已经完成刮擦,如果是则直接显示下方图片:
private void init() { // 原有的初始化代码(保留不动) mTouchPath = new Path(); mErasePaint = new Paint(); mErasePaint.setAntiAlias(true); mErasePaint.setDither(true); mErasePaint.setColor(0xFFFF0000); mErasePaint.setStyle(Paint.Style.STROKE); mErasePaint.setStrokeJoin(Paint.Join.BEVEL); mErasePaint.setStrokeCap(Paint.Cap.ROUND); setStrokeWidth(6); mGradientBgPaint = new Paint(); mErasePath = new Path(); mBitmapPaint = new Paint(Paint.DITHER_FLAG); Bitmap scratchBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_scratch_pattern); mDrawable = new BitmapDrawable(getResources(), scratchBitmap); mDrawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); setEraserMode(); // 新增:读取持久化的刮擦状态 SharedPreferences sp = getContext().getSharedPreferences("ScratchCardStorage", Context.MODE_PRIVATE); boolean hasScratched = sp.getBoolean("scratched_" + getId(), false); if (hasScratched) { clear(); // 直接清除刮擦层,显示下方图片 } }
- 在刮擦完成的回调里保存状态,确保完成后永久生效:
private void checkRevealed() { // 原有的检查逻辑(保留不动) if (mRevealPercent >= 1.0f && mRevealListener != null) { mRevealListener.onRevealed(this); // 新增:保存刮擦完成状态 SharedPreferences sp = getContext().getSharedPreferences("ScratchCardStorage", Context.MODE_PRIVATE); sp.edit().putBoolean("scratched_" + getId(), true).apply(); } }
2. 进阶方案:精确恢复刮擦进度
如果需要恢复用户中途退出时的刮擦进度,可以把刮擦层的Bitmap保存到应用内部存储,启动时读取恢复:
添加Bitmap存储与读取方法
在ScratchImageView中新增两个工具方法:
// 保存刮擦层Bitmap到内部存储 private void saveScratchBitmap() { try { FileOutputStream fos = getContext().openFileOutput("scratch_bitmap_" + getId() + ".png", Context.MODE_PRIVATE); mScratchBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.close(); } catch (IOException e) { e.printStackTrace(); } } // 从内部存储读取保存的刮擦层Bitmap private Bitmap loadScratchBitmap() { try { FileInputStream fis = getContext().openFileInput("scratch_bitmap_" + getId() + ".png"); return BitmapFactory.decodeStream(fis); } catch (IOException e) { e.printStackTrace(); return null; } }
修改初始化与绘制逻辑
- 在组件尺寸变化时,优先读取已保存的Bitmap,没有的话再初始化新的:
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 先尝试读取已保存的Bitmap Bitmap savedBitmap = loadScratchBitmap(); if (savedBitmap != null && savedBitmap.getWidth() == w && savedBitmap.getHeight() == h) { mScratchBitmap = savedBitmap; mCanvas = new Canvas(mScratchBitmap); // 重新绑定画布 } else { // 原有的初始化逻辑(保留不动) mScratchBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mScratchBitmap); Rect rect = new Rect(0, 0, mScratchBitmap.getWidth(), mScratchBitmap.getHeight()); mDrawable.setBounds(rect); int startGradientColor = ContextCompat.getColor(getContext(), R.color.scratch_start_gradient); int endGradientColor = ContextCompat.getColor(getContext(), R.color.scratch_end_gradient); mGradientBgPaint.setShader(new LinearGradient(0, 0, 0, getHeight(), startGradientColor, endGradientColor, Shader.TileMode.MIRROR)); mCanvas.drawRect(rect, mGradientBgPaint); mDrawable.draw(mCanvas); } }
- 在用户刮擦过程中保存状态,确保实时进度不丢失:
private void touch_move(float x, float y) { // 原有的触摸移动逻辑(保留不动) float dx = Math.abs(x - mX); float dy = Math.abs(y - mY); if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { mErasePath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2); mX = x; mY = y; // 新增:每次刮擦移动后保存进度 saveScratchBitmap(); } }
3. 注意事项
- 用
getId()作为状态标识的后缀,支持页面中多个ScratchImageView组件各自独立保存状态。 - 应用内部存储的文件只有本应用可以访问,不需要额外申请权限。
- 如果需要重置刮擦状态,可以通过清除对应SharedPreferences键值或删除内部文件实现。
内容的提问来源于stack exchange,提问作者Swapna
相关产品推荐
相关产品推荐

