Skip to content

Instantly share code, notes, and snippets.

@fdoyle
Last active January 1, 2019 19:50
Show Gist options
  • Save fdoyle/8117037 to your computer and use it in GitHub Desktop.
Save fdoyle/8117037 to your computer and use it in GitHub Desktop.
SupernovaView, as demonstrated by the LG G2 lock screen where you click and drag to "punch a hole" through the lock screen to the home screen. This view extends ImageView to the same effect.Probably not as optimized as it could be, but it seems about as smooth as LGs implementation.
package com.lacronicus.supernova;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
/**
* Created by fdoyle on 12/24/13.
*/
public class SupernovaView extends ImageView {
float minRadius = 100;
float unlockRadius = 400;
public SupernovaView(Context context) {
super(context);
setup();
}
public SupernovaView(Context context, AttributeSet attrs) {
super(context, attrs);
setup();
}
public SupernovaView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setup();
}
float startX = 0;
float startY = 0;
float radius = 0;
private void setup() {
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
SupernovaView.this.postInvalidate();
double x = event.getX();
double y = event.getY();
radius = (float) Math.sqrt( (startX -x) * (startX - x) + (startY - y) * (startY -y));
if(radius < minRadius) {
radius = minRadius;
}
break;
case MotionEvent.ACTION_UP:
final float currentRadius = radius;
if(radius > unlockRadius) {
float maxRadius = (float) Math.sqrt(getWidth() * getWidth() + getHeight() * getHeight());
ValueAnimator valueAnimator = ValueAnimator.ofFloat(currentRadius, maxRadius);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setRadius((Float) animation.getAnimatedValue());
}
});
valueAnimator.setDuration(360);
valueAnimator.start();
} else {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(currentRadius, 0);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setRadius((Float) animation.getAnimatedValue());
}
});
valueAnimator.setDuration(360);
valueAnimator.start();
}
break;
}
return true;
}
});
}
public void setRadius(float radius) {
this.radius = radius;
postInvalidate();
}
Bitmap b;
Canvas maskCanvas;
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(changed) {
b = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.ARGB_8888);
maskCanvas = new Canvas(b);
}
}
Paint clearPaint = new Paint();
Paint atopPaint = new Paint();
Paint imagePaint = new Paint();
PorterDuffXfermode src_atop = new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP);
PorterDuffXfermode overlay = new PorterDuffXfermode(PorterDuff.Mode.OVERLAY);
PorterDuffXfermode clear = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
@Override
protected void onDraw(Canvas canvas) {
clearPaint.setXfermode(clear);
atopPaint.setXfermode(src_atop);
imagePaint.setXfermode(overlay);
super.onDraw(maskCanvas); //draw this imageview to an off-screen buffer
maskCanvas.drawCircle(startX, startY, radius, clearPaint); // make part of that buffer transparent
canvas.drawBitmap(b, 0,0, atopPaint); // draw that buffer to the canvas
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment