国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Android模擬藍(lán)牙藍(lán)牙鍵盤——適配Android和Windows

這篇具有很好參考價(jià)值的文章主要介紹了Android模擬藍(lán)牙藍(lán)牙鍵盤——適配Android和Windows。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

學(xué)校寒假有個(gè)程序設(shè)計(jì)比賽,我也一直想要去寫一個(gè)安卓模擬的藍(lán)牙鍵盤,這樣無(wú)論到哪里,比如班班通和沒(méi)有鍵盤的電腦設(shè)備,有手機(jī)就可以操作它,也比USB方便一些。忙活了一個(gè)寒假,也走了不少歪路,終于整成了,下面分享一些經(jīng)驗(yàn)。

(學(xué)校的軟件設(shè)計(jì)比賽已經(jīng)交了終稿了,我的倉(cāng)庫(kù)開(kāi)源在Gitee和GitHub,求求star:
Gitee:https://gitee.com/FengyunTHU/keyboard
GitHub:https://github.com/FengyunTHU/keyboardOFbluetooth)

自己在寫代碼的過(guò)程中也參考了很多CSDN博客,列舉如下:
藍(lán)牙HID——將android設(shè)備變成藍(lán)牙鍵盤(BluetoothHidDevice)
僅通過(guò)藍(lán)牙HID將安卓手機(jī)模擬成鼠標(biāo)和鍵盤
使用舊手檢做成藍(lán)牙鍵盤
CSDN上大佬真的很多!

代碼思路

①第一步是藍(lán)牙HID的初始化

在安卓API28后開(kāi)放了BluetoothHidDevice類,主要就是用它來(lái)完成。首先是注冊(cè)HID服務(wù):

mBtAdapter.getProfileProxy(context, new BluetoothProfile.ServiceListener() {
            @Override
            public void onServiceConnected(int profile, BluetoothProfile proxy) {
                Log.d(TAG, "onServiceConnected: " + profile);
                Toast.makeText(context, "Okk_connected_service", Toast.LENGTH_SHORT).show();
                if (profile == BluetoothProfile.HID_DEVICE) {
                    Log.d(TAG, "Proxy received but it isn't hid_OUT");
                    if (!(proxy instanceof BluetoothHidDevice)) {
                        Log.e(TAG, "Proxy received but it isn't hid");
                        return;
                    }
                    Log.d(TAG,"Connecting HID…");
                    mHidDevice = (BluetoothHidDevice) proxy;
                    Log.d(TAG, "proxyOK");
                    BluetoothHidDeviceAppSdpSettings Sdpsettings = new BluetoothHidDeviceAppSdpSettings(
                            HidConfig.KEYBOARD_NAME,
                            HidConfig.DESCRIPTION,
                            HidConfig.PROVIDER,
                            BluetoothHidDevice.SUBCLASS1_KEYBOARD,
                            HidConfig.KEYBOARD_COMBO
                    );
                    if (mHidDevice != null) {
                        Toast.makeText(context, "OK for HID profile", Toast.LENGTH_SHORT).show();
                        Log.d(TAG, "HID_OK");
                        Log.d(TAG, "Get in register");
                        //getPermission();
                        // 創(chuàng)建一個(gè)BluetoothHidDeviceAppSdpSettings對(duì)象

                        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                            // TODO: Consider calling
                            Log.d(TAG,"return before register");
                            String[] list = new String[] {
                                    Manifest.permission.BLUETOOTH_SCAN,
                                    Manifest.permission.BLUETOOTH_CONNECT
                            };
                            requestPermissions(activity,list,1);
                            return;
                        }
                        BluetoothHidDeviceAppQosSettings inQos = new BluetoothHidDeviceAppQosSettings(
                                BluetoothHidDeviceAppQosSettings.SERVICE_GUARANTEED, 200, 2, 200,
                                10000 /* 10 ms */, 10000 /* 10 ms */);
                        BluetoothHidDeviceAppQosSettings outQos = new BluetoothHidDeviceAppQosSettings(
                                BluetoothHidDeviceAppQosSettings.SERVICE_GUARANTEED, 900, 9, 900,
                                10000 /* 10 ms */, 10000 /* 10 ms */);
                        mHidDevice.registerApp(Sdpsettings, null, null, Executors.newCachedThreadPool(), mCallback);
                        // registerApp();// 注冊(cè)
                    } else {
                        Toast.makeText(context, "Disable for HID profile", Toast.LENGTH_SHORT).show();
                    }
                    // 啟用設(shè)備發(fā)現(xiàn)
                    // requestLauncher.launch(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE));
                    Log.d(TAG, "Discover");
                }
            }

            @SuppressLint("MissingPermission")
            @Override
            public void onServiceDisconnected(int profile) {// 斷開(kāi)連接
                if (profile == BluetoothProfile.HID_DEVICE) {
                    Log.d(TAG, "Unexpected Disconnected: " + profile);
                    mHidDevice = null;
                    mHidDevice.unregisterApp();
                }
            }
        }, BluetoothProfile.HID_DEVICE);
    }



    public final BluetoothHidDevice.Callback mCallback = new BluetoothHidDevice.Callback() {
        private final int[] mMatchingStates = new int[]{
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_CONNECTING,
                BluetoothProfile.STATE_CONNECTED
        };
        @Override
        public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
            Log.d(TAG, "ccccc_str");
            if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                return;
            }
            Log.d(TAG, "onAppStatusChanged: " + (pluggedDevice != null ? pluggedDevice.getName() : "null") + "registered:" + registered);
            // Toast.makeText(context, "onAppStatusChanged", Toast.LENGTH_SHORT).show();
            IsRegisted = registered;
            if (registered) {
                // 應(yīng)用已注冊(cè)
                Log.d(TAG, "register OK!.......");
//                List<BluetoothDevice> matchingDevices = mHidDevice.getDevicesMatchingConnectionStates(mMatchingStates);
//                Log.d(TAG, "paired devices: " + matchingDevices + "  " + mHidDevice.getConnectionState(pluggedDevice));
//                Toast.makeText(context, "paired devices: " + matchingDevices + "  " + mHidDevice.getConnectionState(pluggedDevice), Toast.LENGTH_SHORT).show();
//                if (pluggedDevice != null && mHidDevice.getConnectionState(pluggedDevice) != BluetoothProfile.STATE_CONNECTED) {
//                    boolean result = mHidDevice.connect(pluggedDevice);// pluggedDevice即為連接到模擬HID的設(shè)備
//                    Log.d(TAG, "hidDevice connect:" + result);
//                    Toast.makeText(context, "hidDevice connect:" + result, Toast.LENGTH_SHORT).show();
//                } else if (matchingDevices != null && matchingDevices.size() > 0) {
//                    // 選擇連接的設(shè)備
//                    mHostDevice = matchingDevices.get(0);// 獲得第一個(gè)已經(jīng)配對(duì)過(guò)的設(shè)備
//                    Toast.makeText(context, "device_is_ok: " + mHostDevice.getName() + mHostDevice.getAddress(), Toast.LENGTH_SHORT).show();
//                } else {
//                    // 注冊(cè)成功未配對(duì)
//                }
            }
