ScratchView android
我在android中有以下自定义的Scratch视图ScratchView android,android,Android,我在android中有以下自定义的Scratch视图 public class ScratchOutView extends SurfaceView implements SurfaceHolder.Callback { private Bitmap drawable; public void setDrawable(Drawable drawable) { Bitmap bitmap = ((BitmapDrawable) drawable).getBitm
public class ScratchOutView extends SurfaceView implements SurfaceHolder.Callback {
private Bitmap drawable;
public void setDrawable(Drawable drawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
this.drawable = bitmap;
invalidate();
}
private ScratchViewThread mThread;
List<Path> mPathList = new ArrayList<Path>();
private Paint mOverlayPaint;
private int mRevealSize;
private boolean mIsScratchable = true;
private boolean mIsAntiAlias = false;
private Path path;
private float startX = 0;
private float startY = 0;
private boolean mScratchStart = false;
private int lastX = -1;
private int lastY = -1;
private boolean scratched;
private boolean bitmap[][];
private int revealed;
private int mScratchAllPercentage;
private int DEFAULT_REVEAL_SIZE = 25;
private int FULL_SCRATCH_PERCENTAGE = 70;
public interface OnFullScratchedListener {
void OnFullScratched();
}
public interface OnScratchListener {
void onScratch(float percentage);
}
private OnScratchListener listener;
public void setOnScratchListener(OnScratchListener listener) {
this.listener = listener;
}
private OnFullScratchedListener listener1;
public void setOnFullScratchedListener(OnFullScratchedListener listener) {
this.listener1 = listener;
}
public ScratchOutView(Context context) {
this(context, null);
}
public ScratchOutView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
init(ctx, attrs);
}
private void init(Context context, AttributeSet attrs) {
mRevealSize = DEFAULT_REVEAL_SIZE;
mScratchAllPercentage = FULL_SCRATCH_PERCENTAGE;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ScrarchViewAttrs, 0, 0);
final int indexCount = ta.getIndexCount();
for (int i = 0; i < indexCount; i++) {
int attr = ta.getIndex(i);
switch (attr) {
case R.styleable.ScrarchViewAttrs_scratchSize:
mRevealSize = ta.getDimensionPixelSize(attr, DEFAULT_REVEAL_SIZE);
break;
case R.styleable.ScrarchViewAttrs_autoScratchPercentage:
mScratchAllPercentage = ta.getInteger(attr, FULL_SCRATCH_PERCENTAGE);
break;
case R.styleable.ScrarchViewAttrs_scratchableDrawable:
Drawable d = ta.getDrawable(R.styleable.ScrarchViewAttrs_scratchableDrawable);
setDrawable(d);
break;
}
}
setZOrderOnTop(true);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
holder.setFormat(PixelFormat.TRANSPARENT);
mOverlayPaint = new Paint();
mOverlayPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mOverlayPaint.setStyle(Paint.Style.STROKE);
mOverlayPaint.setStrokeCap(Paint.Cap.ROUND);
mOverlayPaint.setStrokeJoin(Paint.Join.ROUND);
}
@Override
public void onDraw(Canvas canvas) {
if (scratched) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
return;
}
Rect dest = new Rect(0, 0, drawable.getWidth(), drawable.getHeight());
if (drawable != null) canvas.drawBitmap(drawable, null, dest, new Paint());
else canvas.drawColor(Color.BLACK);
for (Path path : mPathList) {
mOverlayPaint.setAntiAlias(mIsAntiAlias);
mOverlayPaint.setStrokeWidth(mRevealSize);
canvas.drawPath(path, mOverlayPaint);
}
}
private void markRadius(int x, int y) {
int r = mRevealSize / 2 + 1;
for (int i = x - r; i <= x + r; i++) {
for (int j = y - r; j <= y + r; j++) {
if (Math.pow(i - x, 2) + Math.pow(j - y, 2) <= Math.pow(r, 2)) {
if (i >= 0 && j >= 0 && i < drawable.getWidth() && j < drawable.getHeight() && !bitmap[i][j]) {
bitmap[i][j] = true;
revealed++;
}
}
}
}
}
@Override
public boolean onTouchEvent(MotionEvent me) {
synchronized (mThread.getSurfaceHolder()) {
if (!mIsScratchable || scratched) {
return true;
}
if (bitmap == null) {
bitmap = new boolean[drawable.getWidth()][drawable.getHeight()];
revealed = 0;
scratched = false;
}
int x = (int) me.getX();
int y = (int) me.getY();
switch (me.getAction()) {
case MotionEvent.ACTION_DOWN:
path = new Path();
path.moveTo(me.getX(), me.getY());
startX = me.getX();
startY = me.getY();
mPathList.add(path);
markRadius(x, y);
break;
case MotionEvent.ACTION_MOVE:
if (mScratchStart) {
path.lineTo(me.getX(), me.getY());
} else {
if (isScratch(startX, me.getX(), startY, me.getY())) {
mScratchStart = true;
path.lineTo(me.getX(), me.getY());
}
}
if (mScratchStart && lastX != -1 && lastY != -1) {
float step = 0f;
boolean stopX = false;
boolean stopY = false;
int endX = x;
int endY = y;
while (!stopX || !stopY) {
step += 0.25f;
if (step > 1) step = 1;
int newX = (int) (lastX + (endX - lastX) * step);
if (newX == endX) stopX = true;
int newY = (int) (lastY + (endY - lastY) * step);
if (newY == endY) stopY = true;
markRadius(newX, newY);
}
}
break;
case MotionEvent.ACTION_UP:
mScratchStart = false;
markRadius(x, y);
break;
}
lastX = x;
lastY = y;
float ratio = (revealed / (float) (drawable.getWidth() * drawable.getHeight())) * 100;
if (listener != null && !scratched) {
listener.onScratch(ratio);
}
if (ratio > mScratchAllPercentage && !scratched) {
if (listener1 != null)
listener1.OnFullScratched();
scratched = true;
setEnabled(false);
}
return true;
}
}
public void scratchAll() {
if (listener1 != null)
listener1.OnFullScratched();
scratched = true;
setEnabled(false);
}
private boolean isScratch(float oldX, float x, float oldY, float y) {
float distance = (float) Math.sqrt(Math.pow(oldX - x, 2) + Math.pow(oldY - y, 2));
if (distance > mRevealSize * 2) {
return true;
} else {
return false;
}
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
mThread = new ScratchViewThread(getHolder(), this);
mThread.setRunning(true);
mThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
boolean retry = true;
mThread.setRunning(false);
while (retry) {
try {
mThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
class ScratchViewThread extends Thread {
private SurfaceHolder mSurfaceHolder;
private ScratchOutView mView;
private boolean mRun = false;
public ScratchViewThread(SurfaceHolder surfaceHolder, ScratchOutView view) {
mSurfaceHolder = surfaceHolder;
mView = view;
}
public void setRunning(boolean run) {
mRun = run;
}
public SurfaceHolder getSurfaceHolder() {
return mSurfaceHolder;
}
@Override
public void run() {
Canvas c;
while (mRun) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if (c != null) {
mView.onDraw(c);
}
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
public void resetView() {
if (mThread != null) {
synchronized (mThread.getSurfaceHolder()) {
if (mPathList != null) mPathList.clear();
}
}
}
}
我用xml像这样设置参数
<com.example.ScratchOutView xmlns:wsv="http://schemas.android.com/apk/res-auto"
android:id="@+id/scratch_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
wsv:autoScratchPercentage="45"
wsv:scratchSize="30dp"
wsv:scratchableDrawable="@drawable/scratch_area" />
我把scratch_区域图像放在所有可绘制的文件夹中,但创建scratch视图时,它总是得到可绘制hdpi中的图像。我有什么办法可以解决这个问题吗?提前感谢。从drawable获取图像,如下所示
Low density Small screens QVGA 240x320
------------------------------------------------
layout-small-ldpi
layout-small-land-ldpi
Low density Normal screens WVGA400 240x400 (x432)
------------------------------------------------
layout-ldpi
layout-land-ldpi
Medium density Normal screens HVGA 320x480
------------------------------------------------
layout-mdpi
layout-land-mdpi
Medium density Large screens HVGA 320x480
------------------------------------------------
layout-large-mdpi
layout-large-land-mdpi
High density Normal screens WVGA800 480x800 (x854)
------------------------------------------------
layout-hdpi
layout-land-hdpi
Xoom (medium density large but 1280x800 res)
------------------------------------------------
layout-xlarge
layout-xlarge-land
为什么您会怀疑您不应该在hdpi文件夹中获取图像?@tyczj因为我的手机是MDPian,您有什么设备?@tyczj Samsung s2 plus如果您查看此图片,您的设备将不被视为MDPian,它将根据屏幕大小自动获取。您只需将不同大小的图像放在不同的可绘制文件夹中。