a swing architecture overview
DESCRIPTION
hTRANSCRIPT
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 1/12
ASwingArchitectureOverview
TheInsideStoryonJFCComponentDesignByAmyFowler
MostSwingdevelopersknowbynowthatSwingcomponentshaveaseparablemodelandviewdesign.AndmanySwingusershaverunacrossarticlessayingthatSwingisbasedonsomethingcalleda"modifiedMVC(modelviewcontroller)architecture."
ButaccurateexplanationsofhowSwingcomponentsaredesigned,andhowtheirpartsallfittogether,havebeenhardtocomebyuntilnow.
Thesilenceendswiththepublicationofthisarticle,amajorwhitepaperonSwingcomponentdesign.ItprovidesacomprehensivetechnicaloverviewofSwing'smodifiedMVCstructureanddemystifiesmanyotherfacetsofSwingcomponentarchitectureaswell.
ThisdocumentpresentsatechnicaloverviewoftheSwingcomponentarchitecture.Inparticular,itcoversthefollowingareasindetail:
DesigngoalsRootsinMVCSeparablemodelarchitecturePluggablelookandfeelarchitecture
DesignGoalsTheoverallgoalfortheSwingprojectwas:
TobuildasetofextensibleGUIcomponentstoenabledeveloperstomorerapidlydeveloppowerfulJavafrontendsforcommercialapplications.
Tothisend,theSwingteamestablishedasetofdesigngoalsearlyintheprojectthatdrovetheresultingarchitecture.TheseguidelinesmandatedthatSwingwould:
BeimplementedentirelyinJavatopromotecrossplatformconsistencyandeasiermaintenance.ProvideasingleAPIcapableofsupportingmultiplelookandfeelssothatdevelopersandenduserswouldnotbelockedintoasinglelookandfeel.EnablethepowerofmodeldrivenprogrammingwithoutrequiringitinthehighestlevelAPI.AdheretoJavaBeansdesignprinciplestoensurethatcomponentsbehavewellinIDEsandbuildertools.ProvidecompatibilitywithAWTAPIswherethereisoverlapping,toleveragetheAWTknowledgebaseandeaseporting.
RootsinMVCSwingarchitectureisrootedinthemodelviewcontroller(MVC)designthatdatesbacktoSmallTalk.MVCarchitecturecallsforavisualapplicationtobebrokenupintothreeseparateparts:
Amodelthatrepresentsthedatafortheapplication.Theviewthatisthevisualrepresentationofthatdata.Acontrollerthattakesuserinputontheviewandtranslatesthattochangesinthemodel.Earlyon,MVCwasalogicalchoiceforSwingbecauseitprovidedabasisformeetingthefirstthreeofourdesigngoalswithintheboundsofthelattertwo.
ThefirstSwingprototypefollowedatraditionalMVCseparationinwhicheachcomponenthadaseparatemodelobjectanddelegateditslookandfeelimplementationtoseparateviewandcontrollerobjects.
Thedelegate
Wequicklydiscoveredthatthissplitdidn'tworkwellinpracticaltermsbecausetheviewandcontrollerpartsofacomponentrequiredatightcoupling(forexample,itwasverydifficulttowriteagenericcontrollerthatdidn'tknowspecificsabouttheview).SowecollapsedthesetwoentitiesintoasingleUI(userinterface)object,asshowninthisdiagram:
(TheUIdelegateobjectshowninthispictureissometimescalledadelegateobject,orUIdelegate.TheUIdelegateusedinSwingisdescribedinmoredetailinthePluggablelookandfeelsectionofthisarticle,underthesubheading"TheUIdelegate".)
Asthediagramillustrates,SwingarchitectureislooselybasedbutnotstrictlybasedonthetraditionalMVCdesign.IntheworldofSwing,thisnewquasiMVCdesignissometimesreferredtoaseparablemodelarchitecture.
Swing'sseparablemodeldesigntreatsthemodelpartofacomponentasaseparateelement,justastheMVCdesigndoes.ButSwingcollapsestheviewandcontrollerpartsofeachcomponentintoasingleUI(userinterface)object.
ToMVCornottoMVC?
Onenoteworthypointisthatasanapplicationdeveloper,youshouldthinkofacomponent'sview/controllerresponsibilitiesasbeinghandledbythegenericcomponentclass(suchas.JButton,JTree,andsoon).ThecomponentclassthendelegatesthelookandfeelspecificaspectsofthoseresponsibilitiestotheUIobjectthatisprovidedbythecurrentlyinstalledlookandfeel.
Forexample,thecodethatimplementsdoublebufferedpaintingisinSwing'sJComponentclass(the"mother"ofmostSwingcomponentclasses),whilethecodethatrendersaJButton'slabelisinthebutton'sUIdelegateclass.Theprecedingdiagramillustratesthissubtle(andoftenconfusing)point:
SoSwingdoeshaveastrongMVClineage.Butit'salsoimportanttoreiteratethatourMVCarchitectureservestwodistinctpurposes:
First,separatingthemodeldefinitionfromacomponentfacilitatesmodeldrivenprogramminginSwing.Second,theabilitytodelegatesomeofacomponent'sview/controllerresponsibilitiestoseparatelookandfeelobjectsprovidesthebasisforSwing'spluggablelookandfeelarchitecture.AlthoughthesetwoconceptsarelinkedbytheMVCdesign,theymaybetreatedsomewhatorthogonallyfromthedeveloper'sperspective.Theremainderofthisdocumentwillcovereachofthesemechanismsingreaterdetail.
OracleTechnologyNetwork Java
Products Solutions Downloads Store Support Training Partners About OTN
SignIn/Register Help Country Communities Iama... Iwantto... Search
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 2/12
SeparablemodelarchitectureItisgenerallyconsideredgoodpracticetocenterthearchitectureofanapplicationarounditsdataratherthanarounditsuserinterface.Tosupportthisparadigm,Swingdefinesaseparatemodelinterfaceforeachcomponentthathasalogicaldataorvalueabstraction.ThisseparationprovidesprogramswiththeoptionofpluggingintheirownmodelimplementationsforSwingcomponents.
ThefollowingtableshowsthecomponenttomodelmappingforSwing.
Component ModelInterface ModelTypeJButton ButtonModel GUI
JToggleButton ButtonModel GUI/data
JCheckBox ButtonModel GUI/data
JRadioButton ButtonModel GUI/data
JMenu ButtonModel GUI
JMenuItem ButtonModel GUI
JCheckBoxMenuItem ButtonModel GUI/data
JRadioButtonMenuItem ButtonModel GUI/data
JComboBox ComboBoxModel data
JProgressBar BoundedRangeModel GUI/data
JScrollBar BoundedRangeModel GUI/data
JSlider BoundedRangeModel GUI/data
JTabbedPane SingleSelectionModel GUI
JList ListModel data
JList ListSelectionModel GUI
JTable TableModel data
JTable TableColumnModel GUI
JTree TreeModel data
JTree TreeSelectionModel GUI
JEditorPane Document data
JTextPane Document data
JTextArea Document data
JTextField Document data
JPasswordField Document data
GUIstatevs.applicationdatamodelsThemodelsprovidedbySwingfallintotwogeneralcategories:GUIstatemodelsandapplicationdatamodels.
GUIstatemodels
GUIstatemodelsareinterfacesthatdefinethevisualstatusofaGUIcontrol,suchaswhetherabuttonispressedorarmed,orwhichitemsareselectedinalist.GUIstatemodelstypicallyarerelevantonlyinthecontextofagraphicaluserinterface(GUI).WhileitisoftenusefultodevelopprogramsusingGUIstatemodelseparationparticularlyifmultipleGUIcontrolsarelinkedtoacommonstate(suchasinasharedwhiteboardprogram),orifmanipulatingonecontrolautomaticallychangesthevalueofanothertheuseofGUIstatemodelsisnotrequiredbySwing.ItispossibletomanipulatethestateofaGUIcontrolthroughtoplevelmethodsonthecomponent,withoutanydirectinteractionwiththemodelatall.Intheprecedingtable,GUIstatemodelsinSwingarehighlightedinblue.
Applicationdatamodels
Anapplicationdatamodelisaninterfacethatrepresentssomequantifiabledatathathasmeaningprimarilyinthecontextoftheapplication,suchasthevalueofacellinatableortheitemsdisplayedinalist.ThesedatamodelsprovideaverypowerfulprogrammingparadigmforSwingprogramsthatneedacleanseparationbetweentheirapplicationdata/logicandtheirGUI.FortrulydatacentricSwingcomponents,suchasJTreeandJTable,interactionwiththedatamodelisstronglyrecommended.Applicationdatamodelsarehighlightedinredinthetablepresentedatthebeginningofthissection.
Ofcoursewithsomecomponents,themodelcategorizationfallssomewhereinbetweenGUIstatemodelsandapplicationdatamodels,dependingonthecontextinwhichthemodelisused.ThisisthecasewiththeBoundedRangeModelonJSliderorJProgressBar.Thesemodelsarehighlightedinpurpleintheprecedingtable.
Swing'sseparablemodelAPImakesnospecificdistinctionsbetweenGUIstatemodelsandapplicationdatamodelshowever,wehaveclarifiedthisdifferenceheretogivedevelopersabetterunderstandingofwhenandwhytheymightwishtoprogramwiththeseparablemodels.
SharedmodeldefinitionsReferringagaintothetableatthebeginningofthissection,noticethatmodeldefinitionsaresharedacrosscomponentsincaseswherethedataabstractionforeachcomponentissimilarenoughtosupportasingleinterfacewithoutovergenericizingthatinterface.Commonmodelsenableautomaticconnectabilitybetweencomponenttypes.Forexample,becausebothJSliderandJScrollbarusetheBoundedRangeModelinterface,asingleBoundedRangeModelinstancecouldbepluggedintobothaJScrollbarandaJSliderandtheirvisualstatewouldalwaysremaininsync.
TheseparablemodelAPISwingcomponentsthatdefinemodelssupportaJavaBeansboundpropertyforthemodel.Forexample,JSliderusestheBoundedRangeModelinterfaceforitsmodeldefinition.Consequently,itincludesthefollowingmethods:
publicBoundedRangeModelgetModel()publicvoidsetModel(BoundedRangeModelmodel)AllSwingcomponentshaveonethingincommon:Ifyoudon'tsetyourownmodel,adefaultiscreatedandinstalledinternallyinthecomponent.Thenamingconventionforthesedefaultmodelclassesistoprependtheinterfacenamewith"Default."ForJSlider,aDefaultBoundedRangeModelobjectisinstantiatedinitsconstructor:
publicJSlider(intorientation,intmin,
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 3/12
intmax,intvalue){checkOrientation(orientation)this.orientation=orientationthis.model=newDefaultBoundedRangeModel(value,0,min,max)this.model.addChangeListener(changeListener)updateUI()}
IfaprogramsubsequentlycallssetModel(),thisdefaultmodelisreplaced,asinthefollowingexample:
JSliderslider=newJSlider()BoundedRangeModelmyModel=newDefaultBoundedRangeModel(){publicvoidsetValue(intn){System.out.println("SetValue:"+n)super.setValue(n)}})slider.setModel(myModel)
Formorecomplexmodels(suchasthoseforJTableandJList),anabstractmodelimplementationisalsoprovidedtoenabledeveloperstocreatetheirownmodelswithoutstartingfromscratch.Theseclassesareprependedwith"Abstract".
Forexample,JList'smodelinterfaceisListModel,whichprovidesbothDefaultListModelandAbstractListModelclassestohelpthedeveloperinbuildingalistmodel.
ModelchangenotificationModelsmustbeabletonotifyanyinterestedparties(suchasviews)whentheirdataorvaluechanges.SwingmodelsusetheJavaBeansEventmodelfortheimplementationofthisnotification.TherearetwoapproachesforthisnotificationusedinSwing:
Sendalightweightnotificationthatthestatehas"changed"andrequirethelistenertorespondbysendingaquerybacktothemodeltofindoutwhathaschanged.Theadvantageofthisapproachisthatasingleeventinstancecanbeusedforallnotificationsfromaparticularmodelwhichishighlydesirablewhenthenotificationstendtobehighinfrequency(suchaswhenaJScrollBarisdragged).Sendastatefulnotificationthatdescribesmorepreciselyhowthemodelhaschanged.Thisalternativerequiresaneweventinstanceforeachnotification.Itisdesirablewhenagenericnotificationdoesn'tprovidethelistenerwithenoughinformationtodetermineefficientlywhathaschangedbyqueryingthemodel(suchaswhenacolumnofcellschangevalueinaJTable).
LightweightnotificationThefollowingmodelsinSwingusethelightweightnotification,whichisbasedontheChangeListener/ChangeEventAPI:
Model Listener EventBoundedRangeModel ChangeListener ChangeEventButtonModel ChangeListener ChangeEventSingleSelectionModel ChangeListener ChangeEvent
TheChangeListenerinterfacehasasinglegenericmethod:
publicvoidstateChanged(ChangeEvente)TheonlystateinaChangeEventistheevent"source."Becausethesourceisalwaysthesameacrossnotifications,asingleinstancecanbeusedforallnotificationsfromaparticularmodel.ModelsthatusethismechanismsupportthefollowingmethodstoaddandremoveChangeListeners:
publicvoidaddChangeListener(ChangeListenerl)publicvoidremoveChangeListener(ChangeListenerl)Therefore,tobenotifiedwhenthevalueofaJSliderhaschanged,thecodemightlooklikethis:
JSliderslider=newJSlider()BoundedRangeModelmodel=slider.getModel()model.addChangeListener(newChangeListener(){publicvoidstateChanged(ChangeEvente){//needtoquerythemodel//togetupdatedvalue...BoundedRangeModelm=(BoundedRangeModel)e.getSource()System.out.println("modelchanged:"+m.getValue())
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 4/12
}})
Toprovideconvenienceforprogramsthatdon'twishtodealwithseparatemodelobjects,someSwingcomponentclassesalsoprovidetheabilitytoregisterChangeListenersdirectlyonthecomponent(sothecomponentcanlistenforchangesonthemodelinternallyandthenpropagatesthoseeventstoanylistenersregistereddirectlyonthecomponent).Theonlydifferencebetweenthesenotificationsisthatforthemodelcase,theeventsourceisthemodelinstance,whileforthecomponentcase,thesourceisthecomponent.
Sowecouldsimplifytheprecedingexampleto:
JSliderslider=newJSlider()slider.addChangeListener(newChangeListener(){publicvoidstateChanged(ChangeEvente){//thesourcewillbe//thesliderthistime..JSliders=(JSlider)e.getSource()System.out.println("valuechanged:"+s.getValue())}})
StatefulnotificationModelsthatsupportstatefulnotificationprovideeventListenerinterfacesandeventobjectsspecifictotheirpurpose.Thefollowingtableshowsthebreakdownforthosemodels:
Model Listener EventListModel ListDataListener ListDataEventListSelectionModel ListSelectionListener ListSelectionEventComboBoxModel ListDataListener ListDataEventTreeModel TreeModelListener TreeModelEventTreeSelectionModel TreeSelectionListener TreeSelectionEventTableModel TableModelListener TableModelEvent
TableColumnModel TableColumnModelListenerTableColumnModelEvent
Document DocumentListener DocumentEventDocument UndoableEditListener UndoableEditEvent
TheusageoftheseAPIsissimilartothelightweightnotification,exceptthatthelistenercanquerytheeventobjectdirectlytofindoutwhathaschanged.Forexample,thefollowingcodedynamicallytrackstheselectediteminaJList:
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.
IgnoringmodelscompletelyAsmentionedpreviously,mostcomponentsprovidethemodeldefinedAPIdirectlyinthecomponentclasssothatthecomponentcanbemanipulatedwithoutinteractingwiththemodelatall.Thisisconsideredperfectlyacceptableprogrammingpractice(especiallyfortheGUIstatemodels).Forexample,followingisJSlider'simplementationofgetValue(),whichinternallydelegatesthemethodcalltoitsmodel:
publicintgetValue(){
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 5/12
returngetModel().getValue()}Andsoprogramscansimplydothefollowing:
JSliderslider=newJSlider()intvalue=slider.getValue()//what'sa"model,"anyway?
SeparablemodelsummarySowhileit'susefultounderstandhowSwing'smodeldesignworks,itisn'tnecessarytousethemodelAPIforallaspectsofSwingprogramming.Youshouldcarefullyconsideryourapplication'sindividualneedsanddeterminewherethemodelAPIwillenhanceyourcodewithoutintroducingunnecessarycomplexity.
Inparticular,werecommendtheusageoftheApplicationDatacategoryofmodelsforSwing(modelsforJTable,JTree,andthelike)becausetheycangreatlyenhancethescalabilityandmodularityofyourapplicationoverthelongrun.
PluggablelookandfeelarchitectureSwing'spluggablelookandfeelarchitectureallowsustoprovideasinglecomponentAPIwithoutdictatingaparticularlookandfeel.TheSwingtoolkitprovidesadefaultsetoflookandfeelshowever,theAPIis"open"adesignthatadditionallyallowsdeveloperstocreatenewlookandfeelimplementationsbyeitherextendinganexistinglookandfeelorcreatingonefromscratch.AlthoughthepluggablelookandfeelAPIisextensible,itwasintentionallydesignedatalevelbelowthebasiccomponentAPIinsuchawaythatadeveloperdoesnotneedtounderstanditsintricatedetailstobuildSwingGUIs.(Butifyouwanttoknow,readon...)
Whilewedon'texpect(oradvise)themajorityofdeveloperstocreatenewlookandfeelimplementations,werealizePL&Fisaverypowerfulfeatureforasubsetofapplicationsthatwanttocreateauniqueidentity.Asitturnsout,PL&FisalsoideallysuitedforuseinbuildingGUIsthatareaccessibletouserswithdisabilities,suchasvisuallyimpairedusersoruserswhocannotoperateamouse.
Inanutshell,pluggablelookandfeeldesignsimplymeansthattheportionofacomponent'simplementationthatdealswiththepresentation(thelook)andeventhandling(thefeel)isdelegatedtoaseparateUIobjectsuppliedbythecurrentlyinstalledlookandfeel,whichcanbechangedatruntime.
ThepluggablelookandfeelAPIThepluggablelookandfeelAPIincludes:
SomesmallhooksintheSwingcomponentclasses.SometoplevelAPIforlookandfeelmanagement.AmorecomplexAPIthatactuallyimplementslookandfeelsinseparatepackages.Thecomponenthooks
EachSwingcomponentthathaslookandfeelspecificbehaviordefinesanabstractclassintheswing.plafpackagetorepresentitsUIdelegate.Thenamingconventionfortheseclassesistotaketheclassnameforthecomponent,removethe"J"prefix,andappend"UI."Forexample,JButtondefinesitsUIdelegatewiththeplafclassButtonUI.
TheUIdelegateiscreatedinthecomponent'sconstructorandisaccessibleasaJavaBeansboundpropertyonthecomponent.Forexample,JScrollBarprovidesthefollowingmethodstoaccessitsUIdelegate:
publicScrollBarUIgetUI()publicvoidsetUI(ScrollBarUIui)ThisprocessofcreatingaUIdelegateandsettingitasthe"UI"propertyforacomponentisessentiallythe"installation"ofacomponent'slookandfeel.
EachcomponentalsoprovidesamethodwhichcreatesandsetsaUIdelegateforthe"default"lookandfeel(thismethodisusedbytheconstructorwhendoingtheinstallation):
publicvoidupdateUI()AlookandfeelimplementationprovidesconcretesubclassesforeachabstractplafUIclass.Forexample,theWindowslookandfeeldefinesWindowsButtonUI,aWindowsScrollBarUI,andsoon.WhenacomponentinstallsitsUIdelegate,itmusthaveawaytolookuptheappropriateconcreteclassnameforthecurrentdefaultlookandfeeldynamically.ThisoperationisperformedusingahashtableinwhichthekeyisdefinedprogrammaticallybythegetUIClassID()methodinthecomponent.Theconventionistousetheplafabstractclassnameforthesekeys.Forexample,JScrollbarprovides:
publicStringgetUIClassID(){return"ScrollBarUI"}Consequently,thehashtableintheWindowslookandfeelwillprovideanentrythatmaps"ScrollBarUI"to
"com.sun.java.swing.plaf.windows.WindowsScrollBarUI"
Lookandfeelmanagement
SwingdefinesanabstractLookAndFeelclassthatrepresentsalltheinformationcentraltoalookandfeelimplementation,suchasitsname,itsdescription,whetherit'sanativelookandfeelandinparticular,ahashtable(knownasthe"DefaultsTable")forstoringdefaultvaluesforvariouslookandfeelattributes,suchascolorsandfonts.
EachlookandfeelimplementationdefinesasubclassofLookAndFeel(forexample,swing.plaf.motif.MotifLookAndFeel)toprovideSwingwiththenecessaryinformationtomanagethelookandfeel.
TheUIManageristheAPIthroughwhichcomponentsandprogramsaccesslookandfeelinformation(theyshouldrarely,ifever,talkdirectlytoaLookAndFeelinstance).UIManagerisresponsibleforkeepingtrackofwhichLookAndFeelclassesareavailable,whichareinstalled,andwhichiscurrentlythedefault.TheUIManageralsomanagesaccesstotheDefaultsTableforthecurrentlookandfeel.
The'default'lookandfeel
TheUIManageralsoprovidesmethodsforgettingandsettingthecurrentdefaultLookAndFeel:
publicstaticLookAndFeelgetLookAndFeel()
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 6/12
publicstaticvoidsetLookAndFeel(LookAndFeelnewLookAndFeel)publicstaticvoidsetLookAndFeel(StringclassName)
Asadefaultlookandfeel,SwinginitializesthecrossplatformJavalookandfeel(formerlyknownas"Metal").However,ifaSwingprogramwantstosetthedefaultLookandFeelexplicitly,itcandothatusingtheUIManager.setLookAndFeel()method.Forexample,thefollowingcodesamplewillsetthedefaultLookandFeeltobeCDE/Motif:
UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel")Sometimesanapplicationmaynotwanttospecifyaparticularlookandfeel,butinsteadwantstoconfigurealookandfeelinsuchawaythatitdynamicallymatcheswhateverplatformithappenstoberunningon(forinstance,the.WindowslookandfeelifitisrunningonWindowsNT,orCDE/MotififitrunningonSolaris).Or,perhaps,anapplicationmightwanttolockdownthelookandfeeltothecrossplatformJavalookandfeel.
TheUIManagerprovidesthefollowingstaticmethodstoprogrammaticallyobtaintheappropriateLookAndFeelclassnamesforeachofthesecases:
publicstaticStringgetSystemLookAndFeelClassName()publicstaticStringgetCrossPlatformLookAndFeelClassName()So,toensurethataprogramalwaysrunsintheplatform'ssystemlookandfeel,thecodemightlooklikethis:
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())DynamicallyChangingtheDefaultLookandFeel
WhenaSwingapplicationprogrammaticallysetsthelookandfeel(asdescribedabove),theidealplacetodosoisbeforeanySwingcomponentsareinstantiated.ThisisbecausetheUIManager.setLookAndFeel()methodmakesaparticularLookAndFeelthecurrentdefaultbyloadingandinitializingthatLookAndFeelinstance,butitdoesnotautomaticallycauseanyexistingcomponentstochangetheirlookandfeel.
RememberthatcomponentsinitializetheirUIdelegateatconstructtime,therefore,ifthecurrentdefaultchangesaftertheyareconstructed,theywillnotautomaticallyupdatetheirUIsaccordingly.Itisuptotheprogramtoimplementthisdynamicswitchingbytraversingthecontainmenthierarchyandupdatingthecomponentsindividually.(NOTE:SwingprovidestheSwingUtilities.updateComponentTreeUI()methodtoassistwiththisprocess).
ThelookandfeelofacomponentcanbeupdatedatanytimetomatchthecurrentdefaultbyinvokingitsupdateUI()method,whichusesthefollowingstaticmethodonUIManagertogettheappropriateUIdelegate:
publicstaticComponentUIgetUI(JComponentc)Forexample,theimplementationofupdateUI()fortheJScrollBarlookslikethefollowing:
publicvoidupdateUI(){setUI((ScrollBarUI)UIManager.getUI(this))}AndsoifaprogramneedstochangethelookandfeelofaGUIhierarchyafteritwasinstantiated,thecodemightlooklikethefollowing:
//GUIalreadyinstantiated,wheremyframe//istoplevelframetry{
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))}
Managinglookandfeeldata
TheUIManagerdefinesastaticclass,namedUIManager.LookAndFeelInfo,forstoringthehighlevelname(suchas."Metal")andparticularclassname(suchas"com.sun.java.swing.plaf.MetalLookAndFeel")foraLookAndFeel.ItusestheseclassesinternallytomanagetheknownLookAndFeelobjects.ThisinformationcanbeaccessedfromtheUIManagerviathefollowingstaticmethods:
publicstaticLookAndFeelInfo[]
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 7/12
getInstalledLookAndFeels()
publicstaticvoidsetInstalledLookAndFeels(LookAndFeelInfo[]infos)throwsSecurityException
publicstaticvoidinstallLookAndFeel(LookAndFeelInfoinfo)
publicstaticvoidinstallLookAndFeel(Stringname,StringclassName)
Thesemethodscanbeusedtoprogrammaticallydeterminewhichlookandfeelimplementationsareavailable,whichisusefulwhenbuildinguserinterfaceswhichallowtheendusertodynamicallyselectalookandfeel.
Thelookandfeelpackages
TheUIdelegateclassesprovidedinswing.plaf(ButtonUI,ScrollBarUI,andsoon)definethepreciseAPIthatacomponentcanusetointeractwiththeUIdelegateinstance.(NOTE:Interfaceswereoriginallyusedhere,buttheywerereplacedwithabstractclassesbecausewefelttheAPIwasnotmatureenoughtowithstandtheconcretecastingofaninterface.)TheseplafAPIsaretherootofalllookandfeelimplementations.
Eachlookandfeelimplementationprovidesconcretesubclassesoftheseabstractplafclasses.Allsuchclassesdefinedbyaparticularlookandfeelimplementationarecontainedinaseparatepackageundertheswing.plafpackage(forexample,.swing.plaf.motif,swing.plaf.metal,andsoon).Alookandfeelpackagecontainsthefollowing:
TheLookAndFeelsubclass(forinstance,MetalLookAndFeel).Alllookandfeel'sUIdelegateclasses(forexample,MetalButtonUI,MetalTreeUI,andthelike).Anylookandfeelutilityclasses(MetalGraphicsUtils,MetalIconFactory,andsoon).Otherresourcesassociatedwiththelookandfeel,suchasimagefiles.InimplementingthevariousSwinglookandfeels,wesoondiscoveredthattherewasalotofcommonalityamongthem.Wefactoredoutthiscommoncodeintoabaselookandfeelimplementation(called"basic")whichextendstheplafabstractclassesandfromwhichthespecificlookandfeelimplementations(motif,windows,andsoon.)extend.Thebasiclookandfeelpackagesupportsbuildingadesktoplevellookandfeel,suchasWindowsorCDE/Motif.
Thebasiclookandfeelpackageisjustoneexampleofhowtobuildapluggablelookandfeelthearchitectureisflexibleenoughtoaccommodateotherapproachesaswell.
Theremainderofthisdocumentwillshowhowalookandfeelpackageworksatthegenericlevel,leavingthedetailsonthebasicpackageforafuturedocument.
WARNING:AllAPIsdefinedbelowtheswing.plafpackagearenotfrozeninthe1.0.XversionofSwing.WearecurrentlycleaningupthoseAPIsfortheversionofSwingthatwillshipwithJDK1.2beta4,atwhichtimetheywillbecomefrozen.Soifyouaredevelopingyourownlookandfeelimplementationusingthe1.0.1API,thisislikelytoaffectyou.
TheLookAndFeelSubclass
TheLookAndFeelclassdefinesthefollowingabstractmethods,whichallsubclassesmustimplement:
publicStringgetName()publicStringgetID()publicStringgetDescription()publicbooleanisNativeLookAndFeel()publicbooleanisSupportedLookAndFeel()ThegetName(),getID(),andgetDescription()methodsprovidegenericinformationaboutthelookandfeel.
TheisNativeLookAndFeel()methodreturnstrueifthelookandfeelisnativetothecurrentplatform.Forexample,MotifLookAndFeelreturnstrueifitiscurrentlyrunningontheSolarisplatform,andreturnsfalseotherwise.
TheisSupportedLookAndFeel()methodreturnswhetherornotthislookandfeelisauthorizedtorunonthecurrentplatform.Forexample,WindowsLookAndFeelreturnstrueonlyifitisrunningonaWindows95,Windows98,orWindowsNTmachine.
ALookAndFeelclassalsoprovidesmethodsforinitializationanduninitialization:
publicvoidinitialize()publicvoiduninitialize()Theinitialize()methodisinvokedbytheUIManagerwhentheLookAndFeelismadethe"default"usingtheUIManager.setLookAndFeel()method.uninitialize()isinvokedbytheUIManagerwhentheLookAndFeelisabouttobereplacedasthedefault.
TheDefaultsTable
Finally,theLookAndFeelclassprovidesamethodtoreturnthelookandfeel'simplementationoftheDefaultsTable:
publicUIDefaultsgetDefaults()TheDefaultsTableisrepresentedbytheUIDefaultsclass,adirectextensionofjava.util.Hashtable,whichaddsmethodsforaccessingspecifictypesofinformationaboutalookandfeel.ThistablemustincludealltheUIClassIDtoclassnamemappinginformation,aswellasanydefaultvaluesforpresentationrelatedproperties(suchascolor,font,border,andicon)foreachUIdelegate.Forexample,followingisasampleofwhatafragmentofgetDefaults()mightlooklikeforahypotheticallookandfeelinapackagecalled"mine":
publicUIDefaultsgetDefaults(){UIDefaultstable=newUIDefaults()
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 8/12
Object[]uiDefaults={
"ButtonUI","mine.MyButtonUI","CheckBoxUI","mine.MyCheckBoxUI","MenuBarUI","mine.MyMenuBarUI",..."Button.background",newColorUIResource(Color.gray),"Button.foreground",newColorUIResource(Color.black),"Button.font",newFontUIResource("Dialog",Font.PLAIN,12),"CheckBox.background",newColorUIResource(Color.lightGray),"CheckBox.font",newFontUIResource("Dialog",Font.BOLD,12),
...}table.putDefaults(uiDefaults)returntable
}
WhenthedefaultlookandfeelissetwithUIManager.setLookAndFeel(),theUIManagercallsgetDefaults()onthenewLookAndFeelinstanceandstoresthehashtableitreturns.SubsequentcallstotheUIManager'slookupmethodswillbeappliedtothistable.Forexample,aftermaking"mine"thedefaultLookandFeel:
UIManager.get("ButtonUI")=>"mine.MyButtonUI"TheUIclassesaccesstheirdefaultinformationinthesameway.Forexample,ourexampleButtonUIclasswouldinitializetheJButton's"background"propertylikethis:
button.setBackground(UIManager.getColor("Button.background")Thedefaultsareorganizedthiswaytoallowdeveloperstooverridethem.MoredetailaboutSwing'sDefaultsmechanismwillbepublishedinafuturearticle.
DistinguishingbetweenUIsetandappsetproperties
Swingallowsapplicationstosetpropertyvalues(suchascolorandfont)individuallyoncomponents.Soit'scriticaltomakesurethatthesevaluesdon'tgetclobberedwhenalookandfeelsetsupits"default"propertiesforthecomponent.
ThisisnotanissuethefirsttimeaUIdelegateisinstalledonacomponent(atconstructtime)becauseallpropertieswillbeuninitializedandlegallysettablebythelookandfeel.Theproblemoccurswhentheapplicationsetsindividualpropertiesaftercomponentconstructionandthensubsequentlysetsanewlookandfeel(thatis,dynamiclookandfeelswitching).Thismeansthatthelookandfeelmustbeabletodistinguishbetweenpropertyvaluessetbytheapplication,andthosesetbyalookandfeel.
Thisissueishandledbymarkingallvaluessetbythelookandfeelwiththeplaf.UIResourceinterface.Theplafpackageprovidesasetof"marked"classesforrepresentingthesevalues,ColorUIResource,FontUIResource,andBorderUIResource.TheprecedingcodeexampleshowstheusageoftheseclassestomarkthedefaultpropertyvaluesforthehypotheticalMyButtonUIclass.
TheUIdelegateThesuperclassofallUIDelegateclassesisswing.plaf.ComponentUI.Thisclasscontainstheprimary"machinery"formakingthepluggablelookandfeelwork.ItsmethodsdealwithUIinstallationanduninstallation,andwithdelegationofacomponent'sgeometryhandlingandpainting.
ManyoftheUIDelegatesubclassesalsoprovideadditionalmethodsspecifictotheirownrequiredinteractionwiththecomponenthowever,thisdocumentfocusesprimarilyonthegenericmechanismimplementedbyComponentUI.
UIinstallationanddeinstallation
Firstoff,theComponentUIclassdefinesthesemethodsmethodsforUIdelegateinstallationanduninstallation:
publicvoidinstallUI(JComponentc)publicvoiduninstallUI(JComponentc)LookingattheimplementationofJComponent.setUI()(whichisalwaysinvokedfromthesetUImethodonJComponentsubclasses),wecanclearlyseehowUIdelegateinstallation/deinstallationworks:
protectedvoidsetUI(ComponentUInewUI){if(ui!=null){ui.uninstallUI(this)
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 9/12
}ComponentUIoldUI=uiui=newUIif(ui!=null){ui.installUI(this)}invalidate()firePropertyChange("UI",oldUI,newUI)}
UIinstallationillustratedThisarticlecomeswithagiantpostersizechartthatillustratestheprocessinstallingaUIdelegate.Itcanprovideyouwithavaluableoverviewofthedelegateinstallationprocess.
Tofoldoutthechart,justfollowthislink
TheUIdelegate'sinstallUI()methodisresponsibleforthefollowing:
Setdefaultfont,color,border,andopacitypropertiesonthecomponent.Installanappropriatelayoutmanageronthecomponent.AddanyappropriatechildsubcomponentstothecomponentRegisteranyrequiredeventlistenersonthecomponent.Registeranylookandfeelspecifickeyboardactions(mnemonics,etc.)forthecomponent.Registerappropriatemodellistenerstobenotifiedwhentorepaint.Initializeanyappropriateinstancedata.Forexample,theinstallUI()methodforanextensionofButtonUImightlooklikethis:
protectedMyMouseListenermouseListenerprotectedMyChangeListenerchangeListener
publicvoidinstallUI(JComponentc){AbstractButtonb=(AbstractButton)c//Installdefaultcolors&opacityColorbg=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)//InstalllistenersmouseListener=newMyMouseListener()c.addMouseListener(mouseListener)c.addMouseMotionListener(mouseListener)changeListener=newMyChangeListener()b.addChangeListener(changeListener)}
Conventionsforinitializingcomponentproperties
Swingdefinesanumberofconventionsforinitializingcomponentpropertiesatinstalltime,includingthefollowing:
Allvaluesusedforsettingcolors,font,andborderpropertiesshouldbeobtainedfromtheDefaultstable(asdescribedinthesubsectionontheLookAndFeelsubclass).Color,fontandborderpropertiesshouldbesetifandonlyiftheapplicationhasnotalreadysetthem.TofacilitateconventionNo1,theUIManagerclassprovidesanumberofstaticmethodstoextractpropertyvaluesofaparticulartype(forinstance,thestaticmethodsUIManager.getColor(),UIManager.getFont(),andsoon).
ConventionNo.2isimplementedbyalwayscheckingforeitheranullvalueoraninstanceofUIResourcebeforesettingtheproperty.
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 10/12
TheComponentUI'suninstall()methodmustcarefullyundoeverythingthatwasdoneintheinstallUI()methodsothatthecomponentisleftinapristinestateforthenextUIdelegate.Theuninstall()methodisresponsiblefor:
ClearingtheborderpropertyifithasbeensetbyinstallUI().RemovethelayoutmanagerifithadbeensetbyinstallUI().RemoveanysubcomponentsaddedbyinstallUI().Removeanyevent/modellistenersthatwereaddedbyinstallUI().RemoveanylookandfeelspecifickeyboardactionsthatwereinstalledbyinstallUI().Nullifyanyinitializedinstancedata(toallowGCtocleanup).Forexample,anuninstall()methodtoundowhatwedidintheaboveexampleinstallationmightlooklikethis:
publicvoiduninstallUI(JComponentc){AbstractButtonb=(AbstractButton)c//Uninstalllistenersc.removeMouseListener(mouseListener)c.removeMouseMotionListener(mouseListener)mouseListener=nullb.removeChangeListener(changeListener)changeListener=null}
Defininggeometry
IntheAWT(andthusinSwing)acontainer'sLayoutManagerwilllayoutthechildcomponentsaccordingtoitsdefinedalgorithmthisisknownas"validation"ofacontainmenthierarchy.TypicallyLayoutManagerswillquerythechildcomponents'preferredSizeproperty(andsometimesminimumSizeand/ormaximumSizeaswell,dependingonthealgorithm)inordertodeterminepreciselyhowtopositionandsizethosechildren.
Obviously,thesegeometrypropertiesaresomethingthatalookandfeelusuallyneedstodefineforagivencomponent,soComponentUIprovidesthefollowingmethodsforthispurpose:
publicDimensiongetPreferredSize(JComponentc)publicDimensiongetMinimumSize(JComponentc)publicDimensiongetMaximumSize(JComponentc)publicbooleancontains(JComponentc,intx,inty)
JComponent'sparallelmethods(whichareinvokedbytheLayoutManagerduringvalidation)thensimplydelegatetotheUIobject'sgeometrymethodsifthegeometrypropertywasnotexplicitlysetbytheprogram.BelowistheimplementationofJComponent.getPreferredSize()whichshowsthisdelegation:
publicDimensiongetPreferredSize(){if(preferredSize!=null){returnpreferredSize}Dimensionsize=nullif(ui!=null){size=ui.getPreferredSize(this)}return(size!=null)?size:super.getPreferredSize()}
EventhoughtheboundingboxforallcomponentsisaRectangle,it'spossibletosimulateanonrectangularcomponentbyoverridingtheimplementationofthecontains()methodfromjava.awt.Component.(Thismethodisusedforthehittestingofmouseevents).But,liketheothergeometrypropertiesinSwing,theUIdelegatedefinesitsownversionofthecontains()method,whichisalsodelegatedtobyJComponent.contains():
publicbooleancontains(JComponentc,intx,inty){return(ui!=null)?ui.contains(this,x,y):super.contains(x,y)}SoaUIdelegatecouldprovidenonrectangular"feel"bydefiningaparticularimplementationofcontains()(forexample,ifwewantedourMyButtonUIclasstoimplementabuttonwithroundedcorners).
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 11/12
JavaSDKsandTools
JavaSE
JavaEEandGlassfish
Painting
Finally,theUIdelegatemustpaintthecomponentappropriately,henceComponentUIhasthefollowingmethods:
publicvoidpaint(Graphicsg,JComponentc)publicvoidupdate(Graphicsg,JComponentc)Andonceagain,JComponent.paintComponent()takescaretodelegatethepainting:
protectedvoidpaintComponent(Graphicsg){if(ui!=null){GraphicsscratchGraphics=SwingGraphics.createSwingGraphics(g.create())try{ui.update(scratchGraphics,this)}finally{scratchGraphics.dispose()}}}
SimilarlytothewayinwhichthingsaredoneinAWT,theUIdelegate'supdate()methodclearsthebackground(ifopaque)andtheninvokesitspaint()method,whichisultimatelyresponsibleforrenderingthecontentsofthecomponent.
Statelessvs.statefuldelegates
AllthemethodsonComponentUItakeaJComponentobjectasaparameter.ThisconventionenablesastatelessimplementationofaUIdelegate(becausethedelegatecanalwaysquerybacktothespecifiedcomponentinstanceforstateinformation).StatelessUIdelegateimplementationsallowasingleUIdelegateinstancetobeusedforallinstancesofthatcomponentclass,whichcansignificantlyreducethenumberofobjectsinstantiated.
ThisapproachworkswellformanyofthesimplerGUIcomponents.Butformorecomplexcomponents,wefounditnottobea"win"becausetheinefficiencycreatedbyconstantstaterecalculationswasworsethancreatingextraobjects(especiallysincethenumberofcomplexGUIcomponentscreatedinagivenprogramtendstobesmall).
TheComponentUIclassdefinesastaticmethodforreturningadelegateinstance:
publicstaticComponentUIcreateUI(JComponentc)It'stheimplementationofthismethodthatdetermineswhetherthedelegateisstatelessorstateful.That'sbecausetheUIManager.getUI()methodinvokedbythecomponenttocreatetheUIdelegateinternallyinvokesthiscreateUImethodonthedelegateclasstogettheinstance.
TheSwinglookandfeelimplementationsusebothtypesofdelegates.Forexample,Swing'sBasicButtonUIclassimplementsastatelessdelegate:
//SharedUIobjectprotectedstaticButtonUIbuttonUIpublicstaticComponentUIcreateUI(JComponentc)if(buttonUI==null){buttonUI=newBasicButtonUI()}returnbuttonUI}
WhileSwing'sBasicTabbedPaneUIusesthestatefulapproach:
publicstaticComponentUIcreateUI(JComponentc)returnnewBasicTabbedPaneUI()}
PluggableLookandFeelsummaryThepluggablelookandfeelfeatureofSwingisbothpowerfulandcomplex(whichyouunderstandifyou'vegottenthisfar!).Itisdesignedtobeprogrammedbyasmallsubsetofdeveloperswhohaveaparticularneedtodevelopanewlookandfeelimplementation.Ingeneral,applicationdevelopersonlyneedtounderstandthecapabilitiesofthismechanisminordertodecidehowtheywishtosupportlookandfeels(suchaswhethertolockdowntheprogramtoasinglelookandfeelorsupportlookandfeelconfigurationbytheuser).Swing'sUIManagerprovidestheAPIforapplicationstomanagethelookandfeelatthislevel.
Ifyou'reoneofthosedeveloperswhoneeds(orwants)todevelopacustomlookandfeel,it'scriticaltounderstandtheseunderpinningsbeforeyouwriteasinglelineofcode.We'reworkingonprovidingbetterdocumentationtohelpwiththisprocessstartingwiththisdocument,andcontinuingwithothersthatwillfollowsoon.
-
24/12/2014 ASwingArchitectureOverview
http://www.oracle.com/technetwork/java/architecture142923.html?printOnly=1 12/12
JavaME
JavaCard
NetBeansIDE
JavaMissionControl
JavaResources
JavaAPIs
TechnicalArticles
DemosandVideos
Forums
JavaMagazine
Java.net
DeveloperTraining
Tutorials
Java.com