文章摘要:
1、ListView设定的长按监听,在长按时最终通过Vibrate.vibrate来实现震动。如果不想要震动效果,在长按Listener中返回false,即可。
2、View中提供了调用触摸反馈震动的接口,用户可以很方便的为View增加改功能。
3、用户可以在设置-声音和震动(通知)中关闭触摸震动,这样子就不会存在震动效果了。触摸振动接口来自:System.HAPTIC_FEEDBACK_ENABLED。
一、在app开发中,我们可以很方便的开发一个list列表应用程序。如下:
代码如下:MainActivity.java
public class MainActivity extends ListActivity {
public static final String[] TITLES =
{
"Henry IV (1)",
"Henry V",
"Henry VIII",
"Richard II",
"Richard III",
"Merchant of Venice",
"Othello",
"King Lear"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, TITLES));
getListView().setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view,
int position, long id) {
if(position%2==0){
return true;
}
return false;
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
二、通过上面的程序源代码,我们烧录到手机中,可以发现postion = 0、2、4等list条目在长按时,发生了振动,1、3、5没有,难道与OnItemLongClickListener的返回boolean状态有关?
下面我们就来追寻这个问题的答案。
- 1、首先,设定Listener:
frameworks/base/core/java/android/widget/AdapterView.java
/**
* Register a callback to be invoked when an item in this AdapterView has
* been clicked and held
*
* @param listener The callback that will run
*/
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
if (!isLongClickable()) {
setLongClickable(true);
}
mOnItemLongClickListener = listener;
}
- 2、其次:当我们在ListView中长按时:调用performLongPress处理长按事件。
frameworks/base/core/java/android/widget/AbsListView.java#onTouchEvent
@Override
public boolean onTouchEvent(MotionEvent ev) {
vtev.offsetLocation(0, mNestedYOffset);
switch (actionMasked) {
case MotionEvent.ACTION_DOWN: {
onTouchDown(ev);
break;
}
}
frameworks/base/core/java/android/widget/AbsListView.java#onTouchDown
private void onTouchDown(MotionEvent ev) {
if ((motionPosition >= 0) && getAdapter().isEnabled(motionPosition)) {
// User clicked on an actual view (and was not stopping a
// fling). It might be a click or a scroll. Assume it is a
// click until proven otherwise.
mTouchMode = TOUCH_MODE_DOWN;
// FIXME Debounce
if (mPendingCheckForTap == null) {
**mPendingCheckForTap = new CheckForTap();**
}
mPendingCheckForTap.x = ev.getX();
mPendingCheckForTap.y = ev.getY();
**postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());//100ms**
}
}
frameworks/base/core/java/android/widget/AbsListView.java#CheckForTap
private final class CheckForTap implements Runnable {
if (longClickable) {
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress, l ongPressTimeout);
} else {
mTouchMode = TOUCH_MODE_DONE_WAITING;
}
}
frameworks/base/core/java/android/widget/AbsListView.java#CheckForLongPress
boolean handled = false;
if (sameWindow() && !mDataChanged) {
handled = performLongPress(child, longPressPosition, longPressId);
}
if (handled) {
mTouchMode = TOUCH_MODE_REST;
setPressed(false);
child.setPressed(false);
} else {
mTouchMode = TOUCH_MODE_DONE_WAITING;
}
frameworks/base/core/java/android/widget/AbsListView.java#performLongPress
boolean performLongPress(final View child,
final int longPressPosition, final long longPressId) {
boolean handled = false;
if (mOnItemLongClickListener != null) {
handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, child,
longPressPosition, longPressId);
}
if (!handled) {
mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
handled = super.showContextMenuForChild(AbsListView.this);
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
}
总结:当长按时,如果返回true,则会回调用performHapticFeedback,该方法是View中的一个方法,会产生震动效果。
- 3、performHapticFeedback的处理过程
从这个方法的注释可以看出,此处是View为了方便使用,而提供的工具方法。
/**
* 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);
}
frameworks/base/core/java/android/view/View.java#performHapticFeedback
方法中会调用isHapticFeedbackEnabled来检测开关是否开启,开关位置在:设置--声音和震动(通知)--触摸震动(触摸反馈)
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);
}
frameworks/base/core/java/android/view/ViewRootImpl.java#performHapticFeedback
public boolean performHapticFeedback(int effectId, boolean always) {
try {
return mWindowSession.performHapticFeedback(mWindow, effectId, always);
} catch (RemoteException e) {
return false;
}
}
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java#performHapticFeedback
@Override
public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
if (pattern.length == 1) {
// One-shot vibration
mVibrator.vibrate(owningUid, owningPackage, pattern[0], VIBRATION_ATTRIBUTES);
} else {
// Pattern vibration
mVibrator.vibrate(owningUid, owningPackage, pattern, -1, VIBRATION_ATTRIBUTES);
}
return true;
}
总结:经过重重过程,我们看到最后触摸震动(触摸反馈)还是调用mVibrator.vibrate来实现。
三、总结:
1、ListView设定的长按监听,在长按时最终通过Vibrate.vibrate来实现震动。如果不想要震动效果,在长按Listener中返回false,即可。
2、View中提供了调用触摸反馈震动的接口,用户可以很方便的为View增加改功能。
3、用户可以在设置-声音和震动(通知)中关闭触摸震动,这样子就不会存在震动效果了。