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