我正在尝试使用SeekBar进行滑动解锁功能。我的目标是这里显示的:
它由两个图像组成,一个背景和一个按钮。我将背景和SeekBar放在FrameLayout中,以便SeekBar应该位于背景之上。
像这样:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Testing 123..." />
<FrameLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content" >
<ImageView
android:id="@+id/ImageView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="center"
android:src="@drawable/unlockback" />
<SeekBar
android:id="@+id/myseek"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:max="100"
android:progressDrawable="@android:color/transparent"
android:thumb="@drawable/unlockbut" />
</FrameLayout>
</LinearLayout>
不幸的是,最终结果看起来像这样(在eclipse中):
我似乎无法使SeekBar匹配FrameLayout的大小。您可以在上图中看到由细蓝框表示的Seekbar的大小。框架有两个小的实心蓝色方块,您可以使用鼠标指针抓取以调整大小。但是,如果我使用我的鼠标指针拖动小蓝色方块以匹配FrameView的整个宽度,那么只要我松开鼠标,方块就会回到原来的(太小)尺寸。
我该怎么做才能解决这个问题?如果我能以一种根本不同的方式实现刷卡解锁,那么我也对此感兴趣。
答案 0 :(得分:8)
正如我所承诺的,我会看到我能做些什么。我没有使用你的图像,并使用Android图形来绘图,因为这使整个事情更可定制和可扩展。如果您坚持绘制图像,请使用canvas.drawBitmap ...它非常简单。主要逻辑可以保持不变。
我可能会回来添加一些奇特的动画和视觉效果,但是现在我留下了一些注释掉的代码来使用着色器和渐变,因为我现在的时间有点短。
让我们来看看...... /resources/
中的第一个包 attrs.xml 并将其添加到其中。
<?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>
然后 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");
}
}
您可以下载整个项目here。享受:)
这是最终结果。
答案 1 :(得分:1)
以下是可以帮助您的库。