|
#
CCSprite的動作總結
1) CCAction如果是用具體類的actionWithFile(.....)或類似這樣的接口的,則系統默認都是有自動維護引用計數的。即:這類對象創建出來后我們不需要釋放。 2) CCSprite::runAction()接口請一定要與CCSprite::stopAction()或CCSprite::stopAllActions()配合使用。 3) CCSprite在執行CCMoveTo動作時,此時如果再對其設置幀動畫動作,則這兩個動作將是會共存執行的。 ------ 以上3點很重要。
在介紹前,先介紹說幾點: 像opengl、directx是沒有自帶界面的,不過cocos可以為我們創建一個主界面,以用于顯示游戲場景等。該主界面由CCDirector類實例實現。 cocos2d中所有需要用到的類,基本上都繼承自CCObject類。它維護一個引用計數。所有從CCObject出來的類,都可以添加到自動釋放池中進行自動維護。(與objective-c中的對象有點類似) 多數情況下,我們使用的對象,還是從CCNode出來的。CCNode是繼承自CCObject的一個類。
下面簡要介紹下各個主要類的功能 1) CCDirector 主要功能一:負責生成一個游戲主界面及游戲的初始化并。并在該界面上,渲染游戲場景。 主要功能二:負責控制各游戲場景間的相互切換。 主要功能三:負責初始化設置游戲的一些重要屬性。如:游戲渲染的幀率。游戲視窗的尺寸(即:960 X 640 還是 480 X 320等) 主要功能四:負責清空游戲中的緩存數據(個人認為這個還是很重要的。因為到了手機上的游戲,硬內存不多,及時清除是很有必要的) 注意:該對象是單實例的,不需要用戶維護。
2) CCScene 游戲中的場景類。當程序啟動后,必須要為其設置一個場景,然后所有的對象在場景中場景。 主要功能:負責接收游戲中的任何對象。并將它們展現出來(如果可以展現的話,如:CCSprite等) 習慣上,用其維護多個的CCLayer
3) CCLayer 游戲中的層。習慣上,用其對資源展現進行布局管理。
4) CCSprite 游戲中的精靈對象。游戲中,必不可少的。會用到許多的可視對象。這些,多數都是精靈對象。
5) CCTargetedTouchDelegate 觸摸響應類。提供響應觸摸響應開始結束、觸摸移動以及取消觸摸幾個接口。多是需要在其派生類中進行具體實現。如:一個精靈, 可支持觸摸功能,則它需要自行實現這些接口。
以上只是簡單介紹cocos2d中的幾個較為主要及常用的類。初學,如有介紹不對的,請大家不吝指點。共同學習,共同進步。
編程時在C中需要用的類似Java的Split函數來解析一個長字符串,分割成子段使用,查函數庫發現有strtok可用,看了示例卻對它的用法有些疑惑為什么傳值是NULL,于是上網查資料,找到這篇包含strtok函數源碼的解釋,轉過來學習,仔細研讀收獲良多。
查函數庫看到的strtok的解釋和示例:
strtok
Syntax: #include <cstring> char *strtok( char *str1, const char *str2 );
The strtok() function returns a pointer to the next "token" in str1, where str2 contains the delimiters that determine the token. strtok() returns NULL if no token is found. In order to convert a string to tokens, the first call to strtok() should have str1 point to the string to be tokenized. All calls after this should have str1 be NULL.
For example: char str[] = "now # is the time for all # good men to come to the # aid of their country"; char delims[] = "#"; char *result = NULL; result = strtok( str, delims ); while( result != NULL ) { printf( "result is /"%s/"/n", result ); result = strtok( NULL, delims ); }
The above code will display the following output: result is "now " result is " is the time for all " result is " good men to come to the " result is " aid of their country"
下面是查到的網絡文章對源碼的解釋:
原型:char * strtok(char * s,const char * ct)
用途:在s中找出以ct中的字符為分隔的字符串,即是源串中除去了含有分隔串中的所有字符后余下的一段段的字符串,每調用一次找到一串,找不到則返回空串。第一次調用必須傳給它有效的字符串,第二次傳NULL就可以了,每次調用返回找到的子串的時候都會把源串中該子串的尾部字符(原來是搜索串中的某一字符)修改成'/0'字符返回值為每次調用得到的字串。
下面看一下它的使用
char sbody[]= "Presetptz/r/nPreset1=hello/r/nPreset2=ttttt/r/nend/r/n";
///char *pbody= "Presetptz/r/nPreset1=hello/r/nPreset2=ttttt/r/nend/r/n";//errror char except[] = "12/r/n"; char *ptoken = NULL; ptoken = strtok(sbody,except); while(NULL!=ptoken) { printf("%s/n",ptoken); ptoken = strtok(NULL,except); }
輸出為: Presetptz Preset =hello Preset =ttttt end 下面我們看一下它的源碼:
char *___strtok;//關鍵這個全局指針變量
char * strtok(char * s,const char * ct) { char *sbegin, *send; sbegin = s ? s : ___strtok;//不等于NULL用原始字符串,否則用___strtok if (!sbegin) { return NULL;//結尾 } sbegin += strspn(sbegin,ct);// if (*sbegin == '/0') { ___strtok = NULL; return( NULL ); } send = strpbrk( sbegin, ct); if (send && *send != '/0') *send++ = '/0'; ___strtok = send; return (sbegin); }
其中: ssize_t strspn(const char* s,char*accept)// 返回accept中任一字符在s中第一次出現的位置
char * strpbrk(const char * cs,const char * ct)//返回指向ct中任一字符在cs中第一次出現的位置
這個函數不難分析,___strtok指針指向除去第一個有效字串后面的位置,到這里我們應該清楚為什么第二次調用時只要傳NULL就可以了,當然這里也暴露了它的缺點,就是說不能有兩個線程同時使用strtok否則就會出現錯誤。還有就是我在使用這個函數時碰到的問題,如上面的代碼如果我把sbody換成 pbody,則編譯沒有問題,運行時就會出錯,為什么?還是自己的基本功不扎實,pbody在是個靜態字符串,說白了,它是在編譯時就已經賦值而且相當于是一個const常量,不能被修改,而strtok是需要修改字符串的,所以產生問題不足為奇。
原文出處:http://www.cnblogs.com/finallyliuyu/archive/2010/10/11/1848130.html
一、C++中不能使用random()函數
==================================================================================
本文由青松原創并依GPL-V2及其后續版本發放,轉載請注明出處且應包含本行聲明。
C++中常用rand()函數生成隨機數,但嚴格意義上來講生成的只是偽隨機數(pseudo-random integral number)。生成隨機數時需要我們指定一個種子,如果在程序內循環,那么下一次生成隨機數時調用上一次的結果作為種子。但如果分兩次執行程序,那么由于種子相同,生成的“隨機數”也是相同的。
在工程應用時,我們一般將系統當前時間(Unix時間)作為種子,這樣生成的隨機數更接近于實際意義上的隨機數。給一下例程如下:
#include <iostream> #include <ctime> #include <cstdlib> using namespace std;
int main() { double random(double,double); srand(unsigned(time(0))); for(int icnt = 0; icnt != 10; ++icnt) cout << "No." << icnt+1 << ": " << int(random(0,10))<< endl; return 0; }
double random(double start, double end) { return start+(end-start)*rand()/(RAND_MAX + 1.0); } /* 運行結果 * No.1: 3 * No.2: 9 * No.3: 0 * No.4: 9 * No.5: 5 * No.6: 6 * No.7: 9 * No.8: 2 * No.9: 9 * No.10: 6 */ 利用這種方法能不能得到完全意義上的隨機數呢?似乎9有點多哦?卻沒有1,4,7?!我們來做一個概率實驗,生成1000萬個隨機數,看0-9這10個數出現的頻率是不是大致相同的。程序如下: #include <iostream> #include <ctime> #include <cstdlib> #include <iomanip> using namespace std;
int main() { double random(double,double); int a[10] = {0}; const int Gen_max = 10000000; srand(unsigned(time(0))); for(int icnt = 0; icnt != Gen_max; ++icnt) switch(int(random(0,10))) { case 0: a[0]++; break; case 1: a[1]++; break; case 2: a[2]++; break; case 3: a[3]++; break; case 4: a[4]++; break; case 5: a[5]++; break; case 6: a[6]++; break; case 7: a[7]++; break; case 8: a[8]++; break; case 9: a[9]++; break; default: cerr << "Error!" << endl; exit(-1); } for(int icnt = 0; icnt != 10; ++icnt) cout << icnt << ": " << setw(6) << setiosflags(ios::fixed) << setprecision(2) << double(a[icnt])/Gen_max*100 << "%" << endl; return 0; }
double random(double start, double end) { return start+(end-start)*rand()/(RAND_MAX + 1.0); } /* 運行結果 * 0: 10.01% * 1: 9.99% * 2: 9.99% * 3: 9.99% * 4: 9.98% * 5: 10.01% * 6: 10.02% * 7: 10.01% * 8: 10.01% * 9: 9.99% */ 可知用這種方法得到的隨機數是滿足統計規律的。
另:在Linux下利用GCC編譯程序,即使我執行了1000000次運算,是否將random函數定義了inline函數似乎對程序沒有任何影響,有理由相信,GCC已經為我們做了優化。但是冥冥之中我又記得要做inline優化得加O3才行...
不行,于是我們把循環次數改為10億次,用time命令查看執行時間: chinsung@gentoo ~/workspace/test/Debug $ time ./test 0: 10.00% 1: 10.00% 2: 10.00% 3: 10.00% 4: 10.00% 5: 10.00% 6: 10.00% 7: 10.00% 8: 10.00% 9: 10.00%
real 2m7.768s user 2m4.405s sys 0m0.038s chinsung@gentoo ~/workspace/test/Debug $ time ./test 0: 10.00% 1: 10.00% 2: 10.00% 3: 10.00% 4: 10.00% 5: 10.00% 6: 10.00% 7: 10.00% 8: 10.00% 9: 10.00%
real 2m7.269s user 2m4.077s sys 0m0.025s
前一次為進行inline優化的情形,后一次為沒有作inline優化的情形,兩次結果相差不大,甚至各項指標后者還要好一些,不知是何緣由...
=================================================================================
random函數不是ANSI C標準,不能在gcc,vc等編譯器下編譯通過。 可改用C++下的rand函數來實現。 1、C++標準函數庫提供一隨機數生成器rand,返回0-RAND_MAX之間均勻分布的偽隨機整數。 RAND_MAX必須至少為32767。rand()函數不接受參數,默認以1為種子(即起始值)。 隨機數生成器總是以相同的種子開始,所以形成的偽隨機數列也相同,失去了隨機意義。(但這樣便于程序調試) 2、C++中另一函數srand(),可以指定不同的數(無符號整數變元)為種子。但是如果種子相同,偽隨機數列也相同。一個辦法是讓用戶輸入種子,但是仍然不理想。 3、 比較理想的是用變化的數,比如時間來作為隨機數生成器的種子。 time的值每時每刻都不同。所以種子不同,所以,產生的隨機數也不同。 // C++隨機函數(VC program) #include <stdio.h> #include <iostream> #include <time.h> using namespace std; #define MAX 100 int main(int argc, char* argv[]) { srand( (unsigned)time( NULL ) );//srand()函數產生一個以當前時間開始的隨機種子.應該放在for等循環語句前面 不然要很長時間等待 for (int i=0;i<10;i++) cout<<rand()%MAX<<endl;//MAX為最大值,其隨機域為0~MAX-1 return 0; } 二、rand()的用法 rand()不需要參數,它會返回一個從0到最大隨機數的任意整數,最大隨機數的大小通常是固定的一個大整數。 這樣,如果你要產生0~10的10個整數,可以表達為: int N = rand() % 11; 這樣,N的值就是一個0~10的隨機數,如果要產生1~10,則是這樣: int N = 1 + rand() % 10; 總結來說,可以表示為: a + rand() % n 其中的a是起始值,n是整數的范圍。 a + rand() % (b-a+1) 就表示 a~b之間的一個隨機數若要0~1的小數,則可以先取得0~10的整數,然后均除以10即可得到隨機到十分位的10個隨機小數,若要得到隨機到百分位的隨機小數,則需要先得到0~100的10個整數,然后均除以100,其它情況依此類推。 通常rand()產生的隨機數在每次運行的時候都是與上一次相同的,這是有意這樣設計的,是為了便于程序的調試。若要產生每次不同的隨機數,可以使用srand( seed )函數進行隨機化,隨著seed的不同,就能夠產生不同的隨機數。 如大家所說,還可以包含time.h頭文件,然后使用srand(time(0))來使用當前時間使隨機數發生器隨機化,這樣就可以保證每兩次運行時可以得到不同的隨機數序列(只要兩次運行的間隔超過1秒)。
原諒轉載自:http://lukas06.blog.sohu.com/94010246.html
C++編譯器在生成DLL時,會對導出函數進行名字改編,并且不同的編譯器使用的改編規則不一樣,因此改編的名字后的名字是不一樣的。因此,如果利用不同的分別生成DLL文件和訪問DLL文件的客戶端,那么后者在訪問該DLL文件的時候就會出現問題。例如:使用C++編寫了一個DLL,而使用C語言編寫的客戶端進行訪問就會出現問題。由于C++編譯器已經對該導出函數名字進行了改編,所以用C語言編寫的客戶端就找不到DLL的導出函數。這就是DLL導出函數的名字改編問題。
如果希望動態鏈接庫文件在編譯時,導出函數的名稱不要發生改變,那么在定義導出函數時,需要加上限定符:extern "C"。注意:雙引號中的“C”一定要大寫。
例如:Dll1.h頭文件 #ifdef DLL1_API #else #define DLL1_API extern "C" _declspec(dllimport) #endif
DLL1_API int add(int a,int b); DLL1_API int subtract(int a,int b);
Dll1.cpp源文件 #define DLL1_API extern "C" _declspec(dllexport) #include "Dll1.h"
int add(int a,int b) { return a+b; } int subtract(int a,int b) { return a-b; } 這樣利用dumpbin工具可以查看Dll2.dll的導出函數,>dumpbin -exports Dll2.dll,可以發現名字沒有被改編。
利用限定符extern "C"可以解決C++和C語言之間相互調用時函數命名的問題。但是這種方法有一個缺陷:就是不能用于導出一個類的成員函數,而只能用于導出全局函數這種情況。
但是還有一個問題是,如果使用了標準調用約定,也就是pascal調用約定,WINAPI調用約定:_stdcall,此時即使使用了extern "C",仍然會出現導出函數名字被改編的問題。例如:使用C語言編寫一個DLL文件,而客戶端使用Delphi進行編寫,那么在編寫導出函數時,應該指定其使用標準的函數調用約定。此時,就會出現問題,即C語言編寫的DLL文件的導出函數發生了名字改編。在這種情況下,可以使用一個稱為模塊定義文件(DEF)的方式解決名字改編問題。 例如:Dll2.def LIBRARY
EXPORTS add subtract 如果想使用與源文件中定義的不一樣的函數名,可以按照以下語法:entryname=internalname 其中,entryname是要導出的符號名,而internalname是DLL中將要導出的函數名。
示例:
LIBRARY "VerifyLocalResType"
EXPORTS
VerifyDDSSize VerifyDDSType VerifyTGASize VerifyTGAType
無意中看到的一篇,挺不錯的。呵呵。收藏下。
c++字符大小寫轉換
原為某著名軟件公司試題,大意如下:請實現以下兩個函數:char toupper(char c); char tolower(char c); 分別用于將傳入的字母轉為大寫和小寫。兩個函數傳入的參數取值范圍都是[a-zA-Z],并且為ASCII編碼,實現時不用檢查參數合法性。兩個函數的實現不能使用任何形式的分支、跳轉等類型的語句或指令(特別說明:C/C++的條件操作符?:也是分支指令的一種形式,故而不能使用)。請盡可能多的寫出你知道的辦法。
分析解決:此題比較特別,限制嚴格,根據題目要求,排除if else、for、while、do while、switch case、?:外,能使用的語句就只有 =、+=、-=、&、|、^、++、--這些了,想要實現大小寫轉換,只能從這些語句中進行選擇思考,由于字符集為ASCII編碼,且范圍明確為[a-zA-Z],我們知道,a-z對應ASCII值為97-122,A-Z對應ASCII為65-90,觀察這些數字,可以發現97-122都大于96 ,65-90都大于64且小于96,進一步從二進制上考慮,則發現所有小寫字母對應的二進制形式為011XXXXX,大寫字母對應的二進制形式為010XXXXX,一到這里,哈哈,答案就出來了,通過位運算&和|就可實現了。代碼描述如下
1char toupper(char c) 2{ 3 return c & 0x5F; 4} 5 6char tolower(char c) 7{ 8 //c | 0x60也行,但不太好,因為0x60會改變結果的第7位值,根據題目意思,改變第6位值為1,而其它位保持不變就夠了。 9 return c | 0x20; 10} 至于其它方法,我就沒多想了,還希望各位大俠多多分享一下哈。
#include <time.h> //* 方法一 time_t tt = time(NULL);//這句返回的只是一個時間cuo tm* t= localtime(&tt); printf("%d-%02d-%02d %02d:%02d:%02d\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); //* 方法二 SYSTEMTIME st = {0}; GetLocalTime(&st); printf("%d-%02d-%02d %02d:%02d:%02d\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
下面幾個,是網上找的:轉載地址: http://apps.hi.baidu.com/share/detail/17815869
個人覺得第二種還是比較實用的,而且也是最常用的~
不過當計算算法耗時的時候,不要忘記second,不能只要用Milliseconds來減,不然后出現負值,若是算法耗時太長就得用minutes啦。再不然,就hours……
//方案— 優點:僅使用C標準庫;缺點:只能精確到秒級 #include <time.h> #include <stdio.h> int main( void ) { time_t t = time(0); char tmp[64]; strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A 本年第%j天 %z",localtime(&t) ); puts( tmp ); return 0; } size_t strftime(char *strDest, size_t maxsize, const char *format, const struct tm *timeptr); 根據格式字符串生成字符串。 struct tm *localtime(const time_t *timer); 取得當地時間,localtime獲取的結果由結構tm返回 返回的字符串可以依下列的格式而定: %a 星期幾的縮寫。Eg:Tue %A 星期幾的全名。 Eg: Tuesday %b 月份名稱的縮寫。 %B 月份名稱的全名。 %c 本地端日期時間較佳表示字符串。 %d 用數字表示本月的第幾天 (范圍為 00 至 31)。日期 %H 用 24 小時制數字表示小時數 (范圍為 00 至 23)。 %I 用 12 小時制數字表示小時數 (范圍為 01 至 12)。 %j 以數字表示當年度的第幾天 (范圍為 001 至 366)。 %m 月份的數字 (范圍由 1 至 12)。 %M 分鐘。 %p 以 ''AM'' 或 ''PM'' 表示本地端時間。 %S 秒數。 %U 數字表示為本年度的第幾周,第一個星期由第一個周日開始。 %W 數字表示為本年度的第幾周,第一個星期由第一個周一開始。 %w 用數字表示本周的第幾天 ( 0 為周日)。 %x 不含時間的日期表示法。 %X 不含日期的時間表示法。 Eg: 15:26:30 %y 二位數字表示年份 (范圍由 00 至 99)。 %Y 完整的年份數字表示,即四位數。 Eg:2008 %Z(%z) 時區或名稱縮寫。Eg:中國標準時間 %% % 字符。
//方案二 優點:能精確到毫秒級;缺點:使用了windows API #include <windows.h> #include <stdio.h> int main( void ) { SYSTEMTIME sys; GetLocalTime( &sys ); printf( "%4d/%02d/%02d %02d:%02d:%02d.%03d 星期%1d\n",sys.wYear,sys.wMonth,sys.wDay,sys.wHour,sys.wMinute, sys.wSecond,sys.wMilliseconds,sys.wDayOfWeek); return 0; } //方案三,優點:利用系統函數,還能修改系統時間 //此文件必須是c++文件 #include<stdlib.h> #include<iostream> using namespace std; void main() { system("time"); } //方案四,將當前時間折算為秒級,再通過相應的時間換算即可 //此文件必須是c++文件 #include<iostream> #include<ctime> using namespace std; int main() { time_t now_time; now_time = time(NULL); cout<<now_time; return 0; } 注意:GetLocalTime()與GetSystemTime()是有區別的
http://www.4ucode.com/Study/Topic/1996448VS2008中創建DLL工程 文件->新建->項目->visual c++->win32->win32控制臺應用程序(win32項目也可以) 填寫項目名稱MyDLL->確定->下一步->DLL(附加選項 對空項目打鉤)->完成。 到這里DLL工程就創建完畢了,下面新建兩個文件testDLL.cpp和testDLL.h。 C++ .h 文件 #define DLL_EXPORT __declspec(dllexport)
extern "C" DLL_EXPORT int MyMinus(int a,int b);
.cpp 文件 // testDLL.cpp : 定義 DLL 應用程序的導出函數。
//
#include "stdafx.h"
#include "testDLL.h"
int MyMinus(int a,int b)
{
return (a-b);
}
delphi調用代碼 unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
edt1: TEdit;
edt2: TEdit;
lbl1: TLabel;
lbl2: TLabel;
lbl3: TLabel;
btn1: TButton;
lbl4: TLabel;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
const
TestDll = 'testDLL.dll';
function MyMinus(a:Integer;b:Integer):Integer;cdecl;external TestDll;
implementation
{$R *.dfm}
procedure TForm1.btn1Click(Sender: TObject);
begin
lbl4.Caption := IntToStr(MyMinus(StrToInt(edt1.Text),StrToInt(edt2.Text)));
end;
end.
原諒轉載自: http://tech.ddvip.com/2008-11/122662837992492.htmlVisual C++中提供的MFC類CtreeCtrl(樹型控件)用來顯示具有一定層次結構的數據項時方便、直觀,所以它已經被廣泛地應用在各種軟件中,如資源管理器中的磁盤目錄就用的是樹型控件,我們在編程中也會經常用到這個控件,但是這個控件也有缺陷,那就是它并不直接支持拖動節點等高級特性,這使得程序員在編程時使用它受到了很大限制,同時又給軟件用戶帶來了一些不便。為此,本實例通過從 CTreeCtrl 中派生了一個類 CXTreeCtrl ,實現樹型控件中節點的拖動。這個類具有如下的功能:⑴ 基本項目條拖動的實現;⑵ 處理項目條的無意拖動;⑶ 能處理項目條拖動過程中的滾動問題;⑷ 拖動過程中節點會智能展開。程序編譯運行后的效果如圖所示:
570)?'570px':'auto';
}" src="http://img.ddvip.com/2008_11_14/1226628379_ddvip_9835.png">
圖一:樹型控件節點拖動示例
一、實現方法
我們針對上述自定義類的實現功能,介紹實現思路和方法。
(1)基本項目條拖動的實現
當我們要拖動樹型控件的一個項目條時,樹型控件會給它的父窗口發送一個TVN_BEGINDRAG通知消息,所以可以在此消息的響應函數中,調用 CTreeCtrl ::CreateDragImage ()函數創建表示當前項目條正處在拖動操作中的圖象,該函數創建的圖象由項目條的圖象和標簽文本組成。創建了拖動圖象后,調用CImageList::BeginDrag()函數指定拖動圖象的熱點位置,然后調用CImageList::DragEnter()函數顯示拖動圖象。接下來處理 WM_MOUSEMOVE 消息用于更新拖動圖象,我們想讓移動中的圖象經過某些項目時高亮度顯示,這可以調用 CTreeCtrl ::SelectDropTarget() 來實現。在調用 SelectDropTarget()函數之前,需要先調用CImageList::DragShowNolock ( false )函數來隱藏圖象列表,然后再調用CImageList::DragShowNolock ( true ) 函數來恢復圖象列表的顯示,這樣就不會在拖動過程中留下難看的軌跡。最后我們處理 WM_LBUTTONUP 消息用于完成拖動操作,在該消息響應函數中,我們需要完成結束拖動圖象的顯示、刪除拖動圖象、釋放鼠標、節點的拷貝/刪除等操作。在節點的拷貝/刪除操作中,如果是父節點拖到子節點上,我們可以先將父節點拷到根結點下的臨時節點中,再從臨時結點處拷到子節點,然后將根結點下的臨時節點刪除,這樣做的目的是防止產生異常。
(2)處理項目條的無意拖動
牐犎綣在鼠標按下時不小心移動了鼠標,這時系統就認為產生了一個移動操作,這就產生了誤操作。解決這個問題的方法是設置時間延遲,也就是說當用戶按下鼠標后必須在原位置停留一段時間,才能激活拖動操作。
(3)處理拖動過程中的滾動問題
當我們拖動樹型控件的項目條時,如果目的節點不可見,則需要拖動滾動條或收攏其它一些節點以使得目的節點顯示出來,無疑,這會給我們帶來很大的不便。為此就要給樹型控件添加自動滾動支持。首先設置一個定時器,在 WM_TIMER 消息中檢測鼠標的位置,如果靠近樹型控件的下邊緣,則使得控件向下滾動。靠近上邊緣則向上滾動。滾動速度根據鼠標的位置確定。
(4)拖動過程中節點的智能展開
為了實現在拖動過程中鼠標停留在某個節點上一段時間后,該節點會自動展開的功能。設置一個定時器,當鼠標在拖動過程中停止在某個節點上時,定時器被啟動,再設置一變量保存當前的鼠標位置。
二、編程步驟
1、 新建一對話框工程DragTree,編輯資源,在對話框中加入一樹型控件IDC_TREE ,屬性設置為:Has Buttons、Has Lines、Lines at root、Edit Labels、Border;
2、 使用Class Wizard給該控件添加一個成員變量 m_wndTree ,在代碼部分將該控件的類型修改為CXTreeCtrl。
3、 在對話框的OnInitDialog()函數中添加代碼,初始化樹型控件的項目條;
4、 制作一個圖像資源(ID為IDB_TREEIMAGE),其中包含兩個小圖標,用來作為樹型控件項目條的顯示圖標;
5、 添加代碼,編譯運行程序。
三、程序代碼
// XTreeCtrl.h : header file #if !defined(AFX_XTREECTRL_H__3EF12526_EF66_4FD9_A572_59476441D79A__INCLUDED_) #define AFX_XTREECTRL_H__3EF12526_EF66_4FD9_A572_59476441D79A__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CXTreeCtrl : public CTreeCtrl { // Construction public: CXTreeCtrl(); // Attributes public: // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CXTreeCtrl) //}}AFX_VIRTUAL // Implementation public: virtual ~CXTreeCtrl(); // Generated message map functions protected: UINT m_TimerTicks; //處理滾動的定時器所經過的時間 UINT m_nScrollTimerID; //處理滾動的定時器 CPoint m_HoverPoint; //鼠標位置 UINT m_nHoverTimerID; //鼠標敏感定時器 DWORD m_dwDragStart; //按下鼠標左鍵那一刻的時間 BOOL m_bDragging; //標識是否正在拖動過程中 CImageList* m_pDragImage; //拖動時顯示的圖象列表 HTREEITEM m_hItemDragS; //被拖動的標簽 HTREEITEM m_hItemDragD; //接受拖動的標簽 //{{AFX_MSG(CXTreeCtrl) afx_msg void OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnTimer(UINT nIDEvent); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: HTREEITEM CopyBranch(HTREEITEM htiBranch,HTREEITEM htiNewParent,HTREEITEM htiAfter); HTREEITEM CopyItem(HTREEITEM hItem,HTREEITEM htiNewParent,HTREEITEM htiAfter); }; #endif //////////////////////////////////////////////////////////// CXTreeCtrl #include "stdafx.h" #include "DragTree.h" #include "XTreeCtrl.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define DRAG_DELAY 60 CXTreeCtrl::CXTreeCtrl() { m_bDragging = false; } CXTreeCtrl::~CXTreeCtrl() {} BEGIN_MESSAGE_MAP(CXTreeCtrl, CTreeCtrl) //{{AFX_MSG_MAP(CXTreeCtrl) ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag) ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_WM_LBUTTONDOWN() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() void CXTreeCtrl::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; *pResult = 0; //如果是無意拖曳,則放棄操作 if( (GetTickCount() - m_dwDragStart) < DRAG_DELAY ) return; m_hItemDragS = pNMTreeView->itemNew.hItem; m_hItemDragD = NULL; //得到用于拖動時顯示的圖象列表 m_pDragImage = CreateDragImage( m_hItemDragS ); if( !m_pDragImage ) return; m_bDragging = true; m_pDragImage->BeginDrag ( 0,CPoint(8,8) ); CPoint pt = pNMTreeView->ptDrag; ClientToScreen( &pt ); m_pDragImage->DragEnter ( this,pt ); //"this"將拖曳動作限制在該窗口 SetCapture(); m_nScrollTimerID = SetTimer( 2,40,NULL ); } void CXTreeCtrl::OnMouseMove(UINT nFlags, CPoint point) { HTREEITEM hItem; UINT flags; //檢測鼠標敏感定時器是否存在,如果存在則刪除,刪除后再定時 if( m_nHoverTimerID ) { KillTimer( m_nHoverTimerID ); m_nHoverTimerID = 0; } m_nHoverTimerID = SetTimer( 1,800,NULL ); //定時為 0.8 秒則自動展開 m_HoverPoint = point; if( m_bDragging ) { CPoint pt = point; CImageList::DragMove( pt ); //鼠標經過時高亮顯示 CImageList::DragShowNolock( false ); //避免鼠標經過時留下難看的痕跡 if( (hItem = HitTest(point,&flags)) != NULL ) { SelectDropTarget( hItem ); m_hItemDragD = hItem; } CImageList::DragShowNolock( true ); //當條目被拖曳到左邊緣時,將條目放在根下 CRect rect; GetClientRect( &rect ); if( point.x < rect.left + 20 ) m_hItemDragD = NULL; } CTreeCtrl::OnMouseMove(nFlags, point); } void CXTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point) { CTreeCtrl::OnLButtonUp(nFlags, point); if( m_bDragging ) { m_bDragging = FALSE; CImageList::DragLeave( this ); CImageList::EndDrag(); ReleaseCapture(); delete m_pDragImage; SelectDropTarget( NULL ); if( m_hItemDragS == m_hItemDragD ) { KillTimer( m_nScrollTimerID ); return; } Expand( m_hItemDragD,TVE_EXPAND ); HTREEITEM htiParent = m_hItemDragD; while( (htiParent = GetParentItem(htiParent)) != NULL ) { if( htiParent == m_hItemDragS ) { HTREEITEM htiNewTemp = CopyBranch( m_hItemDragS,NULL,TVI_LAST ); HTREEITEM htiNew = CopyBranch( htiNewTemp,m_hItemDragD,TVI_LAST ); DeleteItem( htiNewTemp ); SelectItem( htiNew ); KillTimer( m_nScrollTimerID ); return; } } HTREEITEM htiNew = CopyBranch( m_hItemDragS,m_hItemDragD,TVI_LAST ); DeleteItem( m_hItemDragS ); SelectItem( htiNew ); KillTimer( m_nScrollTimerID ); } } HTREEITEM CXTreeCtrl::CopyItem(HTREEITEM hItem, HTREEITEM htiNewParent, HTREEITEM htiAfter) //拷貝條目 { TV_INSERTSTRUCT tvstruct; HTREEITEM hNewItem; CString sText; //得到源條目的信息 tvstruct.item.hItem = hItem; tvstruct.item.mask=TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE; GetItem( &tvstruct.item ); sText = GetItemText( hItem ); tvstruct.item.cchTextMax = sText.GetLength (); tvstruct.item.pszText = sText.LockBuffer (); //將條目插入到合適的位置 tvstruct.hParent = htiNewParent; tvstruct.hInsertAfter = htiAfter; tvstruct.item.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT; hNewItem = InsertItem( &tvstruct ); sText.ReleaseBuffer (); //限制拷貝條目數據和條目狀態 SetItemData( hNewItem,GetItemData(hItem) ); SetItemState( hNewItem,GetItemState(hItem,TVIS_STATEIMAGEMASK),TVIS_STATEIMAGEMASK); return hNewItem; } HTREEITEM CXTreeCtrl::CopyBranch(HTREEITEM htiBranch, HTREEITEM htiNewParent, HTREEITEM htiAfter) //拷貝分支 { HTREEITEM hChild; HTREEITEM hNewItem = CopyItem( htiBranch,htiNewParent,htiAfter ); hChild = GetChildItem( htiBranch ); while( hChild != NULL ) { CopyBranch( hChild,hNewItem,htiAfter ); hChild = GetNextSiblingItem( hChild ); } return hNewItem; } void CXTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point) //處理無意拖曳 { m_dwDragStart = GetTickCount(); CTreeCtrl::OnLButtonDown(nFlags, point); } void CXTreeCtrl::OnTimer(UINT nIDEvent) { //鼠標敏感節點 if( nIDEvent == m_nHoverTimerID ) { KillTimer( m_nHoverTimerID ); m_nHoverTimerID = 0; HTREEITEM trItem = 0; UINT uFlag = 0; trItem = HitTest( m_HoverPoint,&uFlag ); if( trItem && m_bDragging ) { SelectItem( trItem ); Expand( trItem,TVE_EXPAND ); } } //處理拖曳過程中的滾動問題 else if( nIDEvent == m_nScrollTimerID ) { m_TimerTicks++; CPoint pt; GetCursorPos( &pt ); CRect rect; GetClientRect( &rect ); ClientToScreen( &rect ); HTREEITEM hItem = GetFirstVisibleItem(); if( pt.y < rect.top +10 ) { //向上滾動 int slowscroll = 6 - (rect.top + 10 - pt.y )/20; if( 0 == (m_TimerTicks % ((slowscroll > 0) ? slowscroll : 1)) ) { CImageList::DragShowNolock ( false ); SendMessage( WM_VSCROLL,SB_LINEUP ); SelectDropTarget( hItem ); m_hItemDragD = hItem; CImageList::DragShowNolock ( true ); } } else if( pt.y > rect.bottom - 10 ) { //向下滾動 int slowscroll = 6 - (pt.y - rect.bottom + 10)/20; if( 0 == (m_TimerTicks % ((slowscroll > 0) ? slowscroll : 1)) ) { CImageList::DragShowNolock ( false ); SendMessage( WM_VSCROLL,SB_LINEDOWN ); int nCount = GetVisibleCount(); for( int i=0 ; i<nCount-1 ; i++ ) hItem = GetNextVisibleItem( hItem ); if( hItem ) SelectDropTarget( hItem ); m_hItemDragD = hItem; CImageList::DragShowNolock ( true ); } } } else CTreeCtrl::OnTimer(nIDEvent); } //////////////////////////////////////////////////////////// BOOL CDragTreeDlg::OnInitDialog() { CDialog::OnInitDialog(); …………………….//此處代碼省略 // TODO: Add extra initialization here m_image.Create ( IDB_TREEIMAGE,16,1,RGB(255,255,255) ); m_wndTree.SetImageList ( &m_image,TVSIL_NORMAL ); HTREEITEM hti1 = m_wndTree.InsertItem ( _T("唐詩"),0,1 ); HTREEITEM hti2 = m_wndTree.InsertItem ( _T("宋詞"),0,1 ); HTREEITEM hti3 = m_wndTree.InsertItem ( _T("元曲"),0,1 ); HTREEITEM hti4 = m_wndTree.InsertItem ( _T("李白"),0,1,hti1 ); m_wndTree.InsertItem ( _T("靜夜思(床前明月光)"),0,1,hti4 ); m_wndTree.InsertItem ( _T("將進酒(君不見黃河之水天上來)"),0,1,hti4 ); m_wndTree.InsertItem ( _T("望廬山瀑布(日照香爐生紫煙)"),0,1,hti4 ); m_wndTree.InsertItem ( _T("蜀道難(噫吁戲,危乎高哉)"),0,1,hti4 ); HTREEITEM hti5 = m_wndTree.InsertItem ( _T("杜甫"),0,1,hti1 ); m_wndTree.InsertItem ( _T("蜀相(丞相祠堂何處尋)"),0,1,hti5 ); m_wndTree.InsertItem ( _T("春望(國破山河在)"),0,1,hti5 ); m_wndTree.InsertItem ( _T("茅屋為秋風所破歌(八月秋高風怒號)"),0,1,hti5 ); HTREEITEM hti6 = m_wndTree.InsertItem ( _T("白居易"),0,1,hti1 ); m_wndTree.InsertItem ( _T("長恨歌(漢皇重色思傾國)"),0,1,hti6 ); m_wndTree.InsertItem ( _T("琵琶行並序(潯陽江頭夜送客)"),0,1,hti6 ); m_wndTree.InsertItem ( _T("李清照"),0,1,hti2 ); m_wndTree.InsertItem ( _T("柳永"),0,1,hti2 ); return TRUE; // return TRUE unless you set the focus to a control }
void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
CMenu menu;
menu.CreatePopupMenu();
menu.AppendMenuW(MF_BYCOMMAND | MF_STRING, ID_RIGHT_CLEAR, _T("剪切(&X)"));
menu.AppendMenuW(MF_BYCOMMAND | MF_STRING, ID_RIGHT_COPY, _T("復制(&C)"));
menu.AppendMenuW(MF_BYCOMMAND | MF_STRING, ID_RIGHT_PASTE, _T("粘貼(&P)"));
menu.AppendMenuW(MF_SEPARATOR);
menu.AppendMenuW(MF_BYCOMMAND | MF_STRING, ID_RIGHT_CLEAR, _T("清除\tCtrl + C"));
ClientToScreen(&point);
menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTALIGN, point.x, point.y, this);
CView::OnRButtonDown(nFlags, point);
}
|