Animation on custom view that was added to WindowManager - android

I have an custom view that was insert to windowManager for drag capabilities, and overrides the OnDraw() method. I'm trying to animate it with translate animation but the animation doesn't start (also the animationListener doesn't catch the animation), instead the view "Jumps" from the start point of the animation to it's end point.
This is the custom view:
public class DragView extends View {
#Override
protected void onAnimationEnd() {
// TODO Auto-generated method stub
super.onAnimationEnd();
}
#Override
protected void onAnimationStart() {
// TODO Auto-generated method stub
super.onAnimationStart();
}
// Number of pixels to add to the dragged item for scaling. Should be even
// for pixel alignment.
private static final int DRAG_SCALE = 0; // In Launcher, value is 40
public Bitmap mBitmap;
public Paint mPaint;
public int mRegistrationX;
public int mRegistrationY;
public Context context;
private float mAnimationScale = 1.0f;
private WindowManager.LayoutParams mLayoutParams;
private WindowManager mWindowManager;
public DragView(Context context, Bitmap bitmap, int registrationX, int registrationY, int left, int top, int width, int height,boolean animated) {
super(context);
if (animated)
return;
this.context = context;
// mWindowManager = WindowManagerImpl.getDefault();
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Matrix scale = new Matrix();
float scaleFactor = width;
scaleFactor = (scaleFactor + DRAG_SCALE) / scaleFactor;
scale.setScale(scaleFactor, scaleFactor);
mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
// The point in our scaled bitmap that the touch events are located
mRegistrationX = registrationX + (DRAG_SCALE / 2);
mRegistrationY = registrationY + (DRAG_SCALE / 2);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float scale = mAnimationScale;
if (scale < 0.999f) { // allow for some float error
float width = mBitmap.getWidth();
float offset = (width - (width * scale)) / 2;
canvas.translate(offset, offset);
canvas.scale(scale, scale);
}
canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mBitmap.recycle();
}
public void setPaint(Paint paint) {
mPaint = paint;
invalidate();
}
public void show(IBinder windowToken, int touchX, int touchY) {
final WindowManager.LayoutParams lp;
int pixelFormat;
pixelFormat = PixelFormat.TRANSLUCENT;
lp = new WindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, touchX - mRegistrationX,
touchY - mRegistrationY, WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, pixelFormat);
lp.gravity = Gravity.LEFT | Gravity.TOP;
lp.token = windowToken;
lp.setTitle("DragView");
mLayoutParams = lp;
((FragmentActivity)context).runOnUiThread(new Runnable() {
//#Override
public void run() {
mWindowManager.addView(DragView.this, lp);
}
});
}
void move(int touchX, int touchY) {
// This is what was done in the Launcher code.
WindowManager.LayoutParams lp = mLayoutParams;
lp.x = touchX - mRegistrationX;
lp.y = touchY - mRegistrationY;
mWindowManager.updateViewLayout(this, lp);
}
void remove() {
mWindowManager.removeView(this);
}
This is how I start the animation:
TranslateAnimation translate = new TranslateAnimation
(0, outOfScreenX, 0,outOfScreenY);
translate.setDuration(300);
translate.setFillAfter(true);
mDragView.clearAnimation();
mDragView.startAnimation(translate);

The "solution" to this problem is that WindowManager is a global device component(e.g the dragging of app icons uses it), and it doesn't supports animations at all.

Related

How to create scratch card which allows user to scratch only once

This code is working, I created Scratch image view through which I can scratch the image view to see the the image, but scratch image view is automatically filling with scratch pattern when I reopen the app or I move to previous activity.only once the user should scratch the image view to view image and it should not fill again when I reopen the app or move to previous activity .Can anyone help me
<com.example.swapnanadendla.scratch.ScratchImageView
android:id="#+id/sample_image"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:background="#android:color/white"
android:src="#drawable/image" />
.
public class ScratchImageView extends ImageView{
public interface IRevealListener {
void onRevealed(ScratchImageView iv);
void onRevealPercentChangedListener(ScratchImageView siv, float percent);
}
public static final float STROKE_WIDTH = 12f;
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
/**
* Bitmap holding the scratch region.
*/
private Bitmap mScratchBitmap;
/**
* Drawable canvas area through which the scratchable area is drawn.
*/
private Canvas mCanvas;
/**
* Path holding the erasing path done by the user.
*/
private Path mErasePath;
/**
* Path to indicate where the user have touched.
*/
private Path mTouchPath;
/**
* Paint properties for drawing the scratch area.
*/
private Paint mBitmapPaint;
/**
* Paint properties for erasing the scratch region.
*/
private Paint mErasePaint;
/**
* Gradient paint properties that lies as a background for scratch region.
*/
private Paint mGradientBgPaint;
/**
* Sample Drawable bitmap having the scratch pattern.
*/
private BitmapDrawable mDrawable;
/**
* Listener object callback reference to send back the callback when the image has been revealed.
*/
private IRevealListener mRevealListener;
/**
* Reveal percent value.
*/
private float mRevealPercent;
/**
* Thread Count
*/
private int mThreadCount = 0;
public ScratchImageView(Context context) {
super(context);
init();
}
public ScratchImageView(Context context, AttributeSet set) {
super(context, set);
init();
}
public ScratchImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* Set the strokes width based on the parameter multiplier.
* #param multiplier can be 1,2,3 and so on to set the stroke width of the paint.
*/
public void setStrokeWidth(int multiplier) {
mErasePaint.setStrokeWidth(multiplier * STROKE_WIDTH);
}
/**
* Initialises the paint drawing elements.
*/
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();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
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);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mScratchBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mErasePath, mErasePaint);
}
private void touch_start(float x, float y) {
mErasePath.reset();
mErasePath.moveTo(x, y);
mX = x;
mY = y;
}
/**
* clears the scratch area to reveal the hidden image.
*/
public void clear() {
int[] bounds = getImageBounds();
int left = bounds[0];
int top = bounds[1];
int right = bounds[2];
int bottom = bounds[3];
int width = right - left;
int height = bottom - top;
int centerX = left + width / 2;
int centerY = top + height / 2;
left = centerX - width / 2;
top = centerY - height / 2;
right = left + width;
bottom = top + height;
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
mCanvas.drawRect(left, top, right, bottom, paint);
checkRevealed();
invalidate();
}
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;
drawPath();
}
mTouchPath.reset();
mTouchPath.addCircle(mX, mY, 30, Path.Direction.CW);
}
private void drawPath() {
mErasePath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mErasePath, mErasePaint);
// kill this so we don't double draw
mTouchPath.reset();
mErasePath.reset();
mErasePath.moveTo(mX, mY);
checkRevealed();
//reveal();
}
public void reveal() {
clear();
}
private void touch_up() {
drawPath();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
default:
break;
}
return true;
}
public int getColor() {
return mErasePaint.getColor();
}
public Paint getErasePaint() {
return mErasePaint;
}
public void setEraserMode() {
getErasePaint().setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
}
public void setRevealListener(IRevealListener listener) {
this.mRevealListener = listener;
}
public boolean isRevealed() {
return mRevealPercent == 1;
}
private void checkRevealed() {
if(! isRevealed() && mRevealListener != null) {
int[] bounds = getImageBounds();
int left = bounds[0];
int top = bounds[1];
int width = bounds[2] - left;
int height = bounds[3] - top;
// Do not create multiple calls to compare.
if(mThreadCount > 1) {
Log.d("Captcha", "Count greater than 1");
return;
}
mThreadCount++;
// new AsyncTask<Integer, Void, Float>() {
//
// #Override
// protected Float doInBackground(Integer... params) {
//
// try {
// int left = params[0];
// int top = params[1];
// int width = params[2];
// int height = params[3];
//
// Bitmap croppedBitmap = Bitmap.createBitmap(mScratchBitmap, left, top, width, height);
//
// return BitmapUtils.getTransparentPixelPercent(croppedBitmap);
// } finally {
// mThreadCount--;
// }
// }
//
// public void onPostExecute(Float percentRevealed) {
//
// // check if not revealed before.
// if( ! isRevealed()) {
//
// float oldValue = mRevealPercent;
// mRevealPercent = percentRevealed;
//
// if(oldValue != percentRevealed) {
// mRevealListener.onRevealPercentChangedListener(ScratchImageView.this, percentRevealed);
// }
//
// // if now revealed.
// if( isRevealed()) {
// mRevealListener.onRevealed(ScratchImageView.this);
// }
// }
// }
//
// }.execute(left, top, width, height);
}
}
public int[] getImageBounds() {
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int vwidth = getWidth() - paddingLeft - paddingRight;
int vheight = getHeight() - paddingBottom - paddingTop;
int centerX = vwidth/2;
int centerY = vheight/2;
Drawable drawable = getDrawable();
Rect bounds = drawable.getBounds();
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
if(width <= 0) {
width = bounds.right - bounds.left;
}
if(height <= 0) {
height = bounds.bottom - bounds.top;
}
int left;
int top;
if(height > vheight) {
height = vheight;
}
if(width > vwidth) {
width = vwidth;
}
ScaleType scaleType = getScaleType();
switch (scaleType) {
case FIT_START:
left = paddingLeft;
top = centerY - height / 2;
break;
case FIT_END:
left = vwidth - paddingRight - width;
top = centerY - height / 2;
break;
case CENTER:
left = centerX - width / 2;
top = centerY - height / 2;
break;
default:
left = paddingLeft;
top = paddingTop;
width = vwidth;
height = vheight;
break;
}
return new int[] {left, top, left + width, top + height};
}
}
What you can do is store into Firebase a variable like isScratched = 1 or 0, if it's 1 it's because the user didn't scratch it yet. If the user scratch it that variable will be 0 and then in onStart you put a listener of Firebase database, if the listener finds out the value is 0 the scratch card will not be available.
I will show you some snippet here
private DatabaseReference mDatabase; //First declare your database reference
Then in init() or onCreate()
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("isScratched").setValue("1"); //here we create the isScratched and set it to 1 , meaning that the photo is not even scratched yet
Now, after the photo is scratched just set that value to 0
//After your scratch method or when the user finishes scratching the pick
mDatabase.child("isScratched").setValue("0");
Now in your onStart() or in the Activity where the image appears just attach a listener
mDatabase.child("isScratched").addSingleValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String getScratchedValue = datasnapshot.getValue(String.class);
Log.e("IsScratched : " ,""+getScratchedValue);
if(getScratchedValue.equals(0)){
//Your picture is already scratched, run the method that will show it scratched
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("Database Error: "+databaseError.getDetails());
}
});