//            } else {
//                // 應(yīng)用未注冊(cè)
//            }
        }

        @Override
        public void onConnectionStateChanged(BluetoothDevice device, int state) {
            Log.d(TAG, "onConnectStateChanged:" + device + "  state:" + state);
            // Toast.makeText(context, state, Toast.LENGTH_SHORT).show();
            if (state == BluetoothProfile.STATE_CONNECTED) {// 已經(jīng)連接了
                connected = true;
                mHostDevice = device;
                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                    // TODO: Consider calling
                    return;
                }
                Log.d(TAG,"hid state is connected");
                Log.d(TAG,"-----------------------------------connected HID");
                Log.d(TAG,device.getName().toString());
                // Toast.makeText(context, "device_is_ok: " + mHostDevice.getName() + mHostDevice.getAddress(), Toast.LENGTH_SHORT).show();
            } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
                connected = false;
                Log.d(TAG,"hid state is disconnected");
                // mHostDevice = null;
                // Toast.makeText(context, "device_is_null", Toast.LENGTH_SHORT).show();
            } else if (state == BluetoothProfile.STATE_CONNECTING) {
                Log.d(TAG,"hid state is connecting");
            }
        }
    };

mBtAdapter.getProfileProxy()中注冊(cè),其中onServiceConnected()會(huì)在開(kāi)始注冊(cè)時(shí)調(diào)用,其中的mHidDevice.registerApp()就是注冊(cè)采用的方法,提供的SdpSettings是最主要的的HID描述,其中定義一系列常量用于描述模擬的HID設(shè)備。
進(jìn)行注冊(cè)時(shí)會(huì)有一個(gè)回調(diào)mCallbackonAppStatusChanged()調(diào)用在注冊(cè)成功,onConnectStateChanged()則是在藍(lán)牙連接狀態(tài)改變時(shí)調(diào)用,如連接上、斷開(kāi)、正在連接,其中的日志可以反應(yīng)藍(lán)牙連接的狀態(tài)。
注意注冊(cè)HID時(shí),藍(lán)牙必須處于打開(kāi)狀態(tài)。打開(kāi)藍(lán)牙的代碼我暫未編寫。

