web-dev-qa-db-ja.com

キャンセルするスライド付きの録音ボタンのようなwhatsappを作成する方法

Whatsappの場合と同様に、アニメーションをキャンセルしてフェードするには、再コーディングボタンとスライドが必要です。同様のコードを検索しましたが、見つかりませんでした。 Androidプログラミングは、ヘルプやリンクが役立つ可能性があります。

11
Ranjith

Githubプロジェクトを作成しました。ご覧ください 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;
            }
        });
17
luttu android

あなたは私が作ったライブラリを使うことができます RecordView

セットアップは簡単で、WhatsAppと同じ動作をシミュレートします。

ビューRecordViewRecordButtonを追加するだけです

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

その後、あなたの活動で

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

    //IMPORTANT
    recordButton.setRecordView(recordView);

最後に、レコード状態を処理できます

  • onStart記録開始時
  • onCancelスワイプしてキャンセルする場合
  • onFinish記録が終了すると、記録された時間がミリ秒単位で返されます
  • onLessThanSecond記録時間<= 1秒の場合

    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");
        }
    
7
3llomi

送信状態または記録状態のいずれかになり得るwhatsappアプリケーションのように送信ボタンを実装しました。ここで私の ブログ投稿 でそれを見ることができます。

使い方はとても簡単です。

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

最初と2番目のドローアブルを設定するだけです。また、goToState()メソッドを呼び出して状態を設定する必要があります。

3
Gunhan

ボタンにスケールアニメーションを配置し、ジェスチャーをタッチして、ユーザーの動きを検出できます。

こちらのサンプルをご覧ください。
https://github.com/varunjohn/Audio-Recording-Animation

このサンプルには、whatsappと同様のアニメーションの削除とロック機能もあります。

enter image description 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;
        }
    });
2
varunjohn1990

@ 3llomiによって提供されたコードを使用して、スライドアニメーションのキャンセルなしで展開ボタンのみを生成するクラスを作成しました。ボタンをrecyclerview内に配置したかったので、キャンセルアニメーションは物事を過密にしていたでしょう。私のコードには、録音が完了したときにキャッシュに保存されたオーディオファイルを配信するコールバックが付属しています。これらは必要なクラスとxmlです。

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;
    }
}

次に、拡張の程度を定義できるアニメーションを担当するクラス

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

およびattrs.xmlファイル。残りは単なるマイクアイコンとあなたが自分で見つけることができるいくつかの音です

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

録音ボタンでRecordingFinishedListenerを設定すると、録音されたサウンドの処理に進むことができます。

0