`
jandroid
  • 浏览: 1939977 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Android的全局键(home键/长按耳机键)详解【android源码解析八】

 
阅读更多

如果想在Android手机要想扩展一个实体键,就我知道而言有两种方法,基于Android4.0的源码来分析的和2.3的源码有点区别,区别不大,下面分享给大家:

转载请标明出处:

(一)可以在frameworks层的KeyEvent.java这个类中定义一个值,在PhoneWindowManager.java这个类中做处理就可以了。(Home键就是这么实现的)。效果图如下

(二)可以利用广播的形式,frameworks层PhoneWindow.java这个类的onKeyDown( )对这个实体键发广播,上层接受这个广播来处理也可以达到这个效果。耳机键就是利用广播来接受的。无论在哪个界面长按耳机键,都会进入到音乐的界面。(长按耳机键的效果图如下

下面我详细展开来说明一下:

一、先说Home键的实现的大致流程,即---->为什么点击Home键,都进入到launcher的待机界面;

(1)Home键的定义在

step1: frameworks/base/core/java/android/view/KeyEvent.java这个类中,在KeyEvent.java这个类中有个static的静态块:

  static {
        populateKeycodeSymbolicNames();
    }

step2: 这个populateKeycodeSymbolicNames()方法其实就是加载了许多键的定义,把这些键对应的值都放到Array数组中。

private static void populateKeycodeSymbolicNames() {
        SparseArray<String> names = KEYCODE_SYMBOLIC_NAMES;
        names.append(KEYCODE_UNKNOWN, "KEYCODE_UNKNOWN");
        names.append(KEYCODE_SOFT_LEFT, "KEYCODE_SOFT_LEFT");
        names.append(KEYCODE_SOFT_RIGHT, "KEYCODE_SOFT_RIGHT");
        names.append(KEYCODE_HOME, "KEYCODE_HOME");
        names.append(KEYCODE_BACK, "KEYCODE_BACK");
        names.append(KEYCODE_CALL, "KEYCODE_CALL");
        names.append(KEYCODE_ENDCALL, "KEYCODE_ENDCALL");
        names.append(KEYCODE_0, "KEYCODE_0");
        names.append(KEYCODE_1, "KEYCODE_1");
        names.append(KEYCODE_2, "KEYCODE_2");
        names.append(KEYCODE_3, "KEYCODE_3");
        names.append(KEYCODE_4, "KEYCODE_4");
        names.append(KEYCODE_5, "KEYCODE_5");
        names.append(KEYCODE_6, "KEYCODE_6");
        names.append(KEYCODE_7, "KEYCODE_7");
        names.append(KEYCODE_8, "KEYCODE_8");
        names.append(KEYCODE_9, "KEYCODE_9");

step3: 而Home键对应的值如下:

 /** Key code constant: Home key.
     * This key is handled by the framework and is never delivered to applications. */
    public static final int KEYCODE_HOME            = 3;

(2)Home键的处理如下:在
step1: frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java这个类中:

在这个方法interceptKeyBeforeDispatching(... ... ...)中处理有对Home,Search,menu,音量大小键等等:

  /** {@inheritDoc} */
    @Override
    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
        final boolean keyguardOn = keyguardOn();
        final int keyCode = event.getKeyCode();
        final int repeatCount = event.getRepeatCount();
        final int metaState = event.getMetaState();
        final int flags = event.getFlags();
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        final boolean canceled = event.isCanceled();

        if (false) {
            Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
                    + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed);
        }

        // If we think we might have a volume down & power key chord on the way
        // but we're not sure, then tell the dispatcher to wait a little while and
        // try again later before dispatching.
        if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
            if (mVolumeDownKeyTriggered && !mPowerKeyTriggered) {
                final long now = SystemClock.uptimeMillis();
                final long timeoutTime = mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
                if (now < timeoutTime) {
                    return timeoutTime - now;
                }
            }
            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                    && mVolumeDownKeyConsumedByScreenshotChord) {
                if (!down) {
                    mVolumeDownKeyConsumedByScreenshotChord = false;
                }
                return -1;
            }
        }

        // First we always handle the home key here, so applications
        // can never break it, although if keyguard is on, we do let
        // it handle it, because that gives us the correct 5 second
        // timeout.
        if (keyCode == KeyEvent.KEYCODE_HOME) {
            // If we have released the home key, and didn't do anything else
            // while it was pressed, then it is time to go home!
            if (mHomePressed && !down) {
                mHomePressed = false;
                if (!canceled) {
                    // If an incoming call is ringing, HOME is totally disabled.
                    // (The user is already on the InCallScreen at this point,
                    // and his ONLY options are to answer or reject the call.)
                    boolean incomingRinging = false;
                    try {
                        ITelephony telephonyService = getTelephonyService();
                        if (telephonyService != null) {
                            incomingRinging = telephonyService.isRinging();
                        }
                    } catch (RemoteException ex) {
                        Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
                    }

                    if (incomingRinging) {
                        Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
                    } else {
                        launchHomeFromHotKey();
                    }
                } else {
                    Log.i(TAG, "Ignoring HOME; event canceled.");
                }
                return -1;
            }

            // If a system window has focus, then it doesn't make sense
            // right now to interact with applications.
            WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
            if (attrs != null) {
                final int type = attrs.type;
                if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
                        || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
                    // the "app" is keyguard, so give it the key
                    return 0;
                }
                final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
                for (int i=0; i<typeCount; i++) {
                    if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
                        // don't do anything, but also don't pass it to the app
                        return -1;
                    }
                }
            }

            if (down) {
                if (repeatCount == 0) {
                    mHomePressed = true;
                } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                    if (!keyguardOn) {
                        handleLongPressOnHome();
                    }
                }
            }
            return -1;
        } else if (keyCode == KeyEvent.KEYCODE_MENU) {
           ........


 Step2: 插曲网上有例子说怎么在自己的应用中屏蔽Home键》--->原理:是在你的应用的Activity中加入了锁屏的type,因为系统对锁屏界面,点击Home键失效!网摘代码如下:
public class DMActivity extends Activity {  
      
    private boolean flag = true;//true位屏蔽,false位不屏蔽   
      
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
    }  
    @Override  
    public void onAttachedToWindow() {  
        if(flag) {  
            this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);  
        }  
        super.onAttachedToWindow();  
    }  
  
    @Override  
    public boolean onKeyDown(int keyCode, KeyEvent event) {  
        if(keyCode == KeyEvent.KEYCODE_HOME){  
            return true;  
        }  
        return super.onKeyDown(keyCode, event);  
    }  
}  

Step3: 真正的原因如下,对锁屏模式的处理:

            // If a system window has focus, then it doesn't make sense
            // right now to interact with applications.
            WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
            if (attrs != null) {
                final int type = attrs.type;
                if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
                        || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
                    // the "app" is keyguard, so give it the key
                    return 0;
                }
                final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
                for (int i=0; i<typeCount; i++) {
                    if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
                        // don't do anything, but also don't pass it to the app
                        return -1;
                    }
                }
            }

Step4: 我们来看点击home键,为什么进入到launcher的待机界面:

            // If we have released the home key, and didn't do anything else
            // while it was pressed, then it is time to go home!
            if (mHomePressed && !down) {
                mHomePressed = false;
                if (!canceled) {
                    // If an incoming call is ringing, HOME is totally disabled.
                    // (The user is already on the InCallScreen at this point,
                    // and his ONLY options are to answer or reject the call.)
                    boolean incomingRinging = false;
                    try {
                        ITelephony telephonyService = getTelephonyService();
                        if (telephonyService != null) {
                            incomingRinging = telephonyService.isRinging();
                        }
                    } catch (RemoteException ex) {
                        Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
                    }

                    if (incomingRinging) {
                        Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
                    } else {
                        launchHomeFromHotKey();
                    }
                } else {
                    Log.i(TAG, "Ignoring HOME; event canceled.");
                }
                return -1;
            }

Step5: 系统会判断,当前点击Home键并且没有电话打入的情况,才对Home键进行处理---->launchHomeFromHotKey();进入到----->launchHomeFromHotKey()方法中:

    /**
     * A home key -> launch home action was detected.  Take the appropriate action
     * given the situation with the keyguard.
     */
    void launchHomeFromHotKey() {
        if (mKeyguardMediator.isShowingAndNotHidden()) {
            // don't launch home if keyguard showing
        } else if (!mHideLockScreen && mKeyguardMediator.isInputRestricted()) {
            // when in keyguard restricted mode, must first verify unlock
            // before launching home
            mKeyguardMediator.verifyUnlock(new OnKeyguardExitResult() {
                public void onKeyguardExitResult(boolean success) {
                    if (success) {
                        try {
                            ActivityManagerNative.getDefault().stopAppSwitches();
                        } catch (RemoteException e) {
                        }
                        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
                        startDockOrHome();
                    }
                }
            });
        } else {
            // no keyguard stuff to worry about, just launch home!
            try {
                ActivityManagerNative.getDefault().stopAppSwitches();
            } catch (RemoteException e) {
            }
            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
            startDockOrHome();
        }
    }

Step6: 也是对锁屏模式有个判断,如果不在锁屏模式,就launch Home---->startDockOrHome(),进入——>

   void startDockOrHome() {
        Intent dock = createHomeDockIntent();
        if (dock != null) {
            try {
                mContext.startActivity(dock);
                return;
            } catch (ActivityNotFoundException e) {
            }
        }
        mContext.startActivity(mHomeIntent);
    }

Step 7: 其实这个createHomeDockIntent()方法就是对android手机的几种模式进行判断,

The device is not in either car mode or desk mode
The device is in car mode but ENABLE_CAR_DOCK_HOME_CAPTURE is false
The device is in desk mode but ENABLE_DESK_DOCK_HOME_CAPTURE is false
The device is in car mode but there's no CAR_DOCK app with METADATA_DOCK_HOME
The device is in desk mode but there's no DESK_DOCK app with METADATA_DOCK_HOME

如果是以上模式,车载模式或者桌面模式,就返回dock不为空,否则为空。启动这个mHomeIntent。----->mHomeIntent定义如下

           mHomeIntent =  new Intent(Intent.ACTION_MAIN, null);
        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

Step 8: 这个mHomeIntent就是启动activity中配置Category属性的值为CATEGORY_HOME,启动的时候新启动一个任务,不是在当前的这个任务中启动launcherHome,而是新建一个task。

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。

到这为止,Home键的流程已经分析完了。

二 、下面看看长按耳机键接受的广播的处理方式:

(1)这个长按耳机键捕获是在PhoneWindow.java类的onKeyDown()中,然后发送有序的广播---->如下:

       case KeyEvent.KEYCODE_HEADSETHOOK:                     
            case KeyEvent.KEYCODE_MEDIA_STOP:
            case KeyEvent.KEYCODE_MEDIA_NEXT:
            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
            case KeyEvent.KEYCODE_MEDIA_REWIND:
            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
                getContext().sendOrderedBroadcast(intent, null);
                return true;
            }

接受这个长按耳机键的广播是在Music的app中的----->

publicclassMediaButtonIntentReceiverextendsBroadcastReceiver{......}

, 需要在Manifest.xml中注册这个广播<intent-filter>

<action android:name="android.intent.action.MEDIA_BUTTON" />

</intent-filter>。---->注册MediaButtonReceiver这个广播,

这个类中onReceive()方法定义的:代码如下--->

    @Override
    public void onReceive(Context context, Intent intent) {
        String intentAction = intent.getAction();
        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction)) {
            Intent i = new Intent(MediaPlaybackService.SERVICECMD);
            i.putExtra(MediaPlaybackService.CMDNAME, MediaPlaybackService.CMDPAUSE);
            context.sendBroadcast(i);
        } else if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
            KeyEvent event = (KeyEvent)
                    intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
            
            if (event == null) {
                return;
            }

            int keycode = event.getKeyCode();
            int action = event.getAction();
            long eventtime = event.getEventTime();

            // single quick press: pause/resume. 
            // double press: next track
            // long press: start auto-shuffle mode.
            
            String command = null;
            switch (keycode) {
                case KeyEvent.KEYCODE_MEDIA_STOP:
                    command = MediaPlaybackService.CMDSTOP;
                    break;
                case KeyEvent.KEYCODE_HEADSETHOOK:
                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                    command = MediaPlaybackService.CMDTOGGLEPAUSE;
                    break;
                case KeyEvent.KEYCODE_MEDIA_NEXT:
                    command = MediaPlaybackService.CMDNEXT;
                    break;
                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                    command = MediaPlaybackService.CMDPREVIOUS;
                    break;
                case KeyEvent.KEYCODE_MEDIA_PAUSE:
                    command = MediaPlaybackService.CMDPAUSE;
                    break;
                case KeyEvent.KEYCODE_MEDIA_PLAY:
                    command = MediaPlaybackService.CMDPLAY;
                    break;
            }

            if (command != null) {
                if (action == KeyEvent.ACTION_DOWN) {
                    if (mDown) {
                        if ((MediaPlaybackService.CMDTOGGLEPAUSE.equals(command) ||
                                MediaPlaybackService.CMDPLAY.equals(command))
                                && mLastClickTime != 0 
                                && eventtime - mLastClickTime > LONG_PRESS_DELAY) {
                            mHandler.sendMessage(
                                    mHandler.obtainMessage(MSG_LONGPRESS_TIMEOUT, context));
                        }
                    } else if (event.getRepeatCount() == 0) {
                        // only consider the first event in a sequence, not the repeat events,
                        // so that we don't trigger in cases where the first event went to
                        // a different app (e.g. when the user ends a phone call by
                        // long pressing the headset button)

                        // The service may or may not be running, but we need to send it
                        // a command.
                        Intent i = new Intent(context, MediaPlaybackService.class);
                        i.setAction(MediaPlaybackService.SERVICECMD);
                        if (keycode == KeyEvent.KEYCODE_HEADSETHOOK &&
                                eventtime - mLastClickTime < 300) {
                            i.putExtra(MediaPlaybackService.CMDNAME, MediaPlaybackService.CMDNEXT);
                            context.startService(i);
                            mLastClickTime = 0;
                        } else {
                            i.putExtra(MediaPlaybackService.CMDNAME, command);
                            context.startService(i);
                            mLastClickTime = eventtime;
                        }

                        mLaunched = false;
                        mDown = true;
                    }
                } else {
                    mHandler.removeMessages(MSG_LONGPRESS_TIMEOUT);
                    mDown = false;
                }
                if (isOrderedBroadcast()) {
                    abortBroadcast();
                }
            }
        }
    }

step1: 在方法if (action == KeyEvent.ACTION_DOWN) { ... ... }做的处理,event.getRepeatCount() == 0这个判断的意思是“是否长按耳机键?”,如果长按耳机键event.getRepeatCount() 的值就一直增加。

step 2:短按耳机键:播放/暂停 --->音乐;短按启动MediaPlaybackService.java这个类,并且传入参数---->在这个服务类中有个接受广播的内部类:如下-->

        private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            String cmd = intent.getStringExtra("command");
            MusicUtils.debugLog("mIntentReceiver.onReceive " + action + " / " + cmd);
            if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
                next(true);
            } else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {
                prev();
            } else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {
                if (isPlaying()) {
                    pause();
                    mInternalPause = false;
                } else {
                    play();
                }
            } else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action)) {
                pause();
                mInternalPause = false;
            } else if (CMDPLAY.equals(cmd)) {
                play();
            } else if (CMDSTOP.equals(cmd)) {
                pause();
                mInternalPause = false;
                seek(0);
            } else if (MediaAppWidgetProvider.CMDAPPWIDGETUPDATE.equals(cmd)) {
                // Someone asked us to refresh a set of specific widgets, probably
                // because they were just added.
                int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                mAppWidgetProvider.performUpdate(MediaPlaybackService.this, appWidgetIds);
            }
        }
    };

通过:如下方法来控制点击播放音乐,再次点击暂停,如此循环。

else if (CMDTOGGLEPAUSE.equals(cmd) || TOGGLEPAUSE_ACTION.equals(action)) {

if (isPlaying()) {

pause();

mInternalPause = false;

} else {

play();

}

}

内部类的广播是在启动MediaPlaybackService.java中注册的,解除注册在onDestroy()的方法中。

        IntentFilter commandFilter = new IntentFilter();
        commandFilter.addAction(SERVICECMD);
        commandFilter.addAction(TOGGLEPAUSE_ACTION);
        commandFilter.addAction(PAUSE_ACTION);
        commandFilter.addAction(NEXT_ACTION);
        commandFilter.addAction(PREVIOUS_ACTION);
        commandFilter.addAction(PLAYSTATUS_REQUEST);
        registerReceiver(mIntentReceiver, commandFilter);

Step 3: 长按耳机键--->发消息给mHandler,

       mHandler.removeMessages(MSG_LONGPRESS_TIMEOUT);

在MediaButtonIntentReceiver.java中有个内部类Handler()如下——>

        private static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_LONGPRESS_TIMEOUT:
                    if (!mLaunched) {
                        Context context = (Context)msg.obj;
                        Intent i = new Intent();
                        i.putExtra("autoshuffle", "true");
                        i.setClass(context, MusicBrowserActivity.class);
                        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        context.startActivity(i);
                        mLaunched = true;
                    }
                    break;
            }
        }
    };

长按耳机后就启动MusicBrowserActivity.java这个音乐播放类。并且传入参数“autoshuffle==true”,这个启动和launcher的启动相似,也是启动一个新的任务task,但是这个后面的标志有不同的地方Intent.FLAG_ACTIVITY_CLEAR_TOP。

到这为止就是实现了在任何界面长按耳机键都能进入到music的主界面。

总结如下:

其实启动的时候,要注意当前activity的launcherMode是什么,如果是SingleTask,就要小心一下。比如说:想要长按耳机键,进入到launcher的Mainmenu界面,这时候如果单纯的用以上方法套,返回键点击的时候不会回到上个activity中,会有问题。因为launcher是一直启动的运行于每个task之中的,你再次启动Launcher的时候,无论是否设置属性“i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)”,都会把前一个activity给finish掉,launcher会在栈顶也是栈底。因为launcher的launcherMode=singleTask。我们可以做个实验;

三、例子写两个app,一个属性为singletask,一个为standard。应singletask的启动standard的activity,然后在再次基础上启动singletask的activity,看standard的activity是否会destory掉。

step1:先看截图:

launchMode="singleTask"launchMode="standard"

Step 2:先启动launchMode="singleTask"的activity---->点击调用第二个App2的launchMode="standard"---->

点击按钮调用第一个App1的launchMode="singleTask"---->点击返回键。看log分析:

(1)点击启动launchMode="singleTask"的activity的log如下:

(2)点击调用第二个App2的launchMode="standard"的activity的log如下图:

(3)点击调用第一个App1launchMode="singleTask"的activity的log如下:
分析如下:看到这时候App2Activity--22已经执行了onStop()和onDestroy()方法了。验证了我以上的说法。

(4)点击返回键---->直接回到了launcher界面。log如下:

备注:要想解决以上问题也是可以的。就是在以上第(2)步:点击调用第二个App2的launchMode="standard"的activity的时候设置flag。Intent.FLAG_ACTIVITY_NEW_TASK。就可以解决以上问题了,每次启动一个新的任务,这样就能返回到App2Activity了

(1)点击启动launchMode="singleTask"的activity的log如下:

(2)点击调用第二个App2的launchMode="standard"的activity的log如下图:

(3)点击调用第一个App1launchMode="singleTask"的activity的log如下:

看到如上图:App2Activity--22--->只是onStop()了,没有onDestroy掉。

(4)点击返回键---->直接回到了launcher界面---->log如下:

(5)通过log,我们可以看出返回到App2Activity了,我们再次点击返回键,--->返回到Launcher界面的log如下:

通过以上验证说明我的结论是正确的。launcherMode一直是android的核心技术,通过这次我会更加注意到activity的LauncherMode的。

欢迎各界人士留言,讨论,拍砖!转载请标明出处!





分享到:
评论

相关推荐

    android:自定义长按/长点击事件

    开发者可以根据实际需求,将这个逻辑应用到不同的`View`上,或者在`Activity`或`Fragment`级别进行全局的长按事件监听。 总结来说,自定义长按事件在Android开发中是常见的需求,通过`GestureDetector`可以方便地...

    android 7.0/8.0/9.0/10.0全局手势事件无效解决方案

    android 7.0/8.0/9.0/10.0全局手势事件无效,由于设置状态栏或者导航栏高度为0时,会导致全局手势事件滑不出来

    Android系统添加全局快捷键的方法

    同时,在`frameworks/base/core/Java/android/view/KeyEvent.java`文件中定义了`android.view.KeyEvent`类,该类中定义的整型数值与`KeycodeLabels.h`文件中定义的`KeyCode`枚举值是一一对应的。这意味着开发者可以...

    Android应用源码之按键的处理.zip

    本资料包"Android应用源码之按键的处理.zip"显然聚焦于解析如何在Android应用程序中捕获并响应用户输入的按键操作。下面我们将深入探讨这个主题。 首先,Android系统为开发者提供了两种主要的按键事件处理方式:第...

    android 全局悬浮按钮实现key事件

    &lt;/androidx.coordinatorlayout.widget.CoordinatorLayout&gt; ``` `FloatingActionButton`的`android:src`属性用于设置图标,你可以根据需求替换为自己的图标资源。`android:layout_gravity`属性用于设置按钮的位置,...

    android 媒体键监听 以及 媒体键模拟

    在Android开发中,媒体键监听和模拟媒体键盘的实现是两个重要的功能,它们涉及到系统级事件处理和自定义用户交互。下面将详细讲解这两个概念及其实现。 首先,媒体键监听是指应用程序能够捕获并响应系统中的媒体键...

    按键检测代码,实现短按,长按,连续按下检测功能。全程通畅无阻塞。 详细说明见博客: http://t.csdn.cn/DP43e

    按键检测代码,实现短按,长按,连续按下检测功能。全程通畅无阻塞。 详细说明见博客: http://t.csdn.cn/DP43e 2. 使用说明: 通过修改宏定义可以修改开关时间; 通过查看头文件可以方便移植到不同硬件上; 3. ...

    android 后台监听按键事件方法及demo

    android 后台监听按键事件方法及demoandroid 后台监听按键事件方法及demo

    详解Android 全局弹出对话框SYSTEM_ALERT_WINDOW权限

    在Android开发中,全局弹出对话框是一种常见需求,特别是在某些特定场景下,如通知用户、请求确认或显示重要信息。然而,为了实现全局对话框,开发者需要掌握一种特殊的权限,即`SYSTEM_ALERT_WINDOW`权限。这个权限...

    stm32按键长按短按示例.7z

    "Inc"目录则可能包含相关的头文件,定义了函数原型和全局变量。"Drivers"目录可能包含STM32 HAL库或者其他驱动代码,用于配置和操作STM32的外设。 总的来说,这个示例项目旨在教会开发者如何利用STM32的EXTI和...

    物理按键监听

    在Android系统中,物理按键监听是一项重要的功能,它允许开发者获取到用户对设备硬件按键的交互事件,如电源键、音量键等的按下和释放。这篇内容将深入探讨如何在Android 6.0(API级别23)及更高版本中实现物理按键...

    android全局事件监听器

    在Android开发中,全局事件监听器是一种非常实用的设计模式,它允许开发者在整个应用程序范围内监听特定的事件,而无需在每个需要响应的类中都添加监听代码。标题中的"android全局事件监听器"指的是一个实现此类功能...

    Android 自定义全局Loading页面

    在Android应用开发中,为了提供良好的用户体验,我们经常需要在数据加载、网络请求或其它耗时操作时显示全局Loading页面,以告知用户程序正在进行后台处理并提示他们稍等片刻。本篇文章将深入探讨如何在Android中...

    android键盘弹出/收起监听demo

    在Android开发中,有时我们需要对软键盘的弹出和收起事件进行监听,例如在聊天应用或者表单填写页面,确保布局随着键盘的显示和隐藏做出相应的调整。本示例"android键盘弹出/收起监听demo"提供了一个实用的方法来...

    Android 全局悬浮View实现,以及悬浮view点击事件

    在Android开发中,全局悬浮View通常被称为悬浮按钮(Floating Action Button, FAB),它是一种设计模式,用于在应用中提供一个突出且显眼的交互点,通常用来触发主要的积极操作。这种设计元素常见于现代Android应用...

    Android 设置全局字体大小

    在Android开发中,有时我们需要为应用程序提供全局的字体大小调整功能,以便用户可以根据个人偏好或视力需求定制界面显示。本文将深入探讨如何实现这一功能,主要基于提供的标题"Android 设置全局字体大小"和描述...

    android应用上报按键源码

    本文将深入探讨“android应用上报按键源码”的核心概念、实现机制以及如何在实际项目中应用。 首先,我们需要理解Android中的事件处理模型。在Android系统中,事件处理主要依赖于事件分发链,包括`Activity`、`View...

    android监听屏蔽多任务键

    在Android系统中,多任务键(通常位于设备屏幕下方的硬件按钮或屏幕内的虚拟按键区域)用于开启任务切换器,让用户能够快速在已打开的应用之间切换或者关闭应用。然而,有时候开发者可能希望自定义这个键的功能,...

    Android源码设计模式解析与实战.PDF(完整版)

    根据提供的文件信息“Android源码设计模式解析与实战.PDF(完整版)”,本文将深入探讨其中的关键知识点,包括但不限于Android开发中常见的设计模式及其在实际项目中的应用案例。 ### Android设计模式概述 #### 设计...

Global site tag (gtag.js) - Google Analytics