【问题标题】:How to make slide to unlock button in android如何在android中制作滑动解锁按钮
【发布时间】:2020-05-18 03:55:14
【问题描述】:

您好,我想要一个可以作为 IOS 的“滑动解锁”按钮的按钮

简而言之,我想要一个没有点击效果但可以在拖动时从左向右滑动的按钮,并且在拖动完成时应该考虑点击。

如果可能,请向我推荐任何示例代码。

谢谢!

【问题讨论】:

    标签: android


    【解决方案1】:

    首先,我要感谢@matthias 的回答。 我使用了以下搜索栏并进行了一些自定义:

     <SeekBar
            android:id="@+id/myseek"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:clickable="false"
            android:max="100"
            android:progressDrawable="@android:color/transparent"
            android:thumb="@drawable/ic_launcher" />
    

    在java代码中

    sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
    
                if (seekBar.getProgress() > 95) {
    
                } else {
    
                    seekBar.setThumb(getResources().getDrawable(R.drawable.ic_launcher));
                }
    
            }
    
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
    
    
            }
    
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                if(progress>95){
                    seekBar.setThumb(getResources().getDrawable(R.drawable.load_img1));
                }
    
            }
        });
    

    【讨论】:

    • 虽然代码需要一些调整(至少在我的情况下),但这是迄今为止在 android 上制作“滑动解锁”壮举的最简单方法。干得好!
    • @Jignesh Ansodariya:您好,我在屏幕解锁/锁定方面使用了相同的概念,但我无法禁用点击时的搜索栏
    【解决方案2】:

    我从 Jignesh Ansodariya 发布的示例开始,但正如 Aerrow 指出的那样,用户可以单击 SeekBar 上的任意位置来解锁。这使得它非常不可用,因为具有滑动按钮的要点是应该忽略意外点击。我的解决方案是创建一个 SeekBar 的子类,如下所示:

    public class SlideButton extends SeekBar {
    
        private Drawable thumb;
        private SlideButtonListener listener;
    
        public SlideButton(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public void setThumb(Drawable thumb) {
            super.setThumb(thumb);
            this.thumb = thumb;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                if (thumb.getBounds().contains((int) event.getX(), (int) event.getY())) {
                    super.onTouchEvent(event);
                } else
                    return false;
            } else if (event.getAction() == MotionEvent.ACTION_UP) {
                if (getProgress() > 70)
                    handleSlide();
    
                setProgress(0);
            } else
                super.onTouchEvent(event);
    
            return true;
        }
    
        private void handleSlide() {
            listener.handleSlide();
        }
    
        public void setSlideButtonListener(SlideButtonListener listener) {
            this.listener = listener;
        }   
    }
    
    public interface SlideButtonListener {
        public void handleSlide();
    }
    

    XML:

            <package.SlideButton
                android:id="@+id/unlockButton"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:clickable="false"
                android:max="100"
                android:progressDrawable="@android:color/transparent"
                android:thumb="@drawable/button_lock" >
            </package.SlideButton>
    

    最后是我的 Activity 中的代码:

        ((SlideButton) findViewById(R.id.unlockButton)).setSlideButtonListener(new SlideButtonListener() {  
            @Override
            public void handleSlide() {
                unlockScreen();
            }
        });
    

    【讨论】:

    • 添加自定义矩形按钮会产生问题.. 在起点,矩形按钮的左半部分向左看不到,拖到右半部分后向右看不到.此外,在拖动搜索栏时,圆形效果是可见的。
    • 良好而干净的答案。请注意,Android Studio 建议我宁愿扩展 androidx.appcompat.widget.AppCompatSeekBar,它已经有一个可访问的 thumb 可绘制(不需要有额外的属性),这使得 SlideButton 更加干净。
    【解决方案3】:

    有一些很好的库可以为您解决问题。 如果使用库来执行这对您来说不是问题,那么请考虑尝试这个:

    https://github.com/cortinico/slidetoact

    编码愉快..!! :)

    【讨论】:

      【解决方案4】:

      Android 提供了类似于滑动解锁Switch 小部件。但是,您必须对其进行一些自定义,例如禁用点击更改。

      【讨论】:

        【解决方案5】:

        您可以使用此库快速轻松地自定义您的解锁。

        https://github.com/cheekiat/SlideToUnlock

        在 xml 上使用此代码

         <cheekiat.slideview.SlideView
                android:id="@+id/slide_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:slideBackground="@drawable/orangesquarebutton"
                app:slideSrc="@drawable/slide_image"
                app:slideText="Slide to unlock"
                app:slideTextColor="#ffffff"
                app:slideTextSize="10dp" />
        

        Slide to unlock screenshort

        【讨论】:

        • github 链接是否指向您的项目?如果是这样,请在答案中清楚地说明您的隶属关系;否则,它可能被视为垃圾邮件。详情请参阅this help center page。谢谢。
        【解决方案6】:

        由于某种原因,我无法禁用触摸操作,因此仍有可能意外点击
        我想出了这个解决方案,它基本上不允许每个更改事件将进度更改超过 10 个值

        int unlockLastSeekVal = 0;
        // there are skips btwn changes, use value greater than 1
        // 10 is great for seekbars with 100 values
        int unlockSeekSensitivity = 10;
        // final stage to "unlock"; 0.9 => 90%
        Double unlockFinalStage = 0.9;
        //...........
        unlock.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser){
                    if (Math.abs(unlockLastSeekVal - progress) > unlockSeekSensitivity){
                        // too much delta, revert to last value
                        seekBar.setProgress(unlockLastSeekVal);
                    }else{
                        unlockLastSeekVal = progress;
                    }
                }
            }
        
            public void onStartTrackingTouch(SeekBar seekBar) {
            }
        
            public void onStopTrackingTouch(SeekBar seekBar) {
                if (seekBar.getProgress() > seekBar.getMax() * unlockFinalStage){
                    DoYourThing();
                }
                unlockLastSeekVal = 0;
                seekBar.setProgress(0);         
            }
        });
        

        【讨论】:

          【解决方案7】:

          从 Oskar 的回答开始(感谢您的贡献),我创建了一个简单的示例项目来管理 Slide Button(水平和垂直): https://github.com/rcaboni/AndroidSlideButton

          屏幕截图:https://raw.githubusercontent.com/rcaboni/AndroidSlideButton/master/screenshot.jpg

          这是主要方法:

          public boolean onTouchEvent(MotionEvent event) {
              if (!isEnabled()) {
                  return false;
              }
              if (orientation == ORIENTATION_HORIZONTAL) {
                  if (event.getAction() == MotionEvent.ACTION_DOWN) {
                      int x= (int) event.getX();
                      int y= (int) event.getY();
                      if (thumb.getBounds().contains((int) event.getX(), (int) event.getY())) {
                          super.onTouchEvent(event);
                      } else
                          return false;
                  } else if (event.getAction() == MotionEvent.ACTION_UP) {
                      if (getProgress() > 70)
                          handleSlide();
          
                      setProgress(0);
                  } else
                      super.onTouchEvent(event);
              }else{
                  int i=0;
                  switch (event.getAction()) {
                      case MotionEvent.ACTION_DOWN:
                          if (event.getAction() == MotionEvent.ACTION_DOWN) {
                              int x= (int) event.getX();
                              int y= (int) event.getY();
                              if (!thumb.getBounds().contains((int) event.getY(), (int) event.getX())) {
                                  return false;
                              }
                          }
                      case MotionEvent.ACTION_MOVE:
                          i=getMax() - (int) (getMax() * event.getY() / getHeight());
                          setProgress(100 - i);
                          onSizeChanged(getWidth(), getHeight(), 0, 0);
                          break;
                      case MotionEvent.ACTION_UP:
                          i=getMax() - (int) (getMax() * event.getY() / getHeight());
                          if (i < 30) {
                              handleSlide();
                          }
                          setProgress(0);
                          onSizeChanged(getWidth(), getHeight(), 0, 0);
                          break;
          
                      case MotionEvent.ACTION_CANCEL:
                          break;
                  }
              }
              return true;
          }
          

          垂直按钮的 XML :

          <RelativeLayout
              android:layout_width="75dp"
              android:layout_height="130dp"
              android:background="@drawable/slide_background_green"
              android:id="@+id/lSlideButtonV"
              android:layout_below="@+id/lSlideButton"
              android:layout_marginTop="50dp">
              <TextView
                  android:layout_width="20dp"
                  android:layout_height="match_parent"
                  android:text="SOS"
                  android:id="@+id/tvSlideActionV"
                  android:gravity="center|bottom"
                  android:layout_alignParentRight="false"
                  android:layout_alignParentEnd="false"
                  android:layout_alignParentLeft="false"
                  android:layout_alignParentStart="false"
                  android:textSize="20dp"
                  android:textColor="@android:color/white"
                  android:layout_alignParentTop="false"
                  android:layout_centerHorizontal="true"
                  android:layout_alignParentBottom="false"
                  android:layout_marginBottom="15dp" />
              <it.aldea.android.widget.SlideButton
                  android:id="@+id/unlockButtonV"
                  android:layout_width="match_parent"
                  android:layout_height="150dp"
                  android:clickable="false"
                  android:max="100"
                  slideButton:orientation="vertical"
                  android:progressDrawable="@android:color/transparent"
                  android:thumb="@drawable/slide_track_red"
                  android:indeterminate="false"
                  android:layout_marginRight="5dp"
                  android:layout_marginTop="20dp"
                  android:layout_centerInParent="true"
                  android:layout_marginBottom="10dp"
                  android:thumbOffset="-2dp">
              </it.aldea.android.widget.SlideButton>
          
          </RelativeLayout>
          

          它不是一个真正完整的小部件,因为它由两个视图(TextView 和 SlideButton)组成一个布局,但它是一个易于配置的带有文本的 Slide Button 解决方案。 我希望这对某人有用。

          【讨论】:

            【解决方案8】:

            可能已经很晚了,但我为此创建了一个小型库。它允许您从 xml 滑动和自定义按钮的行为。 Slide Button

            基本上,它涉及覆盖 onTouch() 事件并根据接收到的坐标进行更改。之后根据需要设置背景并自定义文本是一件简单的事情。

            【讨论】:

              【解决方案9】:

              你可以重建正常的 SeekBar 来做你想做的事:

              与:

              • 起点 (20)
              • 交叉线 (90)
              • 并自动重置。

                seekBar.setOnSeekBarChangeListener( 新的 SeekBar.OnSeekBarChangeListener() { 整数点 = 0; 整数起点 = 0; 布尔开始 = true;

                    @Override
                    public void onProgressChanged(SeekBar seekBar, int i, boolean wasUserInput) {
                        point = i;
                
                        if (started && i > 0 && wasUserInput) {
                            startPoint = new Integer(i);
                            started = false;
                        }
                    }
                
                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {
                    }
                
                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {
                        if (point > 90 && startPoint < 20) { // slided to the right correctly.
                            // TODO::
                
                        } else { // reset.
                            resetSeekBar(seekBar, point);
                        }
                
                        startPoint = 0;
                        started = true;
                    }
                });
                

              还有:

              /**
               * Resetting the seekbar, on point at a time.
               * @param seekBar                           Reference to the seekbar made smaller.
               * @param oldPoint                          The point where the dot is atm.
               */
              private void resetSeekBar(final SeekBar seekBar, final int oldPoint) {
                  if (oldPoint > 0) {
                      final int newPoint = oldPoint -1;
                      seekBar.setProgress(newPoint);
              
                      timer.schedule(new TimerTask() {
                          final SeekBar seekBar = seekBarBid;
                          @Override
                          public void run() {
                              resetSeekBar(seekBar, newPoint);
                          }
                      }, 3);
              
                  } else {
                      seekBar.setProgress(oldPoint);
                  }
              }
              

              【讨论】:

                【解决方案10】:

                我希望下面的 Ans 是工作,

                    public class UnlockSliderView extends FrameLayout {
                    @BindView(R2.id.tv_unlock_slider)
                    TextView tvUnlockSlider;
                
                    @BindView(R2.id.iv_circle_slide)
                    ImageView ivCircleSlide;
                
                    private View parentCircle;
                
                    private float xOrigin = 0;
                    private float xOriginCircle = 0;
                    private boolean circleTouched = false;
                
                    public UnlockSliderView(Context context) {
                        super(context);
                        init();
                    }
                
                    public UnlockSliderView(Context context, AttributeSet attrs) {
                        super(context, attrs);
                        init();
                    }
                
                    public UnlockSliderView(Context context, AttributeSet attr, int defStyleAttr) {
                        super(context, attrs, defStyleAttr);
                        init();
                    }
                
                    private void init() {
                        inflate(getContext(), R.layout.layout_unlock_slider_view, this);
                        ButterKnife.bind(this);
                
                        parentCircle = (View) ivCircleSlide.getParent();
                    }
                
                    @Override
                    public boolean onTouchEvent(MotionEvent event) {
                        if (event.getAction() == MotionEvent.ACTION_DOWN) {
                            eventActionDown(event);
                        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                            eventActionMove(event);
                        } else if (event.getAction() == MotionEvent.ACTION_UP) {
                            unlockFinish();
                        }
                        return true;
                    }
                
                    private void eventActionDown(MotionEvent event) {
                        if ((event.getX() >= ivCircleSlide.getX() && event.getX() <= ivCircleSlide.getX() + ivCircleSlide.getWidth())
                                && (event.getY() >= ivCircleSlide.getY() && event.getY() <= ivCircleSlide.getY() + ivCircleSlide.getHeight())) {
                            xOrigin = event.getX();
                            xOriginCircle = ivCircleSlide.getX();
                            circleTouched = true;
                        } else {
                            circleTouched = false;
                        }
                    }
                
                    private void eventActionMove(MotionEvent event) {
                        if (circleTouched) {
                            float newXCircle = xOriginCircle + (event.getX() - xOrigin);
                            newXCircle = (newXCircle < xOriginCircle) ? xOriginCircle : newXCircle;
                            newXCircle = (newXCircle > parentCircle.getWidth() - ivCircleSlide.getWidth() - xOriginCircle) ? parentCircle.getWidth() - ivCircleSlide.getWidth() - xOriginCircle : newXCircle;
                            float alpha = 1 - ((newXCircle - xOriginCircle) / (parentCircle.getWidth() - ivCircleSlide.getWidth() - (xOriginCircle * 2)));
                            tvUnlockSlider.setAlpha(alpha);
                            ivCircleSlide.setX(newXCircle);
                            if (newXCircle == parentCircle.getWidth() - ivCircleSlide.getWidth() - xOriginCircle) {
                                unlockFinish();
                                if (mListener != null) mListener.onUnlock();
                            }
                        }
                    }
                
                    private void unlockFinish() {
                        if (circleTouched) {
                            ivCircleSlide.animate().x(xOriginCircle).setDuration(400).start();
                            tvUnlockSlider.animate().alpha(1).setDuration(400).start();
                            circleTouched = false;
                        }
                    }
                
                
                
                
                   and the xml is,
                
                  <?xml version="1.0" encoding="utf-8"?>
                   <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    xmlns:tools="http://schemas.android.com/tools"
                    android:layout_width="fill_parent"
                    android:layout_height="@dimen/unlock_slider_height"
                    android:layout_margin="@dimen/default_padding"
                    android:background="@drawable/btn_slider_back"
                    android:gravity="center"
                    android:orientation="vertical"
                    android:padding="4dp">
                
                    <TextView
                        android:id="@+id/tv_unlock_slider"
                        style="@style/prelogin_slider"
                        android:layout_width="fill_parent"
                        android:layout_height="fill_parent"
                        android:layout_marginLeft="@dimen/unlock_slider_handle_size"
                        android:layout_marginStart="@dimen/unlock_slider_handle_size"
                        android:text="@string/logon_to_mobile_banking" />
                
                    <ImageView
                
                        android:id="@+id/iv_circle_slide"
                        android:layout_width="@dimen/unlock_slider_handle_size"
                        android:layout_height="@dimen/unlock_slider_handle_size"
                        android:scaleType="fitCenter"
                        android:src="@drawable/btn_slider_handle"
                        tools:ignore="ContentDescription" />
                
                </FrameLayout>
                

                【讨论】:

                  【解决方案11】:

                  感谢@Oskar Lundgren 的回答,我已经更新了一些内容,如果有人希望在 kotlin 中做同样的事情。在这里

                  SlideToConfirm

                  class SlideToConfirm : SeekBar, SeekBar.OnSeekBarChangeListener {
                  
                      private lateinit var listener: SlideButtonListener
                  
                      // To prevent the thumb to going out of track
                      private val maxProgress = 91
                      private val minProgress = 9
                  
                      constructor(context: Context) : super(context) {
                          init()
                      }
                  
                      constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
                          init()
                      }
                  
                      constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
                          init()
                      }
                  
                      fun init() {
                          setDrawables()
                          setProperties()
                          setOnSeekBarChangeListener(this)
                      }
                  
                      private fun setDrawables() {
                          thumb = ContextCompat.getDrawable(context, R.drawable.slider_thumb)
                          progressDrawable = ContextCompat.getDrawable(context, R.drawable.slider_progress_drawable)
                      }
                  
                      private fun setProperties() {
                          isClickable = false
                          splitTrack = false
                          setPadding(0, 0, 0, 0)
                          progress = minProgress
                      }
                  
                      override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                          if (progress < minProgress) {
                              this.progress = minProgress
                          }
                          if (progress > maxProgress) {
                              this.progress = maxProgress
                          }
                      }
                  
                      override fun onStartTrackingTouch(seekBar: SeekBar?) {}
                  
                      override fun onStopTrackingTouch(seekBar: SeekBar?) {}
                  
                      @SuppressLint("ClickableViewAccessibility")
                      override fun onTouchEvent(event: MotionEvent?): Boolean {
                          if (event!!.action == MotionEvent.ACTION_DOWN) {
                              if (thumb.bounds.contains(event.x.toInt(), event.y.toInt())) {
                                  super.onTouchEvent(event)
                              } else
                                  return false
                          } else if (event.action == MotionEvent.ACTION_UP) {
                              if (progress > 70) {
                                  handleSlide()
                                  progress = maxProgress
                              } else {
                                  progress = minProgress
                              }
                          } else {
                              super.onTouchEvent(event)
                          }
                          return true
                      }
                  
                      fun setOnSlideListener(listener: SlideButtonListener) {
                          this.listener = listener
                      }
                  
                      private fun handleSlide() {
                          listener.handleSlide()
                      }
                  
                      interface SlideButtonListener {
                          fun handleSlide()
                      }
                  }
                  

                  拇指

                  <?xml version="1.0" encoding="utf-8"?>
                  <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
                      <item>
                          <shape android:shape="oval">
                              <solid android:color="@color/cyan" />
                              <corners android:radius="8dp" />
                              <size
                                  android:width="50dp"
                                  android:height="50dp" />
                          </shape>
                      </item>
                      <item android:drawable="@drawable/ic_arrow_forward_white" />
                  </layer-list>
                  

                  进度跟踪

                  <?xml version="1.0" encoding="utf-8"?>
                  <shape xmlns:android="http://schemas.android.com/apk/res/android"
                      android:shape="rectangle">
                      <corners android:radius="35dp" />
                      <size
                          android:width="100dp"
                          android:height="50dp" />
                      <stroke
                          android:width="1dp"
                          android:color="@color/errorRed" />
                      <solid android:color="@android:color/white" />
                  </shape>
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-09-18
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-04-08
                    • 2021-04-11
                    相关资源
                    最近更新 更多