Rectangle draw call in mainActivity

I draw a rectangle in the activity_main folder and I get these values to be drawn in integer.xml, but I do not want to do that.getInteger (R.integer.scanner_rect_width and getResources (). getInteger (R.integer.scanner_rect_height I get these values from the integer.xml folder, but how do I assign these values by creating a mainActivity object?
ScannerOverlay scannerOverlay = new ScannerOverlay (); as
ScanerOverlay.java
public class ScannerOverlay extends ViewGroup {
private float left, top, endY;
private int rectWidth, rectHeight;
private int frames;
private boolean revAnimation;
private int lineColor, lineWidth;
public ScannerOverlay(Context context) {
super(context);
}
public void setWidth(int pixels) {
rectWidth = pixels;
requestLayout();
invalidate();
}
public void setHeight(int pixels) {
rectHeight = pixels;
requestLayout();
invalidate();
}
public ScannerOverlay(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScannerOverlay(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ScannerOverlay,
0, 0);
rectWidth = a.getInteger(R.styleable.ScannerOverlay_square_width, 150);
rectHeight = a.getInteger(R.styleable.ScannerOverlay_square_height, 150);
lineColor = a.getColor(R.styleable.ScannerOverlay_line_color, ContextCompat.getColor(context, R.color.scanner_line));
lineWidth = a.getInteger(R.styleable.ScannerOverlay_line_width, getResources().getInteger(R.integer.line_width));
frames = a.getInteger(R.styleable.ScannerOverlay_line_speed, getResources().getInteger(R.integer.line_width));
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
#Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
left = (w - dpToPx(rectWidth)) / 2;
top = (h - dpToPx(rectHeight)) / 2;
endY = top;
super.onSizeChanged(w, h, oldw, oldh);
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
#Override
public boolean shouldDelayChildPressedState() {
return false;
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw transparent rect
int cornerRadius = 0;
Paint eraser = new Paint();
eraser.setAntiAlias(true);
eraser.setColor(Color.WHITE);
RectF rect = new RectF(left, top, dpToPx(rectWidth) + left, dpToPx(rectHeight) + top);
canvas.drawRoundRect(rect, (float) cornerRadius, (float) cornerRadius, eraser);
// draw horizontal line
Paint line = new Paint();
line.setColor(lineColor);
line.setStrokeWidth(Float.valueOf(lineWidth));
// draw the line to product animation
if (endY >= top + dpToPx(rectHeight) + frames) {
revAnimation = true;
} else if (endY == top + frames) {
revAnimation = false;
}
// check if the line has reached to bottom
if (revAnimation) {
endY -= frames;
} else {
endY += frames;
}
canvas.drawLine(left, endY, left + dpToPx(rectWidth), endY, line);
invalidate();
}
}
activity_main.xml
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.yavuz.myapplication.MainActivity">
<com.example.yavuz.myapplication.ScannerOverlay
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#44000000"
app:line_color="#7323DC"
app:line_speed="6"
app:line_width="4"
/>
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
You have to create setters methods for width and height as below:
public void setWidth(int pixels) {
rectWidth = pixels;
requestLayout();
invalidate();
}
public void setHeight(int pixels) {
rectHeight = pixels;
requestLayout();
invalidate();
}
Then to use it MainActivity you use it as below:
ScannerOverlay scannerOverlay = new ScannerOverlay();
scannerOverlay.setWidth(200); // for example
scannerOverlay.setHeight(300);
setContentView(scannerOverlay);
I suppose that ScannerOverlay is a custom view.
from xml layout:
<thepackage_where_theclass.ScannerOverlay
app:scanner_rect_width="200"
app:scanner_rect_height="200"/>

Custom VIew Onclick Style change

I have custom animated play/pause button which is getting highlighted when I press it. How can I remove the highlighted gray color around the button.
Setting background to transparent isn't working.
public class PlayPauseMiniView extends FrameLayout {
private static final long PLAY_PAUSE_ANIMATION_DURATION = 200;
private final PlayPauseDrawable mDrawable;
private final Paint mPaint = new Paint();
private AnimatorSet mAnimatorSet;
private int mWidth;
private int mHeight;
public PlayPauseMiniView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mDrawable = new PlayPauseDrawable(context);
mDrawable.setCallback(this);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(120, 120);
}
#Override
protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mDrawable.setBounds(0, 0, w, h);
mWidth = w;
mHeight = h;
}
#Override
protected boolean verifyDrawable(Drawable who) {
return who == mDrawable || super.verifyDrawable(who);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDrawable.draw(canvas);
}
public void toggle() {
if (mAnimatorSet != null) {
mAnimatorSet.cancel();
}
mAnimatorSet = new AnimatorSet();
final Animator pausePlayAnim = mDrawable.getPausePlayAnimator();
mAnimatorSet.setInterpolator(new DecelerateInterpolator());
mAnimatorSet.setDuration(PLAY_PAUSE_ANIMATION_DURATION);
mAnimatorSet.playTogether(pausePlayAnim);
mAnimatorSet.start();
}
}
public class PlayPauseDrawable extends Drawable {
private static final Property<PlayPauseDrawable, Float> PROGRESS =
new Property<PlayPauseDrawable, Float>(Float.class, "progress") {
#Override
public Float get(PlayPauseDrawable d) {
return d.getProgress();
}
#Override
public void set(PlayPauseDrawable d, Float value) {
d.setProgress(value);
}
};
private final Path mLeftPauseBar = new Path();
private final Path mRightPauseBar = new Path();
private final Paint mPaint = new Paint();
private final RectF mBounds = new RectF(0,0,10,10);
private final float mPauseBarWidth;
private final float mPauseBarHeight;
private final float mPauseBarDistance;
private float mWidth;
private float mHeight;
private float mProgress;
private boolean mIsPlay;
public PlayPauseDrawable(Context context) {
final Resources res = context.getResources();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.DKGRAY);
mPauseBarWidth = res.getDimensionPixelSize(R.dimen.pause_bar_width);
mPauseBarHeight = res.getDimensionPixelSize(R.dimen.pause_bar_height);
mPauseBarDistance = res.getDimensionPixelSize(R.dimen.pause_bar_distance);
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mBounds.set(bounds);
mWidth = mBounds.width();
mHeight = mBounds.height();
}
#Override
public void draw(Canvas canvas) {
mLeftPauseBar.rewind();
mRightPauseBar.rewind();
// The current distance between the two pause bars.
final float barDist = lerp(mPauseBarDistance, 0, mProgress);
// The current width of each pause bar.
final float barWidth = lerp(mPauseBarWidth, mPauseBarHeight / 2f, mProgress);
// The current position of the left pause bar's top left coordinate.
final float firstBarTopLeft = lerp(0, barWidth, mProgress);
// The current position of the right pause bar's top right coordinate.
final float secondBarTopRight = lerp(2 * barWidth + barDist, barWidth + barDist, mProgress);
// Draw the left pause bar. The left pause bar transforms into the
// top half of the play button triangle by animating the position of the
// rectangle's top left coordinate and expanding its bottom width.
mLeftPauseBar.moveTo(0, 0);
mLeftPauseBar.lineTo(firstBarTopLeft, -mPauseBarHeight);
mLeftPauseBar.lineTo(barWidth, -mPauseBarHeight);
mLeftPauseBar.lineTo(barWidth, 0);
mLeftPauseBar.close();
// Draw the right pause bar. The right pause bar transforms into the
// bottom half of the play button triangle by animating the position of the
// rectangle's top right coordinate and expanding its bottom width.
mRightPauseBar.moveTo(barWidth + barDist, 0);
mRightPauseBar.lineTo(barWidth + barDist, -mPauseBarHeight);
mRightPauseBar.lineTo(secondBarTopRight, -mPauseBarHeight);
mRightPauseBar.lineTo(2 * barWidth + barDist, 0);
mRightPauseBar.close();
canvas.save();
// Translate the play button a tiny bit to the right so it looks more centered.
canvas.translate(lerp(0, mPauseBarHeight / 8f, mProgress), 0);
// (1) Pause --> Play: rotate 0 to 90 degrees clockwise.
// (2) Play --> Pause: rotate 90 to 180 degrees clockwise.
final float rotationProgress = mIsPlay ? 1 - mProgress : mProgress;
final float startingRotation = mIsPlay ? 90 : 0;
canvas.rotate(lerp(startingRotation, startingRotation + 90, rotationProgress), mWidth / 2f, mHeight / 2f);
// Position the pause/play button in the center of the drawable's bounds.
canvas.translate(mWidth / 2f - ((2 * barWidth + barDist) / 2f), mHeight / 2f + (mPauseBarHeight / 2f));
// Draw the two bars that form the animated pause/play button.
canvas.drawPath(mLeftPauseBar, mPaint);
canvas.drawPath(mRightPauseBar, mPaint);
canvas.restore();
}
public Animator getPausePlayAnimator() {
final Animator anim = ObjectAnimator.ofFloat(this, PROGRESS, mIsPlay ? 1 : 0, mIsPlay ? 0 : 1);
anim.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mIsPlay = !mIsPlay;
}
});
return anim;
}
public boolean isPlay() {
return mIsPlay;
}
private void setProgress(float progress) {
mProgress = progress;
invalidateSelf();
}
private float getProgress() {
return mProgress;
}
#Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
invalidateSelf();
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
/**
* Linear interpolate between a and b with parameter t.
*/
private static float lerp(float a, float b, float t) {
return a + (b - a) * t;
}
}
You can use ImageView instead of ImageButton
Have You tried android:background="#null" ?

