How to create a whatsapp like recording button with slide to cancel

you can use the library that i have made RecordView

it's easy to setup and it's simulates the same behavior like WhatsApp.

Simply add the Views RecordView and RecordButton

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.devlomi.recordview.MainActivity">

<com.devlomi.record_view.RecordView
    android:id="@+id/record_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_toLeftOf="@id/record_button"
    app:slide_to_cancel_arrow="@drawable/ic_keyboard_arrow_left"
    app:slide_to_cancel_text="Slide To Cancel"
    app:slide_to_cancel_margin_right="10dp"/>

<com.devlomi.record_view.RecordButton
    android:id="@+id/record_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:background="@drawable/bg_mic"
    android:scaleType="centerInside"
    app:src="@drawable/ic_mic_white"
    />

then in your Activity

    RecordView recordView = (RecordView) findViewById(R.id.record_view);
    RecordButton recordButton = (RecordButton) 
     findViewById(R.id.record_button);

    //IMPORTANT
    recordButton.setRecordView(recordView);

lastly you can handle the Record States

  • onStart when start Recording
  • onCancel when swiping to cancel
  • onFinish when finishes record and it returns the recorded time in millis
  • onLessThanSecond when the record time <= 1Second

    recordView.setOnRecordListener(this);
    
    
        @Override
        public void onStart() {
            //Start Recording..
            Log.d("RecordView", "onStart");
        }
    
        @Override
        public void onCancel() {
            //On Swipe To Cancel
            Log.d("RecordView", "onCancel");
    
        }
    
        @Override
        public void onFinish(long recordTime) {
            //Stop Recording..
            String time = getHumanTimeText(recordTime);
            Log.d("RecordView", "onFinish");
    
            Log.d("RecordTime", time);
        }
    
        @Override
        public void onLessThanSecond() {
            //When the record time is less than One Second
            Log.d("RecordView", "onLessThanSecond");
        }
    

I have created a github project.You can take a look at it https://github.com/sarathnk/Audio

audioSendButton.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) slideText
                            .getLayoutParams();
                    params.leftMargin = dp(30);
                    slideText.setLayoutParams(params);
                    ViewProxy.setAlpha(slideText, 1);
                    startedDraggingX = -1;
                    // startRecording();
                    startrecord();
                    audioSendButton.getParent()
                            .requestDisallowInterceptTouchEvent(true);
                    recordPanel.setVisibility(View.VISIBLE);
                } else if (motionEvent.getAction() == MotionEvent.ACTION_UP
                        || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {
                    startedDraggingX = -1;
                    stoprecord();
                    // stopRecording(true);
                } else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
                    float x = motionEvent.getX();
                    if (x < -distCanMove) {
                        stoprecord();
                        // stopRecording(false);
                    }
                    x = x + ViewProxy.getX(audioSendButton);
                    FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) slideText
                            .getLayoutParams();
                    if (startedDraggingX != -1) {
                        float dist = (x - startedDraggingX);
                        params.leftMargin = dp(30) + (int) dist;
                        slideText.setLayoutParams(params);
                        float alpha = 1.0f + dist / distCanMove;
                        if (alpha > 1) {
                            alpha = 1;
                        } else if (alpha < 0) {
                            alpha = 0;
                        }
                        ViewProxy.setAlpha(slideText, alpha);
                    }
                    if (x <= ViewProxy.getX(slideText) + slideText.getWidth()
                            + dp(30)) {
                        if (startedDraggingX == -1) {
                            startedDraggingX = x;
                            distCanMove = (recordPanel.getMeasuredWidth()
                                    - slideText.getMeasuredWidth() - dp(48)) / 2.0f;
                            if (distCanMove <= 0) {
                                distCanMove = dp(80);
                            } else if (distCanMove > dp(80)) {
                                distCanMove = dp(80);
                            }
                        }
                    }
                    if (params.leftMargin > dp(30)) {
                        params.leftMargin = dp(30);
                        slideText.setLayoutParams(params);
                        ViewProxy.setAlpha(slideText, 1);
                        startedDraggingX = -1;
                    }
                }
                view.onTouchEvent(motionEvent);
                return true;
            }
        });

I have implemented the send button as in whatsapp application which can be either in send state or record state. You can take a look at it here on my blog post.

The usage is very simple.

<com.gunhansancar.android.animbutton.AnimButton
        android:id="@+id/animButton"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_width="50dp"
        android:layout_height="50dp"
        app:first="@drawable/ic_mic"
        app:second="@drawable/ic_send" />

You just have to set first and second drawable. And also you have to set the state by calling goToState() method.


You can put a scale animation on the button and touch gestures to detect the user's movements..

Checkout the sample here..
https://github.com/varunjohn/Audio-Recording-Animation

This sample also has delete animation and lock feature similar to whatsapp..

enter image description here

Check Sample code here

imageViewAudio.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {

            if (isDeleting) {
                return true;
            }

            if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {

                cancelOffset = (float) (imageViewAudio.getX() / 2.8);
                lockOffset = (float) (imageViewAudio.getX() / 2.5);

                if (firstX == 0) {
                    firstX = motionEvent.getRawX();
                }

                if (firstY == 0) {
                    firstY = motionEvent.getRawY();
                }

                startRecord();

            } else if (motionEvent.getAction() == MotionEvent.ACTION_UP
                    || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {

                if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                    stopRecording(RecordingBehaviour.RELEASED);
                }

            } else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {

                if (stopTrackingAction) {
                    return true;
                }

                UserBehaviour direction = UserBehaviour.NONE;

                float motionX = Math.abs(firstX - motionEvent.getRawX());
                float motionY = Math.abs(firstY - motionEvent.getRawY());

                if (motionX > directionOffset &&
                        motionX > directionOffset &&
                        lastX < firstX && lastY < firstY) {

                    if (motionX > motionY && lastX < firstX) {
                        direction = UserBehaviour.CANCELING;

                    } else if (motionY > motionX && lastY < firstY) {
                        direction = UserBehaviour.LOCKING;
                    }

                } else if (motionX > motionY && motionX > directionOffset && lastX < firstX) {
                    direction = UserBehaviour.CANCELING;
                } else if (motionY > motionX && motionY > directionOffset && lastY < firstY) {
                    direction = UserBehaviour.LOCKING;
                }

                if (direction == UserBehaviour.CANCELING) {
                    if (userBehaviour == UserBehaviour.NONE || motionEvent.getRawY() + imageViewAudio.getWidth() / 2 > firstY) {
                        userBehaviour = UserBehaviour.CANCELING;
                    }

                    if (userBehaviour == UserBehaviour.CANCELING) {
                        translateX(-(firstX - motionEvent.getRawX()));
                    }
                } else if (direction == UserBehaviour.LOCKING) {
                    if (userBehaviour == UserBehaviour.NONE || motionEvent.getRawX() + imageViewAudio.getWidth() / 2 > firstX) {
                        userBehaviour = UserBehaviour.LOCKING;
                    }

                    if (userBehaviour == UserBehaviour.LOCKING) {
                        translateY(-(firstY - motionEvent.getRawY()));
                    }
                }

                lastX = motionEvent.getRawX();
                lastY = motionEvent.getRawY();
            }
            view.onTouchEvent(motionEvent);
            return true;
        }
    });