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