简体   繁体   English

如何在android中滑动解锁按钮

[英]How to make slide to unlock button in android

Hi I want a button that should work as 'slide to unlock' button of IOS嗨,我想要一个可以用作 IOS 的“滑动解锁”按钮的按钮

in short I want a button that has no click effect but can slide left to right while drag and on drag completion it should considered click.简而言之,我想要一个没有点击效果但可以在拖动时从左向右滑动的按钮,并且在拖动完成时应该考虑点击。

please suggest me any sample code if possible.如果可能,请给我建议任何示例代码。

Thanks!谢谢!

First of all I'd like to thank @matthias for his answer.首先,我要感谢@matthias 的回答。 I have used the following seek bar with some customization:我使用了以下搜索栏并进行了一些自定义:

 <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" />

and in java code并在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));
            }

        }
    });

I started out with the example that Jignesh Ansodariya posted, but as Aerrow points out, the user can click anywhere on the SeekBar to unlock.我从 Jignesh Ansodariya 发布的示例开始,但正如 Aerrow 指出的那样,用户可以单击 SeekBar 上的任意位置来解锁。 That makes it quite unusable, since the point with having a slide button is that accidental clicks should be ignored.这使得它非常无法使用,因为拥有滑动按钮的重点是应该忽略意外点击。 My solution was to create a subclass of SeekBar, like this:我的解决方案是创建 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: 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>

And finally the code inside my Activity:最后是我的 Activity 中的代码:

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

There are some good libraries to do the trick for you.有一些很好的库可以为您解决问题。 If using a library to perform this is not an issue for you, then consider trying this one:如果使用库来执行此操作对您来说不是问题,请考虑尝试以下方法:

https://github.com/cortinico/slidetoact https://github.com/cortinico/slidetoact

在此处输入图片说明

Happy coding..!!快乐编码.. !! :) :)

Android provides the Switch widget that is similar to slide to unlock . Android 提供了类似于滑动解锁Switch小部件。 However, you will have to customize it a little, eg disable change on click.但是,您必须对其进行一些自定义,例如禁用单击更改。

You can use this library to quickly and easy customize your unlock.您可以使用此库快速轻松地自定义您的解锁。

https://github.com/cheekiat/SlideToUnlock https://github.com/cheekiat/SlideToUnlock

Use this code on xml在 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滑动解锁屏幕短片

For some reason, I couldn't disable the touch action so accidental taps are still possible出于某种原因,我无法禁用触摸操作,因此仍然可能会意外点击
I came up with this solution, it basically won't allow changing the progress by more than 10 values per change event我想出了这个解决方案,它基本上不允许每个更改事件更改超过 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);         
    }
});

Starting from Oskar's answer (thanks for your contribute) i create a simple example project to manage Slide Button (horizontal and vertical) : https://github.com/rcaboni/AndroidSlideButton从奥斯卡的回答开始(感谢您的贡献),我创建了一个简单的示例项目来管理滑动按钮(水平和垂直): https : //github.com/rcaboni/AndroidSlideButton

For a screen shot : https://raw.githubusercontent.com/rcaboni/AndroidSlideButton/master/screenshot.jpg对于屏幕截图: https : //raw.githubusercontent.com/rcaboni/AndroidSlideButton/master/screenshot.jpg

This is the main method :这是主要方法:

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 for vertical button :垂直按钮的 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>

It's not a real complete widget because is composed from two view (TextView and SlideButton) into a Layout, but it's a easy configurable solution for Slide Button with text inside.它不是一个真正完整的小部件,因为它是由两个视图(TextView 和 SlideButton)组成的一个布局,但它是一个简单的可配置解决方案,用于内部带有文本的滑动按钮。 I hope this is useful for someone.我希望这对某人有用。

It maybe very late but I have created a small library for this very purpose.可能很晚了,但我为此创建了一个小型图书馆。 It allows you to swipe and customise the behaviour of your button from xml.它允许您从 xml 滑动和自定义按钮的行为。 Slide Button滑动按钮

Basically it involves overriding of the onTouch() event and making changes according to the coordinates that are received.基本上它涉及覆盖 onTouch() 事件并根据接收到的坐标进行更改。 Its a simple thing after that to set the background as you want and customise the text.之后,根据需要设置背景并自定义文本是一件简单的事情。

You can rebuild the normal SeekBar to do what you want:您可以重建正常的 SeekBar 来执行您想要的操作:

With:和:

  • Starting point (20)起点 (20)
  • Crossing line (90)交叉线 (90)
  • And auto reset.并自动复位。

    seekBar.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() { Integer point = 0; Integer startPoint = 0; boolean started = true; seekBar.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() { Integer point = 0; Integer startPoint = 0; boolean started = 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; } });

And:和:

/**
 * 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);
    }
}

I Hope below Ans is work,我希望下面的答案是工作,

    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>

Thanks, @Oskar Lundgren for the answer I have updated some of the things and if anybody is looking to do the same in kotlin.谢谢,@Oskar Lundgren 的回答我已经更新了一些内容,如果有人希望在 kotlin 中做同样的事情。 Here it is这里是

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()
    }
}

Thumb拇指

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

Progress Track进度跟踪

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM