A Swing Architecture Overview
2015-05-08 00:00
274 查看
摘要:MostSwingdevelopersknowbynowthatSwingcomponentshaveaseparablemodel-and-viewdesign.AndmanySwingusershaverunacrossarticlessayingthatSwingisbasedonsomethingcalleda"modifiedMVC(model-view-controller)architecture."
ButaccurateexplanationsofhowSwingcomponentsar...
TheInsideStoryonJFCComponentDesign
ByAmyFowler
MostSwingdevelopersknowbynowthatSwingcomponentshaveaseparablemodel-and-viewdesign.AndmanySwingusershaverunacrossarticlessayingthatSwingisbasedonsomethingcalleda"modifiedMVC(model-view-controller)architecture."
ButaccurateexplanationsofhowSwingcomponentsaredesigned,andhowtheirpartsallfittogether,havebeenhardtocomeby--untilnow.
Thesilenceendswiththepublicationofthisarticle,amajorwhitepaperonSwingcomponentdesign.ItprovidesacomprehensivetechnicaloverviewofSwing'smodifiedMVCstructureanddemystifiesmanyotherfacetsofSwingcomponentarchitectureaswell.
ThisdocumentpresentsatechnicaloverviewoftheSwingcomponentarchitecture.Inparticular,itcoversthefollowingareasindetail:
Designgoals
RootsinMVC
Separablemodelarchitecture
Pluggablelook-and-feelarchitecture
TobuildasetofextensibleGUIcomponentstoenabledeveloperstomorerapidlydeveloppowerfulJavafrontendsforcommercialapplications.
Tothisend,theSwingteamestablishedasetofdesigngoalsearlyintheprojectthatdrovetheresultingarchitecture.TheseguidelinesmandatedthatSwingwould:
BeimplementedentirelyinJavatopromotecross-platformconsistencyandeasiermaintenance.
ProvideasingleAPIcapableofsupportingmultiplelook-and-feelssothatdevelopersandend-userswouldnotbelockedintoasinglelook-and-feel.
Enablethepowerofmodel-drivenprogrammingwithoutrequiringitinthehighest-levelAPI.
AdheretoJavaBeansdesignprinciplestoensurethatcomponentsbehavewellinIDEsandbuildertools.
ProvidecompatibilitywithAWTAPIswherethereisoverlapping,toleveragetheAWTknowledgebaseandeaseporting.
Amodelthatrepresentsthedatafortheapplication.
Theviewthatisthevisualrepresentationofthatdata.
Acontrollerthattakesuserinputontheviewandtranslatesthattochangesinthemodel.
Earlyon,MVCwasalogicalchoiceforSwingbecauseitprovidedabasisformeetingthefirstthreeofourdesigngoalswithintheboundsofthelattertwo.
ThefirstSwingprototypefollowedatraditionalMVCseparationinwhicheachcomponenthadaseparatemodelobjectanddelegateditslook-and-feelimplementationtoseparateviewandcontrollerobjects.
(TheUIdelegateobjectshowninthispictureissometimescalledadelegateobject,orUIdelegate.TheUIdelegateusedinSwingisdescribedinmoredetailinthePluggablelook-and-feelsectionofthisarticle,underthesubheading"TheUIdelegate".)
Asthediagramillustrates,Swingarchitectureislooselybased--butnotstrictlybased--onthetraditionalMVCdesign.IntheworldofSwing,thisnewquasi-MVCdesignissometimesreferredtoaseparablemodelarchitecture.
Swing'sseparablemodeldesigntreatsthemodelpartofacomponentasaseparateelement,justastheMVCdesigndoes.ButSwingcollapsestheviewandcontrollerpartsofeachcomponentintoasingleUI(user-interface)object.
JButton,JTree,andsoon).Thecomponentclassthendelegatesthelook-and-feel-specificaspectsofthoseresponsibilitiestotheUIobjectthatisprovidedbythecurrentlyinstalledlook-and-feel.
Forexample,thecodethatimplementsdouble-bufferedpaintingisinSwing'sJComponentclass(the"mother"ofmostSwingcomponentclasses),whilethecodethatrendersaJButton'slabelisinthebutton'sUIdelegateclass.Theprecedingdiagramillustratesthissubtle(andoftenconfusing)point:
SoSwingdoeshaveastrongMVClineage.Butit'salsoimportanttoreiteratethatourMVCarchitectureservestwodistinctpurposes:
First,separatingthemodeldefinitionfromacomponentfacilitatesmodel-drivenprogramminginSwing.
Second,theabilitytodelegatesomeofacomponent'sview/controllerresponsibilitiestoseparatelook-and-feelobjectsprovidesthebasisforSwing'spluggablelook-and-feelarchitecture.
AlthoughthesetwoconceptsarelinkedbytheMVCdesign,theymaybetreatedsomewhatorthogonallyfromthedeveloper'sperspective.Theremainderofthisdocumentwillcovereachofthesemechanismsingreaterdetail.
Thefollowingtableshowsthecomponent-to-modelmappingforSwing.
tablepresentedatthebeginningofthissection.
Ofcoursewithsomecomponents,themodelcategorizationfallssomewhereinbetweenGUIstatemodelsandapplication-datamodels,dependingonthecontextinwhichthemodelisused.ThisisthecasewiththeBoundedRangeModelonJSliderorJProgressBar.Thesemodelsarehighlightedinpurpleintheprecedingtable.
Swing'sseparablemodelAPImakesnospecificdistinctionsbetweenGUIstatemodelsandapplication-datamodels;however,wehaveclarifiedthisdifferenceheretogivedevelopersabetterunderstandingofwhenandwhytheymightwishtoprogramwiththeseparablemodels.
tableatthebeginningofthissection,noticethatmodeldefinitionsaresharedacrosscomponentsincaseswherethedataabstractionforeachcomponentissimilarenoughtosupportasingleinterfacewithoutover-genericizingthatinterface.Commonmodelsenableautomaticconnectabilitybetweencomponenttypes.Forexample,becausebothJSliderandJScrollbarusetheBoundedRangeModelinterface,asingleBoundedRangeModelinstancecouldbepluggedintobothaJScrollbarandaJSliderandtheirvisualstatewouldalwaysremaininsync.
JSliderusestheBoundedRangeModelinterfaceforitsmodeldefinition.Consequently,itincludesthefollowingmethods:
AllSwingcomponentshaveonethingincommon:Ifyoudon'tsetyourownmodel,adefaultiscreatedandinstalledinternallyinthecomponent.Thenamingconventionforthesedefaultmodelclassesistoprependtheinterfacenamewith"Default."ForJSlider,aDefaultBoundedRangeModelobjectisinstantiatedinitsconstructor:
IfaprogramsubsequentlycallssetModel(),thisdefaultmodelisreplaced,asinthefollowingexample:
Formorecomplexmodels(suchasthoseforJTableandJList),anabstractmodelimplementationisalsoprovidedtoenabledeveloperstocreatetheirownmodelswithoutstartingfromscratch.Theseclassesareprependedwith"Abstract".
Forexample,JList'smodelinterfaceisListModel,whichprovidesbothDefaultListModelandAbstractListModelclassestohelpthedeveloperinbuildingalistmodel.
Sendalightweightnotificationthatthestatehas"changed"andrequirethelistenertorespondbysendingaquerybacktothemodeltofindoutwhathaschanged.Theadvantageofthisapproachisthatasingleeventinstancecanbeusedforallnotificationsfromaparticularmodel--whichishighlydesirablewhenthenotificationstendtobehighinfrequency(suchaswhenaJScrollBarisdragged).
Sendastatefulnotificationthatdescribesmorepreciselyhowthemodelhaschanged.Thisalternativerequiresaneweventinstanceforeachnotification.Itisdesirablewhenagenericnotificationdoesn'tprovidethelistenerwithenoughinformationtodetermineefficientlywhathaschangedbyqueryingthemodel(suchaswhenacolumnofcellschangevalueinaJTable).
ChangeListener/ChangeEventAPI:
TheChangeListenerinterfacehasasinglegenericmethod:
TheonlystateinaChangeEventistheevent"source."Becausethesourceisalwaysthesameacrossnotifications,asingleinstancecanbeusedforallnotificationsfromaparticularmodel.ModelsthatusethismechanismsupportthefollowingmethodstoaddandremoveChangeListeners:
Therefore,tobenotifiedwhenthevalueofaJSliderhaschanged,thecodemightlooklikethis:
Toprovideconvenienceforprogramsthatdon'twishtodealwithseparatemodelobjects,someSwingcomponentclassesalsoprovidetheabilitytoregisterChangeListenersdirectlyonthecomponent(sothecomponentcanlistenforchangesonthemodelinternallyandthenpropagatesthoseeventstoanylistenersregistereddirectlyonthecomponent).Theonlydifferencebetweenthesenotificationsisthatforthemodelcase,theeventsourceisthemodelinstance,whileforthecomponentcase,thesourceisthecomponent.
Sowecouldsimplifytheprecedingexampleto:
TheusageoftheseAPIsissimilartothelightweightnotification,exceptthatthelistenercanquerytheeventobjectdirectlytofindoutwhathaschanged.Forexample,thefollowingcodedynamicallytrackstheselectediteminaJList:
JSlider'simplementationofgetValue(),whichinternallydelegatesthemethodcalltoitsmodel:
Andsoprogramscansimplydothefollowing:
Inparticular,werecommendtheusageoftheApplication-DatacategoryofmodelsforSwing(modelsforJTable,JTree,andthelike)becausetheycangreatlyenhancethescalabilityandmodularityofyourapplicationoverthelongrun.
Whilewedon'texpect(oradvise)themajorityofdeveloperstocreatenewlook-and-feelimplementations,werealizePL&Fisaverypowerfulfeatureforasubsetofapplicationsthatwanttocreateauniqueidentity.Asitturnsout,PL&FisalsoideallysuitedforuseinbuildingGUIsthatareaccessibletouserswithdisabilities,suchasvisuallyimpairedusersoruserswhocannotoperateamouse.
Inanutshell,pluggablelook-and-feeldesignsimplymeansthattheportionofacomponent'simplementationthatdealswiththepresentation(thelook)andevent-handling(thefeel)isdelegatedtoaseparateUIobjectsuppliedbythecurrentlyinstalledlook-and-feel,whichcanbechangedatruntime.
SomesmallhooksintheSwingcomponentclasses.
Sometop-levelAPIforlook-and-feelmanagement.
AmorecomplexAPIthatactuallyimplementslook-and-feelsinseparatepackages.
swing.plafpackagetorepresentitsUIdelegate.Thenamingconventionfortheseclassesistotaketheclassnameforthecomponent,removethe"J"prefix,andappend"UI."Forexample,JButtondefinesitsUIdelegatewiththeplafclassButtonUI.
TheUIdelegateiscreatedinthecomponent'sconstructorandisaccessibleasaJavaBeansboundpropertyonthecomponent.Forexample,JScrollBarprovidesthefollowingmethodstoaccessitsUIdelegate:
ThisprocessofcreatingaUIdelegateandsettingitasthe"UI"propertyforacomponentisessentiallythe"installation"ofacomponent'slook-and-feel.
EachcomponentalsoprovidesamethodwhichcreatesandsetsaUIdelegateforthe"default"look-and-feel(thismethodisusedbytheconstructorwhendoingtheinstallation):
Alook-and-feelimplementationprovidesconcretesubclassesforeachabstractplafUIclass.Forexample,theWindowslook-and-feeldefinesWindowsButtonUI,aWindowsScrollBarUI,andsoon.WhenacomponentinstallsitsUIdelegate,itmusthaveawaytolookuptheappropriateconcreteclassnameforthecurrentdefaultlook-and-feeldynamically.ThisoperationisperformedusingahashtableinwhichthekeyisdefinedprogrammaticallybythegetUIClassID()methodinthecomponent.Theconventionistousetheplafabstractclassnameforthesekeys.Forexample,JScrollbarprovides:
Consequently,thehashtableintheWindowslook-and-feelwillprovideanentrythatmaps"ScrollBarUI"to
"com.sun.java.swing.plaf.windows.WindowsScrollBarUI"
LookAndFeelclassthatrepresentsalltheinformationcentraltoalook-and-feelimplementation,suchasitsname,itsdescription,whetherit'sanativelook-and-feel--andinparticular,ahashtable(knownasthe"DefaultsTable")forstoringdefaultvaluesforvariouslook-and-feelattributes,suchascolorsandfonts.
Eachlook-and-feelimplementationdefinesasubclassofLookAndFeel(forexample,swing.plaf.motif.MotifLookAndFeel)toprovideSwingwiththenecessaryinformationtomanagethelook-and-feel.
TheUIManageristheAPIthroughwhichcomponentsandprogramsaccesslook-and-feelinformation(theyshouldrarely,ifever,talkdirectlytoaLookAndFeelinstance).UIManagerisresponsibleforkeepingtrackofwhichLookAndFeelclassesareavailable,whichareinstalled,andwhichiscurrentlythedefault.TheUIManageralsomanagesaccesstotheDefaultsTableforthecurrentlook-and-feel.
UIManageralsoprovidesmethodsforgettingandsettingthecurrentdefaultLookAndFeel:
Asadefaultlook-and-feel,Swinginitializesthecross-platformJavalookandfeel(formerlyknownas"Metal").However,ifaSwingprogramwantstosetthedefaultLook-and-Feelexplicitly,itcandothatusingtheUIManager.setLookAndFeel()method.Forexample,thefollowingcodesamplewillsetthedefaultLook-and-FeeltobeCDE/Motif:
Sometimesanapplicationmaynotwanttospecifyaparticularlook-and-feel,butinsteadwantstoconfigurealook-and-feelinsuchawaythatitdynamicallymatcheswhateverplatformithappenstoberunningon(forinstance,the.Windowslook-and-feelifitisrunningonWindowsNT,orCDE/MotififitrunningonSolaris).Or,perhaps,anapplicationmightwanttolockdownthelook-and-feeltothecross-platformJavalookandfeel.
TheUIManagerprovidesthefollowingstaticmethodstoprogrammaticallyobtaintheappropriateLookAndFeelclassnamesforeachofthesecases:
So,toensurethataprogramalwaysrunsintheplatform'ssystemlook-and-feel,thecodemightlooklikethis:
UIManager.setLookAndFeel()methodmakesaparticularLookAndFeelthecurrentdefaultbyloadingandinitializingthatLookAndFeelinstance,butitdoesnotautomaticallycauseanyexistingcomponentstochangetheirlook-and-feel.
RememberthatcomponentsinitializetheirUIdelegateatconstructtime,therefore,ifthecurrentdefaultchangesaftertheyareconstructed,theywillnotautomaticallyupdatetheirUIsaccordingly.Itisuptotheprogramtoimplementthisdynamicswitchingbytraversingthecontainmenthierarchyandupdatingthecomponentsindividually.(NOTE:SwingprovidestheSwingUtilities.updateComponentTreeUI()methodtoassistwiththisprocess).
Thelook-and-feelofacomponentcanbeupdatedatanytimetomatchthecurrentdefaultbyinvokingitsupdateUI()method,whichusesthefollowingstaticmethodonUIManagertogettheappropriateUIdelegate:
Forexample,theimplementationofupdateUI()fortheJScrollBarlookslikethefollowing:
Andsoifaprogramneedstochangethelook-and-feelofaGUIhierarchyafteritwasinstantiated,thecodemightlooklikethefollowing:
UIManagerdefinesastaticclass,namedUIManager.LookAndFeelInfo,forstoringthehigh-levelname(suchas."Metal")andparticularclassname(suchas"com.sun.java.swing.plaf.MetalLookAndFeel")foraLookAndFeel.ItusestheseclassesinternallytomanagetheknownLookAndFeelobjects.ThisinformationcanbeaccessedfromtheUIManagerviathefollowingstaticmethods:
Thesemethodscanbeusedtoprogrammaticallydeterminewhichlook-and-feelimplementationsareavailable,whichisusefulwhenbuildinguserinterfaceswhichallowtheend-usertodynamicallyselectalook-and-feel.
swing.plaf(ButtonUI,ScrollBarUI,andsoon)definethepreciseAPIthatacomponentcanusetointeractwiththeUIdelegateinstance.(NOTE:Interfaceswereoriginallyusedhere,buttheywerereplacedwithabstractclassesbecausewefelttheAPIwasnotmatureenoughtowithstandtheconcretecastingofaninterface.)TheseplafAPIsaretherootofalllook-and-feelimplementations.
Eachlook-and-feelimplementationprovidesconcretesubclassesoftheseabstractplafclasses.Allsuchclassesdefinedbyaparticularlook-and-feelimplementationarecontainedinaseparatepackageundertheswing.plafpackage(forexample,.swing.plaf.motif,swing.plaf.metal,andsoon).Alook-and-feelpackagecontainsthefollowing:
TheLookAndFeelsubclass(forinstance,MetalLookAndFeel).
Alllook-and-feel'sUIdelegateclasses(forexample,MetalButtonUI,MetalTreeUI,andthelike).
Anylook-and-feelutilityclasses(MetalGraphicsUtils,MetalIconFactory,andsoon).
Otherresourcesassociatedwiththelook-and-feel,suchasimagefiles.
InimplementingthevariousSwinglook-and-feels,wesoondiscoveredthattherewasalotofcommonalityamongthem.Wefactoredoutthiscommoncodeintoabaselook-and-feelimplementation(called"basic")whichextendstheplafabstractclassesandfromwhichthespecificlook-and-feelimplementations(motif,windows,andsoon.)extend.Thebasiclook-and-feelpackagesupportsbuildingadesktop-levellook-and-feel,suchasWindowsorCDE/Motif.
Thebasiclook-and-feelpackageisjustoneexampleofhowtobuildapluggablelook-and-feel;thearchitectureisflexibleenoughtoaccommodateotherapproachesaswell.
Theremainderofthisdocumentwillshowhowalook-and-feelpackageworksatthegenericlevel,leavingthedetailsonthebasicpackageforafuturedocument.
WARNING:AllAPIsdefinedbelowtheswing.plafpackagearenotfrozeninthe1.0.XversionofSwing.WearecurrentlycleaningupthoseAPIsfortheversionofSwingthatwillshipwithJDK1.2beta4,atwhichtimetheywillbecomefrozen.Soifyouaredevelopingyourownlook-and-feelimplementationusingthe1.0.1API,thisislikelytoaffectyou.
LookAndFeelclassdefinesthefollowingabstractmethods,whichallsubclassesmustimplement:
ThegetName(),getID(),andgetDescription()methodsprovidegenericinformationaboutthelook-and-feel.
TheisNativeLookAndFeel()methodreturnstrueifthelook-and-feelisnativetothecurrentplatform.Forexample,MotifLookAndFeelreturnstrueifitiscurrentlyrunningontheSolarisplatform,andreturnsfalseotherwise.
TheisSupportedLookAndFeel()methodreturnswhetherornotthislook-and-feelisauthorizedtorunonthecurrentplatform.Forexample,WindowsLookAndFeelreturnstrueonlyifitisrunningonaWindows95,Windows98,orWindowsNTmachine.
ALookAndFeelclassalsoprovidesmethodsforinitializationanduninitialization:
Theinitialize()methodisinvokedbytheUIManagerwhentheLookAndFeelismadethe"default"usingtheUIManager.setLookAndFeel()method.uninitialize()isinvokedbytheUIManagerwhentheLookAndFeelisabouttobereplacedasthedefault.
LookAndFeelclassprovidesamethodtoreturnthelook-and-feel'simplementationoftheDefaultsTable:
TheDefaultsTableisrepresentedbytheUIDefaultsclass,adirectextensionofjava.util.Hashtable,whichaddsmethodsforaccessingspecifictypesofinformationaboutalook-and-feel.ThistablemustincludealltheUIClassID-to-classnamemappinginformation,aswellasanydefaultvaluesforpresentation-relatedproperties(suchascolor,font,border,andicon)foreachUIdelegate.Forexample,followingisasampleofwhatafragmentofgetDefaults()mightlooklikeforahypotheticallook-and-feelinapackagecalled"mine":
Whenthedefaultlook-and-feelissetwithUIManager.setLookAndFeel(),theUIManagercallsgetDefaults()onthenewLookAndFeelinstanceandstoresthehashtableitreturns.SubsequentcallstotheUIManager'slookupmethodswillbeappliedtothistable.Forexample,aftermaking"mine"thedefaultLook-and-Feel:
TheUIclassesaccesstheirdefaultinformationinthesameway.Forexample,ourexampleButtonUIclasswouldinitializetheJButton's"background"propertylikethis:
Thedefaultsareorganizedthiswaytoallowdeveloperstooverridethem.MoredetailaboutSwing'sDefaultsmechanismwillbepublishedinafuturearticle.
ThisisnotanissuethefirsttimeaUIdelegateisinstalledonacomponent(atconstructtime)becauseallpropertieswillbeuninitializedandlegallysettablebythelook-and-feel.Theproblemoccurswhentheapplicationsetsindividualpropertiesaftercomponentconstructionandthensubsequentlysetsanewlook-and-feel(thatis,dynamiclook-and-feelswitching).Thismeansthatthelook-and-feelmustbeabletodistinguishbetweenpropertyvaluessetbytheapplication,andthosesetbyalook-and-feel.
Thisissueishandledbymarkingallvaluessetbythelook-and-feelwiththeplaf.UIResourceinterface.Theplafpackageprovidesasetof"marked"classesforrepresentingthesevalues,ColorUIResource,FontUIResource,andBorderUIResource.TheprecedingcodeexampleshowstheusageoftheseclassestomarkthedefaultpropertyvaluesforthehypotheticalMyButtonUIclass.
swing.plaf.ComponentUI.Thisclasscontainstheprimary"machinery"formakingthepluggablelook-and-feelwork.ItsmethodsdealwithUIinstallationanduninstallation,andwithdelegationofacomponent'sgeometry-handlingandpainting.
ManyoftheUIDelegatesubclassesalsoprovideadditionalmethodsspecifictotheirownrequiredinteractionwiththecomponent;however,thisdocumentfocusesprimarilyonthegenericmechanismimplementedbyComponentUI.
LookingattheimplementationofJComponent.setUI()(whichisalwaysinvokedfromthesetUImethodonJComponentsubclasses),wecanclearlyseehowUIdelegateinstallation/de-installationworks:
Tofoldoutthechart,justfollowthislink
TheUIdelegate'sinstallUI()methodisresponsibleforthefollowing:
Setdefaultfont,color,border,andopacitypropertiesonthecomponent.
Installanappropriatelayoutmanageronthecomponent.
Addanyappropriatechildsubcomponentstothecomponent
Registeranyrequiredeventlistenersonthecomponent.
Registeranylook-and-feel-specifickeyboardactions(mnemonics,etc.)forthecomponent.
Registerappropriatemodellistenerstobenotifiedwhentorepaint.
Initializeanyappropriateinstancedata.
Forexample,theinstallUI()methodforanextensionofButtonUImightlooklikethis:
Allvaluesusedforsettingcolors,font,andborderpropertiesshouldbeobtainedfromtheDefaultstable(asdescribedinthesubsectionontheLookAndFeelsubclass).
Color,fontandborderpropertiesshouldbesetif--andonlyif--theapplicationhasnotalreadysetthem.
TofacilitateconventionNo1,theUIManagerclassprovidesanumberofstaticmethodstoextractpropertyvaluesofaparticulartype(forinstance,thestaticmethodsUIManager.getColor(),UIManager.getFont(),andsoon).
ConventionNo.2isimplementedbyalwayscheckingforeitheranullvalueoraninstanceofUIResourcebeforesettingtheproperty.
TheComponentUI'suninstall()methodmustcarefullyundoeverythingthatwasdoneintheinstallUI()methodsothatthecomponentisleftinapristinestateforthenextUIdelegate.Theuninstall()methodisresponsiblefor:
ClearingtheborderpropertyifithasbeensetbyinstallUI().
RemovethelayoutmanagerifithadbeensetbyinstallUI().
RemoveanysubcomponentsaddedbyinstallUI().
Removeanyevent/modellistenersthatwereaddedbyinstallUI().
Removeanylook-and-feel-specifickeyboardactionsthatwereinstalledbyinstallUI().
Nullifyanyinitializedinstancedata(toallowGCtocleanup).
Forexample,anuninstall()methodtoundowhatwedidintheaboveexampleinstallationmightlooklikethis:
Obviously,thesegeometrypropertiesaresomethingthatalook-and-feelusuallyneedstodefineforagivencomponent,soComponentUIprovidesthefollowingmethodsforthispurpose:
JComponent'sparallelmethods(whichareinvokedbytheLayoutManagerduringvalidation)thensimplydelegatetotheUIobject'sgeometrymethodsifthegeometrypropertywasnotexplicitlysetbytheprogram.BelowistheimplementationofJComponent.getPreferredSize()whichshowsthisdelegation:
EventhoughtheboundingboxforallcomponentsisaRectangle,it'spossible
tosimulateanon-rectangularcomponentbyoverridingtheimplementationofthecontains()methodfromjava.awt.Component.(Thismethodisusedforthehit-testingofmouseevents).But,liketheothergeometrypropertiesinSwing,theUIdelegatedefinesitsownversionofthecontains()method,whichisalsodelegatedtobyJComponent.contains():
SoaUIdelegatecouldprovidenon-rectangular"feel"bydefiningaparticularimplementationofcontains()(forexample,ifwewantedourMyButtonUIclasstoimplementabuttonwithroundedcorners).
ComponentUIhasthefollowingmethods:
Andonceagain,JComponent.paintComponent()takescaretodelegatethepainting:
SimilarlytothewayinwhichthingsaredoneinAWT,theUIdelegate'supdate()methodclearsthebackground(ifopaque)andtheninvokesitspaint()method,whichisultimatelyresponsibleforrenderingthecontentsofthecomponent.
ComponentUItakeaJComponentobjectasaparameter.ThisconventionenablesastatelessimplementationofaUIdelegate(becausethedelegatecanalwaysquerybacktothespecifiedcomponentinstanceforstateinformation).StatelessUIdelegateimplementationsallowasingleUIdelegateinstancetobeusedforallinstancesofthatcomponentclass,whichcansignificantlyreducethenumberofobjectsinstantiated.
ThisapproachworkswellformanyofthesimplerGUIcomponents.Butformorecomplexcomponents,wefounditnottobea"win"becausetheinefficiencycreatedbyconstantstaterecalculationswasworsethancreatingextraobjects(especiallysincethenumberofcomplexGUIcomponentscreatedinagivenprogramtendstobesmall).
TheComponentUIclassdefinesastaticmethodforreturningadelegateinstance:
It'stheimplementationofthismethodthatdetermineswhetherthedelegateisstatelessorstateful.That'sbecausetheUIManager.getUI()methodinvokedbythecomponenttocreatetheUIdelegateinternallyinvokesthiscreateUImethodonthedelegateclasstogettheinstance.
TheSwinglook-and-feelimplementationsusebothtypesofdelegates.Forexample,Swing'sBasicButtonUIclassimplementsastatelessdelegate:
WhileSwing'sBasicTabbedPaneUIusesthestatefulapproach:
publicstaticComponentUIcreateUI(JComponentc)
returnnewBasicTabbedPaneUI();
}
UIManagerprovidestheAPIforapplicationstomanagethelook-and-feelatthislevel.
Ifyou'reoneofthosedeveloperswhoneeds(orwants)todevelopacustomlook-and-feel,it'scriticaltounderstandtheseunderpinningsbeforeyouwriteasinglelineofcode.We'reworkingonprovidingbetterdocumentationtohelpwiththisprocess--startingwiththisdocument,andcontinuingwithothersthatwillfollowsoon.
ButaccurateexplanationsofhowSwingcomponentsar...
TheInsideStoryonJFCComponentDesign
ByAmyFowler
MostSwingdevelopersknowbynowthatSwingcomponentshaveaseparablemodel-and-viewdesign.AndmanySwingusershaverunacrossarticlessayingthatSwingisbasedonsomethingcalleda"modifiedMVC(model-view-controller)architecture."
ButaccurateexplanationsofhowSwingcomponentsaredesigned,andhowtheirpartsallfittogether,havebeenhardtocomeby--untilnow.
Thesilenceendswiththepublicationofthisarticle,amajorwhitepaperonSwingcomponentdesign.ItprovidesacomprehensivetechnicaloverviewofSwing'smodifiedMVCstructureanddemystifiesmanyotherfacetsofSwingcomponentarchitectureaswell.
DesignGoals
TheoverallgoalfortheSwingprojectwas:TobuildasetofextensibleGUIcomponentstoenabledeveloperstomorerapidlydeveloppowerfulJavafrontendsforcommercialapplications.
Tothisend,theSwingteamestablishedasetofdesigngoalsearlyintheprojectthatdrovetheresultingarchitecture.TheseguidelinesmandatedthatSwingwould:
BeimplementedentirelyinJavatopromotecross-platformconsistencyandeasiermaintenance.
ProvideasingleAPIcapableofsupportingmultiplelook-and-feelssothatdevelopersandend-userswouldnotbelockedintoasinglelook-and-feel.
Enablethepowerofmodel-drivenprogrammingwithoutrequiringitinthehighest-levelAPI.
AdheretoJavaBeansdesignprinciplestoensurethatcomponentsbehavewellinIDEsandbuildertools.
ProvidecompatibilitywithAWTAPIswherethereisoverlapping,toleveragetheAWTknowledgebaseandeaseporting.
RootsinMVC
Swingarchitectureisrootedinthemodel-view-controller(MVC)designthatdatesbacktoSmallTalk.MVCarchitecturecallsforavisualapplicationtobebrokenupintothreeseparateparts:Amodelthatrepresentsthedatafortheapplication.
Theviewthatisthevisualrepresentationofthatdata.
Acontrollerthattakesuserinputontheviewandtranslatesthattochangesinthemodel.
Earlyon,MVCwasalogicalchoiceforSwingbecauseitprovidedabasisformeetingthefirstthreeofourdesigngoalswithintheboundsofthelattertwo.
ThefirstSwingprototypefollowedatraditionalMVCseparationinwhicheachcomponenthadaseparatemodelobjectanddelegateditslook-and-feelimplementationtoseparateviewandcontrollerobjects.
Thedelegate
Wequicklydiscoveredthatthissplitdidn'tworkwellinpracticaltermsbecausetheviewandcontrollerpartsofacomponentrequiredatightcoupling(forexample,itwasverydifficulttowriteagenericcontrollerthatdidn'tknowspecificsabouttheview).SowecollapsedthesetwoentitiesintoasingleUI(user-interface)object,asshowninthisdiagram:(TheUIdelegateobjectshowninthispictureissometimescalledadelegateobject,orUIdelegate.TheUIdelegateusedinSwingisdescribedinmoredetailinthePluggablelook-and-feelsectionofthisarticle,underthesubheading"
Asthediagramillustrates,Swingarchitectureislooselybased--butnotstrictlybased--onthetraditionalMVCdesign.IntheworldofSwing,thisnewquasi-MVCdesignissometimesreferredtoaseparablemodelarchitecture.
Swing'sseparablemodeldesigntreatsthemodelpartofacomponentasaseparateelement,justastheMVCdesigndoes.ButSwingcollapsestheviewandcontrollerpartsofeachcomponentintoasingleUI(user-interface)object.
ToMVCornottoMVC?
Onenoteworthypointisthatasanapplicationdeveloper,youshouldthinkofacomponent'sview/controllerresponsibilitiesasbeinghandledbythegenericcomponentclass(suchas.Forexample,thecodethatimplementsdouble-bufferedpaintingisinSwing's
SoSwingdoeshaveastrongMVClineage.Butit'salsoimportanttoreiteratethatourMVCarchitectureservestwodistinctpurposes:
First,separatingthemodeldefinitionfromacomponentfacilitatesmodel-drivenprogramminginSwing.
Second,theabilitytodelegatesomeofacomponent'sview/controllerresponsibilitiestoseparatelook-and-feelobjectsprovidesthebasisforSwing'spluggablelook-and-feelarchitecture.
AlthoughthesetwoconceptsarelinkedbytheMVCdesign,theymaybetreatedsomewhatorthogonallyfromthedeveloper'sperspective.Theremainderofthisdocumentwillcovereachofthesemechanismsingreaterdetail.
Separablemodelarchitecture
Itisgenerallyconsideredgoodpracticetocenterthearchitectureofanapplicationarounditsdataratherthanarounditsuserinterface.Tosupportthisparadigm,Swingdefinesaseparatemodelinterfaceforeachcomponentthathasalogicaldataorvalueabstraction.ThisseparationprovidesprogramswiththeoptionofpluggingintheirownmodelimplementationsforSwingcomponents.Thefollowingtableshowsthecomponent-to-modelmappingforSwing.
Component | ModelInterface | ModelType |
---|---|---|
GUI-statevs.application-datamodels
ThemodelsprovidedbySwingfallintotwogeneralcategories:GUI-statemodelsandapplication-datamodels.GUI-statemodels
GUIstatemodelsareinterfacesthatdefinethevisualstatusofaGUIcontrol,suchaswhetherabuttonispressedorarmed,orwhichitemsareselectedinalist.GUI-statemodelstypicallyarerelevantonlyinthecontextofagraphicaluserinterface(GUI).WhileitisoftenusefultodevelopprogramsusingGUI-statemodelseparation--particularlyifmultipleGUIcontrolsarelinkedtoacommonstate(suchasinasharedwhiteboardprogram),orifmanipulatingonecontrolautomaticallychangesthevalueofanother--theuseofGUI-statemodelsisnotrequiredbySwing.ItispossibletomanipulatethestateofaGUIcontrolthroughtop-levelmethodsonthecomponent,withoutanydirectinteractionwiththemodelatall.Intheprecedingtable,GUI-statemodelsinSwingarehighlightedinblue.Application-datamodels
Anapplication-datamodelisaninterfacethatrepresentssomequantifiabledatathathasmeaningprimarilyinthecontextoftheapplication,suchasthevalueofacellinatableortheitemsdisplayedinalist.ThesedatamodelsprovideaverypowerfulprogrammingparadigmforSwingprogramsthatneedacleanseparationbetweentheirapplicationdata/logicandtheirGUI.Fortrulydata-centricSwingcomponents,suchasJTreeandJTable,interactionwiththedatamodelisstronglyrecommended.Application-datamodelsarehighlightedinredintheOfcoursewithsomecomponents,themodelcategorizationfallssomewhereinbetweenGUIstatemodelsandapplication-datamodels,dependingonthecontextinwhichthemodelisused.Thisisthecasewiththe
Swing'sseparablemodelAPImakesnospecificdistinctionsbetweenGUIstatemodelsandapplication-datamodels;however,wehaveclarifiedthisdifferenceheretogivedevelopersabetterunderstandingofwhenandwhytheymightwishtoprogramwiththeseparablemodels.
Sharedmodeldefinitions
ReferringagaintotheTheseparable-modelAPI
SwingcomponentsthatdefinemodelssupportaJavaBeansboundpropertyforthemodel.Forexample,publicBoundedRangeModelgetModel() publicvoidsetModel(BoundedRangeModelmodel)
AllSwingcomponentshaveonethingincommon:Ifyoudon'tsetyourownmodel,adefaultiscreatedandinstalledinternallyinthecomponent.Thenamingconventionforthesedefaultmodelclassesistoprependtheinterfacenamewith"Default."For
publicJSlider(intorientation,intmin, intmax,intvalue) { checkOrientation(orientation); this.orientation=orientation; this.model= newDefaultBoundedRangeModel(value,0,min,max); this.model.addChangeListener(changeListener); updateUI(); } |
JSliderslider=newJSlider(); BoundedRangeModelmyModel= newDefaultBoundedRangeModel(){ publicvoidsetValue(intn){ System.out.println("SetValue:"+n); super.setValue(n); } }); slider.setModel(myModel); |
Forexample,
Modelchangenotification
Modelsmustbeabletonotifyanyinterestedparties(suchasviews)whentheirdataorvaluechanges.SwingmodelsusetheJavaBeansEventmodelfortheimplementationofthisnotification.TherearetwoapproachesforthisnotificationusedinSwing:Sendalightweightnotificationthatthestatehas"changed"andrequirethelistenertorespondbysendingaquerybacktothemodeltofindoutwhathaschanged.Theadvantageofthisapproachisthatasingleeventinstancecanbeusedforallnotificationsfromaparticularmodel--whichishighlydesirablewhenthenotificationstendtobehighinfrequency(suchaswhena
Sendastatefulnotificationthatdescribesmorepreciselyhowthemodelhaschanged.Thisalternativerequiresaneweventinstanceforeachnotification.Itisdesirablewhenagenericnotificationdoesn'tprovidethelistenerwithenoughinformationtodetermineefficientlywhathaschangedbyqueryingthemodel(suchaswhenacolumnofcellschangevalueina
Lightweightnotification
ThefollowingmodelsinSwingusethelightweightnotification,whichisbasedontheModel | Listener | Event |
---|---|---|
publicvoidstateChanged(ChangeEvente)
Theonlystateina
publicvoidaddChangeListener(ChangeListenerl) publicvoidremoveChangeListener(ChangeListenerl)
Therefore,tobenotifiedwhenthevalueofa
JSliderslider=newJSlider(); BoundedRangeModelmodel=slider.getModel(); model.addChangeListener(newChangeListener(){ publicvoidstateChanged(ChangeEvente){ //needtoquerythemodel //togetupdatedvalue... BoundedRangeModelm= (BoundedRangeModel)e.getSource(); System.out.println("modelchanged:"+ m.getValue()); } }); |
Sowecouldsimplifytheprecedingexampleto:
JSliderslider=newJSlider(); slider.addChangeListener(newChangeListener(){ publicvoidstateChanged(ChangeEvente){ //thesourcewillbe //thesliderthistime.. JSliders=(JSlider)e.getSource(); System.out.println("valuechanged:"+ s.getValue()); } }); |
Statefulnotification
ModelsthatsupportstatefulnotificationprovideeventListenerinterfacesandeventobjectsspecifictotheirpurpose.Thefollowingtableshowsthebreakdownforthosemodels:Model | Listener | Event |
---|---|---|
Listener | Event | |
Stringitems[]={"One","Two","Three"); JListlist=newJList(items); ListSelectionModelsModel=list.getSelectionModel(); sModel.addListSelectionListener (newListSelectionListener(){ publicvoidvalueChanged(ListSelectionEvente){ //getchangeinformationdirectly //fromtheeventinstance... if(!e.getValueIsAdjusting()){ System.out.println("selectionchanged:"+ e.getFirstIndex()); } } }); |
AutomaticViewUpdates
Amodeldoesnothaveanyintrinsicknowledgeoftheviewthatrepresentsit.(Thisrequirementiscriticaltoenablemultipleviewsonthesamemodel).Instead,amodelhasonlyalistoflistenersinterestedinknowingwhenitsstatehaschanged.ASwingcomponenttakesresponsibilityforhookinguptheappropriatemodellistenersothatitcanappropriatelyrepaintitselfasthemodelchanges(ifyoufindthatacomponentisnotupdatingautomaticallywhenthemodelchanges,itisabug!).Thisistruewhetheradefaultinternalmodelisusedorwhetheraprograminstallsitsownmodelimplementation.Ignoringmodelscompletely
Asmentionedpreviously,mostcomponentsprovidethemodel-definedAPIdirectlyinthecomponentclasssothatthecomponentcanbemanipulatedwithoutinteractingwiththemodelatall.Thisisconsideredperfectlyacceptableprogrammingpractice(especiallyfortheGUI-statemodels).Forexample,followingispublicintgetValue(){ returngetModel().getValue(); }
Andsoprogramscansimplydothefollowing:
JSliderslider=newJSlider(); intvalue=slider.getValue(); //what'sa"model,"anyway?
Separablemodelsummary
Sowhileit'susefultounderstandhowSwing'smodeldesignworks,itisn'tnecessarytousethemodelAPIforallaspectsofSwingprogramming.Youshouldcarefullyconsideryourapplication'sindividualneedsanddeterminewherethemodelAPIwillenhanceyourcodewithoutintroducingunnecessarycomplexity.Inparticular,werecommendtheusageoftheApplication-DatacategoryofmodelsforSwing(modelsfor
Pluggablelook-and-feelarchitecture
Swing'spluggablelook-and-feelarchitectureallowsustoprovideasinglecomponentAPIwithoutdictatingaparticularlook-and-feel.TheSwingtoolkitprovidesadefaultsetoflook-and-feels;however,theAPIis"open"--adesignthatadditionallyallowsdeveloperstocreatenewlook-and-feelimplementationsbyeitherextendinganexistinglook-and-feelorcreatingonefromscratch.Althoughthepluggablelook-and-feelAPIisextensible,itwasintentionallydesignedatalevelbelowthebasiccomponentAPIinsuchawaythatadeveloperdoesnotneedtounderstanditsintricatedetailstobuildSwingGUIs.(Butifyouwanttoknow,readon...)Whilewedon'texpect(oradvise)themajorityofdeveloperstocreatenewlook-and-feelimplementations,werealizePL&Fisaverypowerfulfeatureforasubsetofapplicationsthatwanttocreateauniqueidentity.Asitturnsout,PL&FisalsoideallysuitedforuseinbuildingGUIsthatareaccessibletouserswithdisabilities,suchasvisuallyimpairedusersoruserswhocannotoperateamouse.
Inanutshell,pluggablelook-and-feeldesignsimplymeansthattheportionofacomponent'simplementationthatdealswiththepresentation(thelook)andevent-handling(thefeel)isdelegatedtoaseparateUIobjectsuppliedbythecurrentlyinstalledlook-and-feel,whichcanbechangedatruntime.
Thepluggablelook-and-feelAPI
Thepluggablelook-and-feelAPIincludes:SomesmallhooksintheSwingcomponentclasses.
Sometop-levelAPIforlook-and-feelmanagement.
AmorecomplexAPIthatactuallyimplementslook-and-feelsinseparatepackages.
Thecomponenthooks
EachSwingcomponentthathaslook-and-feel-specificbehaviordefinesanabstractclassintheTheUIdelegateiscreatedinthecomponent'sconstructorandisaccessibleasaJavaBeansboundpropertyonthecomponent.Forexample,
publicScrollBarUIgetUI() publicvoidsetUI(ScrollBarUIui)
ThisprocessofcreatingaUIdelegateandsettingitasthe"UI"propertyforacomponentisessentiallythe"installation"ofacomponent'slook-and-feel.
EachcomponentalsoprovidesamethodwhichcreatesandsetsaUIdelegateforthe"default"look-and-feel(thismethodisusedbytheconstructorwhendoingtheinstallation):
publicvoidupdateUI()
Alook-and-feelimplementationprovidesconcretesubclassesforeachabstractplafUIclass.Forexample,theWindowslook-and-feeldefines
publicStringgetUIClassID(){ return"ScrollBarUI"; }
Consequently,thehashtableintheWindowslook-and-feelwillprovideanentrythatmaps"ScrollBarUI"to
"com.sun.java.swing.plaf.windows.WindowsScrollBarUI"
Look-and-feelmanagement
SwingdefinesanabstractEachlook-and-feelimplementationdefinesasubclassof
The
The'default'lookandfeel
ThepublicstaticLookAndFeel getLookAndFeel() publicstaticvoid setLookAndFeel(LookAndFeelnewLookAndFeel) publicstaticvoid setLookAndFeel(StringclassName) |
UIManager.setLookAndFeel( "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
Sometimesanapplicationmaynotwanttospecifyaparticularlook-and-feel,butinsteadwantstoconfigurealook-and-feelinsuchawaythatitdynamicallymatcheswhateverplatformithappenstoberunningon(forinstance,the.Windowslook-and-feelifitisrunningonWindowsNT,orCDE/MotififitrunningonSolaris).Or,perhaps,anapplicationmightwanttolockdownthelook-and-feeltothecross-platformJavalookandfeel.
The
publicstaticString getSystemLookAndFeelClassName() publicstaticString getCrossPlatformLookAndFeelClassName()
So,toensurethataprogramalwaysrunsintheplatform'ssystemlook-and-feel,thecodemightlooklikethis:
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName());
DynamicallyChangingtheDefaultLook-and-Feel
WhenaSwingapplicationprogrammaticallysetsthelook-and-feel(asdescribedabove),theidealplacetodosoisbeforeanySwingcomponentsareinstantiated.ThisisbecausetheRememberthatcomponentsinitializetheirUIdelegateatconstructtime,therefore,ifthecurrentdefaultchangesaftertheyareconstructed,theywillnotautomaticallyupdatetheirUIsaccordingly.Itisuptotheprogramtoimplementthisdynamicswitchingbytraversingthecontainmenthierarchyandupdatingthecomponentsindividually.(NOTE:Swingprovidesthe
Thelook-and-feelofacomponentcanbeupdatedatanytimetomatchthecurrentdefaultbyinvokingits
publicstaticComponentUIgetUI(JComponentc)
Forexample,theimplementationof
publicvoidupdateUI(){ setUI((ScrollBarUI)UIManager.getUI(this)); }
Andsoifaprogramneedstochangethelook-and-feelofaGUIhierarchyafteritwasinstantiated,thecodemightlooklikethefollowing:
//GUIalreadyinstantiated,wheremyframe //istop-levelframe try{ UIManager.setLookAndFeel( "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); myframe.setCursor( Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); SwingUtilities.updateComponentTreeUI(myframe); myframe.validate(); }catch(UnsupportedLookAndFeelExceptione){ }finally{ myframe.setCursor (Cursor.getPredefinedCursor (Cursor.DEFAULT_CURSOR)); } |
Managinglook-and-feeldata
ThepublicstaticLookAndFeelInfo[] getInstalledLookAndFeels() publicstaticvoid setInstalledLookAndFeels(LookAndFeelInfo[]infos) throwsSecurityException publicstaticvoid installLookAndFeel(LookAndFeelInfoinfo) publicstaticvoid installLookAndFeel(Stringname,StringclassName) |
Thelook-and-feelpackages
TheUIdelegateclassesprovidedinEachlook-and-feelimplementationprovidesconcretesubclassesoftheseabstractplafclasses.Allsuchclassesdefinedbyaparticularlook-and-feelimplementationarecontainedinaseparatepackageunderthe
The
Alllook-and-feel'sUIdelegateclasses(forexample,
Anylook-and-feelutilityclasses(
Otherresourcesassociatedwiththelook-and-feel,suchasimagefiles.
InimplementingthevariousSwinglook-and-feels,wesoondiscoveredthattherewasalotofcommonalityamongthem.Wefactoredoutthiscommoncodeintoabaselook-and-feelimplementation(called"basic")whichextendstheplafabstractclassesandfromwhichthespecificlook-and-feelimplementations(motif,windows,andsoon.)extend.Thebasiclook-and-feelpackagesupportsbuildingadesktop-levellook-and-feel,suchasWindowsorCDE/Motif.
Thebasiclook-and-feelpackageisjustoneexampleofhowtobuildapluggablelook-and-feel;thearchitectureisflexibleenoughtoaccommodateotherapproachesaswell.
Theremainderofthisdocumentwillshowhowalook-and-feelpackageworksatthegenericlevel,leavingthedetailsonthebasicpackageforafuturedocument.
WARNING:AllAPIsdefinedbelowthe
TheLookAndFeelSubclass
ThepublicStringgetName(); publicStringgetID(); publicStringgetDescription(); publicbooleanisNativeLookAndFeel(); publicbooleanisSupportedLookAndFeel();
The
The
The
A
publicvoidinitialize() publicvoiduninitialize()
The
TheDefaultsTable
Finally,thepublicUIDefaultsgetDefaults()
TheDefaultsTableisrepresentedbythe
publicUIDefaultsgetDefaults(){ |
UIManager.get("ButtonUI")=>"mine.MyButtonUI"
TheUIclassesaccesstheirdefaultinformationinthesameway.Forexample,ourexample
button.setBackground( UIManager.getColor("Button.background");
Thedefaultsareorganizedthiswaytoallowdeveloperstooverridethem.MoredetailaboutSwing'sDefaultsmechanismwillbepublishedinafuturearticle.
DistinguishingbetweenUI-setandapp-setproperties
Swingallowsapplicationstosetpropertyvalues(suchascolorandfont)individuallyoncomponents.Soit'scriticaltomakesurethatthesevaluesdon'tgetclobberedwhenalook-and-feelsetsupits"default"propertiesforthecomponent.ThisisnotanissuethefirsttimeaUIdelegateisinstalledonacomponent(atconstructtime)becauseallpropertieswillbeuninitializedandlegallysettablebythelook-and-feel.Theproblemoccurswhentheapplicationsetsindividualpropertiesaftercomponentconstructionandthensubsequentlysetsanewlook-and-feel(thatis,dynamiclook-and-feelswitching).Thismeansthatthelook-and-feelmustbeabletodistinguishbetweenpropertyvaluessetbytheapplication,andthosesetbyalook-and-feel.
Thisissueishandledbymarkingallvaluessetbythelook-and-feelwiththe
TheUIdelegate
ThesuperclassofallUIDelegateclassesisManyoftheUIDelegatesubclassesalsoprovideadditionalmethodsspecifictotheirownrequiredinteractionwiththecomponent;however,thisdocumentfocusesprimarilyonthegenericmechanismimplementedby
UIinstallationanddeinstallation
Firstoff,theComponentUIclassdefinesthesemethodsmethodsforUIdelegateinstallationanduninstallation:publicvoidinstallUI(JComponentc) publicvoiduninstallUI(JComponentc)
Lookingattheimplementationof
protectedvoidsetUI(ComponentUInewUI){ if(ui!=null){ ui.uninstallUI(this); } ComponentUIoldUI=ui; ui=newUI; if(ui!=null){ ui.installUI(this); } invalidate(); firePropertyChange("UI",oldUI,newUI); } |
UIinstallationillustrated
Thisarticlecomeswithagiantposter-sizechartthatillustratestheprocessinstallingaUIdelegate.Itcanprovideyouwithavaluableoverviewofthedelegate-installationprocess.Tofoldoutthechart,justfollow
Setdefaultfont,color,border,andopacitypropertiesonthecomponent.
Installanappropriatelayoutmanageronthecomponent.
Addanyappropriatechildsubcomponentstothecomponent
Registeranyrequiredeventlistenersonthecomponent.
Registeranylook-and-feel-specifickeyboardactions(mnemonics,etc.)forthecomponent.
Registerappropriatemodellistenerstobenotifiedwhentorepaint.
Initializeanyappropriateinstancedata.
Forexample,the
protectedMyMouseListenermouseListener; protectedMyChangeListenerchangeListener; publicvoidinstallUI(JComponentc){ AbstractButtonb=(AbstractButton)c; //Installdefaultcolors&opacity Colorbg=c.getBackground(); if(bg==null||bginstanceofUIResource){ c.setBackground( UIManager.getColor("Button.background")); } Colorfg=c.getForeground(); if(fg==null||fginstanceofUIResource){ c.setForeground( UIManager.getColor("Button.foreground")); } c.setOpaque(false); //Installlisteners mouseListener=newMyMouseListener(); c.addMouseListener(mouseListener); c.addMouseMotionListener(mouseListener); changeListener=newMyChangeListener(); b.addChangeListener(changeListener); } |
Conventionsforinitializingcomponentproperties
Swingdefinesanumberofconventionsforinitializingcomponentpropertiesatinstall-time,includingthefollowing:Allvaluesusedforsettingcolors,font,andborderpropertiesshouldbeobtainedfromtheDefaultstable(asdescribedinthesubsectionon
Color,fontandborderpropertiesshouldbesetif--andonlyif--theapplicationhasnotalreadysetthem.
Tofacilitate
The
ClearingtheborderpropertyifithasbeensetbyinstallUI().
RemovethelayoutmanagerifithadbeensetbyinstallUI().
RemoveanysubcomponentsaddedbyinstallUI().
Removeanyevent/modellistenersthatwereaddedbyinstallUI().
Removeanylook-and-feel-specifickeyboardactionsthatwereinstalledbyinstallUI().
Nullifyanyinitializedinstancedata(toallowGCtocleanup).
Forexample,an
publicvoiduninstallUI(JComponentc){ AbstractButtonb=(AbstractButton)c; //Uninstalllisteners c.removeMouseListener(mouseListener); c.removeMouseMotionListener(mouseListener); mouseListener=null; b.removeChangeListener(changeListener); changeListener=null; } |
Defininggeometry
IntheAWT(andthusinSwing)acontainer'sLayoutManagerwilllayoutthechildcomponentsaccordingtoitsdefinedalgorithm;thisisknownas"validation"ofacontainmenthierarchy.TypicallyLayoutManagerswillquerythechildcomponents'preferredSizeproperty(andsometimesminimumSizeand/ormaximumSizeaswell,dependingonthealgorithm)inordertodeterminepreciselyhowtopositionandsizethosechildren.Obviously,thesegeometrypropertiesaresomethingthatalook-and-feelusuallyneedstodefineforagivencomponent,so
publicDimension getPreferredSize(JComponentc) publicDimension getMinimumSize(JComponentc) publicDimension getMaximumSize(JComponentc) publicboolean contains(JComponentc,intx,inty) |
publicDimensiongetPreferredSize(){ if(preferredSize!=null){ returnpreferredSize; } Dimensionsize=null; if(ui!=null){ size=ui.getPreferredSize(this); } return(size!=null)?size: super.getPreferredSize(); } |
tosimulateanon-rectangularcomponentbyoverridingtheimplementationofthe
publicbooleancontains(JComponentc,intx,inty){ return(ui!=null)? ui.contains(this,x,y) : super.contains(x,y); }
SoaUIdelegatecouldprovidenon-rectangular"feel"bydefiningaparticularimplementationof
Painting
Finally,theUIdelegatemustpaintthecomponentappropriately,hencepublicvoidpaint(Graphicsg,JComponentc) publicvoidupdate(Graphicsg,JComponentc)
Andonceagain,
protectedvoidpaintComponent(Graphicsg){ if(ui!=null){ GraphicsscratchGraphics= SwingGraphics.createSwingGraphics(g.create()); try{ ui.update(scratchGraphics,this); } finally{ scratchGraphics.dispose(); } } } |
Statelessvs.statefuldelegates
AllthemethodsonThisapproachworkswellformanyofthesimplerGUIcomponents.Butformorecomplexcomponents,wefounditnottobea"win"becausetheinefficiencycreatedbyconstantstaterecalculationswasworsethancreatingextraobjects(especiallysincethenumberofcomplexGUIcomponentscreatedinagivenprogramtendstobesmall).
The
publicstaticComponentUI createUI(JComponentc)
It'stheimplementationofthismethodthatdetermineswhetherthedelegateisstatelessorstateful.That'sbecausethe
TheSwinglook-and-feelimplementationsusebothtypesofdelegates.Forexample,Swing's
//SharedUIobject protectedstaticButtonUIbuttonUI; publicstaticComponentUIcreateUI(JComponentc) if(buttonUI==null){ buttonUI=newBasicButtonUI(); } returnbuttonUI; } |
PluggableLook-and-Feelsummary
Thepluggablelook-and-feelfeatureofSwingisbothpowerfulandcomplex(whichyouunderstandifyou'vegottenthisfar!).Itisdesignedtobeprogrammedbyasmallsubsetofdeveloperswhohaveaparticularneedtodevelopanewlook-and-feelimplementation.Ingeneral,applicationdevelopersonlyneedtounderstandthecapabilitiesofthismechanisminordertodecidehowtheywishtosupportlook-and-feels(suchaswhethertolock-downtheprogramtoasinglelook-and-feelorsupportlook-and-feelconfigurationbytheuser).Swing'sIfyou'reoneofthosedeveloperswhoneeds(orwants)todevelopacustomlook-and-feel,it'scriticaltounderstandtheseunderpinningsbeforeyouwriteasinglelineofcode.We'reworkingonprovidingbetterdocumentationtohelpwiththisprocess--startingwiththisdocument,andcontinuingwithothersthatwillfollowsoon.
相关文章推荐
- A Swing Architecture Overview
- Oracle Database Server Architecture: Overview
- chapter 2 SYSTEM ARCHITECTURE OVERVIEW (2)
- An overview of the ARM architecture
- AS7内部架构概述(AS 7 Internal Architecture Overview)
- XEN Architecture Overview
- An Overview of the Android Architecture (Android Studio)
- oracle architecture overview
- An Overview of Blockchain Technology: Architecture, Consensus, and Future Trends 全文翻译
- Open stack architecture overview-meetup-6-6_2013(需要翻墙)
- Web App Architecture: high-level overview(Head First Servlets and JSP)
- Xen Architecture Overview
- PeopleSoft Architecture && Overview
- X Window System Architecture Overview
- MSDN Architecture Webcast: patterns & practices Live: Enterprise Library Overview (Level 200)
- EMBEDDED MICROPROCESSORS-Blackfin Processor Architecture Overview
- IA32 architecture 学习笔记 (五)<chapter 2 System Architecture Overview>
- OpenStack Project Architecture Overview
- 读书笔记1 oracle architecture overview
- 一个简单的通讯录的设计(Phone Book Architecture Overview)