Tải bản đầy đủ - 0 (trang)
Hack 45. Animating a Sheet Dialog

Hack 45. Animating a Sheet Dialog

Tải bản đầy đủ - 0trang

workforbothdirectionsoftheanimation:whenthesheetis

incoming,theheightwillgetprogressivelygreater;whenthe

sheetisgoingout,theheightwilldecrease.

Toactuallydrawtheanimatingsheetduringtheanimation,you

canuseBufferedImage.getSubimage()tograbaportionofthereal

sheet,andthendrawthatintoitsownGraphicsviapaint()

callbacks.Whentheanimationcompletes,theanimatingsheet

isremovedfromtheglasspaneandtherealsheetisadded.



Aninterestingsideeffectofthisisthattheusercan'tclickthebuttons

asthesheetappearsorretractsbecauseit'sjustanimageofthesheet,

notthesheetitself.Ofcourse,theanimationissoshort(onesecondin

Example6-9,andMacOSX'sactuallycomesoutfasterthanthat),that

it'sunlikelyausercouldtrackthemovingsheetwithhermouseand

successfullyclickabuttonanyway.



Oneadvantageofusingacustomcomponentlikethisisthat

thekindsofthingsthatworkedforbasicsheets[Hack#44]all

workhereaswell.Forexample,youcoulduseasimple

GridBagLayout(wow,there'saphraseyoudon'thearoften)toget

thesheetcenteredatoptheglasspane.Asthesheetisareal

component,itshouldalsohandleresizingappropriately.

Settingasidethedetailsofthecustomcomponentneededto

createtheanimatedversionofthesheet,Example6-9shows

thereworkedversionofthesheetframe,whichI'vecalled

AniSheetableJFrame.



Example6-9.JFrameforanimatingsheet

appearanceanddisappearance





importjavax.swing.*;













importjavax.swing.border.*;

importjava.awt.*;

importjava.awt.event.*;

importjava.awt.image.*;









publicclassAniSheetableJFrameextendsJFrame



implementsActionListener{























publicstaticfinalintINCOMING=1;

publicstaticfinalintOUTGOING=-1;

publicstaticfinalfloatANIMATION_DURATION=

publicstaticfinalintANIMATION_SLEEP=50;







































JComponentsheet;

JPanelglass;

AnimatingSheetanimatingSheet;

booleananimating;

intanimationDirection;

TimeranimationTimer;

longanimationStart;

BufferedImageoffscreenImage;







publicAniSheetableJFrame(Stringname){







super(name);







glass=(JPanel)getGlassPane();







glass.setLayout(newGridBagLayout());







animatingSheet=newAnimatingSheet();







animatingSheet.setBorder(newLineBorde





}

publicJComponentshowJDialogAsSheet(JDialogdialog){







sheet=(JComponent)dialog.getContentP







sheet.setBorder(newLineBorder(Color.b







glass.removeAll();







animationDirection=INCOMING;







startAnimation();







returnsheet;





}























publicvoidhideSheet(){



animationDirection=OUTGOING;



startAnimation();

}























































privatevoidstartAnimation(){



glass.repaint();



//clearglasspaneandsetupanimating



animatingSheet.setSource(sheet);



glass.removeAll();



GridBagConstraintsgbc=newGridBagCon



gbc.anchor=GridBagConstraints.NORTH;



glass.add(animatingSheet,gbc);



gbc.gridy=1;



gbc.weighty=Integer.MAX_VALUE;



glass.add(Box.createGlue(),gbc);



glass.setVisible(true);















































}























privatevoidstopAnimation(){



animationTimer.stop();



animating=false;

}



























//usedbytheTimer

publicvoidactionPerformed(ActionEvente){



if(animating){





//calculateheighttoshow





floatanimationPercent=



//startanimationtimer

animationStart=System.currentTimeMill

if(animationTimer==null)



animationTimer=newTimer(ANI

animating=true;

animationTimer.start();











































(System.currentTimeMillis()-

ANIMATION_DURATION;

animationPercent=Math.min(1.

intanimatingHeight=0;







if(animationDirection==INCOMING){









animatingHeight=









(int)(animationPercent*







}else{









animatingHeight=

(int)((1.0f-animationPercent)*







}







//clipoffthatmuchfromsheetandbl







//intoanimatingSheet







animatingSheet.setAnimatingHeight(anim

animatingSheet.repaint();









if(animationPercent>=1.0f){









stopAnimation();









if(animationDirection==INCOM









finishShowingSheet();









}else{









glass.removeAll();









glass.setVisible(false);









}







}





}



}

privatevoidfinishShowingSheet(){





glass.removeAll();





GridBagConstraintsgbc=newGridBagConstraints





gbc.anchor=GridBagConstraints.NORTH;





glass.add(sheet,gbc);





gbc.gridy=1;





gbc.weighty=Integer.MAX_VALUE;





glass.add(Box.createGlue(),gbc);





glass.revalidate();









}





glass.repaint();

}

//innerclassAnimatedSheetgoeshere



Lookingatthiscodemethodbymethod,theconstructor'sonly

newtaskistocreateaninstanceoftheAnimatingSheetinnerclass

thatwillbeusedbytheanimation.

showJDialogAsSheet()againhijacksthecontentpanefromthe

JDialogandsetsitasideasthesheetinstancevariable.But



insteadofputtingsheetintheglasspaneimmediately,itsets

theanimationDirectiontoINCOMINGandcallsstartAnimation().The

hideSheet()methodmakessimilarchanges:insteadofmessing

withtheglasspanedirectly,itsimplysetsthedirectionto

OUTGOINGandcallsstartAnimation().

startAnimation()beginsbyrefreshingtheglasspanewitha

repaint().IttheninformstheAnimatingSheetofitsnewsource

(i.e.,thesheet)andaddstheAnimatingSheettothelayout.Itthen

setsupajavax.swing.TimertogetcallbackstoanactionPerformed()

methodthatperformseachstepoftheanimation.The

ANIMATION_SLEEPparameteraffectshowsmooththeanimationwill

lookshortersleepswillresultinahigherframerate,buthigher

CPUuse.



I'vesetthisvalueaslowas2msandhaven'thadaproblem,butI'm

onaprettyfastbox(adual1.8GHzG5PowerMac).Youmightwantto

playwiththisvaluetogetanidealperformance-to-smoothnessratio.



stopAnimation()juststopstheTimer,asyoumightexpect.



TheactionPerformed()methodisusedbytheTimercallbacks.It

hastwotasks:calculatingtheheightofthesheettoshow,and

wrappinguptheanimationifithasrunitscourse.Noticethat

theprogresscalculationdoesn'tassumethatithasbeencalled

backinaccordancewiththeANIMATION_SLEEP.Itcalculatesthe

currenttimeoffsetfromwhentheanimationbegan,and

calculatesaprogressfromthat,whichinturnallowsittofigure

outhowmuchofthesheettoshow.Thisisareallygood

practicebecauseifyourcomputercan'tkeepupwithyour

desiredrate,theTimerwillcombinemultiplecallbacksintoone.

Byjustusingthecurrenttime(insteadofapossiblyincorrect

assumptionaboutframerate),you'llkeepupwiththespecified

rateandduration.Onscreen,combinedcallbackswilllooklike

droppedframes,butachoppieranimationispreferabletoone

thattakeslongerthanitshould.

IfactionPerformed()decidesthattheanimationhasfinished,it

callsstopAnimation()andthencleansuptheglasspanebyeither

removingitscontents(ifthedirectionisOUTGOING)orbycalling

finishShowingSheet()(ifdirectionisINCOMING).The

finishShowingSheet()methodclearstheglasspaneandputsthe

realsheetinplace,justlikeshowJDialogAsSheet()didinthelast

lab.



6.6.2.Self-Painting

So,theframeisresponsibleforadding,removing,and

animatingtheAnimatedSheet,andforaddingtherealsheetwhen

theincominganimationcompletes.Whatthatleavesforthe

AnimatedSheetistheabilitytopaintitself(fromaregionofthe

originaldialog)andreportanaccuratesizeforthebenefitofthe

glasspane'sLayoutManager.

Example6-10showsthecodefortheAnimatingSheetinner

class.



Example6-10.Innerclasstopaintsheetduring

animation





































































classAnimatingSheetextendsJPanel{



DimensionanimatingSize=newDimension(0,1);



JComponentsource;



BufferedImageoffscreenImage;



publicAnimatingSheet(){





super();





setOpaque(true);



}



publicvoidsetSource(JComponentsource){





this.source=source;





animatingSize.width=source.getWidth(





makeOffscreenImage(source);



}



publicvoidsetAnimatingHeight(intheight){





animatingSize.height=height;





setSize(animatingSize);



}



privatevoidmakeOffscreenImage(JComponentsour





GraphicsConfigurationgfxConfig=







GraphicsEnvironment.getLocalGra









.getDefaultScreenDev









.getDefaultConfigura





offscreenImage=







gfxConfig.createCompatibleImage



















Graphics2DoffscreenGraphics=







(Graphics2D)offscreenImage.get





source.paint(offscreenGraphics);



}



publicDimensiongetPreferredSize(){return



publicDimensiongetMinimumSize(){returnani



publicDimensiongetMaximumSize(){returnani



publicvoidpaint(Graphicsg){





















//getthebottom-mostnpixelsofsour

//paintthemintog,wherenisheight











































}



















}



BufferedImagefragment=



offscreenImage.getSubimage(0,









































//g.drawImage(fragment,0,0,this);

g.drawImage(fragment,0,0,this);



Theconstructorismoreorlesstrivial,sotakealookatthe

setSource()method.ThismethodstoresthesourceJComponent

(thesheetthattheframecreatedfromadialog)asaninstance

variable,andcalculatesawidthfortheAnimatingSheet.Thiswidth

willbeconstantthroughtheanimation,soit'sassignedatthis

step.

ThemakeOffscreenImage()thatsetSource()callstakesthesource

JComponentanddrawsanoffscreenBufferedImage.Itdoesthisby

callingGraphicsConfiguration.createCompatibleImage()withthesizeof

thesource.UsingcreateCompatibleImage()ishighlyrecommended

forcreatingoffscreenimagesbecauseitwillkeepyoufrom

gettingsurprisedbygreaterorlessercolordepthsonplatforms

otherthanyourown.makeOffscreenImage()thengetstheGraphics2D

forthisimageandtellssourcetopaintitselfontotheGraphics2D.

Theoffscreenimagenowcontainsanimageofthesource

component.

setAnimatingHeight(),calledbytheframe'sTimerjustbeforeit

repaint()stheAnimatingSheet,storesawaytwovaluesthatwillbe



neededbythepaint()callback:theheightinpixelstobe



paintedandthenewsizeoftheanimatingsheet.Thisnewsize

isreturnedbygetPreferredSize(),getMinimumSize(),and

getMaximumSize(),sothelayoutmanagerwillknowhowmuch

spacetoprovideforit.

Andnowforthebigpayoff:paint()iscalledasaresultofthe

frame'sTimercallbackmakingarepaint()call.Ituses

BufferedImage.getSubimage()togetthepixelsforthebottom-most

animatingHeightpixelsoftheoffscreenimage.Itthendrawsthis

sub-imageintotheGraphicsat0,0.Theimage'ssizematchesthe

newlyreportedpreferredsizeoftheAnimatingSheetcomponent,

sothesub-imagefillsitcompletely.

Andthat'sit.Theframeisresponsibleformanagingwhat'sin

theglasspaneanimatingsheet,realsheet,ornothingandfor

runningtheanimation.TheAnimatingSheetisjustresponsiblefor

adjustingitssizewhencalledbytheanimation,andforpainting

anappropriaterepresentationsuitabletothatsize,whichit

doesbygrabbingsub-imagesfromanoffscreenimageofthe

realcomponent.

TheSheetTestclasshasonlyonemeaningfulchangefromthe

versioninthelasthack:theSheetableJFramebecomesan

AniSheetableJFrame.Runitoncetoseetheeffect,andthenplay

withtheANIMATION_DURATIONandANIMATION_SLEEPvaluestoseewhat

itlookslikewhenyouchangethedurationorthesmoothnessof

theanimation.ForthescreenshotsequenceinFigure6-9,I

usedadurationof10,000ms(10seconds)soIcouldget

multipleshotsandshowtheprogressoftheanimation.



Figure6-9.Successivescreenshotsofan

animatedglasspanesheet



Hack46.SlideNotesOutfromtheTaskbar



Popupanoteabovethetaskbarwhenyourapplication

wantsattention.

OnWindows,long-runningapplicationssometimeswillslideina

windowabovethetaskbartocallattentiontothemselveswhen

aninterestingeventoccurs,suchasafinisheddownloadoran

IMbuddy'sappearance.

IfyouwanttodothisinJava,youneedtodealwithapretty

significantproblem:neitherAWTnorSwinghasanyconceptof

thetaskbar(whereitis,howbigitis,whetherit'sauto-hiding,

oranythingelse).Asaresult,youdon'tknowwheretodraw

thewindow,andjusttakingaguessorhardcodingsomethingis

hazardoustoohighandthewindowfloatsinexplicablyonthe

desktop,toolowanditgetsburiedunderthetaskbar.

Furthermore,howisthisgoingtoworkonotheroperating

systems?OntheMac,theproperwaytogetattentionisto

bounceyourapplication'sdockicon.Sincethere'snoAPI

exposingthatfunctionality,canyouatleastuseaWindows-like

slide-inwindowabovethedock?Sure…ifyoucanfigureouthow

tallthedockis(it'suserconfigurable),orwhetherthedockis

evenonthebottomofthescreen(itmightbeontherightor

left,too).

Fortunately,itispossibletofigureoutwhatunobstructedspace

isavailabletoyouonthemaindisplay.Afterthat,it'sjusta

matterofoffscreenimagingandanimation.



6.7.1.FigureOutWhereYouAre



Thekeytofiguringoutyouravailablespaceistogetthelocal

GraphicsEnvironment,whichdescribesthedisplay,andthencall

getMaximumWindowBounds().Thismethod,introducedinJava1.4,

returnsaRectanglerepresentingthelargestcenteredWindowthat

couldfitonthedisplay,accountingforobjectsthatintrudeon

thedisplay'susablespace,liketheWindowstaskbarorthe

Mac'smonolithicmenubar.

ThismeansthatonWindows,theRectanglewillhaveanupperleftcornerat0,0;ontheMac,itwillbeat0,22,whichleaves

spacefortheMac'smenubar.Meanwhile,theheightofthe

Rectanglewon'tbetheheightofyourdisplayunlessyouhave

yourtaskbarsettoauto-hide,oryouhavemovedittotheright

orleft.

So,nowyouhavethebeginningsoftheslide-inabove-taskbar

window.Bysubtractingtheheightofthewindowfromtheycoordinateofthelastusablerow,youcanplacethewindow

directlyabovethetaskbarordock.Todotheslide-in,you'll

needtodoananimationloopinwhichprogressivelylarger

portionsofthecompletewindowareblittedintoasmaller

onscreenversion.Thisisverysimilartotheanimatedsheet

[Hack#45]you'vealreadyseen.It'ssosimilar,infact,that

youcanreusetheinnerclassfromthathacktodothe

progressiveredrawing.

Asimpleimplementation,SlideInNotification,isshownin

Example6-11.



Example6-11.Slidinginawindowimmediately

abovethetaskbarordock











importjavax.swing.*;

importjava.awt.*;

importjava.awt.event.*;

importjava.awt.image.*;



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Hack 45. Animating a Sheet Dialog

Tải bản đầy đủ ngay(0 tr)

×