问题
I am making a mobile e-commerce application that tests the user's mobile screen condition by dragging or swiping on the screen to test whether the screen is broken or not.
I added an image that the user follows on the screen when dragging. i added working code but this is a bit slow when the user follows the line it erases that line bit slow but I am looking for some speed by this approach or any other. I followed many tutorials but didn't get relevant code.
Note: Another method to test the screen is also acceptable.
I will appreciate your response.
----------i put this code in Activity------------
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.screentest_img);
imageToErase.setScratchBitmap(bitmap);
imageToErase.setOnScratchCallback(new WScratchView.OnScratchCallback() {
@Override
public void onScratch(float percentage) {
updatePercentage(percentage);
}
@Override
public void onDetach(boolean fingerDetach) {
if (mPercentage > 99) {
imageToErase.setScratchAll(true);
updatePercentage(100);
}
}
});
---------------another method---------------
private void updatePercentage(float percentage) {
mPercentage = percentage;
if(percentage > 99){
Toast.makeText(context, "Screen Condition is good "+percentage, Toast.LENGTH_SHORT).show();
}
}
----------Class WScratchView----------------------
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import org.geniusmindsystem.digify_design.R;
import java.util.ArrayList;
import java.util.List;
/**
* This view start with full gray color bitmap and onTouch to make it
* transparent
*
* @author winsontan520
*/
public class WScratchView extends SurfaceView implements IWScratchView, SurfaceHolder.Callback {
private static final String TAG = "WScratchView";
// default value constants
private final int DEFAULT_COLOR = 0xff444444; // default color is dark gray
private final int DEFAULT_REVEAL_SIZE = 90;
public static final int DEFAULT_SCRATCH_TEST_SPEED = 4;
private Context mContext;
private WScratchViewThread mThread;
List<Path> mPathList = new ArrayList<Path>();
private int mOverlayColor;
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 Bitmap mScratchBitmap;
private Drawable mScratchDrawable = null;
private Paint mBitmapPaint;
private Matrix mMatrix;
private Bitmap mScratchedTestBitmap;
private Canvas mScratchedTestCanvas;
private OnScratchCallback mOnScratchCallback;
//Enable scratch all area if mClearCanvas is true
private boolean mClearCanvas = false;
//Enable click on WScratchView if mIsClickable is true
private boolean mIsClickable = false;
public WScratchView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
init(ctx, attrs);
}
public WScratchView(Context context) {
super(context);
init(context, null);
}
private void init(Context context, AttributeSet attrs) {
mContext = context;
// default value
mOverlayColor = DEFAULT_COLOR;
mRevealSize = DEFAULT_REVEAL_SIZE;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.WScratchView, 0, 0);
final int indexCount = ta.getIndexCount();
for (int i = 0; i < indexCount; i++) {
int attr = ta.getIndex(i);
switch (attr) {
case R.styleable.WScratchView_overlayColor:
mOverlayColor = ta.getColor(attr, DEFAULT_COLOR);
break;
case R.styleable.WScratchView_revealSize:
mRevealSize = ta.getDimensionPixelSize(attr, DEFAULT_REVEAL_SIZE);
break;
case R.styleable.WScratchView_antiAlias:
mIsAntiAlias = ta.getBoolean(attr, false);
break;
case R.styleable.WScratchView_scratchable:
mIsScratchable = ta.getBoolean(attr, true);
break;
case R.styleable.WScratchView_scratchDrawable:
mScratchDrawable = ta.getDrawable(R.styleable.WScratchView_scratchDrawable);
break;
}
}
setZOrderOnTop(true);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
holder.setFormat(PixelFormat.TRANSPARENT);
mOverlayPaint = new Paint();
mOverlayPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
mOverlayPaint.setStyle(Paint.Style.STROKE);
mOverlayPaint.setStrokeCap(Paint.Cap.ROUND);
mOverlayPaint.setStrokeJoin(Paint.Join.ROUND);
// convert drawable to bitmap if drawable already set in xml
if (mScratchDrawable != null) {
mScratchBitmap = ((BitmapDrawable) mScratchDrawable).getBitmap();
}
mBitmapPaint = new Paint();
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setFilterBitmap(true);
mBitmapPaint.setDither(true);
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Clear all area if mClearCanvas is true
if(mClearCanvas){
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
return;
}
if (mScratchBitmap != null) {
if (mMatrix == null) {
float scaleWidth = (float) canvas.getWidth() / mScratchBitmap.getWidth();
float scaleHeight = (float) canvas.getHeight() / mScratchBitmap.getHeight();
mMatrix = new Matrix();
mMatrix.postScale(scaleWidth, scaleHeight);
}
canvas.drawBitmap(mScratchBitmap, mMatrix, mBitmapPaint);
} else {
canvas.drawColor(mOverlayColor);
}
for (Path path : mPathList) {
mOverlayPaint.setAntiAlias(mIsAntiAlias);
mOverlayPaint.setStrokeWidth(mRevealSize);
canvas.drawPath(path, mOverlayPaint);
}
}
private void updateScratchedPercentage() {
if(mOnScratchCallback == null) return;
mOnScratchCallback.onScratch(getScratchedRatio());
}
@Override
public boolean onTouchEvent(MotionEvent me) {
synchronized (mThread.getSurfaceHolder()) {
if (!mIsScratchable) {
return true;
}
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);
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());
}
}
updateScratchedPercentage();
break;
case MotionEvent.ACTION_UP:
//Set call back if user's finger detach
if(mOnScratchCallback != null){
mOnScratchCallback.onDetach(true);
}
//perform Click action if the motion is not move
//and the WScratchView is clickable
if(!mScratchStart && mIsClickable){
post(new Runnable() {
@Override
public void run() {
performClick();
}
});
}
mScratchStart = false;
break;
}
return true;
}
}
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) {
// do nothing
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
mThread = new WScratchViewThread(getHolder(), this);
mThread.setRunning(true);
mThread.start();
mScratchedTestBitmap = Bitmap.createBitmap(arg0.getSurfaceFrame().width(), arg0.getSurfaceFrame().height(), Bitmap.Config.ARGB_8888);
mScratchedTestCanvas = new Canvas(mScratchedTestBitmap);
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
boolean retry = true;
mThread.setRunning(false);
while (retry) {
try {
mThread.join();
retry = false;
} catch (InterruptedException e) {
// do nothing but keep retry
}
}
}
class WScratchViewThread extends Thread {
private SurfaceHolder mSurfaceHolder;
private WScratchView mView;
private boolean mRun = false;
public WScratchViewThread(SurfaceHolder surfaceHolder, WScratchView 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.draw(c);
}
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
@Override
public void resetView() {
synchronized (mThread.getSurfaceHolder()) {
mPathList.clear();
}
}
@Override
public boolean isScratchable() {
return mIsScratchable;
}
@Override
public void setScratchable(boolean flag) {
mIsScratchable = flag;
}
@Override
public void setOverlayColor(int ResId) {
mOverlayColor = ResId;
}
@Override
public void setRevealSize(int size) {
mRevealSize = size;
}
@Override
public void setAntiAlias(boolean flag) {
mIsAntiAlias = flag;
}
@Override
public void setScratchDrawable(Drawable d) {
mScratchDrawable = d;
if (mScratchDrawable != null) {
mScratchBitmap = ((BitmapDrawable) mScratchDrawable).getBitmap();
}
}
@Override
public void setScratchBitmap(Bitmap b) {
mScratchBitmap = b;
}
@Override
public float getScratchedRatio() {
return getScratchedRatio(DEFAULT_SCRATCH_TEST_SPEED);
}
/**
* thanks to https://github.com/daveyfong for providing this method
*/
@Override
public float getScratchedRatio(int speed) {
if (null == mScratchedTestBitmap) {
return 0;
}
draw(mScratchedTestCanvas);
final int width = mScratchedTestBitmap.getWidth();
final int height = mScratchedTestBitmap.getHeight();
int count = 0;
for (int i = 0; i < width; i += speed) {
for (int j = 0; j < height; j += speed) {
if (0 == Color.alpha(mScratchedTestBitmap.getPixel(i, j))) {
count++;
}
}
}
float completed = (float) count / ((width / speed) * (height / speed)) * 100;
return completed;
}
@Override
public void setOnScratchCallback(OnScratchCallback callback) {
mOnScratchCallback = callback;
}
public static abstract class OnScratchCallback{
public abstract void onScratch(float percentage);
//Call back funtion to monitor the status of finger
public abstract void onDetach(boolean fingerDetach);
}
//Set the mClearCanvas
@Override
public void setScratchAll(boolean scratchAll){
mClearCanvas = scratchAll;
}
//Set the WScartchView clickable
@Override
public void setBackgroundClickable(boolean clickable){
mIsClickable = clickable;
}
}
---------------Interface IWScratchView---------------
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
public interface IWScratchView {
/**
* Whether the view receive user on touch motion
*
* @return true if scratchable
*/
public boolean isScratchable();
/**
* If true, set the view allow receive on touch to reveal the view
* By default, scratchable is true
*
* @param flag - flag for enable/disable scratch
*/
public void setScratchable(boolean flag);
/**
* Set the color of overlay
*
* @param ResId - resources identifier for color in INT type
*/
public void setOverlayColor(int ResId);
/**
* Set the radius size of the circle to be revealed
*
* @param size - radius size of circle in pixel unit
*/
public void setRevealSize(int size);
/**
* Set turn on/off effect of anti alias of circle revealed
* By default, anti alias is turn off
*
* @param flag - set true to turn on anti alias
*/
public void setAntiAlias(boolean flag);
/**
* Reset the scratch view
*
*/
public void resetView();
/**
* Set drawable for scratch view
*
* @param drawable - Set drawable for scratch view
*/
public void setScratchDrawable(Drawable drawable);
/**
* Set bitmap for scratch view
*
* @param -bitmap - Set bitmap for scratch view
*/
public void setScratchBitmap(Bitmap b);
/**
* Get scratched ratio (contribution from daveyfong)
*
* @return float - return Scratched ratio
*/
public float getScratchedRatio();
/**
* Get scratched ratio (contribution from daveyfong)
*
* @param -int - Scratch speed
* @return float - return Scratched ratio
*/
public float getScratchedRatio(int speed);
public void setOnScratchCallback(WScratchView.OnScratchCallback callback);
public void setScratchAll(boolean scratchAll);
public void setBackgroundClickable(boolean clickable);
}
--------in XML------
<org.geniusmindsystem.digify_design.Retailer.WScratchView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:id="@+id/image_to_erase"/>
-------------UPDATE_____________________-
just increase the number of variable (DEFAULT_SCRATCH_TEST_SPEED) in WScratchView class ....it will increase the speed of erasing line.
回答1:
If you're looking for a framework to test the behavior of your app e2e and also record the action of user scenario, I recommend this one https://github.com/wix/Detox
Good luck!
来源:https://stackoverflow.com/questions/58482463/update-working-how-to-test-smartphone-screen-by-swiping-finger-on-the-touch-sc