a swing architecture overview
Post on 17-Sep-2015
226 Views
Preview:
DESCRIPTION
TRANSCRIPT
-
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
top related