一、隨着手勢定製震動
震動是很多應用中常見的交互方式,比如在遊戲中提示用戶任務完成、在鬧鐘應用中提醒用戶該起床了等等。不同的震動模式可以傳遞不同的信息,但是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
微信掃一掃
支付寶掃一掃