- 浏览: 206548 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
xumin_minzi:
我也在使用ndk编译,ffmpeg源码放在哪个目录里面?
[原创]Android ndkr8编译 FFmpeg 0.11.1 "Happiness" -
ericchan2012:
在Dialog调用show方法之后
【原创】Android 设置Dialog的长宽和位置 -
maohualei:
求解: 我设置的时候 位置居然没有反应 dial ...
【原创】Android 设置Dialog的长宽和位置
android中触屏反馈原理
HOPE mt6516 android2.2 linux2.6.32
在用户对软按键或者某些ui操作的时候会反馈振动,达到让用户感知操作ok的效果。
在情景模式(Audio Profile)的选取之后,将会出现对特定情景模式设置的界面(Edit Profile),在这里面就可以设置是否启动振动器
和反馈功能。
情景模式对于的代码在:packages/apps/Settings/src/com/android/settings/audioprofile下,
其中文件:AudioProfileSettings.java是情景模式选择;Editprofile.java是情景模式编辑界面。
如果用户在界面上点击选中了反馈的功能,那么将会调用到函数:setHapticFeedbackEnabled()
@ frameworks/base/core/java/android/view/View.java
public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) {
setFlags(hapticFeedbackEnabled ? HAPTIC_FEEDBACK_ENABLED: 0, HAPTIC_FEEDBACK_ENABLED);
}
先来看看打电话界面的软键盘的触摸反馈功能的实现:
@packages/apps/Phone/src/com/android/phone
进入这个目录一眼就可以看到文件HapticFeedback.java,很显然,它就是实现振动反馈功能的,该文件代码比较简单:
该文件有一段较为详细的注释,说明了如何在代码中添加这个功能,使用它们的函数接口
/**
* Handles the haptic feedback: a light buzz happening when the user
* presses a soft key (UI button or capacitive key(电容虚拟按键)).
*
* The haptic feedback is controlled by:
* - a system resource for the pattern
* The pattern used is tuned per device and stored in an internal
* resource (config_virtualKeyVibePattern.)
*
* - a system setting HAPTIC_FEEDBACK_ENABLED.(这个就是情景模式中设置的反馈使能)
* HAPTIC_FEEDBACK_ENABLED can be changed by the user using the
* system Settings activity. It must be rechecked each time the
* activity comes in the foreground (onResume).
*
* //////后面的代码可以看出,必须要这两个条件同时满足才可以调用振动器的接口。
*
* This class is not thread safe. It assumes it'll be called from the
* UI thead.
*
* Typical usage(典型用法):
* --------------
* static private final boolean HAPTIC_ENABLED = true;
* private HapticFeedback mHaptic = new HapticFeedback();
*
* protected void onCreate(Bundle icicle) {
* mHaptic.init((Context)this, HAPTIC_ENABLED);
* }
*
* protected void onResume() {
* // Refresh the system setting.
* mHaptic.checkSystemSetting();
* }
*
* public void foo() {
* mHaptic.vibrate();
* }
*
*/
public class HapticFeedback {
private static final int VIBRATION_PATTERN_ID =
com.android.internal.R.array.config_virtualKeyVibePattern;
/** If no pattern was found, vibrate for a small amount of time. */
private static final long DURATION = 10; // millisec.
/** Play the haptic pattern only once. */
private static final int NO_REPEAT = -1;
...
private Context mContext;
private long[] mHapticPattern;
private Vibrator mVibrator;
private boolean mEnabled;
private Settings.System mSystemSettings;
private ContentResolver mContentResolver;
private boolean mSettingEnabled;
public void init(Context context, boolean enabled) {
mEnabled = enabled;
if (enabled) {
mVibrator = new Vibrator();
if (!loadHapticSystemPattern(context.getResources())) {
mHapticPattern = new long[] {0, DURATION, 2 * DURATION, 3 * DURATION};
// 设置一个默认的振动模式
}
mSystemSettings = new Settings.System();
mContentResolver = context.getContentResolver();
}
}
// Reload the system settings to check if the user enabled the haptic feedback.
// called in onResume()
public void checkSystemSetting() {
if (!mEnabled) {
return;
}
try {
int val = mSystemSettings.getInt(mContentResolver, System.HAPTIC_FEEDBACK_ENABLED, 0);
mSettingEnabled = val != 0; /////////////////
} catch (Resources.NotFoundException nfe) {
Log.e(TAG, "Could not retrieve system setting.", nfe);
mSettingEnabled = false;
}
}
public void vibrate() {
if (!mEnabled || !mSettingEnabled) {
return; // 看得出,只有调用init时传入enable和用户设置使能反馈后才能调用振动器的接口
}
mVibrator.vibrate(mHapticPattern, NO_REPEAT);
}
/**
* @return true If the system haptic pattern was found.
* @加载振动模式
*/
private boolean loadHapticSystemPattern(Resources r) {
int[] pattern;
mHapticPattern = null;
try {
pattern = r.getIntArray(VIBRATION_PATTERN_ID);
} catch (Resources.NotFoundException nfe) {
Log.e(TAG, "Vibrate pattern missing.", nfe);
return false;
}
if (null == pattern || pattern.length == 0) {
Log.e(TAG, "Haptic pattern is null or empty.");
return false;
}
// int[] to long[] conversion.
mHapticPattern = new long[pattern.length];
for (int i = 0; i < pattern.length; i++) {
mHapticPattern[i] = pattern[i];
}
return true;
}
}
在打电话界面有两个文件中使用到这类API:EmergencyDialer.java (packages\apps\phone\src\com\android\phone)
TwelveKeyDialer.java (packages\apps\contacts\src\com\android\contacts)
现在以拨号键盘为例:TwelveKeyDialer.java,搜索mHaptic.字符串即可找到。
mHaptic.init(this, r.getBoolean(R.bool.config_enable_dialer_key_vibration))
config_enable_dialer_key_vibration在下列文件中定义:
Config.xml (packages\apps\phone\res\values): <bool name="config_enable_dialer_key_vibration">true</bool>
public void onClick(View view) {
switch (view.getId()) {
case R.id.one: {
keyPressed(KeyEvent.KEYCODE_1);
playTone(ToneGenerator.TONE_DTMF_1);
mHaptic.vibrate();
return;
}
...
}
}
protected void onResume() {
...
// Retrieve the haptic feedback setting.
mHaptic.checkSystemSetting();
...
}
从上面的init函数调用和Config.xml可以看出,config_virtualKeyVibePattern模式并没有在phone的资源中定义,
直接使用的默认模式。
那么全局搜索了config_virtualKeyVibePattern这个字符串,发现只有在文件
Config.xml (frameworks\base\core\res\res\values)中有定义,而起还有另外4个类似的振动模式
<!-- Vibrator pattern for feedback about a long screen/key press -->
<integer-array name="config_longPressVibePattern">
<item>0</item>
<item>1</item>
<item>20</item>
<item>21</item>
</integer-array>
<!-- Vibrator pattern for feedback about touching a virtual key -->
<integer-array name="config_virtualKeyVibePattern">
<item>0</item>
<item>10</item>
<item>20</item>
<item>30</item>
</integer-array>
<!-- Vibrator pattern for a very short but reliable vibration for soft keyboard tap -->
<integer-array name="config_keyboardTapVibePattern">
<item>40</item>
</integer-array>
<!-- Vibrator pattern for feedback about booting with safe mode disabled -->
<integer-array name="config_safeModeDisabledVibePattern">
<item>0</item>
<item>1</item>
<item>20</item>
<item>21</item>
</integer-array>
<!-- Vibrator pattern for feedback about booting with safe mode disabled -->
<integer-array name="config_safeModeEnabledVibePattern">
<item>0</item>
<item>1</item>
<item>20</item>
<item>21</item>
<item>500</item>
<item>600</item>
</integer-array>
那么上面类似的5中模式在哪里有使用呢?在刚刚的搜索结果中可以看到赫然出现一个文件:
PhoneWindowManager.java (frameworks\policies\base\phone\com\android\internal\policy\impl)
这个文件中的代码实现系统所有窗口的统一管理,那么显而易见大部分窗口振动都是在这里操作的。
public void init(Context context, IWindowManager windowManager,
LocalPowerManager powerManager) {
...
mVibrator = new Vibrator();
mLongPressVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_longPressVibePattern);
mVirtualKeyVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_virtualKeyVibePattern);
mKeyboardTapVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_keyboardTapVibePattern);
mSafeModeDisabledVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_safeModeDisabledVibePattern);
mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_safeModeEnabledVibePattern);
// 取出振动模式数据,放在long []类型数组中。
}
public boolean performHapticFeedbackLw(WindowState win, int effectId,
boolean always) {
final boolean hapticsDisabled = Settings.System.getInt(mContext
.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED,
0) == 0; // 再次获取用户设置
if (!always
&& (hapticsDisabled || mKeyguardMediator
.isShowingAndNotHidden())) {
return false; //
}
long[] pattern = null;
switch (effectId) { // effectId这个值是觉得采用哪种模式的,长按,还是按了软按键产生等等。
case HapticFeedbackConstants.LONG_PRESS:
pattern = mLongPressVibePattern;
break;
case HapticFeedbackConstants.VIRTUAL_KEY:
pattern = mVirtualKeyVibePattern;
break;
case HapticFeedbackConstants.KEYBOARD_TAP:
pattern = mKeyboardTapVibePattern;
break;
case HapticFeedbackConstants.SAFE_MODE_DISABLED:
pattern = mSafeModeDisabledVibePattern;
break;
case HapticFeedbackConstants.SAFE_MODE_ENABLED:
pattern = mSafeModeEnabledVibePattern;
break;
default:
return false;
}
// 调用振动器接口开始振动
if (pattern.length == 1) {
// One-shot vibration
mVibrator.vibrate(pattern[0]);
} else {
// Pattern vibration
mVibrator.vibrate(pattern, -1);
}
return true;
}
public void keyFeedbackFromInput(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& (event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) {
// 这里有对按键类型做判断看是否是虚拟的硬按键,就对于这虚拟的软按键。
performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY,
false);
}
}
到这里,基本上大致明白了振动反馈的原理,现在要引入一个实际面临的问题。
本来Virtual Key是只的触摸屏上划出一块区域来做几个按键的这类按键,这类按键在原始系统上就是可以进行振动反馈的,
不过呢,现在我们的系统上有2个触摸按键MENU和BACK,他们虽然也是在触摸屏上,不过他们是按照按键上报,而不是按触摸
坐标上报之后生成的虚拟keyevent,所以这样的话,上层就会将他们视为物理按键,但是他们确实是触摸型的。
所以为了让这类按键触摸之后也有振动反馈,所以就需要查找按键上报的流程,然后进行修改。
获取按键的类型flags: event.getFlags(), KeyEvent.java (frameworks\base\core\java\android\view)
想要知道这个flags是如何来的,那么就需要从kernel内核之上开始顺藤摸瓜将按键上报流程缕一下:
EventHub.cpp (frameworks\base\libs\ui)
这个文件中的函数EventHub::getEvent()会从内核中获取input子系统的event事件,其中也包括key事件。
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
{
...
while(1) {
...
pollres = poll(mFDs, mFDCount, -1);
...
for(i = 1; i < mFDCount; i++) {
if(mFDs[i].revents) {
if(mFDs[i].revents & POLLIN) {
res = read(mFDs[i].fd, &iev, sizeof(iev));
if (res == sizeof(iev)) {
*outDeviceId = mDevices[i]->id;
if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
*outType = iev.type;
*outScancode = iev.code;
if (iev.type == EV_KEY) {
err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
// 从*.kl文件中进行键值映射,也得到想要flags值。kernel键值映射到android键值。
...
} else {
*outKeycode = iev.code;
}
*outValue = iev.value;
*outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
return true;
}
...
}
}
}
}
...
}
}
*.kl文件格式:
key 158 BACK
...
key 229 MENU
key 139 MENU
key 59 MENU
...
key 116 POWER WAKE
...
其中第一列是key关键字,第二列是kernel的键值,第3列是android键值,第4列是flag
其中flags有以下几种,可以从文件:KeycodeLabels.h (frameworks\base\include\ui)中查看得到:
static const KeycodeLabel FLAGS[] = {
{ "WAKE", 0x00000001 },
{ "WAKE_DROPPED", 0x00000002 },
{ "SHIFT", 0x00000004 },
{ "CAPS_LOCK", 0x00000008 },
{ "ALT", 0x00000010 },
{ "ALT_GR", 0x00000020 },
{ "MENU", 0x00000040 },
{ "LAUNCHER", 0x00000080 },
{ NULL, 0 }
};
其中也可以看到MENU和BACK在android空间中对于的键值分别是:82和4.
static const KeycodeLabel KEYCODES[] = {
...
{ "BACK", 4 },
...
{ "MENU", 82 },
...
}
上面这些值都是KeyLayoutMap.cpp (frameworks\base\libs\ui)文件中函数
status_t KeyLayoutMap::load(const char* filename)在加载*.kl文件的时候根据文件和上面两个数组设置好了的,最后只需要用map()函数来获取这些值即可。
接下来按键值将会上传至JNI层:com_android_server_KeyInputQueue.cpp (frameworks\base\services\jni)
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, jobject event)
{
gLock.lock();
sp<EventHub> hub = gHub;
if (hub == NULL) {
hub = new EventHub;
gHub = hub;
}
gLock.unlock();
int32_t deviceId;
int32_t type;
int32_t scancode, keycode;
uint32_t flags;
int32_t value;
nsecs_t when;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, &flags, &value, &when); // getEvent
env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
env->SetIntField(event, gInputOffsets.mType, (jint)type);
env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
env->SetIntField(event, gInputOffsets.mValue, value);
env->SetLongField(event, gInputOffsets.mWhen, (jlong)(nanoseconds_to_milliseconds(when)));
// 将这些值传入java空间。
return res;
}
最后在java代码中只需要调用jni接口readEvent即可。
KeyInputQueue.java (frameworks\base\services\java\com\android\server)文件中会创建一个线程来专门读取inputevent值
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
...
RawInputEvent ev = new RawInputEvent();
while (true) {
try {
InputDevice di;
// block, doesn't release the monitor
readEvent(ev);
...
}
}
...
// Is it a key event?
if (type == RawInputEvent.EV_KEY &&
(classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
(scancode < RawInputEvent.BTN_FIRST ||
scancode > RawInputEvent.BTN_LAST)) {
boolean down;
if (ev.value != 0) {
down = true;
di.mKeyDownTime = curTime;
} else {
down = false;
}
int keycode = rotateKeyCodeLocked(ev.keycode);
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mKeyDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
}
...
}
}
generateVirtualKeyDown()根据上报的触摸点坐标等信息生成虚拟硬按键事件。
KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
vk.lastKeycode, 0, vk.scancode,
KeyEvent.FLAG_VIRTUAL_HARD_KEY);
mHapticFeedbackCallback.virtualKeyFeedback(event);
addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
event);
newKeyEvent()生成一个KeyEvent实例,如下:
public static KeyEvent newKeyEvent(InputDevice device, long downTime,
long eventTime, boolean down, int keycode, int repeatCount,
int scancode, int flags) {
return new KeyEvent(
downTime, eventTime,
down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
keycode, repeatCount,
device != null ? device.mMetaKeysState : 0,
device != null ? device.id : -1, scancode,
flags | KeyEvent.FLAG_FROM_SYSTEM);
}
////***********************88 错误的修改 *******************************/
所以为了实现MENU和BACK按键能产生震动反馈,那么可以直接修改这个函数newKeyEvent(),如下:
public static KeyEvent newKeyEvent(InputDevice device, long downTime,
long eventTime, boolean down, int keycode, int repeatCount,
int scancode, int flags) {
+ int nflags = flags;
+ if(keycode == 4 || keycode == 82) // BACK and MENU
+ nflags |= KeyEvent.FLAG_VIRTUAL_HARD_KEY;
return new KeyEvent(
downTime, eventTime,
down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
keycode, repeatCount,
device != null ? device.mMetaKeysState : 0,
device != null ? device.id : -1, scancode,
+/- nflags | KeyEvent.FLAG_FROM_SYSTEM);
}
////***********************88 错误的修改 *******************************/
int keycode = rotateKeyCodeLocked(ev.keycode);
/* Begin: lizhiguo, 2011-10-20, modifiled for BACK and MENU key HapticFeedback.*/
if(1){
int nflags = ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0;
if(keycode == 4 || keycode == 82) // BACK and MENU
nflags |= KeyEvent.FLAG_VIRTUAL_HARD_KEY;
KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, down,
keycode, 0, scancode, nflags);
if(keycode == 4 || keycode == 82) // BACK and MENU
mHapticFeedbackCallback.virtualKeyFeedback(event);
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD, event);
}
else{
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mKeyDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
}
/* End: lizhiguo, 2011-10-20 */
完!
源码列表:
1 KeyInputQueue.java (frameworks\base\services\java\com\android\server)
2 RawInputEvent.java (frameworks\base\core\java\android\view)
3 KeyEvent.java (frameworks\base\core\java\android\view)
4 KeyInputQueue.java (frameworks\base\services\java\com\android\server):2
5 WindowManagerPolicy.java (frameworks\base\core\java\android\view)
6 com_android_server_KeyInputQueue.cpp (frameworks\base\services\jni)
7 EventHub.cpp (frameworks\base\libs\ui)
8 EventHub.cpp (frameworks\base\libs\ui):2
9 KeyLayoutMap.cpp (frameworks\base\libs\ui)
0 KeycodeLabels.h (frameworks\base\include\ui)
A TwelveKeyDialer.java (packages\apps\contacts\src\com\android\contacts)
B PhoneWindowManager.java (frameworks\policies\base\phone\com\android\internal\policy\impl)
C KeyEvent.java (frameworks\base\awt\java\awt\event)
D HapticFeedbackConstants.java (frameworks\base\core\java\android\view)
E PasswordUnlockScreen.java (frameworks\policies\base\phone\com\android\internal\policy\impl)
F LockPatternView.java (frameworks\base\core\java\com\android\internal\widget)
G HapticFeedback.java (packages\apps\phone\src\com\android\phone)
H Donottranslate_config.xml (packages\apps\contacts\res\values)
I EmergencyDialer.java (packages\apps\phone\src\com\android\phone)
J Config.xml (frameworks\base\core\res\res\values)
K Vibrator.java (frameworks\base\core\java\android\os)
L View.java (frameworks\base\core\java\android\view)
M Settings.java (frameworks\base\core\java\android\provider)
N ContentResolver.java (frameworks\base\core\java\android\content)
O System.java (dalvik\libcore\luni-kernel\src\main\java\java\lang)
P Editprofile.java (packages\apps\settings\src\com\android\settings\audioprofile)
Q CheckBoxPreference.java (frameworks\base\core\java\android\preference)
R AudioProfileImpl.java (frameworks\mtk\extensions\media\audio\java\com\mediatek\audioprofile)
S ProfileState.java (frameworks\mtk\extensions\media\audio\java\com\mediatek\audioprofile)
T AudioProfileSettings.java (packages\apps\settings\src\com\android\settings\audioprofile)
U Config.xml (packages\apps\phone\res\values)
V TextKeyListenerTest.java (cts\tests\tests\text\src\android\text\method\cts)
W WindowManagerService.java (frameworks\base\services\java\com\android\server)
X KeyEventTest.java (cts\tests\tests\view\src\android\view\cts)
Y KeyLayoutMap.h (frameworks\base\libs\ui)
Z User_comments_for_4_to_5.xml (frameworks\base\docs\html\sdk\api_diff\5)
missingSinces.txt (frameworks\base\docs\html\sdk\api_diff\5)
5.xml (frameworks\base\api)
MidWindowManager.java (frameworks\policies\base\mid\com\android\internal\policy\impl)
EventHub.h (frameworks\base\include\ui)
Evdev.c (kernel\drivers\input)
Input.h (kernel\include\linux)
Input.c (kernel\drivers\input)
Preference.java (frameworks\base\core\java\android\preference)
HOPE mt6516 android2.2 linux2.6.32
在用户对软按键或者某些ui操作的时候会反馈振动,达到让用户感知操作ok的效果。
在情景模式(Audio Profile)的选取之后,将会出现对特定情景模式设置的界面(Edit Profile),在这里面就可以设置是否启动振动器
和反馈功能。
情景模式对于的代码在:packages/apps/Settings/src/com/android/settings/audioprofile下,
其中文件:AudioProfileSettings.java是情景模式选择;Editprofile.java是情景模式编辑界面。
如果用户在界面上点击选中了反馈的功能,那么将会调用到函数:setHapticFeedbackEnabled()
@ frameworks/base/core/java/android/view/View.java
public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) {
setFlags(hapticFeedbackEnabled ? HAPTIC_FEEDBACK_ENABLED: 0, HAPTIC_FEEDBACK_ENABLED);
}
先来看看打电话界面的软键盘的触摸反馈功能的实现:
@packages/apps/Phone/src/com/android/phone
进入这个目录一眼就可以看到文件HapticFeedback.java,很显然,它就是实现振动反馈功能的,该文件代码比较简单:
该文件有一段较为详细的注释,说明了如何在代码中添加这个功能,使用它们的函数接口
/**
* Handles the haptic feedback: a light buzz happening when the user
* presses a soft key (UI button or capacitive key(电容虚拟按键)).
*
* The haptic feedback is controlled by:
* - a system resource for the pattern
* The pattern used is tuned per device and stored in an internal
* resource (config_virtualKeyVibePattern.)
*
* - a system setting HAPTIC_FEEDBACK_ENABLED.(这个就是情景模式中设置的反馈使能)
* HAPTIC_FEEDBACK_ENABLED can be changed by the user using the
* system Settings activity. It must be rechecked each time the
* activity comes in the foreground (onResume).
*
* //////后面的代码可以看出,必须要这两个条件同时满足才可以调用振动器的接口。
*
* This class is not thread safe. It assumes it'll be called from the
* UI thead.
*
* Typical usage(典型用法):
* --------------
* static private final boolean HAPTIC_ENABLED = true;
* private HapticFeedback mHaptic = new HapticFeedback();
*
* protected void onCreate(Bundle icicle) {
* mHaptic.init((Context)this, HAPTIC_ENABLED);
* }
*
* protected void onResume() {
* // Refresh the system setting.
* mHaptic.checkSystemSetting();
* }
*
* public void foo() {
* mHaptic.vibrate();
* }
*
*/
public class HapticFeedback {
private static final int VIBRATION_PATTERN_ID =
com.android.internal.R.array.config_virtualKeyVibePattern;
/** If no pattern was found, vibrate for a small amount of time. */
private static final long DURATION = 10; // millisec.
/** Play the haptic pattern only once. */
private static final int NO_REPEAT = -1;
...
private Context mContext;
private long[] mHapticPattern;
private Vibrator mVibrator;
private boolean mEnabled;
private Settings.System mSystemSettings;
private ContentResolver mContentResolver;
private boolean mSettingEnabled;
public void init(Context context, boolean enabled) {
mEnabled = enabled;
if (enabled) {
mVibrator = new Vibrator();
if (!loadHapticSystemPattern(context.getResources())) {
mHapticPattern = new long[] {0, DURATION, 2 * DURATION, 3 * DURATION};
// 设置一个默认的振动模式
}
mSystemSettings = new Settings.System();
mContentResolver = context.getContentResolver();
}
}
// Reload the system settings to check if the user enabled the haptic feedback.
// called in onResume()
public void checkSystemSetting() {
if (!mEnabled) {
return;
}
try {
int val = mSystemSettings.getInt(mContentResolver, System.HAPTIC_FEEDBACK_ENABLED, 0);
mSettingEnabled = val != 0; /////////////////
} catch (Resources.NotFoundException nfe) {
Log.e(TAG, "Could not retrieve system setting.", nfe);
mSettingEnabled = false;
}
}
public void vibrate() {
if (!mEnabled || !mSettingEnabled) {
return; // 看得出,只有调用init时传入enable和用户设置使能反馈后才能调用振动器的接口
}
mVibrator.vibrate(mHapticPattern, NO_REPEAT);
}
/**
* @return true If the system haptic pattern was found.
* @加载振动模式
*/
private boolean loadHapticSystemPattern(Resources r) {
int[] pattern;
mHapticPattern = null;
try {
pattern = r.getIntArray(VIBRATION_PATTERN_ID);
} catch (Resources.NotFoundException nfe) {
Log.e(TAG, "Vibrate pattern missing.", nfe);
return false;
}
if (null == pattern || pattern.length == 0) {
Log.e(TAG, "Haptic pattern is null or empty.");
return false;
}
// int[] to long[] conversion.
mHapticPattern = new long[pattern.length];
for (int i = 0; i < pattern.length; i++) {
mHapticPattern[i] = pattern[i];
}
return true;
}
}
在打电话界面有两个文件中使用到这类API:EmergencyDialer.java (packages\apps\phone\src\com\android\phone)
TwelveKeyDialer.java (packages\apps\contacts\src\com\android\contacts)
现在以拨号键盘为例:TwelveKeyDialer.java,搜索mHaptic.字符串即可找到。
mHaptic.init(this, r.getBoolean(R.bool.config_enable_dialer_key_vibration))
config_enable_dialer_key_vibration在下列文件中定义:
Config.xml (packages\apps\phone\res\values): <bool name="config_enable_dialer_key_vibration">true</bool>
public void onClick(View view) {
switch (view.getId()) {
case R.id.one: {
keyPressed(KeyEvent.KEYCODE_1);
playTone(ToneGenerator.TONE_DTMF_1);
mHaptic.vibrate();
return;
}
...
}
}
protected void onResume() {
...
// Retrieve the haptic feedback setting.
mHaptic.checkSystemSetting();
...
}
从上面的init函数调用和Config.xml可以看出,config_virtualKeyVibePattern模式并没有在phone的资源中定义,
直接使用的默认模式。
那么全局搜索了config_virtualKeyVibePattern这个字符串,发现只有在文件
Config.xml (frameworks\base\core\res\res\values)中有定义,而起还有另外4个类似的振动模式
<!-- Vibrator pattern for feedback about a long screen/key press -->
<integer-array name="config_longPressVibePattern">
<item>0</item>
<item>1</item>
<item>20</item>
<item>21</item>
</integer-array>
<!-- Vibrator pattern for feedback about touching a virtual key -->
<integer-array name="config_virtualKeyVibePattern">
<item>0</item>
<item>10</item>
<item>20</item>
<item>30</item>
</integer-array>
<!-- Vibrator pattern for a very short but reliable vibration for soft keyboard tap -->
<integer-array name="config_keyboardTapVibePattern">
<item>40</item>
</integer-array>
<!-- Vibrator pattern for feedback about booting with safe mode disabled -->
<integer-array name="config_safeModeDisabledVibePattern">
<item>0</item>
<item>1</item>
<item>20</item>
<item>21</item>
</integer-array>
<!-- Vibrator pattern for feedback about booting with safe mode disabled -->
<integer-array name="config_safeModeEnabledVibePattern">
<item>0</item>
<item>1</item>
<item>20</item>
<item>21</item>
<item>500</item>
<item>600</item>
</integer-array>
那么上面类似的5中模式在哪里有使用呢?在刚刚的搜索结果中可以看到赫然出现一个文件:
PhoneWindowManager.java (frameworks\policies\base\phone\com\android\internal\policy\impl)
这个文件中的代码实现系统所有窗口的统一管理,那么显而易见大部分窗口振动都是在这里操作的。
public void init(Context context, IWindowManager windowManager,
LocalPowerManager powerManager) {
...
mVibrator = new Vibrator();
mLongPressVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_longPressVibePattern);
mVirtualKeyVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_virtualKeyVibePattern);
mKeyboardTapVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_keyboardTapVibePattern);
mSafeModeDisabledVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_safeModeDisabledVibePattern);
mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_safeModeEnabledVibePattern);
// 取出振动模式数据,放在long []类型数组中。
}
public boolean performHapticFeedbackLw(WindowState win, int effectId,
boolean always) {
final boolean hapticsDisabled = Settings.System.getInt(mContext
.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED,
0) == 0; // 再次获取用户设置
if (!always
&& (hapticsDisabled || mKeyguardMediator
.isShowingAndNotHidden())) {
return false; //
}
long[] pattern = null;
switch (effectId) { // effectId这个值是觉得采用哪种模式的,长按,还是按了软按键产生等等。
case HapticFeedbackConstants.LONG_PRESS:
pattern = mLongPressVibePattern;
break;
case HapticFeedbackConstants.VIRTUAL_KEY:
pattern = mVirtualKeyVibePattern;
break;
case HapticFeedbackConstants.KEYBOARD_TAP:
pattern = mKeyboardTapVibePattern;
break;
case HapticFeedbackConstants.SAFE_MODE_DISABLED:
pattern = mSafeModeDisabledVibePattern;
break;
case HapticFeedbackConstants.SAFE_MODE_ENABLED:
pattern = mSafeModeEnabledVibePattern;
break;
default:
return false;
}
// 调用振动器接口开始振动
if (pattern.length == 1) {
// One-shot vibration
mVibrator.vibrate(pattern[0]);
} else {
// Pattern vibration
mVibrator.vibrate(pattern, -1);
}
return true;
}
public void keyFeedbackFromInput(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& (event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) {
// 这里有对按键类型做判断看是否是虚拟的硬按键,就对于这虚拟的软按键。
performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY,
false);
}
}
到这里,基本上大致明白了振动反馈的原理,现在要引入一个实际面临的问题。
本来Virtual Key是只的触摸屏上划出一块区域来做几个按键的这类按键,这类按键在原始系统上就是可以进行振动反馈的,
不过呢,现在我们的系统上有2个触摸按键MENU和BACK,他们虽然也是在触摸屏上,不过他们是按照按键上报,而不是按触摸
坐标上报之后生成的虚拟keyevent,所以这样的话,上层就会将他们视为物理按键,但是他们确实是触摸型的。
所以为了让这类按键触摸之后也有振动反馈,所以就需要查找按键上报的流程,然后进行修改。
获取按键的类型flags: event.getFlags(), KeyEvent.java (frameworks\base\core\java\android\view)
想要知道这个flags是如何来的,那么就需要从kernel内核之上开始顺藤摸瓜将按键上报流程缕一下:
EventHub.cpp (frameworks\base\libs\ui)
这个文件中的函数EventHub::getEvent()会从内核中获取input子系统的event事件,其中也包括key事件。
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
{
...
while(1) {
...
pollres = poll(mFDs, mFDCount, -1);
...
for(i = 1; i < mFDCount; i++) {
if(mFDs[i].revents) {
if(mFDs[i].revents & POLLIN) {
res = read(mFDs[i].fd, &iev, sizeof(iev));
if (res == sizeof(iev)) {
*outDeviceId = mDevices[i]->id;
if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
*outType = iev.type;
*outScancode = iev.code;
if (iev.type == EV_KEY) {
err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
// 从*.kl文件中进行键值映射,也得到想要flags值。kernel键值映射到android键值。
...
} else {
*outKeycode = iev.code;
}
*outValue = iev.value;
*outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
return true;
}
...
}
}
}
}
...
}
}
*.kl文件格式:
key 158 BACK
...
key 229 MENU
key 139 MENU
key 59 MENU
...
key 116 POWER WAKE
...
其中第一列是key关键字,第二列是kernel的键值,第3列是android键值,第4列是flag
其中flags有以下几种,可以从文件:KeycodeLabels.h (frameworks\base\include\ui)中查看得到:
static const KeycodeLabel FLAGS[] = {
{ "WAKE", 0x00000001 },
{ "WAKE_DROPPED", 0x00000002 },
{ "SHIFT", 0x00000004 },
{ "CAPS_LOCK", 0x00000008 },
{ "ALT", 0x00000010 },
{ "ALT_GR", 0x00000020 },
{ "MENU", 0x00000040 },
{ "LAUNCHER", 0x00000080 },
{ NULL, 0 }
};
其中也可以看到MENU和BACK在android空间中对于的键值分别是:82和4.
static const KeycodeLabel KEYCODES[] = {
...
{ "BACK", 4 },
...
{ "MENU", 82 },
...
}
上面这些值都是KeyLayoutMap.cpp (frameworks\base\libs\ui)文件中函数
status_t KeyLayoutMap::load(const char* filename)在加载*.kl文件的时候根据文件和上面两个数组设置好了的,最后只需要用map()函数来获取这些值即可。
接下来按键值将会上传至JNI层:com_android_server_KeyInputQueue.cpp (frameworks\base\services\jni)
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, jobject event)
{
gLock.lock();
sp<EventHub> hub = gHub;
if (hub == NULL) {
hub = new EventHub;
gHub = hub;
}
gLock.unlock();
int32_t deviceId;
int32_t type;
int32_t scancode, keycode;
uint32_t flags;
int32_t value;
nsecs_t when;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, &flags, &value, &when); // getEvent
env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
env->SetIntField(event, gInputOffsets.mType, (jint)type);
env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
env->SetIntField(event, gInputOffsets.mValue, value);
env->SetLongField(event, gInputOffsets.mWhen, (jlong)(nanoseconds_to_milliseconds(when)));
// 将这些值传入java空间。
return res;
}
最后在java代码中只需要调用jni接口readEvent即可。
KeyInputQueue.java (frameworks\base\services\java\com\android\server)文件中会创建一个线程来专门读取inputevent值
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
...
RawInputEvent ev = new RawInputEvent();
while (true) {
try {
InputDevice di;
// block, doesn't release the monitor
readEvent(ev);
...
}
}
...
// Is it a key event?
if (type == RawInputEvent.EV_KEY &&
(classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
(scancode < RawInputEvent.BTN_FIRST ||
scancode > RawInputEvent.BTN_LAST)) {
boolean down;
if (ev.value != 0) {
down = true;
di.mKeyDownTime = curTime;
} else {
down = false;
}
int keycode = rotateKeyCodeLocked(ev.keycode);
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mKeyDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
}
...
}
}
generateVirtualKeyDown()根据上报的触摸点坐标等信息生成虚拟硬按键事件。
KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
vk.lastKeycode, 0, vk.scancode,
KeyEvent.FLAG_VIRTUAL_HARD_KEY);
mHapticFeedbackCallback.virtualKeyFeedback(event);
addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
event);
newKeyEvent()生成一个KeyEvent实例,如下:
public static KeyEvent newKeyEvent(InputDevice device, long downTime,
long eventTime, boolean down, int keycode, int repeatCount,
int scancode, int flags) {
return new KeyEvent(
downTime, eventTime,
down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
keycode, repeatCount,
device != null ? device.mMetaKeysState : 0,
device != null ? device.id : -1, scancode,
flags | KeyEvent.FLAG_FROM_SYSTEM);
}
////***********************88 错误的修改 *******************************/
所以为了实现MENU和BACK按键能产生震动反馈,那么可以直接修改这个函数newKeyEvent(),如下:
public static KeyEvent newKeyEvent(InputDevice device, long downTime,
long eventTime, boolean down, int keycode, int repeatCount,
int scancode, int flags) {
+ int nflags = flags;
+ if(keycode == 4 || keycode == 82) // BACK and MENU
+ nflags |= KeyEvent.FLAG_VIRTUAL_HARD_KEY;
return new KeyEvent(
downTime, eventTime,
down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
keycode, repeatCount,
device != null ? device.mMetaKeysState : 0,
device != null ? device.id : -1, scancode,
+/- nflags | KeyEvent.FLAG_FROM_SYSTEM);
}
////***********************88 错误的修改 *******************************/
int keycode = rotateKeyCodeLocked(ev.keycode);
/* Begin: lizhiguo, 2011-10-20, modifiled for BACK and MENU key HapticFeedback.*/
if(1){
int nflags = ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0;
if(keycode == 4 || keycode == 82) // BACK and MENU
nflags |= KeyEvent.FLAG_VIRTUAL_HARD_KEY;
KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, down,
keycode, 0, scancode, nflags);
if(keycode == 4 || keycode == 82) // BACK and MENU
mHapticFeedbackCallback.virtualKeyFeedback(event);
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD, event);
}
else{
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mKeyDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
}
/* End: lizhiguo, 2011-10-20 */
完!
源码列表:
1 KeyInputQueue.java (frameworks\base\services\java\com\android\server)
2 RawInputEvent.java (frameworks\base\core\java\android\view)
3 KeyEvent.java (frameworks\base\core\java\android\view)
4 KeyInputQueue.java (frameworks\base\services\java\com\android\server):2
5 WindowManagerPolicy.java (frameworks\base\core\java\android\view)
6 com_android_server_KeyInputQueue.cpp (frameworks\base\services\jni)
7 EventHub.cpp (frameworks\base\libs\ui)
8 EventHub.cpp (frameworks\base\libs\ui):2
9 KeyLayoutMap.cpp (frameworks\base\libs\ui)
0 KeycodeLabels.h (frameworks\base\include\ui)
A TwelveKeyDialer.java (packages\apps\contacts\src\com\android\contacts)
B PhoneWindowManager.java (frameworks\policies\base\phone\com\android\internal\policy\impl)
C KeyEvent.java (frameworks\base\awt\java\awt\event)
D HapticFeedbackConstants.java (frameworks\base\core\java\android\view)
E PasswordUnlockScreen.java (frameworks\policies\base\phone\com\android\internal\policy\impl)
F LockPatternView.java (frameworks\base\core\java\com\android\internal\widget)
G HapticFeedback.java (packages\apps\phone\src\com\android\phone)
H Donottranslate_config.xml (packages\apps\contacts\res\values)
I EmergencyDialer.java (packages\apps\phone\src\com\android\phone)
J Config.xml (frameworks\base\core\res\res\values)
K Vibrator.java (frameworks\base\core\java\android\os)
L View.java (frameworks\base\core\java\android\view)
M Settings.java (frameworks\base\core\java\android\provider)
N ContentResolver.java (frameworks\base\core\java\android\content)
O System.java (dalvik\libcore\luni-kernel\src\main\java\java\lang)
P Editprofile.java (packages\apps\settings\src\com\android\settings\audioprofile)
Q CheckBoxPreference.java (frameworks\base\core\java\android\preference)
R AudioProfileImpl.java (frameworks\mtk\extensions\media\audio\java\com\mediatek\audioprofile)
S ProfileState.java (frameworks\mtk\extensions\media\audio\java\com\mediatek\audioprofile)
T AudioProfileSettings.java (packages\apps\settings\src\com\android\settings\audioprofile)
U Config.xml (packages\apps\phone\res\values)
V TextKeyListenerTest.java (cts\tests\tests\text\src\android\text\method\cts)
W WindowManagerService.java (frameworks\base\services\java\com\android\server)
X KeyEventTest.java (cts\tests\tests\view\src\android\view\cts)
Y KeyLayoutMap.h (frameworks\base\libs\ui)
Z User_comments_for_4_to_5.xml (frameworks\base\docs\html\sdk\api_diff\5)
missingSinces.txt (frameworks\base\docs\html\sdk\api_diff\5)
5.xml (frameworks\base\api)
MidWindowManager.java (frameworks\policies\base\mid\com\android\internal\policy\impl)
EventHub.h (frameworks\base\include\ui)
Evdev.c (kernel\drivers\input)
Input.h (kernel\include\linux)
Input.c (kernel\drivers\input)
Preference.java (frameworks\base\core\java\android\preference)
发表评论
-
Android锁屏的问题
2012-10-19 09:17 1358本文主要讨论Android锁屏的问题,具体有2个需求: 1、 ... -
android 修改开关机铃声
2012-10-18 16:52 18631,boot铃声,在surfaceflinger.cpp中re ... -
android输入法全屏问题
2012-10-18 15:18 1345两种方法: 一是在源码里进行修改。frameworks/bas ... -
android 解锁,锁屏流程
2012-10-16 15:36 7077解锁、锁屏界面状态改 ... -
android 从横屏到竖屏状态出现黑屏
2012-10-16 12:08 1842修改WindowManagerService.java中的pe ... -
android添加reboot选项
2012-10-13 15:33 14881,GlobalAction.java中mItems = Li ... -
手机硬件
2012-10-13 13:39 1007转自http://bbs.meizu.com/thread-4 ... -
Android编译后的文件结构
2012-10-09 18:57 856Android编译完成后,将在根目录中生成一个out文件夹,所 ... -
framework下资源文件---自定义统一的系统风格
2012-09-26 17:46 12221.系统资源文件位置:framework/base/core/ ... -
Android系统默认值的设置
2012-09-26 09:25 11651开机图片: android-logo-mask.png ... -
android apk安装原理分析
2012-09-25 14:16 1793参考了网上的资料和源码,肤浅分析了下,不够深入。 An ... -
Android下的系统Intent大全
2012-08-23 08:58 9561.从google搜索内容 Intent intent = n ... -
修改Android framework定制重启功能
2012-08-15 15:08 4242涉及到的源码(4.0) ics/frameworks/base ... -
Android makefile mk 重要参数解释及 通用模板编写
2012-08-10 09:04 17371. LOCAL_MODULE_TAGS解释: 控制此模块在什 ... -
手机的AP和BP是什么?
2012-08-06 15:56 1397手机的AP和BP根据上下文可以指代硬件和软件两种意思. 1) ... -
Android 上层界面到内核代码的完整的流程分析,以alarm为例子
2012-07-31 17:15 1338Alarm 调用流程,alarm的流程实现了从上层应用一直到下 ... -
彩信APN切换流程(framework)
2012-07-31 15:46 2056TransactionService之前的流程比较简单不在赘 ... -
我架设的程序员问答网站
2012-06-12 18:20 1210我架设了一个程序员问答网站,欢迎来http://program ... -
Android编译系统三
2012-06-15 10:33 1154android编译系统的makefile文件Android.m ... -
Android编译系统二
2012-06-08 14:46 2784一,Android编 译系统结构 android的编译文件 ...
相关推荐
通过分析和学习这个GestureTest项目,开发者不仅可以掌握Android手势识别的基本原理和实现,还可以了解到如何将这些技术融入到实际应用中,提升用户的交互体验。对于Android初学者和毕业设计的参与者来说,这是一个...
【Android游戏源码支持触屏和按键的推箱子】是一个基于Android平台的开源项目,它展示了如何在Android设备上实现一款经典...通过研究这个项目,开发者不仅可以提升Android编程技能,还能深入了解游戏开发的原理和实践。
首先,我们需要理解Android触摸事件的工作原理。在Android中,触摸事件通过MotionEvent类进行处理。当用户触摸屏幕时,系统会发送ACTION_DOWN事件,手指移动时会发送ACTION_MOVE事件,手指离开屏幕时则发送ACTION_UP...
本文将深入探讨Android桌面拖拽效果的实现原理、关键技术和优化方法。 首先,我们需要理解Android桌面的基本架构。Android桌面,也被称为Launcher,是系统提供给用户启动应用、管理屏幕快捷方式和小部件的主要界面...
4. **视觉反馈**:提供解锁成功的动画效果,提升用户体验。 总之,Android 9方格密码锁是Android安全体系中的重要组成部分,通过合理的设计和实现,可以为用户提供便捷且安全的设备解锁体验。对于开发者来说,理解...
在反馈方面,Android提供了较为丰富的反馈机制,如震动反馈、声音反馈以及视觉反馈等。这些反馈机制可以帮助用户更好地理解当前的操作状态,提高用户对应用的信任感。 **评分:** 80/100 ##### 3. 表意(Affordance...
首先,触屏滑动的基本原理是通过电容式触摸屏来实现。电容式触摸屏由多层透明材料组成,包括导电层和绝缘层。当手指接触屏幕时,人体的电容会与屏幕的电容形成耦合,改变屏幕局部的电场分布。这一变化会被传感器检测...
本篇文章将详细探讨移动端触屏滑动导航菜单的设计原理、实现方式以及优化策略。 一、设计原理 1. 滑动菜单的出现:滑动导航菜单通常隐藏在屏幕边缘,当用户触摸并滑动屏幕时,菜单会以动画效果滑出,展示更多的...
本篇文章将深入探讨Android虚拟摇杆的开发技术,包括原理、实现方法以及应用场景。 1. **虚拟摇杆的基本原理** - 虚拟摇杆主要由两个部分组成:一个可移动的“摇杆”图像和一个固定的基础背景。当用户触摸屏幕并...
本文将深入探讨“连连看”这款游戏在Android平台上的实现原理,通过源码解析,帮助开发者理解Android应用开发的核心技术。 一、Android应用开发基础 1. **Activity与Layout** - Android应用的基础单元是Activity...
【标题】"p1_lte_sendEvent触屏事件记录"主要涉及的是移动设备,特别是智能手机和平板电脑中,关于...通过研究这个功能,开发者可以提升对Android系统底层工作原理的理解,从而更好地优化应用程序的性能和用户体验。
4. **响应反馈**:主处理器根据接收到的信息执行相应的操作,如屏幕触控事件的响应,然后将反馈信息传回触控驱动,最终完成用户的操作指令。 在安装【Focaltech(敦泰)触屏通用MTK驱动】时,用户需要注意以下几点...
通过分析和学习`mouseovertest`这个项目,开发者可以深入理解Android中鼠标悬停事件的工作原理,并将其应用到自己的应用中,提高应用的交互性和易用性。无论是为非触屏设备提供支持,还是为用户提供更丰富的触控体验...
一、滑动效果基础原理 滑动效果主要依赖于触摸事件处理,包括`touchstart`、`touchmove`和`touchend`等。当用户在屏幕上触摸并移动手指时,这些事件会被触发,开发者可以通过监听这些事件来获取手指移动的距离和...
首先,我们要理解九宫格解锁的基本原理。它由9个可点击的单元格组成一个3x3的矩阵,每个单元格代表不同的数字或图标。用户需要按照特定的顺序滑动这些单元格来完成解锁。这种解锁方式结合了触屏操作的便捷性和一定的...
本文将从源码分析和说明文档两方面,揭示这款游戏的制作原理和技术要点。 一、触控操作与游戏逻辑 "雷电"游戏的核心玩法是玩家通过触屏控制飞机移动,躲避敌机的子弹并反击。Android系统提供了丰富的触摸事件API,...
这包括了图标、滑块、按钮等元素的设计,以及触控反馈、手势识别等技术。良好的可视化操作设计可以提升用户体验,简化复杂操作,使得电信设备更易于使用。 3. 操作管理系统:电信设备的操作管理系统是其软件层面的...
6. **用户交互反馈**:为了提供良好的用户体验,手势解锁视图需要有视觉反馈,例如点亮用户触摸的格子、显示手势路径以及解锁成功或失败的提示。 7. **事件监听和处理**:在Java中,事件监听通常通过重写`...
本篇文章将深入探讨“智力火柴游戏源码”,分析其设计原理,并结合Android游戏开发基础,为想要踏入移动游戏开发领域的朋友们提供宝贵的实践指导。 首先,我们要理解游戏的核心机制——算法。在这款游戏中,玩家...
### 安卓平台输入法开发解析 #### 一、安卓输入法框架概览 自Android 1.5版本以来,谷歌引入了输入法框架(Input Method Framework...理解并掌握这套框架的原理和机制,对于深入挖掘Android平台的输入潜力至关重要。