Android 系统功能——震动相关实现

https://rainmonth.github.io/posts/A180501.html

Android 震动相关实现

[TOC]

Android中的震动可以通过多种方法实现,其中包括使用Vibrate类和HapticFeedback类,本文主要介绍通过这两个类来实现震动的方式。

Vibrator

震动属于系统服务,获取Vibrator示例的方式如下:

1
mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);

震动效果VibrateEffects(API>=26才可使用)

VibrateEffects类用来描述一次震动效果,常见的效果有一下两种:

  • one shot vibration,只震动一次,通过VibrationEffect的createOneShot方法创建,在API<26时,对应Vibrator下的vibrate(long milliseconds)方法;
  • wave form vibration,周期性震动,通过VibrationEffect的createWaveform方法创建,在API<26时,对应Vibrator下的vibrate(long[] pattern, int repeat)方法;

NOTE具体参数参见源码

取消震动

调用Vibrator下的cancel()即可取消震动,如果震动不取消的话,只要进程没有关闭,会一直震动

HapticFeedback

系统控件震动反馈效果

Android 好多系统应用在长按某个空间(如Button)时会有一个震动反馈,看如下的代码:

1
2
3
4
5
6
7
8
btnSystemFeedback.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Toast.makeText(HapticFeedbackActivity.this,"长按点击", Toast.LENGTH_SHORT).show();
// return false;// 返回false时,是没有长按震动反馈的
return true;// 返回true时,有震动反馈
}
});

上面的代码在onLongClick返回true的时候,有震动反馈;返回false的时候,没有震动反馈。那么查看调用onLongClick的地方,最终定位到View的performLongClickInternal(float x, float y)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private boolean performLongClickInternal(float x, float y) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);

boolean handled = false;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLongClickListener != null) {
handled = li.mOnLongClickListener.onLongClick(View.this);
}
if (!handled) {
final boolean isAnchored = !Float.isNaN(x) && !Float.isNaN(y);
handled = isAnchored ? showContextMenu(x, y) : showContextMenu();
}
if ((mViewFlags & TOOLTIP) == TOOLTIP) {
if (!handled) {
handled = showLongClickTooltip((int) x, (int) y);
}
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
return handled;
}

上面的代码中performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)就是触发震动反馈的,它的执行条件是handled为true,所以冬onLongClick返回为true时就会触发震动反馈,那么可不可以手动触发震动反馈呢?往下看。

手动触发震动反馈效果

从上面的分析可以看出,我们可以手动调用performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)方法来触发震动反馈效果,看下面代码:

1
2
3
4
5
6
btnUserFeedback.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
});

添加完上面代码后,也可触发震动效果。看看performHapticFeedback(int feedbackConstants)的源码,最终调用同名重载函数performHapticFeedback(int feedbackConstants, int flags),其实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* BZZZTT!!1!
*
* <p>Like {@link #performHapticFeedback(int)}, with additional options.
*
* @param feedbackConstant One of the constants defined in
* {@link HapticFeedbackConstants}
* @param flags Additional flags as per {@link HapticFeedbackConstants}.
*/
public boolean performHapticFeedback(int feedbackConstant, int flags) {
if (mAttachInfo == null) {
return false;
}
//noinspection SimplifiableIfStatement
if ((flags & HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING) == 0
&& !isHapticFeedbackEnabled()) {
return false;
}
return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant,
(flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0);
}

从上面可以看到,在不考虑flags参数的情况下,如果要想有震动反馈效果,isHapticFeedbackEnabled()必须返回true,但考虑flags时,在其返回为false的情况下,如果flags的值设置为HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING同样可以出发震动效果。还可以通过设置flags为`HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING即使用户关闭了系统的震动反馈,同样会有效果,不过不建议这样使用。

看代码,这样设置,即使关闭了Button的震动反馈效果(设置android:hapticFeedbackEnabled="false"),同样会有震动:

1
2
3
4
5
6
7
8
9
 Button btnUserFeedback = findViewById(R.id.btn_user_feedback);
btnUserFeedback.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);

v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
});

这样设置,即使用户关闭了系统的震动反馈效果,同样会有震动:

1
2
3
4
5
6
7
8
9
Button btnUserFeedback = findViewById(R.id.btn_user_feedback);
btnUserFeedback.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);

v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
});

震动反馈常量

震动反馈常量在HapticFeedbackConstants类中定义,比较重要的是如下几个:

  • LONG_PRESS,长按
  • FLAG_IGNORE_VIEW_SETTING,忽略View的限制
  • FLAG_IGNORE_GLOBAL_SETTING,忽略系统的限制

本文完,本文源码请点这