讓你的Android設備更有趣——自定義震動

一、隨着手勢定製震動

震動是很多應用中常見的交互方式,比如在遊戲中提示用戶任務完成、在鬧鐘應用中提醒用戶該起床了等等。不同的震動模式可以傳遞不同的信息,但是Android系統默認提供的震動模式有限。那麼怎麼自定義一個比較獨特的震動模式呢?這裡我們提供一個通過手勢實現震動模式定製的例子。

首先我們需要實現一個抽象基類ExtendedGestureListener,它能夠處理用戶的手勢並返回一個數字數組,該數組可以用於定義震動模式。我們可以繼承SimpleOnGestureListener並重寫onDown()、onFling()和onScroll()三個方法來實現定製。

public abstract class ExtendedGestureListener extends SimpleOnGestureListener {
    protected static final String TAG = "ExtendedGestureListener";
    private static final int SWIPE_MIN_DISTANCE = 120;
    private static final int SWIPE_THRESHOLD_VELOCITY = 200;
    private static final int SCROLL_THRESHOLD_DISTANCE = 30;

    private float downX;
    private float downY;

    @Override
    public boolean onDown(MotionEvent e) {
        downX = e.getX();
        downY = e.getY();
        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        float deltaX = e2.getX() - e1.getX();
        float deltaY = e2.getY() - e1.getY();
        float absDeltaX = Math.abs(deltaX);
        float absDeltaY = Math.abs(deltaY);

        if (absDeltaX > absDeltaY) {
            if (absDeltaX > SCROLL_THRESHOLD_DISTANCE) {
                if (deltaX > 0) {
                    // Right swipe
                    return onSwipeRight();
                } else {
                    // Left swipe
                    return onSwipeLeft();
                }
            }
        } else {
            if (absDeltaY > SCROLL_THRESHOLD_DISTANCE) {
                if (deltaY > 0) {
                    // Down swipe
                    return onSwipeDown();
                } else {
                    // Up swipe
                    return onSwipeUp();
                }
            }
        }
        return false;
    }

    protected abstract boolean onSwipeRight();
    protected abstract boolean onSwipeLeft();
    protected abstract boolean onSwipeUp();
    protected abstract boolean onSwipeDown();
}

然後我們實現一個類CustomVibrator,它能夠接收一個數字數組,並轉化為具體的震動模式。下面是它的代碼片段。

public class CustomVibrator {
    private Vibrator vibrator;

    public CustomVibrator(Context context) {
        vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
    }

    /**
     * @param vibration pattern represented by an array of integers. Each element
     *                    specifies the duration of a single vibration in milliseconds.
     *                    The array must contain an even number of elements, with the
     *                    first element specifying the duration to wait before starting
     *                    the vibration and every subsequent pair of elements specifying
     *                    the duration of a vibration and the duration to wait before
     *                    vibrating again.
     */
    public void vibrate(int[] pattern) {
        if (vibrator.hasVibrator()) {
            vibrator.vibrate(pattern, -1);
        } else {
            Log.w(TAG, "No vibrator found on device");
        }
    }
}

最後我們來看一下如何使用上面所述的兩個類。在MainActivity中我們需要實例化CustomVibrator,同時傳遞一個ExtendedGestureListener實例並實現相應的方法來處理用戶手勢輸入。下面是MainActivity的代碼片段。

public class MainActivity extends AppCompatActivity {
    private CustomVibrator vibrator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        vibrator = new CustomVibrator(this);

        final ExtendedGestureListener gestureListener = new ExtendedGestureListener() {
            private List vibrationPattern = new ArrayList();

            @Override
            protected boolean onSwipeRight() {
                vibrationPattern.add(200);
                vibrationPattern.add(200);
                vibrator.vibrate(toIntArray(vibrationPattern));
                return true;
            }

            @Override
            protected boolean onSwipeLeft() {
                vibrationPattern.add(300);
                vibrationPattern.add(200);
                vibrator.vibrate(toIntArray(vibrationPattern));
                return true;
            }

            @Override
            protected boolean onSwipeUp() {
                vibrationPattern.add(400);
                vibrationPattern.add(200);
                vibrator.vibrate(toIntArray(vibrationPattern));
                return true;
            }

            @Override
            protected boolean onSwipeDown() {
                vibrationPattern.clear();
                vibrator.vibrate(new int[]{0});
                return true;
            }

            private int[] toIntArray(List list) {
                int[] array = new int[list.size()];
                for (int i = 0; i < list.size(); i++) {
                    array[i] = list.get(i);
                }
                return array;
            }
        };