②發(fā)起藍(lán)牙連接

在發(fā)起連接上,我試了從電腦端發(fā)起連接、從手機(jī)端發(fā)起連接,而且使用的都是點(diǎn)進(jìn)系統(tǒng)藍(lán)牙列表的方式,均無(wú)法建立穩(wěn)定連接。
后來(lái)看到大佬文章,解決了這個(gè)問(wèn)題,即使用代理連接:

@SuppressLint("MissingPermission")
    public void ConnectotherBluetooth() {
        mHostDevice = mBtAdapter.getRemoteDevice("B4:8C:9D:AD:9B:9A");
        if (mHostDevice!=null) {
            Log.d(TAG,"Connected is OK");
            Log.d(TAG,mHostDevice.getName());
        }
        mHidDevice.connect(mHostDevice);// 代理連接
    }

只要把mac地址改成所想要連接的藍(lán)牙設(shè)備的mac即可。電腦可以采用cmd指令ipconfig /all,拉到最底即可;手機(jī)使用adb連接后,輸入指令adb shell settings get secure bluetooth_address即可。當(dāng)然也可以直接掃描,但我目前還未完成相關(guān)代碼。

③發(fā)送報(bào)告
    @JavascriptInterface
    @SuppressLint("MissingPermission")
    public void sendKey(String key) {
        byte b1 = 0;
        if (key.length()<=1) {
            char keychar = key.charAt(0);
            if ((keychar>=65)&&(keychar<=90)){
                b1 = 2;
            }
        }
        if (keyMap.SHITBYTE.containsKey(key)) {
            b1 = 2;
        }
        Log.d(TAG,"pre_send: "+key);

        mHidDevice.sendReport(mHostDevice,8,new byte[]{
                b1,0,keyMap.KEY2BYTE.get(key.toUpperCase()),0,0,0,0,0
        });
        mHidDevice.sendReport(mHostDevice,8,new byte[]{
                0,0,0,0,0,0,0,0
        });// 這是松開(kāi)按鍵的報(bào)告
        Log.d(TAG,"after_send: "+key);
    }

發(fā)送報(bào)告使用sendReport(),發(fā)送對(duì)應(yīng)ID字節(jié)的報(bào)告即可。

整體寫完其實(shí)代碼量并不多,但是前期對(duì)API的研究還是挺費(fèi)時(shí)間的。

完成代碼后,耗時(shí)間的還有一些配置:

HidConfig.java——HID配置文件

這玩意在安卓上適配都挺好,但Windows上會(huì)有一些問(wèn)題。我自己找了一版描述符,目前是正常的(也是在GitHub上找的):

public class HidConfig {
    public final static String KEYBOARD_NAME = "My Keyboard";
    public final static String DESCRIPTION = "KKKey";
    public final static String PROVIDER = "Alphabet";
    public final static byte ID_KEYBOARD = 1;

