第一篇:iOS 藍牙使用小結 bluetooth
iOS 藍牙使用小結 bluetooth
現將創建藍牙工程的要點總結一下,由于工程主要涉及中心模式,所以只總結中心模式的用法 1,引入CoreBluetooth.framework 2,實現藍牙協議,如:.h文件如下
@protocol CBCentralManagerDelegate;
@protocol CBPeripheralDelegate;@interface ViewController : UIViewController.m文件如下
#import “CoreBluetooth/CoreBluetooth.h”
另外還有代理部分請自行添加3,下面是使藍牙動起來的過程
3.1創建CBCentralManager實例
self.cbCentralMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
設置代理,比如:
self.cbCentralMgr.delegate = self;創建數組管理外設
self.peripheralArray = [NSMutableArray array];3.2掃描周圍的藍牙
實際上周圍的藍牙如果可被發現,則會一直往外發送廣告消息,中心設備就是通過接收這些消息來發現周圍的藍牙的
NSDictionary * dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false],CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[self.cbCentralMgr scanForPeripheralsWithServices:nil options:dic];3.3發現一個藍牙設備
也就是收到了一個周圍的藍牙發來的廣告信息,這是CBCentralManager會通知代理來處理
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
}
如果周圍的藍牙有多個,則這個方法會被調用多次,你可以通過tableView或其他的控件把這些周圍的藍牙的信息打印出來
3.4連接一個藍牙
[self.cbCentralMgr connectPeripheral:peripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];
一個中心設備可以同時連接多個周圍的藍牙設備
當連接上某個藍牙之后,CBCentralManager會通知代理處理-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { }因為在后面我們要從外設藍牙那邊再獲取一些信息,并與之通訊,這些過程會有一些事件可能要處理,所以要給這個外設設置代理,比如: peripheral.delegate = self;
3.5查詢藍牙服務
[peripheral discoverServices:nil];返回的藍牙服務通知通過代理實現
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {for(CBService* service in peripheral.services){
} }
3.6查詢服務所帶的特征值
[peripheral discoverCharacteristics:nil forService:service];
返回的藍牙特征值通知通過代理實現-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {for(CBCharacteristic * characteristic in service.characteristics){ } }
3.7給藍牙發數據
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
這時還會觸發一個代理事件
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { }
3.8處理藍牙發過來的數據
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { }3.9 retrievePeripheralsWithIdentifiers 使用例子
-(IBAction)Retrieve:(id)Sender { [self.tvLog setText:@“"];
NSMutableArray * Identifiers = [NSMutableArray array];
for(CBPeripheral * peripheral in self.peripheralArray){ [Identifiers addObject:peripheral.identifier];}[self addLog:@”[self.cbCentralMgr retrievePeripheralsWithIdentifiers:self.PeripheralIdentifiers]“];
self.retrievePeripherals = [self.cbCentralMgr retrievePeripheralsWithIdentifiers:Identifiers];
for(CBPeripheral* peripheral in self.retrievePeripherals){ [self addLog:[NSString stringWithFormat: @”%@ name:%@“,peripheral,peripheral.name]];}
[self.tableViewPeripheral reloadData];}3.10 retrieveConnectedPeripheralsWithServices 使用例子-(IBAction)Retrieve:(id)Sender { [self.tvLog setText:@”“];
NSMutableArray * services = [NSMutableArray array];
for(CBPeripheral * peripheral in self.peripheralArray){
if(peripheral.isConnected){
for(CBService *service in peripheral.services){ [services addObject:service.UUID];} } }[self addLog:@”[self.cbCentralMgr retrieveConnectedPeripheralsWithServices:peripheral.services]“];
self.retrievePeripherals = [self.cbCentralMgr retrieveConnectedPeripheralsWithServices:services];
for(CBPeripheral* peripheral in self.retrievePeripherals){ [self addLog:[NSString stringWithFormat: @”%@ name:%@",peripheral,peripheral.name]];}
[self.tableViewPeripheral reloadData];}
第二篇:藍牙知識小結
藍牙協議知識總結
藍牙設備 和 主機進行連接和數據通信的 流程如下:外部設備發出廣告(帶有UUID信息等其他信息); 主機(集中器設備)收到廣告信息,進而發送掃描請求;表示我掃描到你的信息; 3 外部設備收到掃描請求后,返回掃描回應,表示我知道你掃描到我的信息; 4 主機進而發送連接請求信息,表示主機要跟設備建立無線連接; 5 設備收到連接請求后,發送相應請求回應;表示已經建立連接;
數據讀寫流程如下進一步(在建立連接的基礎上): 主機發送主服務UUID(設備的廣告UUID)給設備;
服務發現 7 設備收到后回應服務信息; 主機發送特性UUID;
特性發現 9 設備收到后回應特性值句柄;(類似于存儲設備的地址)主機發送特性值句柄;
讀信息 11 設備收到后回應特性值; 主機發送特性值句柄和要寫入值;
寫信息 13 設備回應寫入成功響應;
在睡眠狀態,耗電只有1微安(uA),而在連接事件中最高的是10幾個毫安
連接建立之后,再進行安全密鑰的交換配對,進而進行數據的讀寫;
主機和從機綁定之后,斷開連接后,可以快速的建立連接并進行加密讀寫,而不需要再次配對; 特點 低功耗藍牙速度只有100bps,傳統藍牙有3Mbps 2 低功耗藍牙不需要IOS 的MFI 認證,傳統藍牙必須; 3 低功耗藍牙能紐扣電池能用1年多,傳統藍牙不行;
頻道:
2.4G – 2.48G 總共40個頻段,每2M 一個頻段;
其中 37(2.40G),38(2.426G),39(2.48G)為 3個廣播頻道;這3個頻道避開了wifi 常用的頻道,與wifi可以共存; 其他37個為連接頻道;
1、BLE中主從機建立連接,到配對和綁定的過程如下圖。
正如上圖所示,最簡單一次藍牙通信需要以上相關步驟,包括discovery device,connect,pairing,bond等4個主要部分。
1)廣播:廣播包可以包含廣播數據,廣播包可以無指定或者對指定的設備發送??梢月暶髟撈骷强蛇B接的還是不可連接的。在一次廣播中,廣播包可以在三個廣播通道中同時發送。
廣播類型 :1 未指定可連接 2 指定可連接 3 未指定 不可見 4 未指定不可連接 #define GAP_ADTYPE_ADV_IND
0x00 //!< Connectable undirected advertisement #define GAP_ADTYPE_ADV_DIRECT_IND 0x01 //!< Connectable directed advertisement #define GAP_ADTYPE_ADV_DISCOVER_IND
0x02 //!< Discoverable undirected advertisement #define GAP_ADTYPE_ADV_NONCONN_IND
0x03 //!< Non-Connectable undirected advertisement #define GAP_ADTYPE_SCAN_RSP_IND
0x04 //!< Only used in gapDeviceInfoEvent_t 在peripheral.c中
GAPRole_Init(taskID++);進行了初始化設置
還有有以下函數bStatus_t GAPRole_SetParameter(uint16 param, uint8 len, void *pValue)GAPRole_GetParameter(…..)可以調用進行設置。2)scanning 1)被動掃描: 掃描者監聽廣播頻道的廣播包,收到后將其上傳到host層
2)主動掃描:掃描者監聽廣播頻道的廣播包,當收到廣播包后掃描者發送一個scan Request包,廣播設備回應一個scan reponse包
3)Connection 在掃描設備掃描到一個可連接的廣播消息后,掃描設備可以通過發送 connection reequst 包給廣播設備從而成為連接的發起者
Connection resqust 包含從機鏈路層一系列的參數,這些參數聲明連接時的通道及時序要求。建立連接
GAPCentralRole_EstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,DEFAULT_LINK_WHITE_LIST,addrType, peerAddr);廣播設備接收了連接請求,就進入了連接狀態,發起者成了主機,廣播設備成了從機。兩個已連接的設備的所有通信發生在連接事件中,連接事件周期性的發生,周期由連接間隔參數決定。
連接間隔:使用調頻的間隔;兩個連接事件之間的時間間隔,藍牙傳數據是在一個頻段發送數據后,然后跳到另一個頻段再傳數據,從一個頻道另一個頻段的時間間隔就是連接間隔;即使沒有數據發送,也要調頻切換測試包是否連接斷開;所以,連接間隔是定時的存在;可以認為是一個固定的時序;每隔一段時間就自動調到另一個頻道的去建立連接;這個時間中,是很少功耗的,基本沒有;
單位是1.25毫秒;范圍是 6----3200個單位;也就是1.25ms到4s的范圍 不同的應用 時間間隔不一樣,時間間隔長,功耗就低,傳輸數據慢; 時間間隔短,功耗就高,傳輸數據就快。
從機延時: 從機如果沒有數據發送,可以跳過連接間隔,不用頻繁的定時去建立連接,從而過一段較長時間再去建立連接;這個時間就是從機延時時間;從而功耗降低很多;單位是和連接間隔一樣;范圍是 0---499
管理超時
超過這個時間,還沒有建立連接,則認為是連接丟失,斷開?;氐轿催B接狀態;
單位是10ms,范圍是 10(100ms)-----3200(32s)。超時值必須比有效連接間隔大;有效連接間隔= 連接間隔×(1+從機延時)
如果從機不想使用當前的連接參數,可以向主機發送連接更新請求,從機設備可以在任何時候發送連接更新請求,使得從機可以動態的調整連接參數。
GAPCentralRole_UpdateLink(simpleBLEConnHandle,DEFAULT_UPDATE_MIN_CONN_INTERVAL,DEFAULT_UPDATE_MAX_CONN_INTERVAL,DEFAULT_UPDATE_SLAVE_LATENCY,DEFAULT_UPDATE_CONN_TIMEOUT);無論主機還是從機,都可以無條件的終止當前連接,一方請求終止,另一方必須在斷開連接狀態之前響應。
連接還可以由超時而終止。超時時間小于32s,大于有效連接間隔(連接間隔×(1+從機延時))
終止連接函數:
GAPCentralRole_TerminateLink(simpleBLEConnHandle);主機和從機保存各自的超時計時器,每次收到數據包就清零,一旦達到超時數值,就認為連接已經丟失就會斷開連接。
連接超時判斷,終止連接在程序中還沒找到。
2.BLE中的GAP和GATT GAP個人認為就是監控上圖中的交互狀態,比如從廣播變成連接,到配對等。
GATT通俗理解為用于主從機之間的客戶端和服務器端的數據交互,以Attribute Table來體現。
GAP Role Profile:在GAP所處的4個角色:廣播Advertise,主機central,從機Peripheral,觀察者Observer。GATT Attribute:通用屬性配置文件。
GAP作為Peripheral Role需要設置的核心參數如下 GAPROLE_ADVERT_ENABLED:廣播使能。GAPROLE_ADVERT_DATA:廣播時的參數,GAPROLE_SCAN_RSP_DATA:從機掃描響應,返回的數據包
GAPROLE_MIN_CONN_INTERVAL:處于連接狀態后的設備,都會有個hop,一段時間內進行數據交互,以保證兩者是連接的。當前后兩次交互時,需要等待的最小間隔時間 GAPROLE_MAX_CONN_INTERVAL:...需要等待的最大間隔時間
GAPROLE_SLAVE_LATENCY:處于連接后,從機可以做出不響應連接請求的間隔數目,即跳過n個交互的連接。
GAPROLE_TIMEOUT_MULTIPLIER:從上次成功連接到這次連接成功的最大允許延時。如果規定時間內未成功則認為本次連接失敗,丟棄。該值必須比有效連接的間隔大。GAPROLE_PARAM_UPDATE_ENABLE:請求主機更新參數,主機可以接受也可以拒絕。.GATT Server的相關設置函數。
// Initialize GATT attributes GGS_AddService(GATT_ALL_SERVICES);// GAP Service GATTServApp_AddService(GATT_ALL_SERVICES);// GATT attributes DevInfo_AddService();// Device Information Service SimpleProfile_AddService(GATT_ALL_SERVICES);// Simple GATT Profile 通常一個GATT中GAP server和GATT server是必須強制存在的,還有設備信息服務以及自己設計的profile server.SimpleProfile_AddService就是添加自己設計的profile server 在SimpleProfile_AddService函數中調用了如下函數
GATTServApp_RegisterService(simpleProfileAttrTbl, GATT_NUM_ATTRS(simpleProfileAttrTbl),&simpleProfileCBs);
} simpleProfileCBs 的函數定義如下:
CONST gattServiceCBs_t simpleProfileCBs = { simpleProfile_ReadAttrCB,// Read callback function pointer
simpleProfile_WriteAttrCB, // Write callback function pointer
NULL
// Authorization callback function pointer };實際上就是底層讀寫數據的函數,主機讀數據時從機會調用simpleProfile_ReadAttrCB函數,寫數據時從機會調用simpleProfile_WriteAttrCB函數。這兩個函數在simpleGaatprofile.c 中實現。
注意在simpleProfile_WriteAttrCB,函數中有如下語句 if((notifyApp!= 0xFF)&& simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange)
{
simpleProfile_AppCBs->pfnSimpleProfileChange(notifyApp);
} 注意函數指針的用法,實際是在接收到主機數據發過來的數據后調用simpleProfileChangeCB函數來處理接收到的數據。這個函數在初始化時注冊,下面會講到。作為GATT的server和client,主要通過Attribute來進行交互,當client請求server讀取數據時,通過如下注冊的回調函數來進行訪問。
// Register callback with SimpleGATTprofile
VOID SimpleProfile_RegisterAppCBs(&simpleBLEPeripheral_SimpleProfileCBs);//給應用注冊回調函數,這個函數非常重要 在回調函數中對數據做出處理。
static simpleProfileCBs_t simpleBLEPeripheral_SimpleProfileCBs = {
simpleProfileChangeCB
// Charactersitic value change callback };
在SimpleProfile_RegisterAppCBs 函數中賦值。simpleProfile_AppCBs = simpleProfileChangeCB
在simpleProfileChangeCB函數中可以啟動定時器來給主機發送Notification數據
發送數據函數為GATT_Notification(noti_cHandle,&pReport,FALSE);
如下:
{ static attHandleValueNoti_t pReport;//聲明attHandleValueNoti_t這個結構體
uint16 noti_cHandle;//存放handle
pReport.handle = simpleProfileAttrTbl[11].handle;//讀取notification對應的handle
GAPRole_GetParameter(0x30E, ¬i_cHandle);//獲取Connection Handle
pReport.len = 1;//數據長度
pReport.value[0] = 0x03;//賦值
GATT_Notification(noti_cHandle,&pReport,FALSE);}
主機使能Notification:
這個handle應該是相應的characteristic value的handle的后面一個, 就是characteristic value的handle加 1.例子: { attWriteReq_t writeReq;writeReq.handle =;writeReq.len = 2;writeReq.value[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY);
這里是 0x01 writeReq.value[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY);
這里是 0x00 writeReq.sig = 0;writeReq.cmd = 0;GATT_WriteCharValue(simpleBLEConnHandle, &writeReq, simpleBLETaskId);}
這兩個值目的是打開Notification功能.CCC的參數有兩個, 一個Notification, 一個indication.value[0]就是打開關閉notification, value[1]是打開關閉indication.主機數據處理:
simpleBLECentral.c 這個文件, 里面有個函數:simpleBLECentralProcessGATTMsg()這個函數就是處理各種從peripheral過來的數據.但是在示例代碼中并沒有加入通知, 就是notification的接收, 所以你得自己添加代碼.很簡單, 類似 if((pMsg->method == ATT_READ_RSP)||........), 添加 else if((pMsg->method == ATT_HANDLE_VALUE_NOTI)||......)就可以處理從機Notification的數據。
value 被寫的時候首先 simpleProfile_WriteAttrCB()會被調到.最后才會調用 simpleProfileChangeCB()
GAP通過在啟動設備事件的任務處理中啟動設備,其實主要是向GAP中注冊回調函數,讓系統在發現自身運行狀態變化時,調用該函數,方便應用層進行相關操作。if(events & SBP_START_DEVICE_EVT)
{
// Start the Device VOID GAPRole_StartDevice(&simpleBLEPeripheral_PeripheralCBs);//啟動設備,注冊回調函數,用于監督設備的狀態變化:廣播、連接、配對、綁定等。
// Start Bond Manager VOID GAPBondMgr_Register(&simpleBLEPeripheral_BondMgrCBs);} simpleBLEPeripheral_PeripheralCBs函數定義如下
static gapRolesCBs_t simpleBLEPeripheral_PeripheralCBs = {
peripheralStateNotificationCB, // Profile State Change Callbacks
NULL
// When a valid RSSI is read from controller(not used by application)};static void peripheralStateNotificationCB(gaprole_States_t newState)//傳入參數由GPA自己輸入,內部調用回調函數給用戶,處理連接狀態的改變 simpleBLEPeripheral_BondMgrCBs函數定義如下: static gapBondCBs_t simpleBLEPeripheral_BondMgrCBs = {
ProcessPasscodeCB,// 生成配對密碼,發送給主機
ProcessPairStateCB
//主機密碼的校驗處理。配對狀態管理 };
一、修改廣播功率 { #define LL_EXT_TX_POWER_MINUS_23_DBM
0 //-23dbm 功率 最小 #define LL_EXT_TX_POWER_MINUS_6_DBM
//-6dbm
#define LL_EXT_TX_POWER_0_DBM
// 0dbm
#define LL_EXT_TX_POWER_4_DBM // +dbm 功率 最大
HCI_EXT_SetTxPowerCmd(gTxPower);
更新廣播內容
GAP_UpdateAdvertisingData(simpleBLEPeripheral_TaskID, TRUE, sizeof(advertData_Ex), advertData_Ex);}
二、數據加密解密 { uint8 key[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
// 需要加密的數據
uint8 plaintextData[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
// 加密后數據存放區
uint8 encryptedData[16];
// 解密后數據存放區
uint8 deccryptedData[16];
// 開始加密
LL_Encrypt(key, plaintextData, encryptedData);
// 開始解密
LL_EXT_Decrypt(key, encryptedData, deccryptedData);}
三、設置從機廣播時間 1)、廣播模式必須是Limited Discoverable mode 在advertData 中加入此AD string: 0x02, GAP_ADTYPE_FLAGS, GAP_ADTYPE_FLAGS_LIMITED| GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED 2)、設置時間
//Maximum time to remain advertising, when in Limited Discoverable mode.unit is seconds #define USER_DEF_ADV_TIMEOUT
GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, USER_DEF_ADV_TIMEOUT);
第三篇:Android藍牙開發小結
學習之前先了解兩個基本概念:
一、RFCOMM協議:
一個基于歐洲電信標準協會ETSI07.10規程的串行線性仿真協議。此協議提供RS232控制和狀態信號,如基帶上的損壞,CTS以及數據信號等,為上層業務(如傳統的串行線纜應用)提供了傳送能力。
RFCOMM是一個簡單傳輸協議,其目的是針對如何在兩個不同設備上的應用之間保證一條完整的通信路徑,并在它們之間保持一通信段。
RFCOMM是為了兼容傳統的串口應用,同時取代有線的通信方式,藍牙協議棧需要提供與有線串口一致的通信接口而開發出的協議。RFCOMM協議提供對基于L2CAP協議的串口仿真,基于ETSI07.10??芍С衷趦蓚€BT設備之間同時保持高達60路的通信連接。
RFCOMM只針對直接互連設備之間的連接,或者是設備與網絡接入設備之間的互連。通信兩端設備必須兼容于RFCOMM協議,有兩類設備:DTE(Data Terminal Endpoint,通信終端,如PC,PRINTER)和DCE(Data Circuit Endpoint,通信段的一部分,如Modem)。此兩類設備不作區分。
二、MAC硬件地址
MAC(Medium/MediaAccess Control, 介質訪問控制)MAC地址是燒錄在NetworkInterfaceCard(網卡,NIC)里的.MAC地址,也叫硬件地址,是由48比特長(6字節),16進制的數字組成.0-23位叫做組織唯一標志符(organizationally unique,是識別LAN(局域網)節點的標識.24-47位是由廠家自己分配。其中第40位是組播地址標志位。網卡的物理地址通常是由網卡生產廠家燒入網卡的EPROM(一種閃存芯片,通??梢酝ㄟ^程序擦寫),它存儲的是傳輸數據時真正賴以標識發出數據的電腦和接收數據的主機的地址。
Android平臺提供的藍牙API去實現藍牙設備之間的通信,藍牙設備之間的通信主要包括了四個步驟:設置藍牙設備、尋找局域網內可能或者匹配的設備、連接設備和設備之間的數據傳輸。以下是建立藍牙連接的所需要的一些基本類:
BluetoothAdapter類:代表了一個本地的藍牙適配器。它是所有藍牙交互的的入口點。利用它你可以發現其他藍牙設備,查詢綁定了的設備,使用已知的MAC地址實例化一個藍牙設備和建立一個BluetoothServerSocket(作為服務器端)來監聽來自其他設備的連接。
BluetoothDevice類:代表了一個遠端的藍牙設備,使用它請求遠端藍牙設備連接或者獲取遠端藍牙設備的名稱、地址、種類和綁定狀態。(其信息是封裝在bluetoothsocket中)。
Bluetoothsocket類:代表了一個藍牙套接字的接口(類似于tcp中的套接字),它是應用程序通過輸入、輸出流與其他藍牙設備通信的連接點。
Blueboothserversocket類:代表打開服務連接來監聽可能到來的連接請求(屬于server端),為了連接兩個藍牙設備必須有一個設備作為服務器打開一個服務套接字。當遠端設備發起連接連接請求的時候,并且已經連接到了的時候,Blueboothserversocket類將會返回一個bluetoothsocket。
Bluetoothclass類:描述了一個藍牙設備的一般特點和能力。它的只讀屬性集定義了設備的主、次設備類和一些相關服務。然而,它并沒有準確地描述所有該設備所支持的藍牙文件和服務,而是作為對設備種類來說的一個小小暗示。下面說說具體的編程實現 1.啟動藍牙功能:
首先通過調用靜態方法getDefaultAdapter()獲取藍牙適配器BluetoothAdapter,以后你就可以使用該對象了。如果返回為空,the story is over。例如:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if(mBluetoothAdapter == null){ // Device does not support Bluetooth } 其次,調用isEnabled()來查詢當前藍牙設備的狀態,如果返回為false,則表示藍牙設備沒有開啟,接下來你需要封裝一個ACTION_REQUEST_ENABLE請求到intent里面,調用startActivityForResult()方法使能藍牙設備,例如:
if(!mBluetoothAdapter.isEnabled()){ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);}
2.查找設備:
使用BluetoothAdapter類里的方法,你可以查找遠端設備(大概十米以內)或者查詢在你手機上已經匹配(或者說綁定)的其他手機了。當然需要確定對方藍牙設備已經開啟或者已經開啟了“被發現使能”功能(對方設備是可以被發現的是你能夠發起連接的前提條件)。如果該設備是可以被發現的,會反饋回來一些對方的設備信息,比如名字、MAC地址等,利用這些信息,你的設備就可以選擇去向對方初始化一個連接。
如果你是第一次與該設備連接,那么一個配對的請求就會自動的顯示給用戶。當設備配對好之后,他的一些基本信息(主要是名字和MAC)被保存下來并可以使用藍牙的API來讀取。使用已知的MAC地址就可以對遠端的藍牙設備發起連接請求。
匹配好的設備和連接上的設備的不同點:匹配好只是說明對方設備發現了你的存在,并擁有一個共同的識別碼,并且可以連接。連接上:表示當前設備共享一個RFCOMM信道并且兩者之間可以交換數據。也就是是說藍牙設備在建立RFCOMM信道之前,必須是已經配對好了的。
3.查詢匹配好的設備:
在建立連接之前你必須先查詢配對好了的藍牙設備集(你周圍的藍牙設備可能不止一個),以便你選取哪一個設備進行通信,例如你可以你可以查詢所有配對的藍牙設備,并使用一個數組適配器將其打印顯示出來:
Set
4.掃描設備:
掃描設備,只需要簡單的調用startDiscovery()方法,這個掃描的過程大概持續是12秒,應用程序為了ACTION_FOUND動作需要注冊一個BroadcastReceiver來接受設備掃描到的信息。對于每一個設備,系統都會廣播ACTION_FOUND動作。例如: // Create a BroadcastReceiver for ACTION_FOUND private final BroadcastReceiver mReceiver = new BroadcastReceiver(){ public void onReceive(Context context, Intent intent){ String action = intent.getAction();// When discovery finds a device if(BluetoothDevice.ACTION_FOUND.equals(action)){ // Get the BluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);// Add the name and address to an array adapter to show in a ListView mArrayAdapter.add(device.getName()+ “n” + device.getAddress());} } };// Register the BroadcastReceiver IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);registerReceiver(mReceiver, filter);// Don't forget to unregister during onDestroy 注意:掃描的過程是一個很耗費資源的過程,一旦你找到你需要的設備之后,在發起連接請求之前,確保你的程序調用cancelDiscovery()方法停止掃描。顯然,如果你已經連接上一個設備,啟動掃描會減少你的通信帶寬。
5.使能被發現:Enabling discoverability 如果你想使你的設備能夠被其他設備發現,將ACTION_REQUEST_DISCOVERABLE動作封裝在intent中并調用startActivityForResult(Intent, int)方法就可以了。他將在不使你應用程序退出的情況下使你的設備能夠被發現。缺省情況下的使能時間是120秒,當然你可以可以通過添加EXTRA_DISCOVERABLE_DURATION字段來改變使能時間(最大不超過300秒,這是出于對你設備上的信息安全考慮)。例如: Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);startActivity(discoverableIntent);運行該段代碼之后,系統會彈出一個對話框來提示你啟動設備使能被發現(此過程中如果你的藍牙功能沒有開啟,系統會幫你開啟),并且如果你準備對該遠端設備發現一個連接,你不需要開啟使能設備被發現功能,因為該功能只是在你的應用程序作為服務器端的時候才需要。
6.連接設備:
在應用程序中,想建立兩個藍牙設備之間的連接,必須實現客戶端和服務器端的代碼(因為任何一個設備都必須可以作為服務端或者客戶端)。一個開啟服務來監聽,一個發起連接請求(使用服務器端設備的MAC地址)。當他們都擁有一個藍牙套接字在同一RFECOMM信道上的時候,可以認為他們之間已經連接上了。服務端和客戶端通過不同的方式或其他們的藍牙套接字。當一個連接監聽到的時候,服務端獲取到藍牙套接字。當客戶可打開一個FRCOMM信道給服務器端的時候,客戶端獲取到藍牙套接字。
注意:在此過程中,如果兩個藍牙設備還沒有配對好的,android系統會通過一個通知或者對話框的形式來通知用戶。RFCOMM連接請求會在用戶選擇之前阻塞。如下圖:
7.服務端的連接:
當你想要連接兩臺設備時,一個必須作為服務端(通過持有一個打開的BluetoothServerSocket),目的是監聽外來連接請求,當監聽到以后提供一個連接上的BluetoothSocket給客戶端,當客戶端從BluetoothServerSocket得到BluetoothSocket以后就可以銷毀BluetoothServerSocket,除非你還想監聽更多的連接請求。
建立服務套接字和監聽連接的基本步驟:
首先通過調用listenUsingRfcommWithServiceRecord(String, UUID)方法來獲取BluetoothServerSocket對象,參數String代表了該服務的名稱,UUID代表了和客戶端連接的一個標識(128位格式的字符串ID,相當于PIN碼),UUID必須雙方匹配才可以建立連接。其次調用accept()方法來監聽可能到來的連接請求,當監聽到以后,返回一個連接上的藍牙套接字BluetoothSocket。最后,在監聽到一個連接以后,需要調用close()方法來關閉監聽程序。(一般藍牙設備之間是點對點的傳輸)
注意:accept()方法不應該放在主Acitvity里面,因為它是一種阻塞調用(在沒有監聽到連接請求之前程序就一直停在那里)。解決方法是新建一個線程來管理。例如: private class AcceptThread extends Thread { private final BluetoothServerSocket mmServerSocket;public AcceptThread(){ // Use a temporary object that is later assigned to mmServerSocket, // because mmServerSocket is final BluetoothServerSocket tmp = null;try { // MY_UUID is the app's UUID string, also used by theclient code tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);} catch(IOException e){ } mmServerSocket = tmp;} public void run(){ BluetoothSocket socket = null;// Keep listening until exception occurs or a socket is returned while(true){ try { socket = mmServerSocket.accept();} catch(IOException e){ break;} // If a connection was accepted if(socket!= null){ // Do work to manage the connection(in a separate thread)manageConnectedSocket(socket);mmServerSocket.close();break;} } } /** Will cancel the listening socket, and cause the thread to finish */ public void cancel(){ try { mmServerSocket.close();} catch(IOException e){ } } }
8.客戶端的連接:
為了初始化一個與遠端設備的連接,需要先獲取代表該設備的一個BluetoothDevice對象。通過BluetoothDevice對象來獲取BluetoothSocket并初始化連接,具體步驟:
使用BluetoothDevice對象里的方法createRfcommSocketToServiceRecord(UUID)來獲取BluetoothSocket。UUID就是匹配碼。然后,調用connect()方法來。如果遠端設備接收了該連接,他們將在通信過程中共享RFFCOMM信道,并且connect()方法返回。例如: private class ConnectThread extends Thread { private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;public ConnectThread(BluetoothDevice device){ // Use a temporary object that is later assigned to mmSocket, // because mmSocket is final BluetoothSocket tmp = null;mmDevice = device;// Get a BluetoothSocket to connect with the given BluetoothDevice try { // MY_UUID is the app's UUID string, also used by the server code tmp = device.createRfcommSocketToServiceRecord(MY_UUID);} catch(IOException e){ } mmSocket = tmp;}
public void run(){ // Cancel discovery because it will slow down the connection mAdapter.cancelDiscovery();try { // Connect the device through the socket.This will block // until it succeeds or throws an exception mmSocket.connect();} catch(IOException connectException){ // Unable to connect;close the socket and get out try { mmSocket.close();} catch(IOException closeException){ } return;} // Do work to manage the connection(in a separate thread)manageConnectedSocket(mmSocket);}
注意:conncet()方法也是阻塞調用,一般建立一個獨立的線程中來調用該方法。在設備discover過程中不應該發起連接connect(),這樣會明顯減慢速度以至于連接失敗。且數據傳輸完成只有調用close()方法來關閉連接,這樣可以節省系統內部資源。
9.管理連接(主要涉及數據的傳輸):
當設備連接上以后,每個設備都擁有各自的BluetoothSocket?,F在你就可以實現設備之間數據的共享了。
1> 首先通過調用getInputStream()和getOutputStream()方法來獲取輸入輸出流。然后通過調用read(byte[])和write(byte[]).方法來讀取或者寫數據。
2> 實現細節:以為讀取和寫操作都是阻塞調用,需要建立一個專用現成來管理。3>
private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket;private final InputStream mmInStream;private final OutputStream mmOutStream;public ConnectedThread(BluetoothSocket socket){ mmSocket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;// Get the input and output streams, using temp objects because // member streams are final try { tmpIn = socket.getInputStream();tmpOut = socket.getOutputStream();} catch(IOException e){ } mmInStream = tmpIn;mmOutStream = tmpOut;} public void run(){ byte[] buffer = new byte[1024];// buffer store for the stream int bytes;// bytes returned from read()// Keep listening to the InputStream until an exception occurs while(true){ try { // Read from the InputStream bytes = mmInStream.read(buffer);// Send the obtained bytes to the UI Activity mHandler.obtainMessage(MESSAGE_READ, bytes,-1, buffer).sendToTarget();} catch(IOException e){ break;} } } /* Call this from the main Activity to send data to the remote device */ public void write(byte[] bytes){ try { mmOutStream.write(bytes);} catch(IOException e){ } } /* Call this from the main Activity to shutdown the connection */ public void cancel(){ try { mmSocket.close();} catch(IOException e){ } } }
第四篇:sqlldr使用小結
sqlldr使用小結
sqlldr userid=lgone/tiger control=a.ctl
LOAD DATA
INFILE ’t.dat’ // 要導入的文件
// INFILE ’tt.date’ // 導入多個文件
// INFILE * // 要導入的內容就在control文件里 下面的BEGINDATA后面就是導入的內容
INTO TABLE table_name // 指定裝入的表
BADFILE ’c:\bad.txt’ // 指定壞文件地址
************* 以下是4種裝入表的方式
APPEND // 原先的表有數據 就加在后面
// INSERT // 裝載空表 如果原先的表有數據 sqlloader會停止 默認值
// REPLACE // 原先的表有數據 原先的數據會全部刪除
// TRUNCATE // 指定的內容和replace的相同 會用truncate語句刪除現存數據
************* 指定的TERMINATED可以在表的開頭 也可在表的內部字段部分
FIELDS TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’“’
// 裝載這種數據: 10,lg,”“"lg”“",”lg,lg“
// 在表中結果: 10 lg ”lg“ lg,lg
// TERMINATED BY X ’0Array’ // 以十六進制格式 ’0Array’ 表示的// TERMINATED BY WRITESPACE // 裝載這種數據: 10 lg lg
TRAILING NULLCOLS ************* 表的字段沒有對應的值時允許為空
************* 下面是表的字段
(col_1 , col_2 ,col_filler FILLER // FILLER 關鍵字 此列的數值不會被裝載
// 如: lg,lg,not 結果 lg lg)
// 當沒聲明FIELDS TERMINATED BY ’,’ 時
//(// col_1 [interger external] TERMINATED BY ’,’ ,// col_2 [date ”dd-mon-yyy“] TERMINATED BY ’,’ , // col_3 [char] TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’lg’
//)
// 當沒聲明FIELDS TERMINATED BY ’,’用位置告訴字段裝載數據
//(// col_1 position(1:2),// col_2 position(3:10),// col_3 position(*:16), // 這個字段的開始位置在前一字段的結束位置
// col_4 position(1:16),// col_5 position(3:10)char(8)// 指定字段的類型
//)
BEGINDATA // 對應開始的 INFILE * 要導入的內容就在control文件里
10,Sql,what
20,lg,show
=======================================
//////////// 注意begindata后的數值前面不能有空格***** 普通裝載
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’”’
(DEPTNO,DNAME,LOC)
BEGINDATA
10,Sales,“"”USA“"”
20,Accounting,“Virginia,USA”
30,Consulting,Virginia
40,Finance,Virginia
50,“Finance”,“",Virginia // loc 列將為空
60,”Finance“,Virginia // loc 列將為空***** FIELDS TERMINATED BY WHITESPACE 和 FIELDS TERMINATED BY x’0Array’ 的情況
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY WHITESPACE
--FIELDS TERMINATED BY x’0Array’
(DEPTNO,DNAME,LOC)
BEGINDATA Sales Virginia ***** 指定不裝載那一列
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’”’
(DEPTNO,FILLER_1 FILLER, // 下面的 “Something Not To Be Loaded” 將不會被裝載
DNAME,LOC)
BEGINDATA
20,Something Not To Be Loaded,Accounting,“Virginia,USA” ***** position的列子
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
(DEPTNO position(1:2),DNAME position(*:16), // 這個字段的開始位置在前一字段的結束位置
LOC position(*:2Array),ENTIRE_LINE position(1:2Array))
BEGINDATA
10Accounting Virginia,USA
***** 使用函數 日期的一種表達 TRAILING NULLCOLS的使用
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS // 其實下面的ENTIRE_LINE在BEGINDATA后面的數據中是沒有直接對應
// 的列的值的 如果第一行改為
10,Sales,Virginia,1/5/2000, 就不用TRAILING NULLCOLS了
(DEPTNO,DNAME “upper(:dname)”, // 使用函數
LOC “upper(:loc)”,LAST_UPDATED date ’dd/mm/yyyy’, // 日期的一種表達方式 還有’dd-mon-yyyy’ 等
ENTIRE_LINE “:deptno||:dname||:loc||:last_updated”)
BEGINDATA
10,Sales,Virginia,1/5/2000
20,Accounting,Virginia,21/6/1ArrayArrayArray 30,Consulting,Virginia,5/1/2000
40,Finance,Virginia,15/3/2001 ***** 使用自定義的函數 // 解決的時間問題
create or replace
function my_to_date(p_string in varchar2)return date
as
type fmtArray is table of varchar2(25);
l_fmts fmtArray := fmtArray(’dd-mon-yyyy’, ’dd-month-yyyy’,’dd/mm/yyyy’,’dd/mm/yyyy hh24:mi:ss’);
l_return date;
begin
for i in 1..l_fmts.count
loop
begin
l_return := to_date(p_string, l_fmts(i));
exception
when others then null;
end;EXIT when l_return is not null;
end loop;
if(l_return is null)
then
l_return :=
new_time(to_date(’01011Array70’,’ddmmyyyy’)+ 1/24/60/60 *
p_string, ’GMT’, ’EST’);
end if;
return l_return;
end;
/
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO, DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED “my_to_date(:last_updated)” // 使用自定義的函數)
BEGINDATA
10,Sales,Virginia,01-april-2001
20,Accounting,Virginia,13/04/2001
30,Consulting,Virginia,14/04/2001 12:02:02
40,Finance,Virginia,Array872682Array7
50,Finance,Virginia,02-apr-2001
60,Finance,Virginia,Not a date ***** 合并多行記錄為一行記錄
LOAD DATA
INFILE *
concatenate 3 // 通過關鍵字concatenate 把幾行的記錄看成一行記錄
INTO TABLE DEPT
replace
FIELDS TERMINATED BY ’,’
(DEPTNO, DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED date ’dd/mm/yyyy’)
BEGINDATA
10,Sales, // 其實這3行看成一行 10,Sales,Virginia,1/5/2000
Virginia,1/5/2000
// 這列子用 continueif list=“,” 也可以
告訴sqlldr在每行的末尾找逗號 找到逗號就把下一行附加到上一行
LOAD DATA
INFILE *
continueif this(1:1)= ’-’ // 找每行的開始是否有連接字符-有就把下一行連接為一行
// 如-10,Sales,Virginia,// 1/5/2000 就是一行 10,Sales,Virginia,1/5/2000
// 其中1:1 表示從第一行開始 并在第一行結束 還有continueif next 但continueif list最理想
INTO TABLE DEPT replace
FIELDS TERMINATED BY ’,’
(DEPTNO,DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED date ’dd/mm/yyyy’)
BEGINDATA // 但是好象不能象右面的那樣使用
-10,Sales,Virginia,-10,Sales,Virginia,1/5/2000 1/5/2000
-40, 40,Finance,Virginia,13/04/2001
Finance,Virginia,13/04/2001 ***** 載入每行的行號
load data
infile *
into table t
replace
(seqno RECNUM //載入每行的行號
text Position(1:1024))
BEGINDATA fsdfasj //自動分配一行號給載入 表t 的seqno字段 此行為 1
fasdjfasdfl // 此行為 2...Array ***** 載入有換行符的數據
注意: unix 和 windows 不同 \\n & /n
< 1 > 使用一個非換行符的字符
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO,DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED “my_to_date(:last_updated)”,COMMENTS “replace(:comments,’\n’,chr(10))” // replace 的使用幫助轉換換行符)
BEGINDATA
10,Sales,Virginia,01-april-2001,This is the Sales\nOffice in Virginia
20,Accounting,Virginia,13/04/2001,This is the Accounting\nOffice in Virginia
30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting\nOffice in Virginia
40,Finance,Virginia,Array872682Array7,This is the Finance\nOffice in Virginia
< 2 > 使用fix屬性
LOAD DATA
INFILE demo17.dat “fix 101”
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO,DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED “my_to_date(:last_updated)”,COMMENTS)
demo17.dat 10,Sales,Virginia,01-april-2001,This is the Sales
Office in Virginia
20,Accounting,Virginia,13/04/2001,This is the Accounting
Office in Virginia
30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
Office in Virginia
40,Finance,Virginia,Array872682Array7,This is the Finance
Office in Virginia
// 這樣裝載會把換行符裝入數據庫 下面的方法就不會 但要求數據的格式不同
LOAD DATA
INFILE demo18.dat “fix 101”
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’“’
TRAILING NULLCOLS
(DEPTNO, DNAME ”upper(:dname)“,LOC ”upper(:loc)“,LAST_UPDATED ”my_to_date(:last_updated)“,COMMENTS)
demo18.dat
10,Sales,Virginia,01-april-2001,”This is the Sales
Office in Virginia“
20,Accounting,Virginia,13/04/2001,”This is the Accounting
Office in Virginia“
30,Consulting,Virginia,14/04/2001 12:02:02,”This is the Consulting
Office in Virginia“
40,Finance,Virginia,Array872682Array7,”This is the Finance
Office in Virginia“
< 3 > 使用var屬性
LOAD DATA
INFILE demo1Array.dat ”var 3“
// 3 告訴每個記錄的前3個字節表示記錄的長度 如第一個記錄的 071 表示此記錄有 71 個字節
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO,DNAME ”upper(:dname)“,LOC ”upper(:loc)“,LAST_UPDATED ”my_to_date(:last_updated)“,COMMENTS)
demo1Array.dat
07110,Sales,Virginia,01-april-2001,This is the Sales
Office in Virginia
07820,Accounting,Virginia,13/04/2001,This is the Accounting
Office in Virginia
08730,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
Office in Virginia
07140,Finance,Virginia,Array872682Array7,This is the Finance
Office in Virginia
< 4 > 使用str屬性
// 最靈活的一中 可定義一個新的行結尾符 win 回車換行 : chr(13)||chr(10)
此列中記錄是以 a|\r\n 結束的select utl_raw.cast_to_raw(’|’||chr(13)||chr(10))from dual;
結果 7C0D0A
LOAD DATA
INFILE demo20.dat ”str X’7C0D0A’“
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO,DNAME ”upper(:dname)“,LOC ”upper(:loc)“,LAST_UPDATED ”my_to_date(:last_updated)",COMMENTS)
demo20.dat 10,Sales,Virginia,01-april-2001,This is the Sales
Office in Virginia|
20,Accounting,Virginia,13/04/2001,This is the Accounting
Office in Virginia|
30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
Office in Virginia|
40,Finance,Virginia,Array872682Array7,This is the Finance
Office in Virginia|
================================
象這樣的數據 用 nullif 子句
10-jan-200002350Flipper seemed unusually hungry today.10510-jan-20000ArrayArray45Spread over three meals.id position(1:3)nullif id=blanks // 這里可以是blanks 或者別的表達式
// 下面是另一個列子 第一行的 1 在數據庫中將成為 null
LOAD DATA INFILE *
INTO TABLE T
REPLACE
(n position(1:2)integer external nullif n=’1’,v position(3:8))
BEGINDATA 10
20lg
-----------------------------
第五篇:GridCtrl使用小結
http://www.tmdps.cn/
GridCtrl使用詳解
CGridCtrl類主要是通過grid樣式顯示數據 在單文檔中的使用方法
步驟一 初始化 在CView類的.h頭文件中包含文件:
#include “Gridctrl.h” 并且手寫加入如下的成員函數:
CGridCtrl * m_pGridCtrl;步驟二 構造與析構 構造函數中:
m_pGridCtrl = NULL;析構函數中:
if(m_pGridCtrl)
delete m_pGridCtrl;步驟三 如果需要打印功能的話添加同名打印函數代碼 在CView類的OnBeginPrinting()函數中添加如下代碼: if(m_pGridCtrl)
m_pGridCtrl->OnBeginPrinting(pDC,pInfo);//簡單吧,這就是類的好處其它兩個打印函數也一樣的做法.步驟四 在OnInitaUpdate()函數中或者你自己添加的要顯示Grid的消息函數中如下初始化: //創建非模式對話框 CDlg *dlg;dlg=new CDlg();dlg->Create(IDD_Dlg,this);
//初始化GridCtrl控件 if(m_pGridCtrl!=NULL){ deletem_pGridCtrl;m_pGridCtrl=NULL;} if(m_pGridCtrl == NULL){ // Create the Gridctrl object m_pGridCtrl = new CGridCtrl;if(!m_pGridCtrl)return 0;// Create the Gridctrl window CRectrect;GetClientRect(rect);m_pGridCtrl->Create(rect, this, 100);// fill it up with stuff m_pGridCtrl->SetEditable(false);m_pGridCtrl->SetTextBkColor(RGB(0xFF, 0xFF, 0xE0));//黃色背景 m_pGridCtrl->EnableDragAndDrop(false);try { m_pGridCtrl->SetRowCount(k);//設置行數為k行 m_pGridCtrl->SetColumnCount(4);//k列
m_pGridCtrl->SetFixedRowCount(1);//標題行為一行
http://www.tmdps.cn/
m_pGridCtrl->SetFixedColumnCount(1);//同上 } catch(CMemoryException* e){ e->ReportError();e->Delete();return 0;} //填充列標題 int row=0;for(int col=0;col<4;col++){ GV_ITEM Item;Item.mask = GVIF_TEXT|GVIF_FORMAT;Item.row = row;Item.col = col;if(col==0){ Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.strText.Format(_T(“【類別】”),col);} else if(col==1){ Item.nFormat = DT_LEFT|DT_WORDBREAK;Item.strText.Format(_T(“第一列”),col);} else if(col==2){ Item.nFormat = DT_LEFT|DT_WORDBREAK;Item.strText.Format(_T(“第二列”),col);} m_pGridCtrl->SetItem(&Item);} // fill rows/cols with text for(row = 1;row < k;row++)for(col = 0;col < h;col++){ GV_ITEM Item;Item.mask = GVIF_TEXT|GVIF_FORMAT;Item.row = row;Item.col = col;if(col < 1){ //行標題頭
Item.nFormat = DT_CENTER|DT_VCENTER |DT_SINGLELINE|DT_END_ELLIPSIS |DT_NOPREFIX;Item.strText.Format(_T(“%d”),row);
http://www.tmdps.cn/
} else if(col==1){ //第一列的值 Item.nFormat = DT_CENTER|DT_VCENTER |DT_SINGLELINE|DT_END_ELLIPSIS |DT_NOPREFIX;str=“aa”;Item.strText.Format(_T(“%s”),str);}else if(col==2){ //第二列第值 Item.nFormat = DT_CENTER|DT_VCENTER |DT_SINGLELINE|DT_END_ELLIPSIS |DT_NOPREFIX;CStringstr;str=“bb”;Item.strText.Format(_T(“%s”),str);} m_pGridCtrl->SetItem(&Item);} m_pGridCtrl->AutoSize();
//--------------設置行列距------------------for(int a=1;a
步驟五: 添加WM_SIZE消息,調整控件的界面占屏幕大小
if(m_pGridCtrl->GetSafeHWnd())
{
CRectrect;
GetClientRect(rect);
m_pGridCtrl->MoveWindow(rect);
}
在對話框中的使用方法 步驟一 創建數據顯示表格對話框
在資源管理器中新創建一個對話框,假設為CDlgTestReportBox。從工具箱中加入Custom Control,就是人頭像的那個,將其區域拉伸至要顯示數據表格的大小,充滿整個對話框。
在CDlgTestReportBox類的頭文件中: #include “GridCtrl.h”
http://www.tmdps.cn/
再定義成員變量: CGridCtrl* m_pGrid;添加OnShowWindow()消息處理函數如下:
voidCDlgTestReportBox::OnShowWindow(BOOL bShow, UINT nStatus){ CDialog::OnShowWindow(bShow, nStatus);// TODO: Add your message handler code here if(m_pGrid!=NULL){ deletem_pGrid;m_pGrid=NULL;} if(m_pGrid==NULL){ m_pGrid=new CGridCtrl;CRectrect;GetDlgItem(IDC_ReportAera)->GetWindowRect(rect);//得到顯示區域 ScreenToClient(&rect);m_pGrid->Create(rect,this,100);m_pGrid->SetEditable(false);m_pGrid->SetTextBkColor(RGB(0xFF, 0xFF, 0xE0));//黃色背景 try { m_pGrid->SetRowCount(10);//初始為10行
m_pGrid->SetColumnCount(11);//初始化為11列 m_pGrid->SetFixedRowCount(1);//表頭為一行 m_pGrid->SetFixedColumnCount(1);//表頭為一列 } catch(CMemoryException* e){ e->ReportError();e->Delete();// return FALSE;} for(int row = 0;row
http://www.tmdps.cn/
{ Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.szText.Format(_T(“報表顯示”),col);} else if(row < 1)//設置0行表頭顯示 { Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.szText.Format(_T(“ 項目%d”),col);} else if(col < 1)//設置0列表頭顯示 { if(row
步驟二 嵌入上面的對話框 顯示數據
在你需要顯示數據的對話框上的頭文件中,假設為CDlgTest,加入 #include ”GridCtrl.h“ CDlgTestReportBox* m_pTestReportBox;將數據顯示對話框放入你的對話框相應位置上,在CDlgTest::OnInitDialog()中:
if(!m_pTestReportBox){
m_pTestReportBox=new CDlgTestReportBox(this);} m_pTestReportBox->Create(IDD_DlgTestReportBox,this);
http://www.tmdps.cn/
//定義區域變量 CRectrectDraw;GetDlgItem(IDC_AeraReport)->GetWindowRect(rectDraw);ScreenToClient(&rectDraw);//動態測試數據顯示區域rectDraw //將對應的對話框放到指定區域 m_pTestReportBox->MoveWindow(rectDraw);m_pTestReportBox->ShowWindow(SW_SHOW);自定義填充數據的函數:CDlgTest::FillGrid()如下: CGridCtrl* pGrid=m_pTestReportBox->m_pGrid;for(int row = pGrid->GetRowCount()-1;row >= pGrid->GetRowCount()-3;row--){ for(int col = 1;col <= pGrid->GetColumnCount();col++){ GV_ITEM Item;Item.mask = GVIF_TEXT|GVIF_FORMAT;Item.row = row;Item.col = col;if(row==pGrid->GetRowCount()-3&&col>0)//平均值 { if(col==10){ Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.szText.Format(_T(” %6.2f “),avjch);} else{ Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.szText.Format(_T(” %6.2f “),av[col-1]);} } pGrid->SetItem(&Item);//提交數據 if(row==0||col==0){ COLORREF clr = RGB(0, 0, 0);pGrid->SetItemBkColour(row, col, clr);pGrid->SetItemFgColour(row, col, RGB(255,0,0));} }//循環結束
pGrid->Invalidate();} CGRIFCTRL原理:
DBGRID和一般的GRID的不同之處在于,一般的GRID并不適合顯示大的數據量,如果一個表中有上萬條記錄都要插入到GRID中,這將是一個很慢的過程,并且在GRID中移動滾動條時,它的記錄的滾動也是很慢。而DBGRID并不會真正把這些記錄的數據全部插入到控件中,當DBGRID的滾動條滾動時,它會根據DBGRID的顯示面積的大小和查詢得到的總記錄數計算出當前應該顯示哪些行,然后插入
http://www.tmdps.cn/
到表格中,這樣一來,速度肯定快,而且沒有數據量多少的限制。幸運的是,CGridCtrl類已經為我們提供了這種機制,它是采用虛模式實現的。使用這種方式,即使你向這個該控件插入一百萬條數據,它并不會真的生成一百萬行,而是隨著你的滾動條的滾動,計算出在屏幕上要顯示的行和列,然后會向你提供一個接口,通過這個接口,你可以在這兒設置你要顯示的數據。下面給出使用CGridCtrl控件的虛模式的步驟: 步驟一 初始化
在視圖的初始化函數里添加如下代碼:
void SetVirtualMode(TRUE)
設為虛模式
BOOL SetRowCount(intnRows)
設置總的行數。
BOOL SetFixedRowCount(intnFixedRows = 1)
設置固定的行數據 BOOL SetColumnCount(intnCols)
設置列數 BOOL SetFixedColumnCount(intnFixedCols = 1)設置固定的列數 步驟二 響應消息 顯示數據
我們假設CGridCtrl是放在單文檔視圖中,而且它關聯的變量是m_GridCtrl,利用ClassWizard添加視圖的OnNotify響應函數。這個響應函數的寫法是固定的,類似下面的代碼:
BOOL CGridCtrlTestView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult){ if(wParam ==(WPARAM)m_Grid.GetDlgCtrlID()){ *pResult = 1;GV_DISPINFO *pDispInfo =(GV_DISPINFO*)lParam;if(GVN_GETDISPINFO == pDispInfo->hdr.code){ //這是添加的函數,在這個函數里設置當前要顯示的數據 SetGridItem(pDispInfo);return TRUE;} } returnCGridCtrlTestView::OnNotify(wParam, lParam, pResult);} 在上面的代碼中,SetGridItem(pDispInfo)是添加的函數,在這個函數里我們設置當前要顯示的數據。pDispInfo是一個GV_DISPINFO的結構體對象,它包含了每個單元格的信息,如行號,列號,有沒有位圖,背景色,前景色等。CGRIDCTRL會把當前要顯示那個單元格行號,列號傳遞給我們,我們只要設置里面顯示的數據就可以了。如下面是一個顯示數據的例子。
voidCGridCtrlTestView::SetGridItem(GV_DISPINFO *pDispInfo){
pDispInfo->item.strText.Format(”row%d,col%d",pDispInfo->it
http://www.tmdps.cn/
em.row, pDispInfo->item.col);}