        final GestureDetector gestureDetector = new GestureDetector(this, gestureListener);
        View gestureView = findViewById(R.id.gesture_view);

        gestureView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return gestureDetector.onTouchEvent(event);
            }
        });
    }
}

二、根據音樂節奏震動

當你在聽音樂時,你是否有過這樣的體驗:如果能夠把手機的震動和音樂的節奏同步,那該多好啊!這裡我們提供一個例子來實現這一點。

首先,我們需要讀取音樂的節奏信息。這裡我們可以使用Android系統提供的MediaMetadataRetriever類來獲取一個媒體文件的元數據信息,包括它的時長、MIME類型以及其中的所有ID3標籤和章節信息。

下面是如何使用MediaMetadataRetriever類讀取音樂標籤信息的示例代碼。

MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(path);
String duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
String mimeType = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE);
byte[] art = retriever.getEmbeddedPicture();
int bitrate = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE));

然後,我們需要根據讀取到的音樂信息,計算出每一個時間片段應該持續多長時間。這裡我們可以使用Android系統提供的Visualizer類。它可以用FFT計算出實時音頻頻譜,並提供了有用的回調函數,如onWaveFormDataCapture()和onFftDataCapture()。

public class MusicVisualizerView extends View implements Visualizer.OnDataCaptureListener {
    private static final int MAX_AMPLITUDE = 32768;

    private Visualizer visualizer;
    private Paint paint;
    private byte[] waveformBytes;

    public MusicVisualizerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
    }

    public void setVisualizer(Visualizer visualizer) {
        this.visualizer = visualizer;
        this.visualizer.setDataCaptureListener(this,
                Visualizer.getMaxCaptureRate() / 2, true, false);
    }

    @Override
    public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
        waveformBytes = waveform;
        invalidate();
    }

    @Override
    public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (waveformBytes == null) {
            return;
        }

        int width = getWidth();
        int height = getHeight();

        paint.setStrokeWidth(1.5f);
        paint.setColor(Color.BLUE);

        for (int i = 0; i < waveformBytes.length - 1; i++) {
            float left = width * i / (waveformBytes.length - 1);
            float top = (0.5f + ((byte) (waveformBytes[i] + 128)) / (2 * MAX_AMPLITUDE)) * height;
            float right = width * (i + 1) / (waveformBytes.length - 1);
            float bottom = (0.5f + ((byte) (waveformBytes[i + 1] + 128)) / (2 * MAX_AMPLITUDE)) * height;
            canvas.drawColor(Color.WHITE);
            canvas.drawLine(left, top, right, bottom, paint);
        }
    }
}

最後我們將震動和的音樂節奏同步起來。為了更好地控制時間粒度,我們使用Handler來定時執行震動任務,同時音樂的節奏信息也是按照一定的時間片段來處理。代碼實現如下:

final CustomVibrator vibrator = new CustomVibrator(this);
final Visualizer visualizer = new Visualizer(0);
visualizer.setEnabled(true);
final MusicVisualizerView musicVisualizerView = findViewById(R.id.music_visualizer_view);
musicVisualizerView.setVisualizer(visualizer);

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        final int size = Visualizer.getCaptureSizeRange()[1];

        if (waveformBytes == null) {
            return;
        }

        int bytesPerSecond = (waveformBytes.length * 1000 / Integer.parseInt(duration));
        int millisecondsPerSample = size * 1000 / Integer.parseInt(duration);
        int samplesPerSecond = size / millisecondsPerSample;
        int frequencyBucketSize = (int) Math.floor((double) samplesPerSecond / 20);

        int maxFrequencyVolume = 0;
        for (int i = 0; i < frequencyBucketSize; i++) {
            int start = i * millisecondsPerSample;
            int end = start + millisecondsPerSample;
            int volume = 0;

            for (int j = start; j < end && j = 20) {
            int durationInMillis = (int) Math.ceil((double) bytesPerSecond / frequencyBucketSize) * 10;
            Log.d(TAG, "vibrate durationInMillis: " + durationInMillis);

            int[] pattern = new int[2];
            pattern[0] = 0;
            pattern[1] = durationInMillis;

            vibrator.vibrate(pattern);
        }

        handler.postDelayed(this, 10);
    }
}, 10);