    // HID碼表【不知道干啥的】
    public static final byte[] KEYBOARD_COMBO =
        {
				(byte) 0x05, (byte) 0x01,                         // Usage Page (Generic Desktop)
                (byte) 0x09, (byte) 0x06,                         // Usage (Keyboard)
                (byte) 0xA1, (byte) 0x01,                         // Collection (Application)
                (byte) 0x85, (byte) 0x08,                         //   REPORT_ID (Keyboard)
                (byte) 0x05, (byte) 0x07,                         //   Usage Page (Key Codes)
                (byte) 0x19, (byte) 0xE0,                         //   Usage Minimum (224)
                (byte) 0x29, (byte) 0xE7,                         //   Usage Maximum (231)
                (byte) 0x15, (byte) 0x00,                         //   Logical Minimum (0)
                (byte) 0x25, (byte) 0x01,                         //   Logical Maximum (1)
                (byte) 0x75, (byte) 0x01,                         //   Report Size (1)
                (byte) 0x95, (byte) 0x08,                         //   Report Count (8)
                (byte) 0x81, (byte) 0x02,                         //   Input (Data, Variable, Absolute)
                (byte) 0x95, (byte) 0x01,                         //   Report Count (1)
                (byte) 0x75, (byte) 0x08,                         //   Report Size (8)
                (byte) 0x81, (byte) 0x01,                         //   Input (Constant) reserved byte(1)
                (byte) 0x95, (byte) 0x05,                         //   Report Count (5)
                (byte) 0x75, (byte) 0x01,                         //   Report Size (1)
                (byte) 0x05, (byte) 0x08,                         //   Usage Page (Page# for LEDs)
                (byte) 0x19, (byte) 0x01,                         //   Usage Minimum (1)
                (byte) 0x29, (byte) 0x05,                         //   Usage Maximum (5)
                (byte) 0x91, (byte) 0x02,                         //   Output (Data, Variable, Absolute), Led report
                (byte) 0x95, (byte) 0x01,                         //   Report Count (1)
                (byte) 0x75, (byte) 0x03,                         //   Report Size (3)
                (byte) 0x91, (byte) 0x01,                         //   Output (Data, Variable, Absolute), Led report padding
                (byte) 0x95, (byte) 0x06,                         //   Report Count (6)
                (byte) 0x75, (byte) 0x08,                         //   Report Size (8)
                (byte) 0x15, (byte) 0x00,                         //   Logical Minimum (0)
                (byte) 0x25, (byte) 0x65,                         //   Logical Maximum (101)
                (byte) 0x05, (byte) 0x07,                         //   Usage Page (Key codes)
                (byte) 0x19, (byte) 0x00,                         //   Usage Minimum (0)
                (byte) 0x29, (byte) 0x65,                         //   Usage Maximum (101)
                (byte) 0x81, (byte) 0x00,                         //   Input (Data, Array) Key array(6 bytes)
                (byte) 0xC0                                       // End Collection (Application)
        };
}

也確實(shí)嘗試了很多版,這版可以。但需要注意其中的(byte) 0x85, (byte) 0x08, // REPORT_ID (Keyboard)反映了報(bào)告的ID = 8,需要和report中的相對(duì)應(yīng),如:

mHidDevice.sendReport(mHostDevice,/*ID = 8*/8,new byte[]{
                0,0,0,0,0,0,0,0
        });// 這是松開(kāi)按鍵的報(bào)告

②Windows上的適配

實(shí)際測(cè)試發(fā)現(xiàn),Android適配很好,但Windows總是沒(méi)反應(yīng),也困擾了我很長(zhǎng)時(shí)間。
后來(lái)使用Wireshark對(duì)藍(lán)牙抓包,發(fā)現(xiàn)安卓是這樣的:
android 設(shè)備做從模擬鼠標(biāo)鍵盤,android,windows,計(jì)算機(jī)外設(shè)
而Windows總是這樣:
android 設(shè)備做從模擬鼠標(biāo)鍵盤,android,windows,計(jì)算機(jī)外設(shè)
顯示正在Pending,無(wú)法直接success;而且相同SCID的request最后會(huì)以PSM not support請(qǐng)求失敗。HID-Control對(duì)應(yīng)的PSM是0x0011。
在Windows開(kāi)發(fā)文檔上看到了以下:

