简体   繁体   中英

How to make slide to unlock button in android

Hi I want a button that should work as 'slide to unlock' button of 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. 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

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. 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:

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>

And finally the code inside my 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

在此处输入图片说明

Happy coding..!! :)

Android provides the Switch widget that is similar to slide to unlock . 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

Use this code on 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

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

For a screen shot : 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 :

<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. 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. Slide Button

Basically it involves overriding of the onTouch() event and making changes according to the coordinates that are received. 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:

With:

  • Starting point (20)
  • Crossing line (90)
  • And auto reset.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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