java repaint 重画图形,学习笔记:WINDOWS的图形重绘基础
OnPaint()與OnDraw()的區(qū)別:
OnPaint是WM_PAINT的消息響應(yīng)函數(shù),在MFC的基類里OnPaint函數(shù)調(diào)用了OnDraw()函數(shù)。
OnPaint函數(shù)另外還調(diào)用了OnPrepareDC()函數(shù)。
如果在窗口子類覆蓋了OnPaint函數(shù),當(dāng)MFC調(diào)用我們重寫的OnPaint函數(shù)時,就調(diào)不到OnDraw()函數(shù)了,
除非我們?nèi)フ{(diào)用OnDraw()函數(shù)。
Invalidate函數(shù)族介紹:
函數(shù): Invalidate(BOOL bErase = TRUE)
函數(shù): InvalidateRect(CRect* rect,BOOL bErase = TRUE)
函數(shù): InvalidateRgn(CRgn* rgn,BOOL bErase = TRUE)
將一個區(qū)域放入Update Region中。[Update
Region]是窗口的無效區(qū)域。[無效區(qū)域]是需要重繪的區(qū)域。
為何需要重繪呢?
第1類事件:當(dāng)需要展現(xiàn)某窗口的“新的”區(qū)域時,就需要重繪。
當(dāng)創(chuàng)建一個窗口時;當(dāng)把窗口從另一個窗口的背后彈到前面時;當(dāng)從圖標(biāo)化到最大化轉(zhuǎn)變時;
當(dāng)滾動式的窗口,發(fā)生滾動事件時;當(dāng)把遮擋在前面的窗口一點(diǎn)一點(diǎn)拖開,
讓被遮擋的窗口一點(diǎn)一點(diǎn)的露出時
就需要重繪。這些動作都是由WINDOWS系統(tǒng)管理的,系統(tǒng)會很肯定的認(rèn)為,在上述事件發(fā)生時,必須重繪。
注:如果把被遮擋的窗口,一點(diǎn)一點(diǎn)的遮蓋住,就不需要重繪。
第2類事件:當(dāng)有業(yè)務(wù)數(shù)據(jù)改變的事件發(fā)生時。
窗口是用來顯示業(yè)務(wù)數(shù)據(jù)的。比如我的窗口正在顯示一個橢圓,后臺將業(yè)務(wù)數(shù)據(jù)變成
了三角形,我需要顯示這個三角形,這時就需要重繪了。對于第2類事件,
WINDOWS不可能感知到你需要重繪。
例如:我有一個變量 int m_shape=1; 1代表橢圓,2代表三角形。
我需要讓窗口的圖形顯示m_shape代表的形狀
當(dāng)我把m_shape的值由1變成2時,WINDOWS根本不知道我需要重繪一個三角形。
對于第1類事件,WINDOWS會自動發(fā)出WM_PAINT消息,窗口的對應(yīng)處理函數(shù)OnPaint()就會被調(diào)用。
程序員不必關(guān)心“在何時”和“在何地”重繪。對于第2類事件,程序員必須通知WINDOWS,在何地重繪。
至于“何時”重繪,WINDOWS會挑選一個合適的時機(jī)。
Invalidate函數(shù)族同第2類事件有關(guān)。通過調(diào)用Invalidate函數(shù)族,通知windows系統(tǒng),
我有一些窗口區(qū)域需要重繪。CWnd::Invalidate()是說整個窗口都需要重繪。
CWnd::InvalidateRect()是說窗口的某個矩形區(qū)域需要
重繪。CWnd::InvalidateRgn是說窗口的某個不規(guī)則區(qū)域需要重繪。
“不規(guī)則區(qū)域”可以是任意多邊形,橢圓形,當(dāng)然也包括矩形。
用偽代碼說明上述三個函數(shù)的等價關(guān)系。
CRgn rgn;
rgn.CreateRectRgn(...);
CWnd::InvalidateRgn(&rgn,...);
等價于
CRect rect;
CWnd::InvalidateRect(&rect,...);
CRect rect;
GetClientRect(&rect);
CWnd::InvalidateRect(&rect,...);
等價于
CWnd::Invalidate(...);
Invalidate函數(shù)族中,都有一個bErase參數(shù)。此參數(shù)的含義:bErase=TRUE擦除背景,
bErase=FALSE不擦除背景
何為背景:想象窗口就是小朋友的畫紙。當(dāng)你把這張畫紙給另一個小朋友畫畫時,
前一個小朋友畫的東西就是
“背景”。一般我們不希望兩個小朋友畫的東西夾雜在一起。我們就需要擦除前一個小朋友畫的“背景”。
用什么擦除背景呢?WINDOWS允許我們設(shè)置“背景刷”,就是用某種顏色的刷子把整個畫紙涂抹一遍,
有點(diǎn)像刷白墻。
Invalidate函數(shù)族的調(diào)用不會立刻引發(fā)窗口重繪。Invalidate函數(shù)族只是累積和標(biāo)記需要重繪的區(qū)域。
下一次"WM_PAINT message
occurs"時(MSDN語),一次性處理累積和標(biāo)記的所有需要重繪的區(qū)域。顯然從
Invalidate調(diào)用,到實(shí)際的重繪動作是異步調(diào)用的。人類視覺有延遲現(xiàn)象,一秒連續(xù)播放24幀就可以認(rèn)為是
“動畫”了,所以上述重繪方式人類是察覺不出異樣的。假設(shè)每次Invalidate都同步的引發(fā)重繪OnPaint,有兩個不良后果:一是程序效率太差,二是可能讓人察覺出閃爍感。
那么何時下一次"WM_PAINT message occurs"呢?
當(dāng)應(yīng)用的消息隊(duì)列沒有其他消息時,并且窗口的[Update Region]不為空時,
系統(tǒng)就會自動產(chǎn)生WM_PAINT消息。
例子:演示“失效區(qū)域”是如何起作用的。
//每次重繪,會交替展現(xiàn)兩個不同的橢圓形。
void XXX::OnPaint()
{
CPaintDC dc(this);
static
int x=0;
if
(x==0){
dc.Ellipse (0, 0, 100, 200);
//橫向的橢圓形
x=1;
}
else{
dc.Ellipse (0, 0, 200, 100);
//豎向的橢圓形
x=0;
}
}
某CButton中,OnBnClicked偽代碼:
CRect rect;
XXX->GetClientRect(&rect);
rect.bottom = rect.bottom/2;
XXX->InvalidateRect(&rect,true);
//擦除背景
即使我擦除了背景,仍舊能看到前一個橢圓。因?yàn)槲以O(shè)定的“非法區(qū)域”只是rect的上半部分。
CRect rect;
XXX->GetClientRect(&rect);
XXX->InvalidateRect(&rect,true);
//擦除背景
可以正常的展現(xiàn),能交替展現(xiàn)兩個不同的橢圓形。
Validate函數(shù)族:
作用同Invalidate函數(shù)族相反,將一個區(qū)域從[Update Region]排除,這樣就不會被重繪。
當(dāng)然了,Validate要在下一次"WM_PAINT message occurs"之前的調(diào)用才能起作用。
如果發(fā)生了第1類事件,會造成大面積的區(qū)域變成“需重繪區(qū)域”,Validate設(shè)定的“不需重繪區(qū)域”
又會變成“需重繪區(qū)域了”。
UpdateWindow函數(shù):
UpdateWindow會檢查窗口的Update Region,當(dāng)其不為空時才發(fā)送WM_PAINT消息。
UpdateWindow可以繞開應(yīng)用程序消息循環(huán),直接發(fā)送WM_PAINT消息給窗口。
RedrawWindows函數(shù):
可以簡單理解為Invlidate + UpdateWindow,但是功能更強(qiáng)大一些。
SetRedraw函數(shù):
可以阻止窗口重繪。是解決窗口閃爍的一個辦法
MSDN的一個例子:
m_List.SetRedraw(FALSE); //暫時阻止窗口m_List重繪
...//大規(guī)模對m_List改頭換面
m_List.SetRedraw(TRUE);
//解除阻止窗口m_List重繪
m_List.Invalidate();
m_List.UpdateWindow(); ?//觸發(fā)WM_PAINT消息
SetRedraw函數(shù)好像是戲臺的前幕,后面切換場景時,先遮擋一下。
<>介紹了圖形密集型程序“閃”的原因。
主要技術(shù)為:1 選用黑色背景或者背景同前景相近的顏色,作為背景刷。
2 雙緩沖技術(shù),就是先在內(nèi)存設(shè)備DC里準(zhǔn)備好需要顯示的內(nèi)容,然后拷貝到屏幕設(shè)備DC
3 剪裁區(qū)域的合理利用。
總結(jié)
以上是生活随笔為你收集整理的java repaint 重画图形,学习笔记:WINDOWS的图形重绘基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言程序设计移动字母,C语言程序设计模
- 下一篇: python3.6程序_python3.