接收傳入 L2CAP 連接請(qǐng)求

若要接收來(lái)自特定 PSM 的任何遠(yuǎn)程設(shè)備的傳入 L2CAP 連接請(qǐng)求,配置文件驅(qū)動(dòng)程序應(yīng)首先生成并發(fā)送 BRB_L2CA_REGISTER_標(biāo)準(zhǔn)版RVER 請(qǐng)求,并在請(qǐng)求的 _BRB_L2CA_REGISTER_標(biāo)準(zhǔn)版RVER 結(jié)構(gòu)的 Psm 成員中指定 NULL,并在請(qǐng)求的 _BRB_L2CA_REGISTER_標(biāo)準(zhǔn)版RVER 結(jié)構(gòu)的 Psm 成員中指定 NULL。 發(fā)送 BRB_L2CA_REGISTER_標(biāo)準(zhǔn)版RVER 請(qǐng)求時(shí),配置文件驅(qū)動(dòng)程序還必須向藍(lán)牙驅(qū)動(dòng)程序堆棧注冊(cè) L2CAP 回調(diào)函數(shù)。 這使藍(lán)牙驅(qū)動(dòng)程序堆棧能夠通知配置文件驅(qū)動(dòng)程序傳入 L2CAP 連接請(qǐng)求。
然后,配置文件驅(qū)動(dòng)程序應(yīng)生成并發(fā)送BRB_REGISTER_PSM請(qǐng)求,以便藍(lán)牙驅(qū)動(dòng)程序堆棧將接受請(qǐng)求注冊(cè)的 PSM 的連接。 否則,藍(lán)牙驅(qū)動(dòng)程序堆棧將拒絕具有未知(未注冊(cè))連接請(qǐng)求的所有連接請(qǐng)求。 有關(guān) PSM 的詳細(xì)信息,請(qǐng)參閱 _BRB_PSM 結(jié)構(gòu)。

所以就感覺(jué)是不是驅(qū)動(dòng)的問(wèn)題。最后下載更新最新版的藍(lán)牙驅(qū)動(dòng)即可。注意更新完后要重啟。
于是問(wèn)題就解決了。抓包結(jié)果是,雖然也不是立刻success,但是最后依然請(qǐng)求成功。這估計(jì)是因?yàn)閃indows多了以上的請(qǐng)求過(guò)程機(jī)制。

④一些連接問(wèn)題

  • 不要在手機(jī)或電腦的系統(tǒng)列表中點(diǎn)擊設(shè)備進(jìn)行連接。 直接在模擬鍵盤端使用connect()的代理連接即可,直接連到mac地址;
  • 需要兩個(gè)設(shè)備提前配對(duì)。當(dāng)然在發(fā)起連接的過(guò)程中配對(duì)也可以。如果無(wú)法連接,嘗試刪除設(shè)備后在重新配對(duì)連接;
  • iOSMac系統(tǒng),因?yàn)槲覜](méi)有對(duì)應(yīng)的設(shè)備,沒(méi)有進(jìn)行測(cè)試。不過(guò)也可以參考我參考文章中的第二篇;
  • 注意到很容易斷開(kāi)連接。所以可能需要在斷開(kāi)時(shí)控制繼續(xù)連上。測(cè)試下來(lái)繼續(xù)連接的用時(shí)是很短的。

所有代碼開(kāi)源在Gitee倉(cāng)庫(kù)keyboard。筆者不是計(jì)算機(jī)專業(yè)的學(xué)生,甚至所學(xué)專業(yè)相差甚遠(yuǎn);作為在校大學(xué)生,時(shí)間也十分有限,Java也是邊寫邊學(xué)的,代碼格式不規(guī)范有勞大家諒解。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-860930.html

