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

前端 未结 5 1900
逝去的感伤
逝去的感伤 2020-12-11 02:07

As in whatsapp I need a recoding button and a slide to cancel and fade animation , I have searched for similar code but didn\'t got one. I am new to android programming any

相关标签:
5条回答
  • 2020-12-11 02:41

    I used the code provided by @3llomi to create a class that only produces the expanding button without the cancel on slide animation. As I wanted to place my buttons inside a recyclerview the cancel animation would just have overcrowded things. My code comes with a callback that delivers an audiofile stored in cache when recording is completed these are the necessary clases and xmls:

    public class RecordButton extends AppCompatImageView implements View.OnTouchListener{
    
        private ScaleAnim scaleAnim;
        private boolean listenForRecord = true;
        private int commId;
        private MediaRecorder recorder = null;
        private boolean isRecording;
    
        private static SoundPool soundPool = null;
        private static int soundStart,soundEnd;
    
    
        public RecordButton(Context context, int id) {
            super(context);
            init(context, null);
            commId=id;
        }
    
        public RecordButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context, attrs);
        }
    
        public RecordButton(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context, attrs);
        }
    
    
        private void init(Context context, AttributeSet attrs) {
            if (attrs != null) {
                TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecordButton);
    
                int imageResource = typedArray.getResourceId(R.styleable.RecordButton_mic_icon, -1);
    
                if (imageResource != -1) {
                    setTheImageResource(imageResource);
                }
    
                typedArray.recycle();
            }
    
            if (soundPool==null){
                soundPool = new SoundPool.Builder()
                        .setMaxStreams(5)
                        .build();
                //these are just two wav files with short intro and exit sounds
                soundStart=soundPool.load(getContext(),R.raw.start_recording_sound,0);
                soundEnd=soundPool.load(getContext(),R.raw.end_recording_sound,0);
            }
    
            scaleAnim = new ScaleAnim(this);
            this.setOnTouchListener(this);
        }
    
        private void setTheImageResource(int imageResource) {
            Drawable image = AppCompatResources.getDrawable(getContext(), imageResource);
            setImageDrawable(image);
        }
    
        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            setClip(this);
        }
    
        public void setClip(View v) {
            if (v.getParent() == null) {
                return;
            }
    
            if (v instanceof ViewGroup) {
                ((ViewGroup) v).setClipChildren(false);
                ((ViewGroup) v).setClipToPadding(false);
            }
    
            if (v.getParent() instanceof View) {
                setClip((View) v.getParent());
            }
        }
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (isListenForRecord()) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        ((RecordButton) v).startScale();
                        soundPool.play(soundStart,1,1,0,0,1);
                        startRecording();
                        break;
    
                    case MotionEvent.ACTION_UP:
                        stopRecording();
                        soundPool.play(soundEnd,1,1,0,0,1);
                        ((RecordButton) v).stopScale();
                        break;
                }
    
            }
            return isListenForRecord();
        }
    
        File file;
        //audio recording tools
        private void startRecording() {
            if (isRecording){
                stopRecording();
            }
            String fileName=getContext().getExternalCacheDir().getAbsolutePath()+"/"+commId+"_"+new Date().getTime()+".amr";
            file=new File(fileName);
            recorder = new MediaRecorder();
            recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_WB);
            recorder.setOutputFile(fileName);
            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_WB);
            recorder.setAudioSamplingRate(8000);
            recorder.setAudioChannels(1);
            recorder.setAudioEncodingBitRate(12000);
            try {
                recorder.prepare();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Toast.makeText(getContext(),fileName,Toast.LENGTH_LONG).show();
            recorder.start();
    
            setIsRecording(true);
        }
    
        private void stopRecording() {
            if (isRecording) {
                recorder.stop();
                recorder.release();
                setIsRecording(false);
                recordingFinishedListener.onRecordingFinished(file);
                recorder = null;
            }
        }
    
        private void setIsRecording(boolean isRecording){
            this.isRecording=isRecording;
        }
    
    
        protected void startScale() {
            scaleAnim.start();
        }
    
        protected void stopScale() {
            scaleAnim.stop();
        }
    
        public void setListenForRecord(boolean listenForRecord) {
            this.listenForRecord = listenForRecord;
        }
    
        public boolean isListenForRecord() {
            return listenForRecord;
        }
    
        //callback for when a recording has been made
        public interface RecordingFinishedListener{
            void onRecordingFinished(File file);
        }
    
        RecordingFinishedListener recordingFinishedListener;
    
        public void setRecordingFinishedListener(RecordingFinishedListener recordingFinishedListener) {
            this.recordingFinishedListener = recordingFinishedListener;
        }
    }
    

    then the class responsible for animations which allows you to define the degree of the expansion

    public class ScaleAnim {
        private View view;
        public ScaleAnim(View view) {
            this.view = view;
        }
    
        void start() {
            AnimatorSet set = new AnimatorSet();
            ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.3f);
            ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.3f);
            set.setDuration(150);
            set.setInterpolator(new AccelerateDecelerateInterpolator());
            set.playTogether(scaleY, scaleX);
            set.start();
        }
    
        void stop() {
            AnimatorSet set = new AnimatorSet();
            ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f);
            //        scaleY.setDuration(250);
            //        scaleY.setInterpolator(new DecelerateInterpolator());
    
            ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f);
            //        scaleX.setDuration(250);
            //        scaleX.setInterpolator(new DecelerateInterpolator());
    
            set.setDuration(150);
            set.setInterpolator(new AccelerateDecelerateInterpolator());
            set.playTogether(scaleY, scaleX);
            set.start();
        }
    }
    

    and the attrs.xml file. The rest are just a mic icon and some some sounds that you can find by yourself

    <resources>
        <declare-styleable name="RecordButton">
            <attr name="mic_icon" format="reference" />
        </declare-styleable>
    </resources>
    

    by setting the RecordingFinishedListener on the recordbutton you can go on to process the recorded sound.

    0 讨论(0)
  • 2020-12-11 02:44

    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.

    0 讨论(0)
  • 2020-12-11 02:45

    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");
          }
      
    0 讨论(0)
  • 2020-12-11 02:48

    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;
                }
            });
    
    0 讨论(0)
  • 2020-12-11 02:53

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

    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;
            }
        });
    
    0 讨论(0)
提交回复
热议问题