ScratchView android

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

我在android中有以下自定义的Scratch视图

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,它将根据屏幕大小自动获取。您只需将不同大小的图像放在不同的可绘制文件夹中。