Progress bar with divider

Can someone please explain to me how to implement a progress bar with a divider just like its shown on the image below?
For the progress bar I am using https://github.com/akexorcist/Android-RoundCornerProgressBar but this does not seem to have a divider option.
replace ProgressDrawable from my answer with the modified one:
class ProgressDrawable extends Drawable {
private static final int NUM_SEGMENTS = 4;
private final int mForeground;
private final int mBackground;
private final Paint mPaint = new Paint();
private final RectF mSegment = new RectF();
public ProgressDrawable(int fgColor, int bgColor) {
mForeground = fgColor;
mBackground = bgColor;
}
#Override
protected boolean onLevelChange(int level) {
invalidateSelf();
return true;
}
#Override
public void draw(Canvas canvas) {
float level = getLevel() / 10000f;
Rect b = getBounds();
float gapWidth = b.height() / 2f;
float segmentWidth = (b.width() - (NUM_SEGMENTS - 1) * gapWidth) / NUM_SEGMENTS;
mSegment.set(0, 0, segmentWidth, b.height());
mPaint.setColor(mForeground);
for (int i = 0; i < NUM_SEGMENTS; i++) {
float loLevel = i / (float) NUM_SEGMENTS;
float hiLevel = (i + 1) / (float) NUM_SEGMENTS;
if (loLevel <= level && level <= hiLevel) {
float middle = mSegment.left + NUM_SEGMENTS * segmentWidth * (level - loLevel);
canvas.drawRect(mSegment.left, mSegment.top, middle, mSegment.bottom, mPaint);
mPaint.setColor(mBackground);
canvas.drawRect(middle, mSegment.top, mSegment.right, mSegment.bottom, mPaint);
} else {
canvas.drawRect(mSegment, mPaint);
}
mSegment.offset(mSegment.width() + gapWidth, 0);
}
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter cf) {
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
and create it like this:
Drawable d = new ProgressDrawable(0xdd00ff00, 0x4400ff00);
/**
* Created by nagendra on 16/06/15.
*/
public class ProgressBarDrawable extends Drawable {
private int parts = 10;
private Paint paint = null;
private int fillColor = Color.parseColor("#2D6EB9");
private int emptyColor = Color.parseColor("#233952");
private int separatorColor = Color.parseColor("#FFFFFF");
private RectF rectFill = null;
private RectF rectEmpty = null;
private List<RectF> separators = null;
public ProgressBarDrawable(int parts)
{
this.parts = parts;
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.separators = new ArrayList<RectF>();
}
#Override
protected boolean onLevelChange(int level)
{
invalidateSelf();
return true;
}
#Override
public void draw(Canvas canvas)
{
// Calculate values
Rect b = getBounds();
float width = b.width();
float height = b.height();
int spaceFilled = (int)(getLevel() * width / 10000);
this.rectFill = new RectF(0, 0, spaceFilled, height);
this.rectEmpty = new RectF(spaceFilled, 0, width, height);
int spaceBetween = (int)(width / 100);
int widthPart = (int)(width / this.parts - (int)(0.9 * spaceBetween));
int startX = widthPart;
for (int i=0; i<this.parts - 1; i++)
{
this.separators.add( new RectF(startX, 0, startX + spaceBetween, height) );
startX += spaceBetween + widthPart;
}
// Foreground
this.paint.setColor(this.fillColor);
canvas.drawRect(this.rectFill, this.paint);
// Background
this.paint.setColor(this.emptyColor);
canvas.drawRect(this.rectEmpty, this.paint);
// Separator
this.paint.setColor(this.separatorColor);
for (RectF separator : this.separators)
{
canvas.drawRect(separator, this.paint);
}
}
#Override
public void setAlpha(int alpha)
{
}
#Override
public void setColorFilter(ColorFilter cf)
{
}
#Override
public int getOpacity()
{
return PixelFormat.TRANSLUCENT;
}
}
in XM Layout
<ProgressBar
android:id="#+id/progress_bar_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
android:progress="10"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
/>
ProgressBar progressBar= (ProgressBar)findViewById(R.id.progress_bar_test);
ProgressBarDrawable bgProgress= new ProgressBarDrawable(5);
progressBar.setProgressDrawable(bgProgress);
With the help of this and this answers, I could create my customized version of the segmented horizontal progress bar.
First, Create a class as follows.
public class SegmentedProgressDrawable extends Drawable {
private int parts;
private Paint paint;
private int fillColor;
private int emptyColor;
private int cutOffWidth;
private int separatorColor;
public SegmentedProgressDrawable(int parts, int fillColor, int emptyColor, int separatorColor) {
this.parts = parts;
this.fillColor = fillColor;
this.emptyColor = emptyColor;
this.separatorColor = separatorColor;
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected boolean onLevelChange(int level) {
invalidateSelf();
return true;
}
#Override
public void draw(#NonNull Canvas canvas) {
// Calculate values
Rect bounds = getBounds();
float actualWidth = bounds.width();
float actualHeight = bounds.height();
//width with dividers + segment width
int fullBlockWidth = (int) (actualWidth / this.parts);
//ToDo: to change the width of segment change this line
int segmentWidth = (int) (fullBlockWidth * 0.2f);
// int dividerWidth =fullBlockWidth-segmentWidth;
cutOffWidth = (int) (getLevel() * actualWidth / 10000);
//Draw separator as background
RectF fullBox = new RectF(0, 0, actualWidth, actualHeight);
this.paint.setColor(this.separatorColor);
canvas.drawRect(fullBox, this.paint);
//start drawing lines as segmented bars
int startX = 0;
for (int i = 0; i < this.parts; i++) {
int endX = startX + segmentWidth;
//in ideal condition this would be the rectangle
RectF part = new RectF(startX, 0, endX, actualHeight);
//if the segment is below level the paint color should be fill color
if ((startX + segmentWidth) <= cutOffWidth) {
this.paint.setColor(this.fillColor);
canvas.drawRect(part, this.paint);
}
//if the segment is started below the level but ends above the level than we need to create 2 different rectangle
else if (startX < cutOffWidth) {
RectF part1 = new RectF(startX, 0, cutOffWidth, actualHeight);
this.paint.setColor(this.fillColor);
canvas.drawRect(part1, this.paint);
RectF part2 = new RectF(cutOffWidth, 0, startX + segmentWidth, actualHeight);
this.paint.setColor(this.emptyColor);
canvas.drawRect(part2, this.paint);
}
//if the segment is above level the paint color should be empty color
else {
this.paint.setColor(this.emptyColor);
canvas.drawRect(part, this.paint);
}
//update the startX to start the new segment with the gap of divider and segment width
startX += fullBlockWidth;
}
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter cf) {
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
And I, used it as follows:
horizontalProgressBar = findViewById(R.id.horizontal_progress_bar);
int fillColor = ContextCompat.getColor(getActivity(), R.color.primary);
int emptyColor = ContextCompat.getColor(getActivity(), R.color.color_redeem_badge_bg);
int separatorColor = ContextCompat.getColor(getActivity(), R.color.transparent);
SegmentedProgressDrawable progressDrawable = new SegmentedProgressDrawable(20, fillColor, emptyColor, separatorColor);
horizontalProgressBar.setProgressDrawable(progressDrawable);
horizontalProgressBar.setProgress(60);

How to draw an audio waveform on Android

I have a custom view that I want to use to display the amplitude of audio coming in through the microphone in a line graph.
Getting the amplitude and all that I have no problem with, and drawing the lines is not really a problem either.
What I want to do is show the amplitude starting at the far right edge, moving left. So with each new sample I want to translate the bitmap to the left, then draw a line from the last point to the new point. I'm not sure what the easiest way to achieve this is. I originally was able to do it by drawing Paths and just adding a new point to the path with each sample, the problem was that after like a minute the path was too big to be drawn. So I thought about it and wanted to switch to using a cached bitmap, translate that on each iteration, and draw from the last point to the new point. However this is tricky to do as (after experimentation). When I translate the bitmap it doesn't move the far left pixels off the bitmap, it just moves the entire bitmap in the canvas and I have no way to write pixels to the right side.
Below is a description of what I'm trying to do:
Given this:
I want to translate that to the left:
Then draw a line to a new point the space space on the right
Of course, step 2 and 3 should happen at essentially the same time.
How can I achieve this? I'm open to new ideas altogether, like perhaps saving all the points for up to 1 screen worth and drawing them out on each onDraw call. I'd prefer to just save them in a bitmap and do some kind of translation/clipping etc to achieve the same thing with perhaps less overhead.
private static final int MAX_AMPLITUDE = 32767;
float lx, ly;
private Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.Black);
}
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
height = h;
width = w;
ly = height;
lx = width;
amplitudeDivisor = ((float) MAX_AMPLITUDE / (float) height);
}
#Override
public void onDraw(Canvas canvas) {
mAmplitude = (float)(MAX_AMPLITUDE * Math.random());
float dx = width - delta;
float dy = height - (mAmplitude / amplitudeDivisor);
mCanvas.drawLine(lx, ly, dx, dy, mPaint);
mCanvas.translate(-delta, 0);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
lx = dx;
ly = dy;
delta+=10;
postInvalidateDelayed(200);
}
The above is just a sample, I'm just using a random value for the amplitude to simplify for now. I've tried a bunch of things with no luck. Any help would be greatly appreciated.
I ended up getting this working by saving the points to an array. I draw a white line before the recording starts. Note that I use an EvictingQueue from the Guava library as a circular buffer of points to render on a line. To use this, once a recording starts call start() and when it ends call stop. From your activity you will need to send MediaRecorder getMaxAmplitude() values to the updateAmplitude() method of this class, and do so at an interval of say 50 ms. The view also supports rotation.
public class AmplitudeWaveFormView extends View {
private static final String TAG = AmplitudeWaveFormView.class.getSimpleName();
private static final int MAX_AMPLITUDE = 32767;
private static final int SAMPLES_PER_SCREEN = 100;
private float mAmplitude = 0;
private Paint mRecordingPaint, mNotRecordingPaint;
private int height = -1;
private int width = -1;
private boolean mIsStarted;
private float[] lastPoints;
private int oldWidth = -1, oldHeight = -1;
private int mCurrentSample;
private float amplitudeDivisor = 1;
private float lx,ly, deltaX;
private EvictingQueue<Float> mPointQueue;
private int recordColor;
private int notRecordingColor;
public AmplitudeWaveFormView(Context context) {
super(context);
init();
}
public AmplitudeWaveFormView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AmplitudeWaveFormView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void start() {
mIsStarted = true;
}
public void stop() {
mIsStarted = false;
}
public void updateAmplitude(float amplitude) {
mAmplitude = amplitude;
postInvalidate();
}
private void init() {
recordColor = getResources().getColor(R.color.mint);
notRecordingColor = getResources().getColor(R.color.alpine);
mRecordingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mRecordingPaint.setStyle(Paint.Style.STROKE);
mRecordingPaint.setStrokeWidth(5);
mRecordingPaint.setColor(recordColor);
mNotRecordingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mNotRecordingPaint.setStyle(Paint.Style.STROKE);
mNotRecordingPaint.setStrokeWidth(5);
mNotRecordingPaint.setColor(notRecordingColor);
}
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
height = h;
width = w;
ly = height;
lx = width;
deltaX = (float)width / (float)SAMPLES_PER_SCREEN;
amplitudeDivisor = ((float) MAX_AMPLITUDE / (float) height);
mPointQueue = EvictingQueue.create(SAMPLES_PER_SCREEN * 4);
if (lastPoints != null && lastPoints.length > 0) {
float xScale = (float) width/oldWidth;
float yScale = (float) height/oldHeight;
Matrix matrix = new Matrix();
matrix.setScale(xScale, yScale);
matrix.mapPoints(lastPoints);
mPointQueue.addAll(Floats.asList(lastPoints));
ly = lastPoints[lastPoints.length-1];
lx= lastPoints[lastPoints.length -2];
lastPoints = null;
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mCurrentSample = bundle.getInt("sample");
lastPoints = bundle.getFloatArray("lines");
oldWidth = bundle.getInt("oldWidth");
oldHeight = bundle.getInt("oldHeight");
state = ((Bundle) state).getParcelable("parent");
}
super.onRestoreInstanceState(state);
}
#Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putFloatArray("lines", Floats.toArray(mPointQueue));
bundle.putInt("sample", mCurrentSample);
bundle.putParcelable("parent", super.onSaveInstanceState());
bundle.putInt("oldWidth", width);
bundle.putInt("oldHeight", height);
return bundle;
}
#Override
public void onDraw(Canvas canvas) {
if (mIsStarted) {
float x = lx + deltaX;
float y = height - (mAmplitude / amplitudeDivisor);
mPointQueue.add(lx);
mPointQueue.add(ly);
mPointQueue.add(x);
mPointQueue.add(y);
lastPoints = Floats.toArray(mPointQueue);
lx = x;
ly = y;
}
if (lastPoints != null && lastPoints.length > 0) {
int len = mPointQueue.size() / 4 >= SAMPLES_PER_SCREEN ? SAMPLES_PER_SCREEN * 4 : mPointQueue.size();
float translateX = width - lastPoints[lastPoints.length - 2];
canvas.translate(translateX, 0);
canvas.drawLines(lastPoints, 0, len, mRecordingPaint);
}
if (mCurrentSample <= SAMPLES_PER_SCREEN) {
drawNotRecordingLine(canvas);
}
mCurrentSample++;
}
private void drawNotRecordingLine(Canvas canvas) {
canvas.drawLine(0,height, width, height, mNotRecordingPaint);
}
}

Categories

Resources