Android HapticFeedback(震動反饋)介紹
本篇文章已授權微信公眾號 hongyangAndroid (鴻洋)獨家釋出
Android中長按一個控制元件的時候,想以震動提示使用者,除了用Vibrate類來做,還可以用到(HapticFeedback)震動反饋實現。
本篇部落格,我們就一起來熟悉一下Android震動反饋,首先我們開啟手機上的振動模式開光,這裡我是以小米手機來做模擬的,位置在設定—>聲音和震動—>觸控時震動,如下圖所示:
震動強度,我選擇了較強,以讓震動更明顯。
系統觸發震動
下面從一個例子,來開始本篇部落格,對一個button註冊長按監聽:
Button click= (Button) findViewById(R.id.click);
click.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Toast.makeText(MainActivity.this,"長按點選",Toast.LENGTH_SHORT).show();
//觸發震動反饋
return true;
//return false;
}
});
當你長按此button,彈出一個toast,並且震動了,但是,返回false並不會觸發震動。
現在看原始碼分析一下,這是為何。
button實現setOnLongClickListener方法,在父類TextView的父類View中,
View.setOnLongClickListener原始碼:
/**
* Register a callback to be invoked when this view is clicked and held. If this view is not
* long clickable, it becomes long clickable.
*
* @param l The callback that will run
*
* @see #setLongClickable(boolean)
*/
public void setOnLongClickListener(@Nullable OnLongClickListener l) {
if (!isLongClickable()) {
setLongClickable(true);
}
getListenerInfo().mOnLongClickListener = l;
}
我們要看mOnLongClickListener是在哪裡呼叫的介面onLongClick方法,最終在View的原始碼中找到
View.performLongClick原始碼:
/**
* Call this view's OnLongClickListener, if it is defined. Invokes the context menu if the
* OnLongClickListener did not consume the event.
*
* @return True if one of the above receivers consumed the event, false otherwise.
*/
public boolean performLongClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
boolean handled = false;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLongClickListener != null) {
handled = li.mOnLongClickListener.onLongClick(View.this);
}
if (!handled) {
handled = showContextMenu();
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
return handled;
}
可以看到
第13行執行了onLongClick方法,並且將返回值給了變數handled,
在第18行,hangdled為true,執行performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);該方法最終觸發了震動反饋。
這就是為什麼,onLongClick返回true的時候,才會有震動效果。
自定義觸發震動
上節提到,在performHapticFeedback觸發震動,觀察原始碼得知,使用者可以自己通過程式碼來觸發。
如下文所示,點選也會觸發震動反饋了:
click.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
});
現在我們就去performHapticFeedback原始碼看下,都執行了什麼,
View.performHapticFeedback原始碼:
/**
* BZZZTT!!1!
*
* <p>Provide haptic feedback to the user for this view.
*
* <p>The framework will provide haptic feedback for some built in actions,
* such as long presses, but you may wish to provide feedback for your
* own widget.
*
* <p>The feedback will only be performed if
* {@link #isHapticFeedbackEnabled()} is true.
*
* @param feedbackConstant One of the constants defined in
* {@link HapticFeedbackConstants}
*/
public boolean performHapticFeedback(int feedbackConstant) {
return performHapticFeedback(feedbackConstant, 0);
}
這裡解釋三個知識點:
1.只有在isHapticFeedbackEnabled()為true的情況下,才會觸發震動。之後會解釋在為false的情況下,為何不會觸發震動。
在xml裡,可以通過android:hapticFeedbackEnabled=”false|true”來進行設定
在java程式碼裡,可以通過view.setHapticFeedbackEnabled(boolean)來設定,
不過預設是true哦。
2.HapticFeedbackConstants的常量值,我們要用到的有三個,一個是LONG_PRESS(長按),第二個是FLAG_IGNORE_VIEW_SETTING(不受view的設定影響,即不受isHapticFeedbackEnabled()的影響),第三個是FLAG_IGNORE_GLOBAL_SETTING(不受系統設定的影響,即不受是否開啟震動反饋的影響)
3.我們看到該方法最終是返回的performHapticFeedback(int feedbackConstant, int flags)這個方法,
View.performHapticFeedback(int feedbackConstant, int flags)原始碼:
/**
* 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);
}
看第15行的if語句,當flags=0時,flags & HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING為0,又isHapticFeedbackEnabled()為false,整個條件為真,所以會執行17行,直接return。這也是為什麼performHapticFeedback(int feedbackConstant)方法一定要在isHapticFeedbackEnabled()為ture的情況下才會觸發震動。
在這裡說一下,&是按位與,返回數值,&&邏輯與,返回布林值。
第19-20行,就是觸發底層震動的程式碼了,之後程式碼不做分析。
HapticFeedbackConstants常量
接下來,看下HapticFeedbackConstants三個常量,還是之前的程式碼,如下所示:
click.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS
);
}
});
在單擊後,會觸發震動,但是如果xml加上 android:hapticFeedbackEnabled=”false”這句話,單擊則沒有震動效果了。如下所示:
<Button
android:layout_width="wrap_content"
android:id="@+id/click"
android:layout_height="wrap_content"
android:hapticFeedbackEnabled="false"
android:text="make" />
如果這時,想讓其震動,可以用如下方法來做:
click.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
);
}
});
忽略view的屬性設定。
還記得本篇文章之前,說去設定裡開啟觸控時震動的開關嗎,其實,使用者不開啟,照樣可以讓其震動,只需要用如下的方法:
click.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
);
}
});
忽略系統設定,哈哈,是不是很變態的方法,不過不建議這樣做,畢竟使用者禁止了觸控反饋,我們就沒必要繼續挑戰使用者極限了。
最後,我還要說一點,就是以上的方法,不需要震動許可權,不需要震動許可權,不需要震動許可權.重要的事情說三遍。