2015-04-26 26 views
8

我正在嘗試使用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的整個寬度,那麼只要我鬆開鼠標,方塊就會回到其原始(太小)的尺寸。

我該怎麼做才能解決這個問題?如果我能夠以一種根本不同的方式實現輕掃解鎖,那麼我也對此感興趣。

+0

您是否已經找到了解決方案? – Techfist

+0

@Techfist:不,所以我剛開始賞金。 – Mick

+0

@Luksprog:這聽起來很不錯(我現在無法測試),但無論如何,您應該將您的「評論」剪切並粘貼到「答案」中,否則您無法收集獎金。 – Mick

回答

7

正如我所承諾的,我會看看我能做些什麼。我沒有使用過你的圖片,並使用android圖形來完成繪圖,因爲這使得整個事物更具可定製性和可擴展性。如果您堅持繪製圖像,請使用canvas.drawBitmap ...這非常簡單。主要邏輯可以保持不變。

我可能會回來並添加一些花哨的動畫和視覺效果,但現在我留下了一些註釋掉的代碼來使用着色器和漸變,因爲此刻我的時間有點短暫。

讓我們開始吧......第一個箱子attrs.xml/resources/並將其添加到它。

<?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

+0

恐怕我不知道這個gradle是什麼。我試着把attrs.xml直接放在resources目錄下,但是eclipse抱怨「無效的資源目錄名」,我想這意味着它需要放在資源內部的某個特定目錄中......我的猜測是... layout? – Mick

+0

對不起,我假設你正在使用Android工作室。它使用你提到的gradle東西。對於eclipse,只需按照一步一步的教程。我沒有在這臺電腦上發生日食,但我可能會嘗試在我回家時做一個日食項目。 –

+0

所以我把attrs.xml放在resources \ layout中,然後創建類SlideToUnlock.java ... eclipse抱怨「styleable無法解析或者不是字段」。奇怪的是,另一個錯誤是「Canvas類型中的方法drawRoundRect(RectF,float,float,Paint)不適用於參數(int,int,int,int,float,float,Paint)」 – Mick