I am attempting to make a swipe to unlock feature using a SeekBar. The look I am aiming for is shown here:
As I promised I will see what I can do. I have not used your images and used android graphics to do the drawing as that makes the whole thing more customizable and and scalable. If you do insist in drawing your images, use canvas.drawBitmap... it's pretty simple. The main logic can stay the same.
I may come back and add some fancy animations and visual effects, but for now I left some commented out code to play with shaders and gradients as I am a bit short on time at the moment.
Let's get to it... First crate attrs.xml in /resources/
and add this to it.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SlideToUnlock">
<attr name="sliderColor" format="color"/>
<attr name="cancelOnYExit" format="boolean"/>
<attr name="slideToUnlockText" format="string"/>
<attr name="slideToUnlockTextColor" format="color"/>
<attr name="slideToUnlockBackgroundColor" format="color"/>
<attr name="cornerRadiusX" format="dimension"/>
<attr name="cornerRadiusY" format="dimension"/>
</declare-styleable>
</resources>
And then SlideToUnlock.java
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.EmbossMaskFilter;
import android.graphics.MaskFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Typeface;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by ksenchy on 29.4.2015.
*/
public class SlideToUnlock extends View {
public interface OnSlideToUnlockEventListener {
public void onSlideToUnlockCanceled();
public void onSlideToUnlockDone();
}
private int measuredWidth, measuredHeight;
private float density;
private OnSlideToUnlockEventListener externalListener;
private Paint mBackgroundPaint, mTextPaint, mSliderPaint;
private float rx, ry; // Corner radius
private Path mRoundedRectPath;
private String text = "Unlock →";
float x;
float event_x, event_y;
float radius;
float X_MIN, X_MAX;
private boolean ignoreTouchEvents;
// Do we cancel when the Y coordinate leaves the view?
private boolean cancelOnYExit;
private boolean useDefaultCornerRadiusX, useDefaultCornerRadiusY;
/**
* Default values *
*/
int backgroundColor = 0xFF807B7B;
int textColor = 0xFFFFFFFF;
int sliderColor = 0xAA404040;
public SlideToUnlock(Context context) {
super(context);
init(context, null, 0);
}
public SlideToUnlock(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public SlideToUnlock(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
public OnSlideToUnlockEventListener getExternalListener() {
return externalListener;
}
public void setExternalListener(OnSlideToUnlockEventListener externalListener) {
this.externalListener = externalListener;
}
private void init(Context context, AttributeSet attrs, int style) {
Resources res = getResources();
density = res.getDisplayMetrics().density;
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.SlideToUnlock, style, 0);
String tmp = a.getString(R.styleable.SlideToUnlock_slideToUnlockText);
text = TextUtils.isEmpty(tmp) ? text : tmp;
rx = a.getDimension(R.styleable.SlideToUnlock_cornerRadiusX, rx);
useDefaultCornerRadiusX = rx == 0;
ry = a.getDimension(R.styleable.SlideToUnlock_cornerRadiusX, ry);
useDefaultCornerRadiusY = ry == 0;
backgroundColor = a.getColor(R.styleable.SlideToUnlock_slideToUnlockBackgroundColor, backgroundColor);
textColor = a.getColor(R.styleable.SlideToUnlock_slideToUnlockTextColor, textColor);
sliderColor = a.getColor(R.styleable.SlideToUnlock_sliderColor, sliderColor);
cancelOnYExit = a.getBoolean(R.styleable.SlideToUnlock_cancelOnYExit, false);
a.recycle();
mRoundedRectPath = new Path();
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBackgroundPaint.setStyle(Paint.Style.FILL);
mBackgroundPaint.setColor(backgroundColor);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setColor(textColor);
mTextPaint.setTypeface(Typeface.create("Roboto-Thin", Typeface.NORMAL));
mSliderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mSliderPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mSliderPaint.setColor(sliderColor);
mSliderPaint.setStrokeWidth(2 * density);
if (!isInEditMode()) {
// Edit mode does not support shadow layers
// mSliderPaint.setShadowLayer(10.0f, 0.0f, 2.0f, 0xFF000000);
//mSliderPaint.setMaskFilter(new EmbossMaskFilter(new float[]{1, 1, 1}, 0.4f, 10, 8.2f));
float[] direction = new float[]{0.0f, -1.0f, 0.5f};
MaskFilter filter = new EmbossMaskFilter(direction, 0.8f, 15f, 1f);
mSliderPaint.setMaskFilter(filter);
//mSliderPaint.setShader(new LinearGradient(8f, 80f, 30f, 20f, Color.RED,Color.WHITE, Shader.TileMode.MIRROR));
//mSliderPaint.setShader(new RadialGradient(8f, 80f, 90f, Color.RED,Color.WHITE, Shader.TileMode.MIRROR));
//mSliderPaint.setShader(new SweepGradient(80, 80, Color.RED, Color.WHITE));
//mSliderPaint.setMaskFilter(new BlurMaskFilter(15, BlurMaskFilter.Blur.OUTER));
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
if (useDefaultCornerRadiusX) {
rx = measuredHeight * 0.52f;
}
if (useDefaultCornerRadiusY) {
ry = measuredHeight * 0.52f;
}
mTextPaint.setTextSize(measuredHeight / 3.0f);
radius = measuredHeight * 0.45f;
X_MIN = 1.2f * radius;
X_MAX = measuredWidth - X_MIN;
x = X_MIN;
setMeasuredDimension(measuredWidth, measuredHeight);
}
private void drawRoundRect(Canvas c) {
mRoundedRectPath.reset();
mRoundedRectPath.moveTo(rx, 0);
mRoundedRectPath.lineTo(measuredWidth - rx, 0);
mRoundedRectPath.quadTo(measuredWidth, 0, measuredWidth, ry);
mRoundedRectPath.lineTo(measuredWidth, measuredHeight - ry);
mRoundedRectPath.quadTo(measuredWidth, measuredHeight, measuredWidth - rx, measuredHeight);
mRoundedRectPath.lineTo(rx, measuredHeight);
mRoundedRectPath.quadTo(0, measuredHeight, 0, measuredHeight - ry);
mRoundedRectPath.lineTo(0, ry);
mRoundedRectPath.quadTo(0, 0, rx, 0);
c.drawPath(mRoundedRectPath, mBackgroundPaint);
}
@SuppressLint("NewApi")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (measuredHeight <= 0 || measuredWidth <= 0) {
// There is not much we can draw :/
return;
}
if (Build.VERSION.SDK_INT >= 21) {
canvas.drawRoundRect(0, 0, measuredWidth, measuredHeight, rx, ry, mBackgroundPaint);
}
else {
drawRoundRect(canvas);
}
// Draw the text in center
float xPos = ((measuredWidth - mTextPaint.measureText(text)) / 2.0f);
float yPos = (measuredHeight / 2.0f);
float titleHeight = Math.abs(mTextPaint.descent() + mTextPaint.ascent());
yPos += titleHeight / 2.0f;
canvas.drawText(text, xPos, yPos, mTextPaint);
canvas.drawCircle(x, measuredHeight * 0.5f, radius, mSliderPaint);
}
private void onCancel() {
reset();
if (externalListener != null) {
externalListener.onSlideToUnlockCanceled();
}
}
private void onUnlock() {
if (externalListener != null) {
externalListener.onSlideToUnlockDone();
}
}
private void reset() {
x = X_MIN;
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
ignoreTouchEvents = false;
reset();
return true;
case MotionEvent.ACTION_DOWN:
// Is within the circle??
event_x = event.getX(0);
event_y = event.getY(0);
double squareRadius = radius * radius;
double squaredXDistance = (event_x - X_MIN) * (event_x - X_MIN);
double squaredYDistance = (event_y - measuredHeight / 2) * (event_y - measuredHeight / 2);
if (squaredXDistance + squaredYDistance > squareRadius) {
// User touched outside the button, ignore his touch
ignoreTouchEvents = true;
}
return true;
case MotionEvent.ACTION_CANCEL:
ignoreTouchEvents = true;
onCancel();
case MotionEvent.ACTION_MOVE:
if (!ignoreTouchEvents) {
event_x = event.getX(0);
if (cancelOnYExit) {
event_y = event.getY(0);
if (event_y < 0 || event_y > measuredHeight) {
ignoreTouchEvents = true;
onCancel();
}
}
x = event_x > X_MAX ? X_MAX : event_x < X_MIN ? X_MIN : event_x;
if (event_x >= X_MAX) {
ignoreTouchEvents = true;
onUnlock();
}
invalidate();
}
return true;
default:
return super.onTouchEvent(event);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF000000">
<your.package.SlideToUnlock
android:id="@+id/slideToUnlock"
android:layout_width="200dp"
android:layout_height="50dp"
android:layout_centerInParent="true"/>
<your.package.SlideToUnlock
android:id="@+id/slideToUnlock2"
android:layout_width="200dp"
android:layout_height="50dp"
android:layout_below="@+id/slideToUnlock"
android:layout_centerInParent="true"
android:layout_marginTop="50dp"
app:cancelOnYExit="true"
app:slideToUnlockBackgroundColor="#808080"
app:slideToUnlockText="Slide to unlock"
app:slideToUnlockTextColor="#03A9F4"
app:sliderColor="#AAFFE97F"/>
</RelativeLayout>
MainActivity.java
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity implements SlideToUnlock.OnSlideToUnlockEventListener {
private SlideToUnlock slideToUnlockView, slideToUnlockView2;
private Toast toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
slideToUnlockView = (SlideToUnlock) findViewById(R.id.slideToUnlock);
slideToUnlockView.setExternalListener(this);
slideToUnlockView2 = (SlideToUnlock) findViewById(R.id.slideToUnlock2);
slideToUnlockView2.setExternalListener(this);
}
private void showToast(String text) {
if (toast != null) {
toast.cancel();
}
toast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
toast.show();
}
@Override
public void onSlideToUnlockCanceled() {
showToast("Canceled");
}
@Override
public void onSlideToUnlockDone() {
showToast("Unlocked");
}
}
You can download the whole project here. Enjoy :)
This is the final result.
Here are the libraries that may helps you.
https://github.com/Ghedeon/SlideToUnlockProject