到了這里,關(guān)于Android模擬藍(lán)牙藍(lán)牙鍵盤——適配Android和Windows的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Android連接藍(lán)牙設(shè)備問(wèn)題(android.permission.BLUETOOTH)

    ????????近期遇到一個(gè)問(wèn)題,之前發(fā)布的APP連接藍(lán)牙都是正常的,現(xiàn)在有人反映連不上了。經(jīng)過(guò)測(cè)試發(fā)現(xiàn):android 12 和 harmonyOS 3.0.0 都會(huì)有這個(gè)問(wèn)題,而之前的版本就不會(huì)有這個(gè)。 ????????經(jīng)過(guò)網(wǎng)上一番查找,原來(lái)是因?yàn)樽罱麲oogle發(fā)布的Android 12,新引入了 BLUETOOTH_SCAN、

    2024年01月16日
    瀏覽(24)
  • RK3288 Android11 RTL8723DS WiFi 和 藍(lán)牙Bluetooth 適配

    RK3288 Android11 RTL8723DS WiFi 和 藍(lán)牙Bluetooth 適配

    ??瑞芯微RK系列對(duì)“REALTEK瑞昱公司”的wifi、藍(lán)牙雙模的模組都有內(nèi)置適配的,因此HAL層、Framework層、協(xié)議棧及庫(kù)文件都不需要移植適配,只需修改設(shè)備樹(shù)和一些配置文件即可。 補(bǔ)充: RTL8723DS時(shí)鐘輸入源講解 ??RTL8723DS模組的第24號(hào)引腳是模組的時(shí)鐘輸入源,需要給此提供

    2024年02月07日
    瀏覽(19)
  • 《Android學(xué)習(xí)筆記》Android12藍(lán)牙掃描不到設(shè)備的權(quán)限問(wèn)題

    Android12 關(guān)于藍(lán)牙這部分新增了 BLUETOOTH_SCAN 、 BLUETOOTH_ADVERTISE 和 BLUETOOTH_CONNECT 權(quán)限,這些權(quán)限都屬于敏感權(quán)限,都需要在代碼中動(dòng)態(tài)申請(qǐng)。移除了Android11 及以下版本中必須申請(qǐng)的位置權(quán)限[ FINE_LOCATION ] 和 [ COARES_LOCATION ]。 1、在Manifest.xml清單文件中添加對(duì)應(yīng)的權(quán)限。 其中 An

    2024年02月15日
    瀏覽(23)
  • 基于XG24-EK2703A的BLE HID藍(lán)牙鍵盤+鼠標(biāo)復(fù)合設(shè)備功能開(kāi)發(fā)(BLE+HID+FreeRTOS+Gecko SDK)

    基于XG24-EK2703A的BLE HID藍(lán)牙鍵盤+鼠標(biāo)復(fù)合設(shè)備功能開(kāi)發(fā)(BLE+HID+FreeRTOS+Gecko SDK)

    ?? 【Funpack3-1】基于XG24-EK2703A的BLE HID藍(lán)牙鍵盤+鼠標(biāo)復(fù)合設(shè)備 ?? Github: EmbeddedCamerata/XG24_ble_hid_keymouse 本項(xiàng)目基于Silicon Labs XG24-EK2703A開(kāi)發(fā)板,通過(guò)HID協(xié)議實(shí)現(xiàn)了一個(gè)藍(lán)牙鍵盤+鼠標(biāo)復(fù)合設(shè)備,可通過(guò)按鍵實(shí)現(xiàn)上下翻頁(yè)、發(fā)送字符功能。使用板載兩個(gè)按鍵,當(dāng)BTN0按下,向上翻頁(yè);

    2024年01月25日
    瀏覽(25)
  • 【Unity 實(shí)用插件篇】 | UI適配神器 Device Simulator 移動(dòng)設(shè)備模擬器 的詳細(xì)使用方法

    【Unity 實(shí)用插件篇】 | UI適配神器 Device Simulator 移動(dòng)設(shè)備模擬器 的詳細(xì)使用方法

    前言 今天帶來(lái)的是Unity提供的一個(gè)設(shè)備模擬器 Device Simulator 。 它可以幫助開(kāi)發(fā)者在編輯器中模擬出移動(dòng)端的環(huán)境,直接進(jìn)行測(cè)試。 所有操作都可以在編輯器上進(jìn)行#

    2024年02月11日
    瀏覽(27)
  • Android 全局監(jiān)聽(tīng)軟鍵盤彈起隱藏 動(dòng)態(tài)修改布局并適配無(wú)限循環(huán)的問(wèn)題

    要在 Android 應(yīng)用中全局檢測(cè)軟鍵盤的彈起,您可以使用 ViewTreeObserver.OnGlobalLayoutListener 監(jiān)聽(tīng)器來(lái)監(jiān)聽(tīng)布局樹(shù)的變化。當(dāng)軟鍵盤彈起或隱藏時(shí),布局樹(shù)會(huì)發(fā)生變化,因此您可以在監(jiān)聽(tīng)器中捕獲這些變化。 在上面的代碼中, rootView 是您布局的根視圖,您需要將其替換為您實(shí)際布局

    2024年02月11日
    瀏覽(25)
  • 【Unity 實(shí)用工具篇】? | UI適配神器 Device Simulator 移動(dòng)設(shè)備模擬器 的詳細(xì)使用方法

    【Unity 實(shí)用工具篇】? | UI適配神器 Device Simulator 移動(dòng)設(shè)備模擬器 的詳細(xì)使用方法

    前言 今天帶來(lái)的是Unity提供的一個(gè)設(shè)備模擬器 Device Simulator 。 它可以幫助開(kāi)發(fā)者在編輯器中模擬出移動(dòng)端的環(huán)境,直接進(jìn)行測(cè)試。 所有操作都可以在編輯器上進(jìn)行#

    2024年02月14日
    瀏覽(36)
  • Android Ble藍(lán)牙App(六)請(qǐng)求MTU與顯示設(shè)備信息

    Android Ble藍(lán)牙App(六)請(qǐng)求MTU與顯示設(shè)備信息

    ??在上一篇文章中已經(jīng)了解了數(shù)據(jù)操作的方式,而數(shù)據(jù)交互的字節(jié)長(zhǎng)度取決于我們手機(jī)與藍(lán)牙設(shè)備的最大支持長(zhǎng)度。 Ble藍(lán)牙App(一)掃描 Ble藍(lán)牙App(二)連接與發(fā)現(xiàn)服務(wù) Ble藍(lán)牙App(三)特性和屬性 Ble藍(lán)牙App(四)UI優(yōu)化和描述符 Ble藍(lán)牙App(五)數(shù)據(jù)操作 Ble藍(lán)牙App(六)

    2024年02月04日
    瀏覽(25)
  • Android HID設(shè)備(鍵盤、遙控等)功能實(shí)現(xiàn)流程及鍵值映射關(guān)系

    Android HID設(shè)備(鍵盤、遙控等)功能實(shí)現(xiàn)流程及鍵值映射關(guān)系

    HID (Human Interface Device,人機(jī)接口設(shè)備)是USB設(shè)備中常用的設(shè)備類型,是直接與人交互的USB設(shè)備,例如鍵盤、遙控器、鼠標(biāo)與游戲桿等。在USB設(shè)備中,HID設(shè)備的成本較低。 ? ? 之前文章?android 鍵盤(遙控)鍵值定義大全?中整理了android中各種功能鍵值定義,那么從鍵盤按鍵到

    2024年02月04日
    瀏覽(17)
  • 如何啟動(dòng)Android studio 中的設(shè)備模擬器

    如何啟動(dòng)Android studio 中的設(shè)備模擬器

    一、在 Android Studio 中單獨(dú)啟動(dòng)設(shè)備模擬器 打開(kāi) Android Studio,確保您已經(jīng)創(chuàng)建了一個(gè)虛擬設(shè)備或者正在運(yùn)行一個(gè)設(shè)備模擬器。 在工具欄中找到并點(diǎn)擊 \\\"AVD Manager\\\" 按鈕。 在 AVD Manager 窗口中,找到您要啟動(dòng)的設(shè)備模擬器,并點(diǎn)擊其右側(cè)的 \\\"Play\\\" 按鈕。 此時(shí),Android Studio 將會(huì)啟動(dòng)

    2024年02月12日
    瀏覽(36)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包