设置SeekBar的宽度以使“滑动解锁”效果

时间:2015-04-26 09:50:19

标签: java android xml swipe android-seekbar

我正在尝试使用SeekBar进行滑动解锁功能。我的目标是这里显示的:

enter image description here

它由两个图像组成,一个背景和一个按钮。我将背景和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中):

enter image description here

我似乎无法使SeekBar匹配FrameLayout的大小。您可以在上图中看到由细蓝框表示的Seekbar的大小。框架有两个小的实心蓝色方块,您可以使用鼠标指针抓取以调整大小。但是,如果我使用我的鼠标指针拖动小蓝色方块以匹配FrameView的整个宽度,那么只要我松开鼠标,方块就会回到原来的(太小)尺寸。

我该怎么做才能解决这个问题?如果我能以一种根本不同的方式实现刷卡解锁,那么我也对此感兴趣。

2 个答案:

答案 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。享受:)

这是最终结果。

Slide to unlock

答案 1 :(得分:1)

以下是可以帮助您的库。

https://github.com/Ghedeon/SlideToUnlockProject