三、Android系統提供的震動模式

除了自定義震動模式,Android系統還提供了一些預定義的震動模式,比如調用某個API時產生的模式、短訊、電話掛斷等事件產生的模式等等。下面是一個例子,展示如何調用這些API。

private void playSystemVibrationPattern() {
    // Vibrate for 500 milliseconds
    vibrator.vibrate(500);

    // Play a vibration pattern defined in an XML resource
    vibrator.vibrate(getResources().getIntArray(R.array.fade_in_out), -1);

    // Play a vibration pattern designed for notifications
    vibrator.vibrate(NotificationManager.IMPORTANCE_HIGH);

    // Play a vibration pattern similar to the system's low battery alert
    vibrator.vibrate(NotificationManager.IMPORTANCE_LOW);
}

以上是三種不同的音樂震動模式實現例子,開發者可以根據自己的需求來進行選擇和改進。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/200136.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-05 14:03
下一篇 2024-12-05 14:03

相關推薦

  • Python官網中文版:解決你的編程問題

    Python是一種高級編程語言,它可以用於Web開發、科學計算、人工智能等領域。Python官網中文版提供了全面的資源和教程,可以幫助你入門學習和進一步提高編程技能。 一、Pyth…

    編程 2025-04-29
  • 掌握magic-api item.import,為你的項目注入靈魂

    你是否曾經想要導入一個模塊,但卻不知道如何實現?又或者,你是否在使用magic-api時遇到了無法導入的問題?那麼,你來到了正確的地方。在本文中,我們將詳細闡述magic-api的…

    編程 2025-04-29
  • Python中自定義函數必須有return語句

    自定義函數是Python中最常見、最基本也是最重要的語句之一。在Python中,自定義函數必須有明確的返回值,即必須要有return語句。本篇文章將從以下幾個方面對此進行詳細闡述。…

    編程 2025-04-29
  • 如何解決egalaxtouch設備未找到的問題

    egalaxtouch設備未找到問題通常出現在Windows或Linux操作系統上。如果你遇到了這個問題,不要慌張,下面我們從多個方面進行詳細闡述解決方案。 一、檢查硬件連接 首先…

    編程 2025-04-29
  • Codemaid插件——讓你的代碼優美整潔

    你是否曾為了混雜在代碼里的冗餘空格、重複代碼而感到煩惱?你是否曾因為代碼缺少注釋而陷入困境?為了解決這些問題,今天我要為大家推薦一款Visual Studio擴展插件——Codem…

    編程 2025-04-28
  • Python自定義列表

    本文將為大家介紹Python中自定義列表的方法和應用場景。對自定義列表進行詳細的闡述,包括列表的基本操作、切片、列表推導式、列表的嵌套以及列表的排序,希望能夠幫助大家更好地理解和應…

    編程 2025-04-27
  • 如何添加Python自定義模塊?

    Python是一種非常流行的腳本語言,因其易學易用和功能強大而備受歡迎。自定義模塊是Python開發中經常使用的功能之一。本文將從多個方面為您介紹如何添加Python自定義模塊。 …

    編程 2025-04-27
  • NB設備上傳數據方案

    NB(Narrow Band)是一種物聯網通信技術,可以實現低功耗、寬覆蓋、多連接等特點。本文旨在探討如何使用NB設備上傳數據。在這篇文章中,我們將介紹NB設備上傳數據的基本原理、…

    編程 2025-04-27
  • Python左補0,讓你的數據更美觀

    本文將從以下幾個方面,詳細闡述Python左補0的作用及使用方法: 一、什麼是Python左補0 在Python中,數據在輸出時如果希望達到一定的美觀效果,就需要對數字進行左補0,…

    編程 2025-04-27
  • 昆明愛因森會計培訓:打造你的財務管理佳績

    本文將從以下幾個方面,詳細闡述昆明愛因森會計培訓的特點及其課程設置。 一、專業師資 昆明愛因森會計培訓擁有一支高素質的教師團隊,他們都具備很高的教學經驗與實際工作能力,且熟知國內外…

    編程 2025-04-27

發表回復

登錄後才能評論