├── .gitignore ├── 8KE0220325004764 ├── 2021-01-03_15-38-52 │ ├── deviceInfo.txt │ ├── logcat.txt │ └── monkey.txt ├── blacklist.txt ├── packagelist.txt └── whitelist.txt ├── LICENSE ├── README.md ├── bin └── images │ └── monkey.ico ├── project-files └── image │ ├── MonkeyAnalyzeLog.png │ ├── MonkeyAreas.png │ ├── MonkeyEventWeights1.png │ ├── MonkeyEventWeights2.png │ ├── MonkeyExcelReport.png │ ├── MonkeyExportReport.png │ ├── MonkeyHome.png │ ├── MonkeyImportExportConfig.png │ ├── MonkeyInstallApps.png │ ├── MonkeyJars.png │ ├── MonkeyRoot.png │ ├── MonkeyRun.png │ ├── MonkeyUninstallApps.png │ └── 公众号二维码.jpg └── src ├── com ├── demo │ ├── XSSFUtils.java │ └── jfreechart │ │ ├── BarChart.java │ │ ├── DualaxisChart.java │ │ ├── LineChart.java │ │ ├── PieChart.java │ │ ├── StackedBarChart.java │ │ ├── TimeSeriesChart.java │ │ └── utils │ │ ├── ChartUtils.java │ │ └── Serie.java ├── desay │ └── utils │ │ ├── AnrCrashUtils.java │ │ ├── EmailUtils.java │ │ ├── JfreeChartUtil.java │ │ ├── MyUtils.java │ │ ├── PoiUtils.java │ │ ├── PropUtil.java │ │ └── RecordUtils.java └── desaysv │ ├── KeyCodePc.java │ ├── LogcatThread.java │ ├── MyDialog.java │ ├── Settings.java │ ├── SvMonkey.java │ ├── TimerThread.java │ ├── WinUtil.java │ └── bean │ ├── AnrCrash.java │ ├── Device.java │ ├── DeviceInfo.java │ ├── DeviceRecord.java │ ├── MonkeyTaskBean.java │ ├── PackageErrorInfo.java │ └── Record.java ├── images └── monkey.ico └── org └── eclipse └── wb └── swt └── SWTResourceManager.java /.gitignore: -------------------------------------------------------------------------------- 1 | # java Eclipse failes 2 | *.class 3 | .classpath 4 | .project 5 | .fatjar 6 | *.properties 7 | .settings/ 8 | record.txt 9 | getLogCmds.txt -------------------------------------------------------------------------------- /8KE0220325004764/2021-01-03_15-38-52/deviceInfo.txt: -------------------------------------------------------------------------------- 1 | testStartTime=2021-01-03 15:38:52 2 | ro.product.model=ELE-AL00 3 | ro.build.display.id=ELE-AL00 10.1.0.162(C00E160R2P11) 4 | ro.build.version.release=10 5 | ro.build.date=Mon Jul 13 11:57:37 CST 2020 6 | ro.build.version.sdk=29 7 | monkeyCmdStr=adb -s 8KE0220325004764 shell monkey --pct-touch 50 --pct-motion 50 -s 0 -v -v -v --throttle 300 --bugreport 10 8 | logcatPath=D:\Users\uidq0460\workspace\SVmonkey\8KE0220325004764\2021-01-03_15-38-52\logcat.txt 9 | MonkeySeed=1609750349748 10 | TestCostTime=1476 11 | -------------------------------------------------------------------------------- /8KE0220325004764/2021-01-03_15-38-52/monkey.txt: -------------------------------------------------------------------------------- 1 | bash arg: --pct-touch 2 | bash arg: 50 3 | bash arg: --pct-motion 4 | bash arg: 50 5 | bash arg: -s 6 | bash arg: 0 7 | bash arg: -v 8 | bash arg: -v 9 | bash arg: -v 10 | bash arg: --throttle 11 | bash arg: 300 12 | bash arg: --bugreport 13 | bash arg: 10 14 | args: [--pct-touch, 50, --pct-motion, 50, -s, 0, -v, -v, -v, --throttle, 300, --bugreport, 10] 15 | arg: "--pct-touch" 16 | arg: "50" 17 | arg: "--pct-motion" 18 | arg: "50" 19 | arg: "-s" 20 | arg: "0" 21 | arg: "-v" 22 | arg: "-v" 23 | arg: "-v" 24 | arg: "--throttle" 25 | arg: "300" 26 | arg: "--bugreport" 27 | arg: "10" 28 | arg="--pct-touch" mCurArgData="null" mNextArg=1 argwas="--pct-touch" nextarg="50" 29 | data="50" 30 | arg="--pct-motion" mCurArgData="null" mNextArg=3 argwas="--pct-motion" nextarg="50" 31 | data="50" 32 | data="0" 33 | arg="--throttle" mCurArgData="null" mNextArg=10 argwas="--throttle" nextarg="300" 34 | data="300" 35 | arg="--bugreport" mCurArgData="null" mNextArg=12 argwas="--bugreport" nextarg="10" 36 | :Monkey: seed=1609750349748 count=10 37 | :IncludeCategory: android.intent.category.LAUNCHER 38 | :IncludeCategory: android.intent.category.MONKEY 39 | // Selecting main activities from category android.intent.category.LAUNCHER 40 | // + Using main activity com.android.calendar.AllInOneActivity (from package com.android.calendar) 41 | // + Using main activity com.android.contacts.activities.PeopleActivity (from package com.android.contacts) 42 | // + Using main activity com.android.contacts.activities.DialtactsActivity (from package com.android.contacts) 43 | // + Using main activity com.android.deskclock.AlarmsMainActivity (from package com.android.deskclock) 44 | // + Using main activity com.android.email.activity.Welcome (from package com.android.email) 45 | // + Using main activity com.huawei.gallery.app.GalleryMain (from package com.android.gallery3d) 46 | // + Using main activity com.android.mediacenter.PageActivity (from package com.android.mediacenter) 47 | // + Using main activity com.android.mms.ui.ConversationList (from package com.android.mms) 48 | // + Using main activity com.android.settings.HWSettings (from package com.android.settings) 49 | // + Using main activity com.example.android.notepad.NotePadActivity (from package com.example.android.notepad) 50 | // + Using main activity com.huawei.android.findmyphone.ui.findphone.FindMyPhoneActivity (from package com.huawei.android.findmyphone) 51 | // + Using main activity com.huawei.android.tips.DrawerDisplayActivity (from package com.huawei.android.tips) 52 | // + Using main activity com.huawei.browser.Main2 (from package com.huawei.browser) 53 | // + Using main activity com.huawei.camera (from package com.huawei.camera) 54 | // + Using main activity com.huawei.gamebox.GameBoxActivity (from package com.huawei.gamebox) 55 | // + Using main activity com.huawei.hidisk.filemanager.FileManager (from package com.huawei.hidisk) 56 | // + Using main activity com.huawei.himovie.ui.login.SplashScreenActivity (from package com.huawei.himovie) 57 | // + Using main activity com.huawei.skytone.account.welcome.SplashActivity (from package com.huawei.hiskytone) 58 | // + Using main activity com.chaozh.iReader.ui.activity.WelcomeActivity (from package com.huawei.hwireader) 59 | // + Using main activity com.huawei.vassistant.ui.main.VAssistantActivity (from package com.huawei.vassistant) 60 | // + Using main activity com.achievo.vipshop.activity.LodingActivity (from package com.achievo.vipshop) 61 | // + Using main activity com.sankuai.meituan.activity.PreloadedWelcome (from package com.sankuai.meituan) 62 | // + Using main activity com.android.documentsui.LauncherActivity (from package com.android.documentsui) 63 | // + Using main activity com.android.soundrecorder.RecordListActivity (from package com.android.soundrecorder) 64 | // + Using main activity com.android.stk.StkLauncherActivity (from package com.android.stk) 65 | // + Using main activity com.android.stk.StkLauncherActivity2 (from package com.android.stk) 66 | // + Using main activity com.huawei.android.clone.activity.sender.ChooseReceiveSendActivity (from package com.hicloud.android.clone) 67 | // + Using main activity com.android.systemui.flashlight.FlashlightActivity (from package com.huawei.HwMultiScreenShot) 68 | // + Using main activity com.huawei.android.hwouc.ui.activities.MainEntranceActivity (from package com.huawei.android.hwouc) 69 | // + Using main activity com.huawei.android.thememanager.HwThemeManagerActivity (from package com.huawei.android.thememanager) 70 | // + Using main activity com.huawei.android.totemweather.WeatherHome (from package com.huawei.android.totemweather) 71 | // + Using main activity com.huawei.appmarket.MainActivity (from package com.huawei.appmarket) 72 | // + Using main activity com.huawei.calculator.Calculator (from package com.huawei.calculator) 73 | // + Using main activity com.huawei.ahdp.wi.WIActivity (from package com.huawei.cloud) 74 | // + Using main activity com.huawei.compass.MainActivity (from package com.huawei.compass) 75 | // + Using main activity com.huawei.fans.SplashActivity (from package com.huawei.fans) 76 | // + Using main activity com.huawei.gameassistant.views.GameassistantMainActivity (from package com.huawei.gameassistant) 77 | // + Using main activity com.huawei.health.MainActivity (from package com.huawei.health) 78 | // + Using main activity com.huawei.hwvplayer.framework.MainActivity (from package com.huawei.hwvplayer.youku) 79 | // + Using main activity com.huawei.lifeservice.basefunction.ui.entry.LandingActivity (from package com.huawei.lives) 80 | // + Using main activity com.huawei.mirror.Mirror (from package com.huawei.mirror) 81 | // + Using main activity com.huawei.mycenter.module.main.view.MainActivity (from package com.huawei.mycenter) 82 | // + Using main activity com.huawei.phoneservice.LaunchActivity (from package com.huawei.phoneservice) 83 | // + Using main activity com.huawei.smarthome.login.LauncherActivity (from package com.huawei.smarthome) 84 | // + Using main activity com.huawei.systemmanager.mainscreen.MainScreenActivity (from package com.huawei.systemmanager) 85 | // + Using main activity com.huawei.wallet.view.MainActivity (from package com.huawei.wallet) 86 | // + Using main activity com.microsoft.translator.activity.SplashActivity (from package com.microsoft.translator) 87 | // + Using main activity com.vmall.client.splash.fragment.SplashActivity (from package com.vmall.client) 88 | // + Using main activity cn.wps.moffice.documentmanager.PreStartActivity (from package cn.wps.moffice_eng) 89 | // + Using main activity com.UCMobile.main.UCMobile (from package com.UCMobile) 90 | // + Using main activity com.alibaba.android.rimet.biz.LaunchHomeActivity (from package com.alibaba.android.rimet) 91 | // + Using main activity com.autonavi.map.activity.SplashActivity (from package com.autonavi.minimap) 92 | // + Using main activity com.baidu.carlife.CarlifeActivity (from package com.baidu.carlife) 93 | // + Using main activity com.baidu.searchbox.SplashActivity (from package com.baidu.searchbox) 94 | // + Using main activity com.benqu.wuta.activities.splash.SplashActivity (from package com.benqu.wuta) 95 | // + Using main activity com.booking.startup.HomeActivity (from package com.booking) 96 | // + Using main activity com.buscode.whatsinput.SettingsActivity (from package com.buscode.whatsinput) 97 | // + Using main activity com.chery.telematicnew.login.activity.TspLoadingActivity (from package com.chery.zhiyunhulian.assistant) 98 | // + Using main activity com.eg.android.AlipayGphone.AlipayLogin (from package com.eg.android.AlipayGphone) 99 | // + Using main activity com.github.uiautomator.MainActivity (from package com.github.uiautomator) 100 | // + Using main activity com.jingdong.app.mall.main.MainActivity (from package com.jingdong.app.mall) 101 | // + Using main activity com.netease.cloudmusic.activity.LoadingActivity (from package com.netease.cloudmusic) 102 | // + Using main activity com.qiyi.video.WelcomeActivity (from package com.qiyi.video) 103 | // + Using main activity com.sina.news.module.launch.activity.PowerOnScreen (from package com.sina.news) 104 | // + Using main activity com.sina.weibo.SplashActivity (from package com.sina.weibo) 105 | // + Using main activity com.ss.android.article.news.activity.LaunchActivity (from package com.ss.android.article.news) 106 | // + Using main activity com.ss.android.ugc.aweme.splash.AliasSplashActivity (from package com.ss.android.ugc.aweme) 107 | // + Using main activity com.suning.mobile.ebuy.host.InitialActivity (from package com.suning.mobile.ebuy) 108 | // + Using main activity com.eg.android.AlipayGphone.AlipayLogin (from package com.taobao.mobile.dipei) 109 | // + Using main activity com.taobao.tao.welcome.Welcome (from package com.taobao.taobao) 110 | // + Using main activity com.tencent.mm.ui.LauncherUI (from package com.tencent.mm) 111 | // + Using main activity com.tianyancha.skyeye.activity.SplashActivity (from package com.tianyancha.skyeye) 112 | // + Using main activity com.wuba.activity.launch.LaunchActivity (from package com.wuba) 113 | // + Using main activity com.ximalaya.ting.android.host.activity.WelComeActivity (from package com.ximalaya.ting.android) 114 | // + Using main activity com.xingin.xhs.preload.EmptySplashActivity (from package com.xingin.xhs) 115 | // + Using main activity ctrip.android.view.view.CtripDeclarationActivity (from package ctrip.android.view) 116 | // Selecting main activities from category android.intent.category.MONKEY 117 | // + Using main activity com.android.emergency.view.ViewInfoActivity (from package com.android.emergency) 118 | // + Using main activity com.android.phone.MobileNetworkSettings (from package com.android.phone) 119 | // + Using main activity com.android.phone.MSimCallFeaturesSetting (from package com.android.phone) 120 | // + Using main activity com.android.phone.MSimMobileNetworkSettings (from package com.android.phone) 121 | // + Using main activity com.android.phone.CallFeaturesSetting (from package com.android.phone) 122 | // + Using main activity com.android.settings.Settings$RunningServicesActivity (from package com.android.settings) 123 | // + Using main activity com.android.settings.Settings$StorageUseActivity (from package com.android.settings) 124 | // + Using main activity com.huawei.android.launcher.unihome.UniHomeLauncher (from package com.huawei.android.launcher) 125 | // + Using main activity com.huawei.dsdscardmanager.HWCardManagerActivity (from package com.huawei.dsdscardmanager) 126 | // + Using main activity com.huawei.parentcontrol.ui.activity.HomeActivity (from package com.huawei.parentcontrol) 127 | // + Using main activity com.android.emergency.settings.SosEmergencyContactSettings (from package com.android.emergency) 128 | // + Using main activity com.huawei.KoBackup.InitializeActivity (from package com.huawei.KoBackup) 129 | // + Using main activity com.huawei.android.FloatTasks.settings.FloatTaskSuggestionSettings (from package com.huawei.android.FloatTasks) 130 | // Seeded: 1609750349748 131 | // Event percentages: 132 | // 0: 50.0% 133 | // 1: 50.0% 134 | // 2: 0.0% 135 | // 3: 0.0% 136 | // 4: -0.0% 137 | // 5: -0.0% 138 | // 6: 0.0% 139 | // 7: 0.0% 140 | // 8: 0.0% 141 | // 9: 0.0% 142 | // 10: 0.0% 143 | // 11: 0.0% 144 | :Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.phone/.CallFeaturesSetting;end 145 | // Allowing start of Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.phone/.CallFeaturesSetting } in package com.android.phone 146 | Sleeping for 300 milliseconds 147 | // Allowing start of Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.phone/.MSimCallFeaturesSetting } in package com.android.phone 148 | // activityResuming(com.android.phone) 149 | :Sending Touch (ACTION_DOWN): 0:(765.0,1212.0) 150 | :Sending Touch (ACTION_MOVE): 0:(756.03094,1209.3081) 151 | :Sending Touch (ACTION_MOVE): 0:(753.03064,1204.2982) 152 | :Sending Touch (ACTION_MOVE): 0:(749.9698,1198.3247) 153 | :Sending Touch (ACTION_MOVE): 0:(738.19965,1194.9695) 154 | :Sending Touch (ACTION_MOVE): 0:(732.8324,1194.7056) 155 | :Sending Touch (ACTION_UP): 0:(722.15656,1192.5432) 156 | Sleeping for 300 milliseconds 157 | :Sending Touch (ACTION_DOWN): 0:(374.0,598.0) 158 | :Sending Touch (ACTION_MOVE): 0:(391.04916,599.2212) 159 | Events injected: 10 160 | :Sending rotation degree=0, persist=false 161 | :Dropped: keys=0 pointers=0 trackballs=0 flips=0 rotations=0 162 | ## Network stats: elapsed time=657ms (0ms mobile, 0ms wifi, 657ms not connected) 163 | // Monkey finished 164 | -------------------------------------------------------------------------------- /8KE0220325004764/blacklist.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/8KE0220325004764/blacklist.txt -------------------------------------------------------------------------------- /8KE0220325004764/packagelist.txt: -------------------------------------------------------------------------------- 1 | com.huawei.scenepack 2 | com.huawei.hifolder 3 | com.android.cts.priv.ctsshim 4 | com.huawei.camera 5 | com.huawei.android.tips 6 | com.huawei.camerakit.impl 7 | com.huawei.synergy 8 | com.huawei.android.launcher 9 | com.android.mediacenter 10 | com.android.providers.telephony 11 | com.unionpay.tsmservice 12 | com.huawei.hwdcallservice 13 | com.huawei.androidx 14 | com.huawei.android.UEInfoCheck 15 | com.android.providers.calendar 16 | com.huawei.featurelayer.featureframework 17 | com.huawei.health 18 | com.huawei.hicard 19 | com.huawei.hidisk 20 | com.huawei.hiview 21 | com.huawei.hwddmp 22 | com.huawei.iaware 23 | com.huawei.bluetooth 24 | com.android.providers.media 25 | com.huawei.android.thememanager 26 | com.huawei.android.chr 27 | com.huawei.android.hsf 28 | com.google.android.onetimeinitializer 29 | com.google.android.ext.shared 30 | com.android.wallpapercropper 31 | com.huawei.mirror 32 | com.huawei.imedia.dolby 33 | com.huawei.nearby 34 | com.huawei.android.FloatTasks 35 | com.huawei.desktop.systemui 36 | com.huawei.intelligent 37 | com.huawei.motionservice 38 | com.huawei.appmarket 39 | com.huawei.recsys 40 | com.xy.smartmmsplugin.remote 41 | com.huawei.parentcontrol 42 | com.huawei.search 43 | com.huawei.secime 44 | com.android.documentsui 45 | com.huawei.numberidentity 46 | com.android.externalstorage 47 | com.huawei.multimedia.audioengine 48 | com.sohu.sohuvideo.emplayer 49 | com.android.htmlviewer 50 | com.android.companiondevicemanager 51 | com.huawei.regservice 52 | com.iflytek.speechsuite 53 | com.android.mms.service 54 | com.huawei.wallet 55 | com.huawei.android.totemweather 56 | com.android.providers.downloads 57 | com.huawei.gameassistant 58 | com.android.networkstack.inprocess 59 | com.huawei.android.findmyphone 60 | com.huawei.trustcircle 61 | com.huawei.fastapp 62 | com.huawei.phoneservice 63 | com.huawei.desktop.explorer 64 | com.huawei.screenrecorder 65 | com.huawei.arengine.service 66 | com.huawei.hwpolicyservice 67 | com.huawei.spaceservice 68 | com.huawei.videoeditor 69 | com.huawei.securitymgr 70 | com.google.android.configupdater 71 | com.android.soundrecorder 72 | com.huawei.iconnect 73 | com.huawei.android.AutoRegSms 74 | com.huawei.dsdscardmanager 75 | com.google.android.overlay.settingsProvider 76 | com.huawei.KoBackup 77 | com.huawei.hwvplayer.youku 78 | com.huawei.hiviewtunnel 79 | com.google.ar.core 80 | com.android.providers.downloads.ui 81 | com.android.vending 82 | com.android.pacprocessor 83 | com.android.simappdialog 84 | com.android.systemui.overlay 85 | com.huawei.smarthome 86 | com.huawei.smartshot 87 | androidhwext 88 | com.huawei.pengine 89 | com.baidu.input_huawei 90 | com.android.frameworkhwext.honor 91 | com.android.modulemetadata 92 | com.android.certinstaller 93 | com.huawei.phone.recorder 94 | com.android.carrierconfig 95 | com.huawei.wallet.sdk.walletsdk 96 | com.google.android.marvin.talkback 97 | android 98 | com.huawei.fans 99 | com.huawei.hiai 100 | com.huawei.hwid 101 | com.huawei.msdp 102 | com.huawei.tips 103 | com.android.contacts 104 | com.huawei.contentsensor 105 | com.huawei.systemserver 106 | com.huawei.mycenter 107 | com.android.mms 108 | com.android.mtp 109 | com.android.nfc 110 | com.android.ons 111 | com.android.stk 112 | com.huawei.livewallpaper.paradise 113 | com.android.backupconfirm 114 | com.huawei.calculator 115 | com.huawei.android.instantshare 116 | com.huawei.himovie.partner1 117 | com.huawei.himovie.partner2 118 | com.huawei.trustagent 119 | com.huawei.profile 120 | com.huawei.trustspace 121 | com.android.statementservice 122 | com.huawei.printservice 123 | com.huawei.android.internal.app 124 | com.huawei.gamebox 125 | com.huawei.cloud 126 | com.huawei.dmsdp 127 | com.huawei.magazine 128 | com.huawei.hicar 129 | com.huawei.hwasm 130 | com.huawei.lives 131 | com.android.calendar 132 | com.huawei.wifiprobqeservice 133 | com.huawei.remotepassword 134 | com.huawei.smartlocation 135 | com.android.providers.settings 136 | com.android.sharedstoragebackup 137 | com.android.printspooler 138 | com.android.frameworkres.overlay 139 | com.huawei.featurelayer.sharedfeature.map 140 | com.android.dreams.basic 141 | com.android.incallui 142 | com.huawei.systemmanager 143 | com.huawei.nb.service 144 | com.android.se 145 | com.huawei.hwpanpayservice 146 | com.android.inputdevices 147 | com.huawei.browser 148 | com.huawei.securityserver 149 | com.huawei.controlcenter 150 | com.hicloud.android.clone 151 | com.google.android.overlay.gmsconfig 152 | com.huawei.contacts.sync 153 | com.huawei.hilink.framework 154 | com.android.cellbroadcastreceiver 155 | com.google.android.webview 156 | com.huawei.HwMultiScreenShot 157 | com.huawei.vassistant 158 | com.android.server.telecom 159 | com.huawei.hwdockbar 160 | com.google.android.syncadapters.contacts 161 | com.example.android.notepad 162 | com.android.keychain 163 | com.microsoft.translator 164 | com.huawei.languagedownloader 165 | com.android.gallery3d 166 | com.google.android.gms 167 | com.google.android.gsf 168 | android.ext.services 169 | com.huawei.hwstartupguide 170 | com.android.calllogbackup 171 | com.google.android.partnersetup 172 | com.huawei.fido.uafclient 173 | com.android.localtransport 174 | com.android.packageinstaller 175 | com.huawei.hwireader 176 | com.android.proxyhandler 177 | com.huawei.contactscamcard 178 | com.google.android.printservice.recommendation 179 | com.android.managedprovisioning 180 | com.huawei.hicloud 181 | com.huawei.himovie 182 | com.huawei.hitouch 183 | com.huawei.compass 184 | com.android.dreams.phototable 185 | com.huawei.coauthservice 186 | com.huawei.vrservice 187 | com.huawei.securitypluginbase 188 | com.huawei.android.hwaps 189 | com.huawei.android.hwouc 190 | com.huawei.android.wfdft 191 | com.tencent.qqlivehuawei 192 | com.android.wallpaper.livepicker 193 | com.huawei.mmitest 194 | com.android.apps.tag 195 | com.huawei.powergenie 196 | com.huawei.behaviorauth 197 | com.huawei.android.instantonline 198 | com.android.storagemanager 199 | com.android.settings 200 | com.android.networkstack.permissionconfig 201 | com.vmall.client 202 | com.huawei.deviceauth 203 | com.huawei.pcassistant 204 | com.huawei.android.projectmenu 205 | com.android.cts.ctsshim 206 | com.huawei.android.pushagent 207 | com.android.vpndialogs 208 | com.huawei.android.hwupgradeguide 209 | com.android.email 210 | com.android.phone 211 | com.android.shell 212 | com.android.wallpaperbackup 213 | com.android.providers.blockednumber 214 | com.android.providers.userdictionary 215 | com.android.emergency 216 | com.huawei.scanner 217 | com.huawei.suggestion 218 | com.android.location.fused 219 | com.android.deskclock 220 | com.android.systemui 221 | com.huawei.hwblockchain 222 | com.android.bluetoothmidiservice 223 | com.huawei.assetsync 224 | com.huawei.hwdetectrepair 225 | com.android.permissioncontroller 226 | com.huawei.hwvoipservice 227 | com.huawei.hwusbearphoneupdate 228 | com.huawei.android.karaoke 229 | com.huawei.game.kitserver 230 | com.huawei.bd 231 | com.huawei.ca 232 | com.huawei.skytone 233 | com.huawei.assetsyncservice 234 | com.huawei.hiskytone 235 | com.huawei.aod 236 | com.huawei.hff 237 | com.huawei.ims 238 | com.huawei.lbs 239 | com.android.bluetooth 240 | com.android.providers.contacts 241 | com.android.captiveportallogin 242 | com.huawei.hiaction 243 | com.huawei.android.airsharing 244 | com.huawei.rcsserviceapplication 245 | com.github.uiautomator 246 | com.qiyi.video 247 | com.benqu.wuta 248 | com.sankuai.meituan 249 | com.github.uiautomator.test 250 | com.xingin.xhs 251 | com.tencent.mm 252 | com.chery.zhiyunhulian.assistant 253 | com.baidu.searchbox 254 | com.wuba 255 | com.taobao.mobile.dipei 256 | com.ss.android.article.news 257 | com.buscode.whatsinput 258 | com.UCMobile 259 | com.ss.android.ugc.aweme 260 | com.suning.mobile.ebuy 261 | com.baidu.carlife 262 | com.ximalaya.ting.android 263 | com.jingdong.app.mall 264 | com.autonavi.minimap 265 | ctrip.android.view 266 | com.alibaba.android.rimet 267 | com.booking 268 | com.tianyancha.skyeye 269 | com.sina.weibo 270 | com.taobao.taobao 271 | com.netease.cloudmusic 272 | com.sina.news 273 | com.eg.android.AlipayGphone 274 | cn.wps.moffice_eng 275 | com.achievo.vipshop 276 | -------------------------------------------------------------------------------- /8KE0220325004764/whitelist.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/8KE0220325004764/whitelist.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 TonsenWei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SVmonkey 2 | 3 | A Tool use for running android monkey test on PC. 4 | 5 | 6 | 7 | # 一、简介 8 | Android系统自带的monkey测试,一般用于测试Android系统上各个app的稳定性,对各个界面发送随机事件,检测是否会产生CRASH或ANR等异常。 9 | 详情可访问谷歌官网了解: [UI/Application Exerciser Monkey](https://developer.android.com/studio/test/monkey?hl=zh-cn) 10 | 11 | 目前测试人员进行monkey测试一般在终端输入一长串指令,或者把指令写成一个脚本,指令有改变需要打开脚本修改脚本参数再执行,有些麻烦和低效。 12 | 该工具简化Monkey执行步骤,简化参数配置。 13 | 14 | ![](project-files/image/MonkeyHome.png) 15 | 16 | 测试过程监控ANR和CRASH以及其他EXCEPTION。 17 | 18 | ![](project-files/image/MonkeyAreas.png) 19 | 20 | 实时保存logcat信息。 21 | 22 | 测试结束可以导出Excel格式的测试报告,统计发生ANR和CRASH数量以及发生的包名。 23 | 24 | # 二、使用准备 25 | 26 | 1. java语言编写,依赖java环境,运行环境为windows 27 | 28 | 2. 依赖adb,把adb配置为环境变量 29 | 30 | 3. 添加依赖的jar包 31 | 32 | ![](project-files/image/MonkeyJars.png) 33 | 34 | 4. 打包成jar后使用java命令运行 35 | 36 | java -jar SVmonkey.jar 37 | 38 | 5. 也可以把jar打包成exe后双击运行 39 | 40 | # 三、执行monkey测试 41 | 42 | ![](project-files/image/MonkeyRun.png) 43 | 44 | * 随机测试:查找设备后,配置monkey参数,在待测设备上右键,点击随机测试。(不指定应用程序包,进行随机测试) 45 | 46 | * 指定包测试:到所有应用、系统应用等子列表下,右键,点击“对此程序包进行测试” 47 | 48 | * 白名单测试:待测设备ID上右键->开始白名单测试(需要先往白名单中添加应用程序包) 49 | 50 | * 黑名单测试:待测设备ID上右键->开始黑名单测试(需要先往黑名单中添加应用程序包) 51 | 52 | * 顺序名单测试:待测设备ID上右键->开始顺序名单测试(需要先往名单中添加应用程序包),以当前的配置对该名单中每个包依次进行测试) 53 | 54 | # 四、测试报告 55 | 56 | 测试结束,右键对应的测试记录,即可导出报告到excel。 57 | 58 | ![](project-files/image/MonkeyExportReport.png) 59 | 60 | 61 | ![](project-files/image/MonkeyExcelReport.png) 62 | 63 | # 五、其他功能 64 | 65 | * 安装apk:在对应设备的第三方应用上右键->安装第三方apk,支持多选安装。 66 | 67 | ![](project-files/image/MonkeyInstallApps.png) 68 | 69 | * 卸载第三方应用:在对应设备下的第三方应用下在待测应用上右键->卸载该应用。 70 | 71 | ![](project-files/image/MonkeyUninstallApps.png) 72 | 73 | * 解析已有monkey日志:在测试记录表格中右键->解析Monkey日志,能解析使用命令行执行monkey命令时重定向输出的monkey日志,解析后会把解析结果作为一次monkey测试记录添加到表格中。 74 | 75 | ![](project-files/image/MonkeyAnalyzeLog.png) 76 | 77 | * 导出和导入monkey参数:配置好monkey参数后,右键待测设备,即可导出该配置,或导入新的配置。 78 | (配置文件后缀“.properties” ) 79 | 80 | ![](project-files/image/MonkeyImportExportConfig.png) 81 | 82 | 83 | 84 | # 六、参数说明 85 | 86 | 87 | --ignore-crashes: 是否在出现crash时继续测试。 88 | 89 | --ignore-timeouts: 是否在出现anr时继续测试。 90 | 91 | --ignore-security-exceptions: 是否在出现指定当应用程序发生许可错误时(如证书许可,网络许可等)继续测试。 92 | 93 | --monitor-native-crash:是否监视本地代码crash事件。 94 | 95 | --ignore-native-crashes:是否忽略本地代码crash事件继续测试(监视本地代码crash事件前提下可用)。 96 | 97 | --permission-target-system:是否是运行时权限系统(android6.0及以上) 98 | 99 | Throttle Millisec: 两个事件之间发送的时间间隔,单位毫秒。 100 | 101 | Log Level: 输出日志等级,由低到高分别为-v , -v -v , -v -v -v 102 | 103 | Seed: 随机事件种子值(用于产生随机事件序列)。 104 | 105 | Event Count: monkey测试总事件数。 106 | 107 | 108 | 109 | ## *注意:如果配置了事件百分比,则所有事件的百分比都应该填写,且所有事件加起来的值应该为100%* 110 | 111 | 112 | 113 | Trackball Percent: 轨迹事件的百分比(轨迹事件由一个或几个随机的移动组成,有时还伴随有点击)。 114 | 115 | AppSwitch Percent: Activity启动事件百分比。 116 | 117 | Flip Percent: 键盘翻转事件百分比。 118 | 119 | Touch Percent: 触摸事件的百分比(触摸事件是一个down-up事件)。 120 | 121 | SysKeys Percent: 系统按键事件的百分比(这些按键通常被保留,由系统使用,如Home、Back、Start Call、End Call及音量控制键) 122 | 123 | AnyEvent Percent: 其它类型事件的百分比。它包罗了所有其它类型的事件,如:按键、其它不常用的设备按钮、等等(这个按键可能会按到power,因此可考虑设置为0)。 124 | 125 | PinchZoon Percent: 缩放事件百分比。 126 | 127 | Nav Percent: 基本导航事件的百分比(导航事件由来自方向输入设备的up/down/left/right组成)。 128 | 129 | MajorNav Percent: 主要导航事件的百分比(这些导航事件通常引发图形界面中的动作,如:5-way键盘的中间按键、回退按键、菜单按键)。 130 | 131 | Motion Percent: 滑动事件的百分比(滑动事件由屏幕上某处的一个down事件、一系列的伪随机事件和一个up事件组成)。 132 | 133 | Permission Percent: 权限百分比,申请运行时权限事件的百分比 134 | 135 | # 七、常见问题 136 | 137 | 一、没有root的设备可能会有如下错误导致monkey提前结束,root设备即可。Root方法:选中设备后按下 Ctrl + R(一般不需要root,很多设备也不能root). 138 | 139 | 或者设备连接电脑时,允许运行USB调试。 140 | 141 | ![](project-files/image/MonkeyRoot.png) 142 | 143 | 二、monkey参数中所有事件的百分比之和必须等于100%,或者不填,否则执行失败(提示如下图中错误信息)。 144 | 145 | ![](project-files/image/MonkeyEventWeights1.png) ![](project-files/image/MonkeyEventWeights2.png) 146 | 147 | 三、设备软件信息获取失败显示未知:请尝试root后再点击识别设备。(一般不需要root,很多设备也不能root). 148 | 149 | 四、停止monkey测试需要待测设备支持,在adb shell中执行ps | grep monkey能找到monkey进程,否则不支持结束该设备的monkey测试,需要手动停止(重启待测设备,有可能还需要重启SVmonkey)。 150 | 151 | 152 | 153 | # 八、编译打包好的可执行文件下载 154 | 155 | 微信扫码关注公众号输入 monkey 或者 猴子 即可获取试用 156 | 157 | ![](project-files/image/公众号二维码.jpg) 158 | -------------------------------------------------------------------------------- /bin/images/monkey.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/bin/images/monkey.ico -------------------------------------------------------------------------------- /project-files/image/MonkeyAnalyzeLog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyAnalyzeLog.png -------------------------------------------------------------------------------- /project-files/image/MonkeyAreas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyAreas.png -------------------------------------------------------------------------------- /project-files/image/MonkeyEventWeights1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyEventWeights1.png -------------------------------------------------------------------------------- /project-files/image/MonkeyEventWeights2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyEventWeights2.png -------------------------------------------------------------------------------- /project-files/image/MonkeyExcelReport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyExcelReport.png -------------------------------------------------------------------------------- /project-files/image/MonkeyExportReport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyExportReport.png -------------------------------------------------------------------------------- /project-files/image/MonkeyHome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyHome.png -------------------------------------------------------------------------------- /project-files/image/MonkeyImportExportConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyImportExportConfig.png -------------------------------------------------------------------------------- /project-files/image/MonkeyInstallApps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyInstallApps.png -------------------------------------------------------------------------------- /project-files/image/MonkeyJars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyJars.png -------------------------------------------------------------------------------- /project-files/image/MonkeyRoot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyRoot.png -------------------------------------------------------------------------------- /project-files/image/MonkeyRun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyRun.png -------------------------------------------------------------------------------- /project-files/image/MonkeyUninstallApps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/MonkeyUninstallApps.png -------------------------------------------------------------------------------- /project-files/image/公众号二维码.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/project-files/image/公众号二维码.jpg -------------------------------------------------------------------------------- /src/com/demo/XSSFUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.demo; 5 | 6 | import java.awt.Color; 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import org.apache.poi.ss.usermodel.Cell; 14 | import org.apache.poi.ss.usermodel.Row; 15 | import org.apache.poi.ss.util.CellRangeAddress; 16 | import org.apache.poi.util.Units; 17 | import org.apache.poi.xssf.usermodel.XSSFChart; 18 | import org.apache.poi.xssf.usermodel.XSSFClientAnchor; 19 | import org.apache.poi.xssf.usermodel.XSSFColor; 20 | import org.apache.poi.xssf.usermodel.XSSFDrawing; 21 | import org.apache.poi.xssf.usermodel.XSSFSheet; 22 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 23 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; 24 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTBarChart; 25 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTBarSer; 26 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTBoolean; 27 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx; 28 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; 29 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTDLbls; 30 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTLegend; 31 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTLineChart; 32 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTLineSer; 33 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTMarker; 34 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTMarkerStyle; 35 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; 36 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumRef; 37 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; 38 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTScaling; 39 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx; 40 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrRef; 41 | import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx; 42 | import org.openxmlformats.schemas.drawingml.x2006.chart.STAxPos; 43 | import org.openxmlformats.schemas.drawingml.x2006.chart.STBarDir; 44 | import org.openxmlformats.schemas.drawingml.x2006.chart.STBarGrouping; 45 | import org.openxmlformats.schemas.drawingml.x2006.chart.STCrosses; 46 | import org.openxmlformats.schemas.drawingml.x2006.chart.STDispBlanksAs; 47 | import org.openxmlformats.schemas.drawingml.x2006.chart.STGrouping; 48 | import org.openxmlformats.schemas.drawingml.x2006.chart.STLblAlgn; 49 | import org.openxmlformats.schemas.drawingml.x2006.chart.STLegendPos; 50 | import org.openxmlformats.schemas.drawingml.x2006.chart.STMarkerStyle; 51 | import org.openxmlformats.schemas.drawingml.x2006.chart.STOrientation; 52 | import org.openxmlformats.schemas.drawingml.x2006.chart.STTickLblPos; 53 | import org.openxmlformats.schemas.drawingml.x2006.chart.STTickMark; 54 | 55 | import com.desay.utils.PoiUtils; 56 | 57 | /** 58 | * @author 韦冬成 E-mail: 470029260@qq.com 59 | * @version 创建时间:2020年8月21日 上午10:14:08 60 | * 类说明 61 | */ 62 | public class XSSFUtils { 63 | 64 | /** 65 | * @param args 66 | */ 67 | public static void main(String[] args) { 68 | //创建excel工作薄 69 | XSSFWorkbook workbook = new XSSFWorkbook(); 70 | //创建一个工作表sheet 71 | XSSFSheet sheetResult = workbook.createSheet("SVMonkeyResult"); 72 | testForLineChart(sheetResult); 73 | try { 74 | PoiUtils.outputXlsxToFile(workbook, "D:\\xssfUtils.xlsx"); 75 | } catch (IOException e) { 76 | // TODO Auto-generated catch block 77 | e.printStackTrace(); 78 | } 79 | } 80 | 81 | public static void testForLineChart(XSSFSheet sheet) { 82 | Map arams = new HashMap(); 83 | 84 | // 图表位置(B36左上角:AA53左上角),偏移量均为0 85 | int[] chartPositon = new int[] {getColumnIndexByAddress("B"), 35, getColumnIndexByAddress("AA"), 52, 0, 0, 0, 0}; 86 | 87 | // 设置x轴坐标区域(B60:B90),即“Day”列 88 | int[] xAxisRange = new int[] {59, 89, XSSFUtils.getColumnIndexByAddress("B"), XSSFUtils.getColumnIndexByAddress("B")}; 89 | 90 | // 设置数据区域,即每个系列的数据(D60:D90、J60:J90、P60:P90) 91 | for (int i = 0; i < 90; i++) { 92 | Row row = sheet.createRow(i); 93 | Cell cellD = row.createCell(3); 94 | cellD.setCellValue(i*5); 95 | 96 | Cell cellJ = row.createCell(9);//0A 1B 2C 3D 4e 5f 6g 7h 8i 9j 10k 11l 12m 13n 14o 15p 16q 17r 97 | cellJ.setCellValue(i*5 + 10); 98 | 99 | Cell cellP = row.createCell(15); 100 | cellP.setCellValue(i*5 + 50); 101 | } 102 | // 图例标题(D59、J59、P59) 103 | List seriesRangeList = new ArrayList(); 104 | seriesRangeList.add(new int[] { 105 | 58, 58, XSSFUtils.getColumnIndexByAddress("D"), XSSFUtils.getColumnIndexByAddress("D"), 106 | 59, 89, XSSFUtils.getColumnIndexByAddress("D"), XSSFUtils.getColumnIndexByAddress("D")}); 107 | seriesRangeList.add(new int[] { 108 | 58, 58, XSSFUtils.getColumnIndexByAddress("J"), XSSFUtils.getColumnIndexByAddress("J"), 109 | 59, 89, XSSFUtils.getColumnIndexByAddress("J"), XSSFUtils.getColumnIndexByAddress("J")}); 110 | seriesRangeList.add(new int[] { 111 | 58, 58, XSSFUtils.getColumnIndexByAddress("P"), XSSFUtils.getColumnIndexByAddress("P"), 112 | 59, 89, XSSFUtils.getColumnIndexByAddress("P"), XSSFUtils.getColumnIndexByAddress("P")}); 113 | 114 | arams.put("chartPosition", chartPositon); 115 | arams.put("chartTitle", ""); 116 | arams.put("dispBlanksAs", "zero"); 117 | arams.put("legendPosition", "t"); 118 | arams.put("xAxisDataCellRange", xAxisRange); 119 | arams.put("seriesDataCellRangeList", seriesRangeList); 120 | arams.put("lineColor", null); 121 | arams.put("isXAxisDelete", false); 122 | arams.put("yAxisPosition", "l"); 123 | 124 | createLineChart(sheet, arams); 125 | } 126 | 127 | public static void testForBarChart(XSSFSheet sheet) { 128 | Map params = new HashMap(); 129 | 130 | // 图表位置(G20自左上角向右偏移50个EMU point:N39自左上角向右偏移50个EMU point) 131 | int[] chartPositon = new int[] {XSSFUtils.getColumnIndexByAddress("G"), 19, XSSFUtils.getColumnIndexByAddress("N"), 38, 50, 0, 50, 0}; 132 | 133 | // 图表标题(A9) 134 | String chartTitle = sheet.getRow(8).getCell(XSSFUtils.getColumnIndexByAddress("A")).getStringCellValue(); 135 | 136 | // 设置x轴坐标区域(C8:N8),即“表头月份”行 137 | int[] xAxisRange = new int[] {7, 7, XSSFUtils.getColumnIndexByAddress("C"), XSSFUtils.getColumnIndexByAddress("N")}; 138 | 139 | // 设置数据区域,即每个系列的数据(C11:N11) 140 | // 图例标题(B11) 141 | List seriesRangeList = new ArrayList(); 142 | seriesRangeList.add( 143 | new int[] { 144 | 10, 10, XSSFUtils.getColumnIndexByAddress("B"), XSSFUtils.getColumnIndexByAddress("B"), 145 | 10, 10, XSSFUtils.getColumnIndexByAddress("C"), XSSFUtils.getColumnIndexByAddress("N")}); 146 | 147 | params.put("chartPosition", chartPositon); 148 | params.put("chartTitle", chartTitle); 149 | params.put("dispBlanksAs", "gap"); 150 | params.put("legendPosition", "r"); 151 | params.put("xAxisDataCellRange", xAxisRange); 152 | params.put("seriesDataCellRangeList", seriesRangeList); 153 | params.put("barColor", new XSSFColor(new Color(247, 150, 70))); 154 | 155 | createBarChart(sheet, params); 156 | } 157 | 158 | public static void testForComboChart(XSSFSheet sheet) { 159 | Map params = new HashMap(); 160 | 161 | // 图表位置(A20自左上角向右偏移10个EMU point:G39自左上角向右偏移30个EMU point) 162 | int[] chartPosition = 163 | new int[] {XSSFUtils.getColumnIndexByAddress("A"), 19, XSSFUtils.getColumnIndexByAddress("G"), 38, 10, 0, 30, 0}; 164 | 165 | // 图表标题(A9) 166 | String chartTitle = sheet.getRow(8).getCell(XSSFUtils.getColumnIndexByAddress("A")).getStringCellValue(); 167 | 168 | // 设置x轴坐标区域(C8:N8),即“表头月份”行 169 | int[] xAxisRange = new int[] {7, 7, XSSFUtils.getColumnIndexByAddress("C"), XSSFUtils.getColumnIndexByAddress("N")}; 170 | 171 | // 柱状图设置数据区域,即每个系列的数据(C9:N9) 172 | // 图例标题(B9) 173 | List barSeriesRangeList = new ArrayList(); 174 | barSeriesRangeList.add( 175 | new int[] { 176 | 8, 8, XSSFUtils.getColumnIndexByAddress("B"), XSSFUtils.getColumnIndexByAddress("B"), 177 | 8, 8, XSSFUtils.getColumnIndexByAddress("C"), XSSFUtils.getColumnIndexByAddress("N")}); 178 | 179 | // 折线图设置数据区域,即每个系列的数据(C10:N10) 180 | // 图例标题(B10) 181 | List lineSeriesRangeList = new ArrayList(); 182 | lineSeriesRangeList.add( 183 | new int[] { 184 | 9, 9, XSSFUtils.getColumnIndexByAddress("B"), XSSFUtils.getColumnIndexByAddress("B"), 185 | 9, 9, XSSFUtils.getColumnIndexByAddress("C"), XSSFUtils.getColumnIndexByAddress("N")}); 186 | 187 | params.put("chartPosition", chartPosition); 188 | params.put("chartTitle", chartTitle); 189 | params.put("dispBlanksAs", "gap"); 190 | params.put("legendPosition", "r"); 191 | params.put("barXAxisDataCellRange", xAxisRange); 192 | params.put("barSeriesDataCellRangeList", barSeriesRangeList); 193 | params.put("barColor", new XSSFColor(new Color(79, 129, 189))); 194 | params.put("lineXAxisDataCellRange", xAxisRange); 195 | params.put("lineSeriesDataCellRangeList", lineSeriesRangeList); 196 | params.put("lineColor", new XSSFColor(new Color(190, 75, 72))); 197 | params.put("lineIsXAxisDelete", true); 198 | params.put("lineYAxisPosition", "r"); 199 | 200 | XSSFUtils.createComboChart(sheet, params); 201 | } 202 | 203 | /** 204 | * create line chart 205 | * @param sheet 206 | * @param params 207 | * chartPosition int[]{startCol, startRow, endCol, endRow, 208 | * xOffsetInStartCell, yOffsetInStartCell, 209 | * xOffsetInEndCell, yOffsetInEndCell} 210 | * chartTitle String 211 | * xAxisDataCellRange int[]{startRow, endRow, startCol, endCol} 212 | * seriesDataCellRangeList List 215 | * lineColor XSSFColor 216 | */ 217 | public static void createLineChart(XSSFSheet sheet, Map params) { 218 | 219 | // 创建绘图区 220 | CTPlotArea ctPlotArea = createCTPlotArea(sheet, params); 221 | 222 | // 绘制图表 223 | createCTLineChart(sheet, ctPlotArea, params); 224 | } 225 | 226 | /** 227 | * create bar chart 228 | * @param sheet 229 | * @param params 230 | * chartPosition int[]{startCol, startRow, endCol, endRow, 231 | * xOffsetInStartCell, yOffsetInStartCell, 232 | * xOffsetInEndCell, yOffsetInEndCell} 233 | * chartTitle String 234 | * xAxisDataCellRange int[]{startRow, endRow, startCol, endCol} 235 | * seriesDataCellRangeList List 238 | * barColor XSSFColor 239 | */ 240 | public static void createBarChart(XSSFSheet sheet, Map params) { 241 | 242 | // 创建绘图区 243 | CTPlotArea ctPlotArea = createCTPlotArea(sheet, params); 244 | 245 | // 绘制图表 246 | createCTBarChart(sheet, ctPlotArea, params); 247 | } 248 | 249 | /** 250 | * create combination chart 251 | * @param sheet 252 | * @param params 253 | * chartPosition int[]{startCol, startRow, endCol, endRow, 254 | * xOffsetInStartCell, yOffsetInStartCell, 255 | * xOffsetInEndCell, yOffsetInEndCell} 256 | * chartTitle String 257 | * barXAxisDataCellRange int[]{startRow, endRow, startCol, endCol} 258 | * barSeriesDataCellRangeList List 261 | * barColor XSSFColor 262 | * lineXAxisDataCellRange int[]{startRow, endRow, startCol, endCol} 263 | * lineSeriesDataCellRangeList List 266 | * lineColor XSSFColor 267 | * lineIsXAxisDelete Boolean 268 | * lineYAxisPosition String 269 | */ 270 | public static void createComboChart(XSSFSheet sheet, Map params) { 271 | 272 | Map barParams = new HashMap(); 273 | Map lineParams = new HashMap(); 274 | barParams.put("xAxisDataCellRange", params.get("barXAxisDataCellRange")); 275 | barParams.put("seriesDataCellRangeList", params.get("barSeriesDataCellRangeList")); 276 | barParams.put("barColor", params.get("barColor")); 277 | lineParams.put("xAxisDataCellRange", params.get("lineXAxisDataCellRange")); 278 | lineParams.put("seriesDataCellRangeList", params.get("lineSeriesDataCellRangeList")); 279 | lineParams.put("lineColor", params.get("lineColor")); 280 | lineParams.put("isXAxisDelete", params.get("lineIsXAxisDelete")); 281 | lineParams.put("yAxisPosition", params.get("lineYAxisPosition")); 282 | 283 | // 创建绘图区 284 | CTPlotArea ctPlotArea = createCTPlotArea(sheet, params); 285 | 286 | // 绘制图表 287 | createCTBarChart(sheet, ctPlotArea, barParams); 288 | createCTLineChart(sheet, ctPlotArea, lineParams); 289 | } 290 | 291 | /** 292 | * create line chart 293 | * @param params 294 | * xAxisDataCellRange int[]{startRow, endRow, startCol, endCol} 295 | * seriesDataCellRangeList List 298 | * lineColor XSSFColor 299 | * isXAxisDelete Boolean 300 | * yAxisPosition String 301 | */ 302 | @SuppressWarnings("unchecked") 303 | private static void createCTLineChart(XSSFSheet sheet, CTPlotArea ctPlotArea, Map params) { 304 | 305 | int[] xAxisDataCellRange = (int[]) params.get("xAxisDataCellRange"); 306 | List seriesDataCellRangeList = (List) params.get("seriesDataCellRangeList"); 307 | XSSFColor lineColor = (XSSFColor) params.get("lineColor"); 308 | Boolean isXAxisDelete = (Boolean) params.get("isXAxisDelete"); 309 | String yAxisPosition = (String) params.get("yAxisPosition"); 310 | 311 | CTLineChart ctLineChart = ctPlotArea.addNewLineChart(); 312 | ctLineChart.addNewGrouping().setVal(STGrouping.STANDARD); 313 | ctLineChart.addNewVaryColors().setVal(true); 314 | 315 | // 绘制每条折线 316 | for (int i = 0; i <= seriesDataCellRangeList.size() - 1; i++) { 317 | int[] seriesDataCellRange = seriesDataCellRangeList.get(i); 318 | 319 | CTLineSer ctLineSer = ctLineChart.addNewSer(); 320 | 321 | // 设置折线的颜色 322 | if (lineColor != null) { 323 | ctLineSer.addNewSpPr().addNewSolidFill().addNewSrgbClr().setVal(lineColor.getRGB()); 324 | } 325 | 326 | CTSerTx ctSerTx = ctLineSer.addNewTx(); 327 | 328 | // 图例标题 329 | CTStrRef ctStrRef = ctSerTx.addNewStrRef(); 330 | ctStrRef.setF(new CellRangeAddress( 331 | seriesDataCellRange[0], seriesDataCellRange[1], seriesDataCellRange[2], seriesDataCellRange[3]) 332 | .formatAsString(sheet.getSheetName(), true)); 333 | ctLineSer.addNewIdx().setVal(i + 1); 334 | 335 | // 设置x坐标区域 336 | CTAxDataSource cttAxDataSource = ctLineSer.addNewCat(); 337 | ctStrRef = cttAxDataSource.addNewStrRef(); 338 | ctStrRef.setF(new CellRangeAddress( 339 | xAxisDataCellRange[0], xAxisDataCellRange[1], xAxisDataCellRange[2], xAxisDataCellRange[3]) 340 | .formatAsString(sheet.getSheetName(), true)); 341 | 342 | // 设置数据区域,即每个系列的数据 343 | CTNumDataSource ctNumDataSource = ctLineSer.addNewVal(); 344 | CTNumRef ctNumRef = ctNumDataSource.addNewNumRef(); 345 | ctNumRef.setF(new CellRangeAddress( 346 | seriesDataCellRange[4], seriesDataCellRange[5], seriesDataCellRange[6], seriesDataCellRange[7]) 347 | .formatAsString(sheet.getSheetName(), true)); 348 | 349 | // 是否为平滑线 350 | CTBoolean addNewSmooth = ctLineSer.addNewSmooth(); 351 | addNewSmooth.setVal(false); 352 | 353 | CTMarker addNewMarker = ctLineSer.addNewMarker(); 354 | CTMarkerStyle addNewSymbol = addNewMarker.addNewSymbol(); 355 | addNewSymbol.setVal(STMarkerStyle.NONE); 356 | } 357 | 358 | int xAxisId = seriesDataCellRangeList.size() + 1 + 10000; 359 | int yAxisId = seriesDataCellRangeList.size() + 2 + 10000; 360 | 361 | ctLineChart.addNewAxId().setVal(xAxisId); 362 | ctLineChart.addNewAxId().setVal(yAxisId); 363 | 364 | // 设置x轴属性 365 | CTCatAx ctCatAx = ctPlotArea.addNewCatAx(); 366 | ctCatAx.addNewAxId().setVal(xAxisId); 367 | CTScaling ctScaling = ctCatAx.addNewScaling(); 368 | ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX); 369 | ctCatAx.addNewDelete().setVal(isXAxisDelete);// 是否显示x轴 370 | ctCatAx.addNewAxPos().setVal(STAxPos.B);// x轴位置(左右上下) 371 | ctCatAx.addNewMajorTickMark().setVal(STTickMark.OUT);// 主刻度线在轴上的位置(内、外、交叉、无) 372 | ctCatAx.addNewMinorTickMark().setVal(STTickMark.NONE);// 次刻度线在轴上的位置(内、外、交叉、无) 373 | ctCatAx.addNewAuto().setVal(true); 374 | ctCatAx.addNewLblAlgn().setVal(STLblAlgn.CTR); 375 | ctCatAx.addNewCrossAx().setVal(yAxisId); 376 | ctCatAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);// 标签(即刻度文字)的位置(轴旁、高、低、无) 377 | ctCatAx.addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal( 378 | new XSSFColor(new Color(134, 134, 134)).getRGB());// x轴颜色 379 | 380 | // 设置y轴属性 381 | CTValAx ctValAx = ctPlotArea.addNewValAx(); 382 | ctValAx.addNewAxId().setVal(yAxisId); 383 | ctScaling = ctValAx.addNewScaling(); 384 | ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX); 385 | ctValAx.addNewDelete().setVal(false);// 是否显示x轴 386 | switch (yAxisPosition) {// y轴位置(左右上下) 387 | case "l" : 388 | ctValAx.addNewAxPos().setVal(STAxPos.L); 389 | ctValAx.addNewCrosses().setVal(STCrosses.MIN);// 纵坐标交叉位置(最大、最小、0、指定某一刻度),也可不用设置,此处如果设置为MAX,则上面设置的左侧失效 390 | break; 391 | case "r" : 392 | ctValAx.addNewAxPos().setVal(STAxPos.R); 393 | ctValAx.addNewCrosses().setVal(STCrosses.MAX); 394 | break; 395 | case "t" : 396 | ctValAx.addNewAxPos().setVal(STAxPos.T); 397 | break; 398 | case "b" : 399 | ctValAx.addNewAxPos().setVal(STAxPos.B); 400 | break; 401 | default : 402 | ctValAx.addNewAxPos().setVal(STAxPos.L); 403 | ctValAx.addNewCrosses().setVal(STCrosses.MIN); 404 | break; 405 | } 406 | ctValAx.addNewMajorTickMark().setVal(STTickMark.OUT);// 主刻度线在轴上的位置(内、外、交叉、无) 407 | ctValAx.addNewMinorTickMark().setVal(STTickMark.NONE);// 次刻度线在轴上的位置(内、外、交叉、无) 408 | ctValAx.addNewCrossAx().setVal(xAxisId); 409 | ctValAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);// 标签(即刻度文字)的位置(轴旁、高、低、无) 410 | ctValAx.addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal( 411 | new XSSFColor(new Color(134, 134, 134)).getRGB());// y轴颜色 412 | ctValAx.addNewMajorGridlines().addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal( 413 | new XSSFColor(new Color(134, 134, 134)).getRGB());// 显示主要网格线,并设置颜色 414 | } 415 | 416 | /** 417 | * create bar chart 418 | * @param params 419 | * xAxisDataCellRange int[]{startRow, endRow, startCol, endCol} 420 | * seriesDataCellRangeList List 423 | * barColor XSSFColor 424 | */ 425 | private static void createCTBarChart(XSSFSheet sheet, CTPlotArea ctPlotArea, Map params) { 426 | 427 | int[] xAxisDataCellRange = (int[]) params.get("xAxisDataCellRange"); 428 | List seriesDataCellRangeList = (List) params.get("seriesDataCellRangeList"); 429 | XSSFColor barColor = (XSSFColor) params.get("barColor"); 430 | 431 | CTBarChart ctBarChart = ctPlotArea.addNewBarChart(); 432 | CTBoolean ctBoolean = ctBarChart.addNewVaryColors(); 433 | ctBarChart.getVaryColors().setVal(true); 434 | ctBarChart.addNewGrouping().setVal(STBarGrouping.CLUSTERED); 435 | ctBarChart.addNewBarDir().setVal(STBarDir.COL); 436 | 437 | for (int i = 0; i <= seriesDataCellRangeList.size() - 1; i++) { 438 | int[] seriesDataCellRange = seriesDataCellRangeList.get(i); 439 | 440 | CTBarSer ctBarSer = ctBarChart.addNewSer(); 441 | 442 | // set bar color 443 | if (barColor != null) { 444 | ctBarSer.addNewSpPr().addNewSolidFill().addNewSrgbClr().setVal(barColor.getRGB()); 445 | } 446 | 447 | CTSerTx ctSerTx = ctBarSer.addNewTx(); 448 | 449 | // legend 450 | CTStrRef ctStrRef = ctSerTx.addNewStrRef(); 451 | ctStrRef.setF(new CellRangeAddress( 452 | seriesDataCellRange[0], seriesDataCellRange[1], seriesDataCellRange[2], seriesDataCellRange[3]) 453 | .formatAsString(sheet.getSheetName(), true)); 454 | ctBarSer.addNewIdx().setVal(i + 1); 455 | 456 | // x 457 | CTAxDataSource ctAxDataSource = ctBarSer.addNewCat(); 458 | ctStrRef = ctAxDataSource.addNewStrRef(); 459 | ctStrRef.setF(new CellRangeAddress( 460 | xAxisDataCellRange[0], xAxisDataCellRange[1], xAxisDataCellRange[2], xAxisDataCellRange[3]) 461 | .formatAsString(sheet.getSheetName(), true)); 462 | 463 | // y 464 | CTNumDataSource ctNumDataSource = ctBarSer.addNewVal(); 465 | CTNumRef ctNumRef = ctNumDataSource.addNewNumRef(); 466 | ctNumRef.setF(new CellRangeAddress( 467 | seriesDataCellRange[4], seriesDataCellRange[5], seriesDataCellRange[6], seriesDataCellRange[7]) 468 | .formatAsString(sheet.getSheetName(), true)); 469 | 470 | ctBarSer.addNewInvertIfNegative().setVal(false); 471 | 472 | ctBoolean.setVal(false); 473 | CTDLbls newDLbls = ctBarSer.addNewDLbls(); 474 | newDLbls.setShowLegendKey(ctBoolean); 475 | newDLbls.setShowVal(ctBoolean); 476 | newDLbls.setShowCatName(ctBoolean); 477 | newDLbls.setShowSerName(ctBoolean); 478 | newDLbls.setShowPercent(ctBoolean); 479 | newDLbls.setShowBubbleSize(ctBoolean); 480 | newDLbls.setShowLeaderLines(ctBoolean); 481 | } 482 | 483 | int xAxisId = seriesDataCellRangeList.size() + 1; 484 | int yAxisId = seriesDataCellRangeList.size() + 2; 485 | 486 | ctBarChart.addNewAxId().setVal(xAxisId); 487 | ctBarChart.addNewAxId().setVal(yAxisId); 488 | 489 | // 设置x轴属性 490 | CTCatAx ctCatAx = ctPlotArea.addNewCatAx(); 491 | ctCatAx.addNewAxId().setVal(xAxisId); 492 | CTScaling ctScaling = ctCatAx.addNewScaling(); 493 | ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX); 494 | ctCatAx.addNewDelete().setVal(false);// 是否显示x轴 495 | ctCatAx.addNewAxPos().setVal(STAxPos.B);// x轴位置(左右上下) 496 | ctCatAx.addNewMajorTickMark().setVal(STTickMark.OUT);// 主刻度线在轴上的位置(内、外、交叉、无) 497 | ctCatAx.addNewMinorTickMark().setVal(STTickMark.NONE);// 次刻度线在轴上的位置(内、外、交叉、无) 498 | ctCatAx.addNewCrossAx().setVal(yAxisId); 499 | ctCatAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);// 标签(即刻度文字)的位置(轴旁、高、低、无) 500 | ctCatAx.addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal( 501 | new XSSFColor(new Color(134, 134, 134)).getRGB());// x轴颜色 502 | 503 | // 设置y轴属性 504 | CTValAx ctValAx = ctPlotArea.addNewValAx(); 505 | ctValAx.addNewAxId().setVal(yAxisId); 506 | ctScaling = ctValAx.addNewScaling(); 507 | ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX); 508 | ctValAx.addNewDelete().setVal(false);// 是否显示y轴 509 | ctValAx.addNewAxPos().setVal(STAxPos.L);// y轴位置(左右上下) 510 | ctValAx.addNewMajorTickMark().setVal(STTickMark.OUT);// 主刻度线在轴上的位置(内、外、交叉、无) 511 | ctValAx.addNewMinorTickMark().setVal(STTickMark.NONE);// 次刻度线在轴上的位置(内、外、交叉、无) 512 | ctValAx.addNewCrossAx().setVal(xAxisId); 513 | ctValAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);// 标签(即刻度文字)的位置(轴旁、高、低、无) 514 | ctValAx.addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal( 515 | new XSSFColor(new Color(134, 134, 134)).getRGB());// x轴颜色 516 | ctValAx.addNewMajorGridlines().addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal( 517 | new XSSFColor(new Color(134, 134, 134)).getRGB());// 显示主要网格线,并设置颜色 518 | } 519 | 520 | /** 521 | * create CTPlotArea 522 | * @param position int[]{startCol, startRow, endCol, endRow, 523 | * xOffsetInStartCell, yOffsetInStartCell, xOffsetInEndCell, yOffsetInEndCell} 524 | */ 525 | private static CTPlotArea createCTPlotArea(XSSFSheet sheet, Map params) { 526 | 527 | int[] chartPosition = (int[]) params.get("chartPosition"); 528 | String chartTitle = (String) params.get("chartTitle"); 529 | String dispBlanksAs = (String) params.get("dispBlanksAs"); 530 | String legendPosition = (String) params.get("legendPosition"); 531 | 532 | XSSFDrawing drawing = sheet.createDrawingPatriarch(); 533 | XSSFClientAnchor anchor = 534 | drawing.createAnchor( 535 | chartPosition[4] * Units.EMU_PER_POINT, chartPosition[5] * Units.EMU_PER_POINT, 536 | chartPosition[6] * Units.EMU_PER_POINT, chartPosition[7] * Units.EMU_PER_POINT, 537 | chartPosition[0], chartPosition[1], chartPosition[2], chartPosition[3]); 538 | XSSFChart chart = drawing.createChart(anchor); 539 | CTChart ctChart = chart.getCTChart(); 540 | CTPlotArea ctPlotArea = ctChart.getPlotArea(); 541 | 542 | // set chart title 543 | chart.setTitleText(chartTitle); 544 | ctChart.getTitle().addNewOverlay().setVal(false);// 图表标题位置(图标上方、居中覆盖) 545 | 546 | ctChart.addNewShowDLblsOverMax().setVal(true); 547 | 548 | switch (dispBlanksAs) { 549 | case "span" : 550 | ctChart.addNewDispBlanksAs().setVal(STDispBlanksAs.SPAN); 551 | break; 552 | case "gap" : 553 | ctChart.addNewDispBlanksAs().setVal(STDispBlanksAs.GAP); 554 | break; 555 | case "zero" : 556 | ctChart.addNewDispBlanksAs().setVal(STDispBlanksAs.ZERO); 557 | break; 558 | default: 559 | ctChart.addNewDispBlanksAs().setVal(STDispBlanksAs.ZERO); 560 | break; 561 | } 562 | 563 | // legend 564 | CTLegend ctLegend = ctChart.addNewLegend(); 565 | ctLegend.addNewOverlay().setVal(false); 566 | 567 | switch (legendPosition) { 568 | case "b" : 569 | ctLegend.addNewLegendPos().setVal(STLegendPos.B); 570 | break; 571 | case "tr" : 572 | ctLegend.addNewLegendPos().setVal(STLegendPos.TR); 573 | break; 574 | case "l" : 575 | ctLegend.addNewLegendPos().setVal(STLegendPos.L); 576 | break; 577 | case "r" : 578 | ctLegend.addNewLegendPos().setVal(STLegendPos.R); 579 | break; 580 | case "t" : 581 | ctLegend.addNewLegendPos().setVal(STLegendPos.T); 582 | break; 583 | default: 584 | ctLegend.addNewLegendPos().setVal(STLegendPos.T); 585 | break; 586 | } 587 | 588 | return ctPlotArea; 589 | } 590 | 591 | /** 592 | * get column index by column address 593 | */ 594 | public static int getColumnIndexByAddress(String columnAddress) { 595 | int colNum = 0; 596 | 597 | for (int i = 0; i < columnAddress.length(); i++) { 598 | char ch = columnAddress.charAt(columnAddress.length() - 1 - i); 599 | colNum += (ch - 'A' + 1) * Math.pow(26, i); 600 | } 601 | 602 | return colNum - 1; 603 | } 604 | } 605 | -------------------------------------------------------------------------------- /src/com/demo/jfreechart/BarChart.java: -------------------------------------------------------------------------------- 1 | package com.demo.jfreechart; 2 | 3 | import java.awt.Color; 4 | import java.util.Vector; 5 | import javax.swing.JFrame; 6 | import javax.swing.SwingUtilities; 7 | import org.jfree.chart.ChartFactory; 8 | import org.jfree.chart.ChartPanel; 9 | import org.jfree.chart.JFreeChart; 10 | import org.jfree.chart.block.BlockBorder; 11 | import org.jfree.data.category.DefaultCategoryDataset; 12 | 13 | import com.demo.jfreechart.utils.ChartUtils; 14 | import com.demo.jfreechart.utils.Serie; 15 | 16 | /** 17 | * 18 | * @author ccw 19 | * @date 2014-6-11 20 | *

21 | * 创建图表步骤:
22 | * 1:创建数据集合
23 | * 2:创建Chart:
24 | * 3:设置抗锯齿,防止字体显示不清楚
25 | * 4:对柱子进行渲染,
26 | * 5:对其他部分进行渲染
27 | * 6:使用chartPanel接收
28 | * 29 | *

30 | */ 31 | public class BarChart { 32 | public BarChart() { 33 | } 34 | 35 | public DefaultCategoryDataset createDataset() { 36 | // 标注类别 37 | String[] categories = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 38 | Vector series = new Vector(); 39 | // 柱子名称:柱子所有的值集合 40 | series.add(new Serie("Tokyo", new Double[] { 49.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4 })); 41 | series.add(new Serie("New York", new Double[] { 83.6, 78.8, 98.5, 93.4, 106.0, 84.5, 105.0, 104.3, 91.2, 83.5, 106.6, 92.3 })); 42 | series.add(new Serie("London", new Double[] { 48.9, 38.8, 39.3, 41.4, 47.0, 48.3, 59.0, 59.6, 52.4, 65.2, 59.3, 51.2 })); 43 | series.add(new Serie("Berlin", new Double[] { 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 57.4, 60.4, 47.6, 39.1, 46.8, 51.1 })); 44 | // 1:创建数据集合 45 | DefaultCategoryDataset dataset = ChartUtils.createDefaultCategoryDataset(series, categories); 46 | return dataset; 47 | } 48 | 49 | public ChartPanel createChart() { 50 | // 2:创建Chart 51 | JFreeChart chart = ChartFactory.createBarChart("Monthly Average Rainfall", "", "Rainfall (mm)", createDataset()); 52 | // 3:设置抗锯齿,防止字体显示不清楚 53 | ChartUtils.setAntiAlias(chart);// 抗锯齿 54 | // 4:对柱子进行渲染 55 | ChartUtils.setBarRenderer(chart.getCategoryPlot(), false);// 56 | // 5:对其他部分进行渲染 57 | ChartUtils.setXAixs(chart.getCategoryPlot());// X坐标轴渲染 58 | ChartUtils.setYAixs(chart.getCategoryPlot());// Y坐标轴渲染 59 | // 设置标注无边框 60 | chart.getLegend().setFrame(new BlockBorder(Color.WHITE)); 61 | // 6:使用chartPanel接收 62 | ChartPanel chartPanel = new ChartPanel(chart); 63 | return chartPanel; 64 | } 65 | 66 | public static void main(String[] args) { 67 | final JFrame frame = new JFrame(); 68 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 69 | frame.setSize(1024, 420); 70 | frame.setLocationRelativeTo(null); 71 | 72 | SwingUtilities.invokeLater(new Runnable() { 73 | @Override 74 | public void run() { 75 | // 创建图形 76 | ChartPanel chartPanel = new BarChart().createChart(); 77 | frame.getContentPane().add(chartPanel); 78 | frame.setVisible(true); 79 | } 80 | }); 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/com/demo/jfreechart/DualaxisChart.java: -------------------------------------------------------------------------------- 1 | package com.demo.jfreechart; 2 | 3 | import java.awt.Color; 4 | import java.util.Vector; 5 | 6 | import javax.swing.JFrame; 7 | import javax.swing.SwingUtilities; 8 | 9 | import org.jfree.chart.ChartFactory; 10 | import org.jfree.chart.ChartPanel; 11 | import org.jfree.chart.JFreeChart; 12 | import org.jfree.chart.axis.CategoryAxis; 13 | import org.jfree.chart.axis.CategoryLabelPositions; 14 | import org.jfree.chart.axis.NumberAxis; 15 | import org.jfree.chart.block.BlockBorder; 16 | import org.jfree.chart.plot.CategoryPlot; 17 | import org.jfree.chart.plot.DatasetRenderingOrder; 18 | import org.jfree.chart.renderer.category.LineAndShapeRenderer; 19 | import org.jfree.data.category.DefaultCategoryDataset; 20 | import org.jfree.ui.RectangleEdge; 21 | 22 | import com.demo.jfreechart.utils.ChartUtils; 23 | import com.demo.jfreechart.utils.Serie; 24 | 25 | /** 26 | * 27 | * @author ccw 28 | * @date 2014-6-11 29 | *

30 | * 2个Y轴图形
31 | * 创建图表步骤:
32 | * 1:创建数据集合
33 | * 2:创建Chart:
34 | * 3:设置抗锯齿,防止字体显示不清楚
35 | * 4:对柱子进行渲染,
36 | * 5:对其他部分进行渲染
37 | * 6:使用chartPanel接收
38 | * 39 | *

40 | */ 41 | 42 | public class DualaxisChart { 43 | public DualaxisChart() { 44 | } 45 | 46 | public ChartPanel createChart() { 47 | String[] categories = { "1999-12-31", "2000-12-31", "2001-12-31", "2002-12-31", "2003-12-31", "2004-12-31", "2005-12-31", "2006-12-31", "2007-12-31", 48 | "2008-12-31", "2009-12-31", "2010-12-31", "2011-12-31", "2012-12-31", "2013-12-31" }; 49 | for (int i = 0; i < categories.length; i++) { 50 | categories[i]=categories[i].substring(0, 4); 51 | } 52 | Vector seriesNetProfit = new Vector(); 53 | // 净利润 54 | Object[] netProfit = { 92669.20, 95790.47, 106187.80, 128530.88, 156608.82, 193003.08, 255800.38, 335302.66, 549877.54, 1251596.81, 1321658.11, 55 | 1917721.09, 2728598.10, 3418600.00, 4092200.00 }; 56 | // 股利支付率 57 | Object[] payoutRatio = { "39.01", "--", "45.39", "30.46", "27.50", "24.34", "19.90", "19.48", "12.67", "10.40", "10.02", "11.97", "20.51", "30.01", 58 | " --" }; 59 | seriesNetProfit.add(new Serie("净利润", netProfit)); 60 | 61 | Vector seriesPayoutRatio = new Vector(); 62 | seriesPayoutRatio.add(new Serie("股利支付率", payoutRatio)); 63 | 64 | DefaultCategoryDataset datasetNetProfit = ChartUtils.createDefaultCategoryDataset(seriesNetProfit, categories); 65 | JFreeChart chart = ChartFactory.createBarChart("", "", "净利润(万元)", datasetNetProfit); 66 | ChartUtils.setAntiAlias(chart);// 抗锯齿 67 | ChartUtils.setBarRenderer(chart.getCategoryPlot(), false); 68 | // 设置坐标轴 69 | ChartUtils.setXAixs(chart.getCategoryPlot()); 70 | ChartUtils.setYAixs(chart.getCategoryPlot()); 71 | // linechart 72 | CategoryPlot categoryplot = (CategoryPlot) chart.getPlot(); 73 | DefaultCategoryDataset datasetPayoutRatio = ChartUtils.createDefaultCategoryDataset(seriesPayoutRatio, categories); 74 | categoryplot.setDataset(1, datasetPayoutRatio); 75 | categoryplot.mapDatasetToRangeAxis(1, 1); 76 | // X轴刻度 77 | CategoryAxis categoryaxis = categoryplot.getDomainAxis(); 78 | categoryaxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45); 79 | // 右侧Y轴 80 | NumberAxis numberaxis = new NumberAxis("股利支付率(%)"); 81 | categoryplot.setRangeAxis(1, numberaxis); 82 | // 隐藏Y刻度 83 | numberaxis.setAxisLineVisible(false); 84 | numberaxis.setTickMarksVisible(false); 85 | 86 | // 设置折线图样式 87 | LineAndShapeRenderer lineRenderer = new LineAndShapeRenderer(); 88 | lineRenderer.setSeriesPaint(0, new Color(255, 185, 1)); 89 | lineRenderer.setBaseShapesVisible(true);// 数据点绘制形状 90 | categoryplot.setRenderer(1, lineRenderer); 91 | 92 | categoryplot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);// 绘制Z-index, 将折线图在前面 93 | chart.getLegend().setPosition(RectangleEdge.TOP);//标注在顶部 94 | chart.getLegend().setFrame(new BlockBorder(Color.WHITE)); 95 | ChartPanel chartPanel = new ChartPanel(chart); 96 | return chartPanel; 97 | } 98 | 99 | public static void main(String[] args) { 100 | final JFrame frame = new JFrame(); 101 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 102 | frame.setSize(1024, 420); 103 | frame.setLocationRelativeTo(null); 104 | 105 | SwingUtilities.invokeLater(new Runnable() { 106 | @Override 107 | public void run() { 108 | // 创建图形 109 | ChartPanel chartPanel = new DualaxisChart().createChart(); 110 | frame.getContentPane().add(chartPanel); 111 | frame.setVisible(true); 112 | } 113 | }); 114 | 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/com/demo/jfreechart/LineChart.java: -------------------------------------------------------------------------------- 1 | package com.demo.jfreechart; 2 | 3 | import java.awt.Color; 4 | import java.util.Vector; 5 | 6 | import javax.swing.JFrame; 7 | import javax.swing.SwingUtilities; 8 | 9 | import org.jfree.chart.ChartFactory; 10 | import org.jfree.chart.ChartPanel; 11 | import org.jfree.chart.JFreeChart; 12 | import org.jfree.chart.block.BlockBorder; 13 | import org.jfree.data.category.DefaultCategoryDataset; 14 | 15 | import com.demo.jfreechart.utils.ChartUtils; 16 | import com.demo.jfreechart.utils.Serie; 17 | 18 | /** 19 | * 20 | * @author ccw 21 | * @date 2014-6-11 22 | *

23 | * 创建图表步骤:
24 | * 1:创建数据集合
25 | * 2:创建Chart:
26 | * 3:设置抗锯齿,防止字体显示不清楚
27 | * 4:对柱子进行渲染,
28 | * 5:对其他部分进行渲染
29 | * 6:使用chartPanel接收
30 | * 31 | *

32 | */ 33 | public class LineChart { 34 | public LineChart() { 35 | } 36 | 37 | public DefaultCategoryDataset createDataset() { 38 | // 标注类别 39 | String[] categories = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 40 | Vector series = new Vector(); 41 | // 柱子名称:柱子所有的值集合 42 | series.add(new Serie("Tokyo", new Double[] { 49.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4 })); 43 | series.add(new Serie("New York", new Double[] { 83.6, 78.8, 98.5, 93.4, 106.0, 84.5, 105.0, 104.3, 91.2, 83.5, 106.6, 92.3 })); 44 | series.add(new Serie("London", new Double[] { 48.9, 38.8, 39.3, 41.4, 47.0, 48.3, 59.0, 59.6, 52.4, 65.2, 59.3, 51.2 })); 45 | series.add(new Serie("Berlin", new Double[] { 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 57.4, 60.4, 47.6, 39.1, 46.8, 51.1 })); 46 | // 1:创建数据集合 47 | DefaultCategoryDataset dataset = ChartUtils.createDefaultCategoryDataset(series, categories); 48 | return dataset; 49 | } 50 | 51 | public ChartPanel createChart() { 52 | // 2:创建Chart[创建不同图形] 53 | JFreeChart chart = ChartFactory.createLineChart("Monthly Average Rainfall", "", "Rainfall (mm)", createDataset()); 54 | // 3:设置抗锯齿,防止字体显示不清楚 55 | ChartUtils.setAntiAlias(chart);// 抗锯齿 56 | // 4:对柱子进行渲染[[采用不同渲染]] 57 | ChartUtils.setLineRender(chart.getCategoryPlot(), false,true);// 58 | // 5:对其他部分进行渲染 59 | ChartUtils.setXAixs(chart.getCategoryPlot());// X坐标轴渲染 60 | ChartUtils.setYAixs(chart.getCategoryPlot());// Y坐标轴渲染 61 | // 设置标注无边框 62 | chart.getLegend().setFrame(new BlockBorder(Color.WHITE)); 63 | // 6:使用chartPanel接收 64 | ChartPanel chartPanel = new ChartPanel(chart); 65 | return chartPanel; 66 | } 67 | 68 | public static void main(String[] args) { 69 | final JFrame frame = new JFrame(); 70 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 71 | frame.setSize(1024, 420); 72 | frame.setLocationRelativeTo(null); 73 | 74 | SwingUtilities.invokeLater(new Runnable() { 75 | @Override 76 | public void run() { 77 | // 创建图形 78 | ChartPanel chartPanel = new LineChart().createChart(); 79 | frame.getContentPane().add(chartPanel); 80 | frame.setVisible(true); 81 | } 82 | }); 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/com/demo/jfreechart/PieChart.java: -------------------------------------------------------------------------------- 1 | package com.demo.jfreechart; 2 | 3 | import java.awt.Color; 4 | 5 | import javax.swing.JFrame; 6 | import javax.swing.SwingUtilities; 7 | 8 | import org.jfree.chart.ChartFactory; 9 | import org.jfree.chart.ChartPanel; 10 | import org.jfree.chart.JFreeChart; 11 | import org.jfree.chart.block.BlockBorder; 12 | import org.jfree.data.general.DefaultPieDataset; 13 | import org.jfree.ui.RectangleEdge; 14 | 15 | import com.demo.jfreechart.utils.ChartUtils; 16 | 17 | /** 18 | * 19 | * @author ccw 20 | * @date 2014-6-11 21 | *

22 | * 创建图表步骤:
23 | * 1:创建数据集合
24 | * 2:创建Chart:
25 | * 3:设置抗锯齿,防止字体显示不清楚
26 | * 4:对柱子进行渲染,
27 | * 5:对其他部分进行渲染
28 | * 6:使用chartPanel接收
29 | * 30 | *

31 | */ 32 | public class PieChart { 33 | public PieChart() { 34 | } 35 | 36 | public DefaultPieDataset createDataset() { 37 | String[] categories = { "Bananas", "Kiwi", "Mixed nuts", "Oranges", "Apples", "Pears", "Clementines", "Reddish (bag)", "Grapes (bunch)", }; 38 | Object[] datas = { 8, 3, 1, 6, 8, 4, 4, 1, 1 }; 39 | DefaultPieDataset dataset = ChartUtils.createDefaultPieDataset(categories, datas); 40 | return dataset; 41 | } 42 | 43 | public ChartPanel createChart() { 44 | // 2:创建Chart[创建不同图形] 45 | JFreeChart chart = ChartFactory.createPieChart("Contents of Highsoft's weekly fruit delivery", createDataset()); 46 | // 3:设置抗锯齿,防止字体显示不清楚 47 | ChartUtils.setAntiAlias(chart);// 抗锯齿 48 | // 4:对柱子进行渲染[创建不同图形] 49 | ChartUtils.setPieRender(chart.getPlot()); 50 | /** 51 | * 可以注释测试 52 | */ 53 | // plot.setSimpleLabels(true);//简单标签,不绘制线条 54 | // plot.setLabelGenerator(null);//不显示数字 55 | // 设置标注无边框 56 | chart.getLegend().setFrame(new BlockBorder(Color.WHITE)); 57 | // 标注位于右侧 58 | chart.getLegend().setPosition(RectangleEdge.RIGHT); 59 | // 6:使用chartPanel接收 60 | ChartPanel chartPanel = new ChartPanel(chart); 61 | return chartPanel; 62 | } 63 | 64 | public static void main(String[] args) { 65 | final JFrame frame = new JFrame(); 66 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 67 | frame.setSize(1024, 420); 68 | frame.setLocationRelativeTo(null); 69 | 70 | SwingUtilities.invokeLater(new Runnable() { 71 | @Override 72 | public void run() { 73 | // 创建图形 74 | ChartPanel chartPanel = new PieChart().createChart(); 75 | frame.getContentPane().add(chartPanel); 76 | frame.setVisible(true); 77 | } 78 | }); 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/com/demo/jfreechart/StackedBarChart.java: -------------------------------------------------------------------------------- 1 | package com.demo.jfreechart; 2 | 3 | import java.awt.Color; 4 | import java.util.Vector; 5 | import javax.swing.JFrame; 6 | import javax.swing.SwingUtilities; 7 | import org.jfree.chart.ChartFactory; 8 | import org.jfree.chart.ChartPanel; 9 | import org.jfree.chart.JFreeChart; 10 | import org.jfree.chart.block.BlockBorder; 11 | import org.jfree.data.category.DefaultCategoryDataset; 12 | 13 | import com.demo.jfreechart.utils.ChartUtils; 14 | import com.demo.jfreechart.utils.Serie; 15 | 16 | /** 17 | * 18 | * @author ccw 19 | * @date 2014-6-11 20 | *

21 | * 创建图表步骤:
22 | * 1:创建数据集合
23 | * 2:创建Chart:
24 | * 3:设置抗锯齿,防止字体显示不清楚
25 | * 4:对柱子进行渲染,
26 | * 5:对其他部分进行渲染
27 | * 6:使用chartPanel接收
28 | * 29 | *

30 | */ 31 | public class StackedBarChart { 32 | public StackedBarChart() { 33 | } 34 | 35 | public DefaultCategoryDataset createDataset() { 36 | // 标注类别 37 | String[] categories = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 38 | Vector series = new Vector(); 39 | // 柱子名称:柱子所有的值集合 40 | series.add(new Serie("Tokyo", new Double[] { 49.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4 })); 41 | series.add(new Serie("New York", new Double[] { 83.6, 78.8, 98.5, 93.4, 106.0, 84.5, 105.0, 104.3, 91.2, 83.5, 106.6, 92.3 })); 42 | series.add(new Serie("London", new Double[] { 48.9, 38.8, 39.3, 41.4, 47.0, 48.3, 59.0, 59.6, 52.4, 65.2, 59.3, 51.2 })); 43 | series.add(new Serie("Berlin", new Double[] { 42.4, 33.2, 34.5, 39.7, 52.6, 75.5, 57.4, 60.4, 47.6, 39.1, 46.8, 51.1 })); 44 | // 1:创建数据集合 45 | DefaultCategoryDataset dataset = ChartUtils.createDefaultCategoryDataset(series, categories); 46 | return dataset; 47 | } 48 | 49 | public ChartPanel createChart() { 50 | // 2:创建Chart[创建不同图形] 51 | JFreeChart chart = ChartFactory.createStackedBarChart("Monthly Average Rainfall", "", "Rainfall (mm)", createDataset()); 52 | // 3:设置抗锯齿,防止字体显示不清楚 53 | ChartUtils.setAntiAlias(chart);// 抗锯齿 54 | // 4:对柱子进行渲染[创建不同图形] 55 | ChartUtils.setStackBarRender(chart.getCategoryPlot()); 56 | // 5:对其他部分进行渲染 57 | ChartUtils.setXAixs(chart.getCategoryPlot());// X坐标轴渲染 58 | ChartUtils.setYAixs(chart.getCategoryPlot());// Y坐标轴渲染 59 | // 设置标注无边框 60 | chart.getLegend().setFrame(new BlockBorder(Color.WHITE)); 61 | // 6:使用chartPanel接收 62 | ChartPanel chartPanel = new ChartPanel(chart); 63 | return chartPanel; 64 | } 65 | 66 | public static void main(String[] args) { 67 | final JFrame frame = new JFrame(); 68 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 69 | frame.setSize(1024, 420); 70 | frame.setLocationRelativeTo(null); 71 | 72 | SwingUtilities.invokeLater(new Runnable() { 73 | @Override 74 | public void run() { 75 | // 创建图形 76 | ChartPanel chartPanel = new StackedBarChart().createChart(); 77 | frame.getContentPane().add(chartPanel); 78 | frame.setVisible(true); 79 | } 80 | }); 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/com/demo/jfreechart/TimeSeriesChart.java: -------------------------------------------------------------------------------- 1 | package com.demo.jfreechart; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Random; 5 | import java.util.Vector; 6 | 7 | import javax.swing.JFrame; 8 | import javax.swing.SwingUtilities; 9 | 10 | import org.jfree.chart.ChartFactory; 11 | import org.jfree.chart.ChartPanel; 12 | import org.jfree.chart.JFreeChart; 13 | import org.jfree.chart.axis.DateAxis; 14 | import org.jfree.chart.axis.DateTickUnit; 15 | import org.jfree.chart.axis.DateTickUnitType; 16 | import org.jfree.chart.plot.XYPlot; 17 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 18 | import org.jfree.data.time.TimeSeries; 19 | import org.jfree.data.time.TimeSeriesCollection; 20 | 21 | import com.demo.jfreechart.utils.ChartUtils; 22 | 23 | /** 24 | * 25 | * @author ccw 26 | * @date 2014-6-11 27 | *

28 | * 创建图表步骤:
29 | * 1:创建数据集合
30 | * 2:创建Chart:
31 | * 3:设置抗锯齿,防止字体显示不清楚
32 | * 4:对柱子进行渲染,
33 | * 5:对其他部分进行渲染
34 | * 6:使用chartPanel接收
35 | * 36 | *

37 | */ 38 | public class TimeSeriesChart { 39 | public TimeSeriesChart() { 40 | } 41 | 42 | /** 43 | * 创建数据集合 44 | * 45 | * @return 46 | */ 47 | public TimeSeriesCollection createDataset() { 48 | TimeSeriesCollection dataset = new TimeSeriesCollection(); 49 | String[] categories = { "股东总户数", "前十名持股比例" }; 50 | 51 | Random random = new Random(); 52 | for (int row = 0; row < categories.length; row++) { 53 | Vector dateValues = new Vector(); 54 | for (int i = 0; i < 20; i++) { 55 | String date = (2000 + i) + "-0" + i + "-21"; 56 | System.out.println(date); 57 | Object[] dateValue = { date, random.nextInt(10) }; 58 | dateValues.add(dateValue); 59 | 60 | } 61 | TimeSeries timeSeries = ChartUtils.createTimeseries(categories[row], dateValues); 62 | dataset.addSeries(timeSeries); 63 | } 64 | return dataset; 65 | } 66 | 67 | public ChartPanel createChart() { 68 | // 2:创建Chart[创建不同图形] 69 | TimeSeriesCollection dataset = createDataset(); 70 | JFreeChart chart = ChartFactory.createTimeSeriesChart("股东总户数", "", "", dataset); 71 | // 3:设置抗锯齿,防止字体显示不清楚 72 | ChartUtils.setAntiAlias(chart);// 抗锯齿 73 | // 4:对柱子进行渲染[创建不同图形] 74 | ChartUtils.setTimeSeriesRender(chart.getPlot(), true, true); 75 | // 5:对其他部分进行渲染 76 | XYPlot xyplot = (XYPlot) chart.getPlot(); 77 | ChartUtils.setXY_XAixs(xyplot); 78 | ChartUtils.setXY_YAixs(xyplot); 79 | // 日期X坐标轴 80 | DateAxis domainAxis = (DateAxis) xyplot.getDomainAxis(); 81 | domainAxis.setAutoTickUnitSelection(false); 82 | DateTickUnit dateTickUnit = null; 83 | if (dataset.getItemCount(0) < 16) { 84 | //刻度单位月,半年为间隔 85 | dateTickUnit = new DateTickUnit(DateTickUnitType.MONTH, 6, new SimpleDateFormat("yyyy-MM")); // 第二个参数是时间轴间距 86 | } else {// 数据过多,不显示数据 87 | XYLineAndShapeRenderer xyRenderer = (XYLineAndShapeRenderer) xyplot.getRenderer(); 88 | xyRenderer.setBaseItemLabelsVisible(false); 89 | dateTickUnit = new DateTickUnit(DateTickUnitType.YEAR, 1, new SimpleDateFormat("yyyy")); // 第二个参数是时间轴间距 90 | } 91 | // 设置时间单位 92 | domainAxis.setTickUnit(dateTickUnit); 93 | ChartUtils.setLegendEmptyBorder(chart); 94 | // 设置图例位置 95 | // 6:使用chartPanel接收 96 | ChartPanel chartPanel = new ChartPanel(chart); 97 | return chartPanel; 98 | } 99 | 100 | public static void main(String[] args) { 101 | final JFrame frame = new JFrame(); 102 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 103 | frame.setSize(1024, 420); 104 | frame.setLocationRelativeTo(null); 105 | 106 | SwingUtilities.invokeLater(new Runnable() { 107 | @Override 108 | public void run() { 109 | // 创建图形 110 | ChartPanel chartPanel = new TimeSeriesChart().createChart(); 111 | frame.getContentPane().add(chartPanel); 112 | frame.setVisible(true); 113 | } 114 | }); 115 | 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/com/demo/jfreechart/utils/ChartUtils.java: -------------------------------------------------------------------------------- 1 | package com.demo.jfreechart.utils; 2 | 3 | import java.awt.BasicStroke; 4 | import java.awt.Color; 5 | import java.awt.Font; 6 | import java.awt.Paint; 7 | import java.awt.Rectangle; 8 | import java.text.DecimalFormat; 9 | import java.text.NumberFormat; 10 | import java.text.ParseException; 11 | import java.text.SimpleDateFormat; 12 | import java.util.Date; 13 | import java.util.Vector; 14 | 15 | import org.jfree.chart.ChartFactory; 16 | import org.jfree.chart.JFreeChart; 17 | import org.jfree.chart.StandardChartTheme; 18 | import org.jfree.chart.axis.DateAxis; 19 | import org.jfree.chart.axis.DateTickUnit; 20 | import org.jfree.chart.axis.DateTickUnitType; 21 | import org.jfree.chart.axis.ValueAxis; 22 | import org.jfree.chart.block.BlockBorder; 23 | import org.jfree.chart.labels.ItemLabelAnchor; 24 | import org.jfree.chart.labels.ItemLabelPosition; 25 | import org.jfree.chart.labels.StandardCategoryItemLabelGenerator; 26 | import org.jfree.chart.labels.StandardPieSectionLabelGenerator; 27 | import org.jfree.chart.labels.StandardXYItemLabelGenerator; 28 | import org.jfree.chart.labels.StandardXYToolTipGenerator; 29 | import org.jfree.chart.plot.CategoryPlot; 30 | import org.jfree.chart.plot.DefaultDrawingSupplier; 31 | import org.jfree.chart.plot.PieLabelLinkStyle; 32 | import org.jfree.chart.plot.PiePlot; 33 | import org.jfree.chart.plot.Plot; 34 | import org.jfree.chart.plot.XYPlot; 35 | import org.jfree.chart.renderer.category.BarRenderer; 36 | import org.jfree.chart.renderer.category.LineAndShapeRenderer; 37 | import org.jfree.chart.renderer.category.StackedBarRenderer; 38 | import org.jfree.chart.renderer.category.StandardBarPainter; 39 | import org.jfree.chart.renderer.xy.StandardXYBarPainter; 40 | import org.jfree.chart.renderer.xy.XYBarRenderer; 41 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 42 | import org.jfree.data.category.DefaultCategoryDataset; 43 | import org.jfree.data.general.DefaultPieDataset; 44 | import org.jfree.data.time.Day; 45 | import org.jfree.data.time.TimeSeries; 46 | import org.jfree.ui.RectangleInsets; 47 | import org.jfree.ui.TextAnchor; 48 | 49 | /** 50 | * Jfreechart工具类 51 | *

52 | * 解决中午乱码问题
53 | * 用来创建类别图表数据集、创建饼图数据集、时间序列图数据集
54 | * 用来对柱状图、折线图、饼图、堆积柱状图、时间序列图的样式进行渲染
55 | * 设置X-Y坐标轴样式 56 | *

57 | * 58 | * 59 | * @author chenchangwen 60 | * @since:2014-2-18 61 | * 62 | */ 63 | public class ChartUtils { 64 | private static String NO_DATA_MSG = "数据加载失败"; 65 | private static Font FONT = new Font("宋体", Font.PLAIN, 12); 66 | public static Color[] CHART_COLORS = { 67 | new Color(31,129,188), new Color(92,92,97), new Color(144,237,125), new Color(255,188,117), 68 | new Color(153,158,255), new Color(255,117,153), new Color(253,236,109), new Color(128,133,232), 69 | new Color(158,90,102),new Color(255, 204, 102) };// 颜色 70 | 71 | static { 72 | setChartTheme(); 73 | } 74 | 75 | public ChartUtils() { 76 | } 77 | 78 | /** 79 | * 中文主题样式 解决乱码 80 | */ 81 | public static void setChartTheme() { 82 | // 设置中文主题样式 解决乱码 83 | StandardChartTheme chartTheme = new StandardChartTheme("CN"); 84 | // 设置标题字体 85 | chartTheme.setExtraLargeFont(FONT); 86 | // 设置图例的字体 87 | chartTheme.setRegularFont(FONT); 88 | // 设置轴向的字体 89 | chartTheme.setLargeFont(FONT); 90 | chartTheme.setSmallFont(FONT); 91 | chartTheme.setTitlePaint(new Color(51, 51, 51)); 92 | chartTheme.setSubtitlePaint(new Color(85, 85, 85)); 93 | 94 | chartTheme.setLegendBackgroundPaint(Color.WHITE);// 设置标注 95 | chartTheme.setLegendItemPaint(Color.BLACK);// 96 | chartTheme.setChartBackgroundPaint(Color.WHITE); 97 | // 绘制颜色绘制颜色.轮廓供应商 98 | // paintSequence,outlinePaintSequence,strokeSequence,outlineStrokeSequence,shapeSequence 99 | 100 | Paint[] OUTLINE_PAINT_SEQUENCE = new Paint[] { Color.WHITE }; 101 | // 绘制器颜色源 102 | DefaultDrawingSupplier drawingSupplier = new DefaultDrawingSupplier(CHART_COLORS, CHART_COLORS, OUTLINE_PAINT_SEQUENCE, 103 | DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE, DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE, 104 | DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE); 105 | chartTheme.setDrawingSupplier(drawingSupplier); 106 | 107 | chartTheme.setPlotBackgroundPaint(Color.WHITE);// 绘制区域 108 | chartTheme.setPlotOutlinePaint(Color.WHITE);// 绘制区域外边框 109 | chartTheme.setLabelLinkPaint(new Color(8, 55, 114));// 链接标签颜色 110 | chartTheme.setLabelLinkStyle(PieLabelLinkStyle.CUBIC_CURVE); 111 | 112 | chartTheme.setAxisOffset(new RectangleInsets(5, 12, 5, 12)); 113 | chartTheme.setDomainGridlinePaint(new Color(192, 208, 224));// X坐标轴垂直网格颜色 114 | chartTheme.setRangeGridlinePaint(new Color(192, 192, 192));// Y坐标轴水平网格颜色 115 | 116 | chartTheme.setBaselinePaint(Color.WHITE); 117 | chartTheme.setCrosshairPaint(Color.BLUE);// 不确定含义 118 | chartTheme.setAxisLabelPaint(new Color(51, 51, 51));// 坐标轴标题文字颜色 119 | chartTheme.setTickLabelPaint(new Color(67, 67, 72));// 刻度数字 120 | chartTheme.setBarPainter(new StandardBarPainter());// 设置柱状图渲染 121 | chartTheme.setXYBarPainter(new StandardXYBarPainter());// XYBar 渲染 122 | 123 | chartTheme.setItemLabelPaint(Color.black); 124 | chartTheme.setThermometerPaint(Color.white);// 温度计 125 | 126 | ChartFactory.setChartTheme(chartTheme); 127 | } 128 | 129 | /** 130 | * 必须设置文本抗锯齿 131 | */ 132 | public static void setAntiAlias(JFreeChart chart) { 133 | chart.setTextAntiAlias(false); 134 | 135 | } 136 | 137 | /** 138 | * 设置图例无边框,默认黑色边框 139 | */ 140 | public static void setLegendEmptyBorder(JFreeChart chart) { 141 | chart.getLegend().setFrame(new BlockBorder(Color.WHITE)); 142 | 143 | } 144 | 145 | /** 146 | * 创建类别数据集合 147 | */ 148 | public static DefaultCategoryDataset createDefaultCategoryDataset(Vector series, String[] categories) { 149 | DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 150 | 151 | for (Serie serie : series) { 152 | String name = serie.getName(); 153 | Vector data = serie.getData(); 154 | if (data != null && categories != null && data.size() == categories.length) { 155 | for (int index = 0; index < data.size(); index++) { 156 | String value = data.get(index) == null ? "" : data.get(index).toString(); 157 | if (isPercent(value)) { 158 | value = value.substring(0, value.length() - 1); 159 | } 160 | if (isNumber(value)) { 161 | dataset.setValue(Double.parseDouble(value), name, categories[index]); 162 | } 163 | } 164 | } 165 | 166 | } 167 | return dataset; 168 | 169 | } 170 | 171 | /** 172 | * 创建饼图数据集合 173 | */ 174 | public static DefaultPieDataset createDefaultPieDataset(String[] categories, Object[] datas) { 175 | DefaultPieDataset dataset = new DefaultPieDataset(); 176 | for (int i = 0; i < categories.length && categories != null; i++) { 177 | String value = datas[i].toString(); 178 | if (isPercent(value)) { 179 | value = value.substring(0, value.length() - 1); 180 | } 181 | if (isNumber(value)) { 182 | dataset.setValue(categories[i], Double.valueOf(value)); 183 | } 184 | } 185 | return dataset; 186 | 187 | } 188 | 189 | /** 190 | * 创建时间序列数据 191 | * 192 | * @param category 193 | * 类别 194 | * @param dateValues 195 | * 日期-值 数组 196 | * @param xAxisTitle 197 | * X坐标轴标题 198 | * @return 199 | */ 200 | public static TimeSeries createTimeseries(String category, Vector dateValues) { 201 | TimeSeries timeseries = new TimeSeries(category); 202 | 203 | if (dateValues != null) { 204 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 205 | for (Object[] objects : dateValues) { 206 | Date date = null; 207 | try { 208 | date = dateFormat.parse(objects[0].toString()); 209 | } catch (ParseException e) { 210 | } 211 | String sValue = objects[1].toString(); 212 | double dValue = 0; 213 | if (date != null && isNumber(sValue)) { 214 | dValue = Double.parseDouble(sValue); 215 | timeseries.add(new Day(date), dValue); 216 | } 217 | } 218 | } 219 | 220 | return timeseries; 221 | } 222 | 223 | /** 224 | * 设置 折线图样式 225 | * 226 | * @param plot 227 | * @param isShowDataLabels 228 | * 是否显示数据标签 默认不显示节点形状 229 | */ 230 | public static void setLineRender(CategoryPlot plot, boolean isShowDataLabels) { 231 | setLineRender(plot, isShowDataLabels, false); 232 | } 233 | 234 | /** 235 | * 设置折线图样式 236 | * 237 | * @param plot 238 | * @param isShowDataLabels 239 | * 是否显示数据标签 240 | */ 241 | @SuppressWarnings("deprecation") 242 | public static void setLineRender(CategoryPlot plot, boolean isShowDataLabels, boolean isShapesVisible) { 243 | plot.setNoDataMessage(NO_DATA_MSG); 244 | plot.setInsets(new RectangleInsets(10, 10, 0, 10), false); 245 | LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer(); 246 | 247 | renderer.setStroke(new BasicStroke(1.5F)); 248 | if (isShowDataLabels) { 249 | renderer.setBaseItemLabelsVisible(true); 250 | renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator(StandardCategoryItemLabelGenerator.DEFAULT_LABEL_FORMAT_STRING, 251 | NumberFormat.getInstance())); 252 | renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE1, TextAnchor.BOTTOM_CENTER));// weizhi 253 | } 254 | renderer.setBaseShapesVisible(isShapesVisible);// 数据点绘制形状 255 | setXAixs(plot); 256 | setYAixs(plot); 257 | 258 | } 259 | 260 | /** 261 | * 设置时间序列图样式 262 | * 263 | * @param plot 264 | * @param isShowData 265 | * 是否显示数据 266 | * @param isShapesVisible 267 | * 是否显示数据节点形状 268 | */ 269 | public static void setTimeSeriesRender(Plot plot, boolean isShowData, boolean isShapesVisible) { 270 | 271 | XYPlot xyplot = (XYPlot) plot; 272 | xyplot.setNoDataMessage(NO_DATA_MSG); 273 | xyplot.setInsets(new RectangleInsets(10, 10, 5, 10)); 274 | 275 | XYLineAndShapeRenderer xyRenderer = (XYLineAndShapeRenderer) xyplot.getRenderer(); 276 | 277 | xyRenderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator()); 278 | xyRenderer.setBaseShapesVisible(false); 279 | if (isShowData) { 280 | xyRenderer.setBaseItemLabelsVisible(true); 281 | xyRenderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator()); 282 | xyRenderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE1, TextAnchor.BOTTOM_CENTER));// weizhi 283 | } 284 | xyRenderer.setBaseShapesVisible(isShapesVisible);// 数据点绘制形状 285 | 286 | DateAxis domainAxis = (DateAxis) xyplot.getDomainAxis(); 287 | domainAxis.setAutoTickUnitSelection(false); 288 | DateTickUnit dateTickUnit = new DateTickUnit(DateTickUnitType.YEAR, 1, new SimpleDateFormat("yyyy-MM")); // 第二个参数是时间轴间距 289 | domainAxis.setTickUnit(dateTickUnit); 290 | 291 | StandardXYToolTipGenerator xyTooltipGenerator = new StandardXYToolTipGenerator("{1}:{2}", new SimpleDateFormat("yyyy-MM-dd"), new DecimalFormat("0")); 292 | xyRenderer.setBaseToolTipGenerator(xyTooltipGenerator); 293 | 294 | setXY_XAixs(xyplot); 295 | setXY_YAixs(xyplot); 296 | 297 | } 298 | 299 | /** 300 | * 设置时间序列图样式 -默认不显示数据节点形状 301 | * 302 | * @param plot 303 | * @param isShowData 304 | * 是否显示数据 305 | */ 306 | 307 | public static void setTimeSeriesRender(Plot plot, boolean isShowData) { 308 | setTimeSeriesRender(plot, isShowData, false); 309 | } 310 | 311 | /** 312 | * 设置时间序列图渲染:但是存在一个问题:如果timeseries里面的日期是按照天组织, 那么柱子的宽度会非常小,和直线一样粗细 313 | * 314 | * @param plot 315 | * @param isShowDataLabels 316 | */ 317 | 318 | public static void setTimeSeriesBarRender(Plot plot, boolean isShowDataLabels) { 319 | 320 | XYPlot xyplot = (XYPlot) plot; 321 | xyplot.setNoDataMessage(NO_DATA_MSG); 322 | 323 | XYBarRenderer xyRenderer = new XYBarRenderer(0.1D); 324 | xyRenderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator()); 325 | 326 | if (isShowDataLabels) { 327 | xyRenderer.setBaseItemLabelsVisible(true); 328 | xyRenderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator()); 329 | } 330 | 331 | StandardXYToolTipGenerator xyTooltipGenerator = new StandardXYToolTipGenerator("{1}:{2}", new SimpleDateFormat("yyyy-MM-dd"), new DecimalFormat("0")); 332 | xyRenderer.setBaseToolTipGenerator(xyTooltipGenerator); 333 | setXY_XAixs(xyplot); 334 | setXY_YAixs(xyplot); 335 | 336 | } 337 | 338 | /** 339 | * 设置柱状图渲染 340 | * 341 | * @param plot 342 | * @param isShowDataLabels 343 | */ 344 | public static void setBarRenderer(CategoryPlot plot, boolean isShowDataLabels) { 345 | 346 | plot.setNoDataMessage(NO_DATA_MSG); 347 | plot.setInsets(new RectangleInsets(10, 10, 5, 10)); 348 | BarRenderer renderer = (BarRenderer) plot.getRenderer(); 349 | renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator()); 350 | renderer.setMaximumBarWidth(0.075);// 设置柱子最大宽度 351 | 352 | if (isShowDataLabels) { 353 | renderer.setBaseItemLabelsVisible(true); 354 | } 355 | 356 | setXAixs(plot); 357 | setYAixs(plot); 358 | } 359 | 360 | /** 361 | * 设置堆积柱状图渲染 362 | * 363 | * @param plot 364 | */ 365 | 366 | public static void setStackBarRender(CategoryPlot plot) { 367 | plot.setNoDataMessage(NO_DATA_MSG); 368 | plot.setInsets(new RectangleInsets(10, 10, 5, 10)); 369 | StackedBarRenderer renderer = (StackedBarRenderer) plot.getRenderer(); 370 | renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator()); 371 | plot.setRenderer(renderer); 372 | setXAixs(plot); 373 | setYAixs(plot); 374 | } 375 | 376 | /** 377 | * 设置类别图表(CategoryPlot) X坐标轴线条颜色和样式 378 | * 379 | * @param axis 380 | */ 381 | public static void setXAixs(CategoryPlot plot) { 382 | Color lineColor = new Color(31, 121, 170); 383 | plot.getDomainAxis().setAxisLinePaint(lineColor);// X坐标轴颜色 384 | plot.getDomainAxis().setTickMarkPaint(lineColor);// X坐标轴标记|竖线颜色 385 | 386 | } 387 | 388 | /** 389 | * 设置类别图表(CategoryPlot) Y坐标轴线条颜色和样式 同时防止数据无法显示 390 | * 391 | * @param axis 392 | */ 393 | public static void setYAixs(CategoryPlot plot) { 394 | Color lineColor = new Color(192, 208, 224); 395 | ValueAxis axis = plot.getRangeAxis(); 396 | axis.setAxisLinePaint(lineColor);// Y坐标轴颜色 397 | axis.setTickMarkPaint(lineColor);// Y坐标轴标记|竖线颜色 398 | // 隐藏Y刻度 399 | axis.setAxisLineVisible(false); 400 | axis.setTickMarksVisible(false); 401 | // Y轴网格线条 402 | plot.setRangeGridlinePaint(new Color(192, 192, 192)); 403 | plot.setRangeGridlineStroke(new BasicStroke(1)); 404 | 405 | plot.getRangeAxis().setUpperMargin(0.1);// 设置顶部Y坐标轴间距,防止数据无法显示 406 | plot.getRangeAxis().setLowerMargin(0.1);// 设置底部Y坐标轴间距 407 | 408 | } 409 | 410 | /** 411 | * 设置XY图表(XYPlot) X坐标轴线条颜色和样式 412 | * 413 | * @param axis 414 | */ 415 | public static void setXY_XAixs(XYPlot plot) { 416 | Color lineColor = new Color(31, 121, 170); 417 | plot.getDomainAxis().setAxisLinePaint(lineColor);// X坐标轴颜色 418 | plot.getDomainAxis().setTickMarkPaint(lineColor);// X坐标轴标记|竖线颜色 419 | 420 | } 421 | 422 | /** 423 | * 设置XY图表(XYPlot) Y坐标轴线条颜色和样式 同时防止数据无法显示 424 | * 425 | * @param axis 426 | */ 427 | public static void setXY_YAixs(XYPlot plot) { 428 | Color lineColor = new Color(192, 208, 224); 429 | ValueAxis axis = plot.getRangeAxis(); 430 | axis.setAxisLinePaint(lineColor);// X坐标轴颜色 431 | axis.setTickMarkPaint(lineColor);// X坐标轴标记|竖线颜色 432 | // 隐藏Y刻度 433 | axis.setAxisLineVisible(false); 434 | axis.setTickMarksVisible(false); 435 | // Y轴网格线条 436 | plot.setRangeGridlinePaint(new Color(192, 192, 192)); 437 | plot.setRangeGridlineStroke(new BasicStroke(1)); 438 | plot.setDomainGridlinesVisible(false); 439 | 440 | plot.getRangeAxis().setUpperMargin(0.12);// 设置顶部Y坐标轴间距,防止数据无法显示 441 | plot.getRangeAxis().setLowerMargin(0.12);// 设置底部Y坐标轴间距 442 | 443 | } 444 | 445 | /** 446 | * 设置饼状图渲染 447 | */ 448 | public static void setPieRender(Plot plot) { 449 | 450 | plot.setNoDataMessage(NO_DATA_MSG); 451 | plot.setInsets(new RectangleInsets(10, 10, 5, 10)); 452 | PiePlot piePlot = (PiePlot) plot; 453 | piePlot.setInsets(new RectangleInsets(0, 0, 0, 0)); 454 | piePlot.setCircular(true);// 圆形 455 | 456 | // piePlot.setSimpleLabels(true);// 简单标签 457 | piePlot.setLabelGap(0.01); 458 | piePlot.setInteriorGap(0.05D); 459 | piePlot.setLegendItemShape(new Rectangle(10, 10));// 图例形状 460 | piePlot.setIgnoreNullValues(true); 461 | piePlot.setLabelBackgroundPaint(null);// 去掉背景色 462 | piePlot.setLabelShadowPaint(null);// 去掉阴影 463 | piePlot.setLabelOutlinePaint(null);// 去掉边框 464 | piePlot.setShadowPaint(null); 465 | // 0:category 1:value:2 :percentage 466 | piePlot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}:{2}"));// 显示标签数据 467 | } 468 | 469 | /** 470 | * 是不是一个%形式的百分比 471 | * 472 | * @param str 473 | * @return 474 | */ 475 | public static boolean isPercent(String str) { 476 | return str != null ? str.endsWith("%") && isNumber(str.substring(0, str.length() - 1)) : false; 477 | } 478 | 479 | /** 480 | * 是不是一个数字 481 | * 482 | * @param str 483 | * @return 484 | */ 485 | public static boolean isNumber(String str) { 486 | return str != null ? str.matches("^[-+]?(([0-9]+)((([.]{0})([0-9]*))|(([.]{1})([0-9]+))))$") : false; 487 | } 488 | 489 | } 490 | -------------------------------------------------------------------------------- /src/com/demo/jfreechart/utils/Serie.java: -------------------------------------------------------------------------------- 1 | package com.demo.jfreechart.utils; 2 | 3 | import java.io.Serializable; 4 | import java.util.Vector; 5 | 6 | 7 | /** 8 | * 系列:名字和数据集合 构成一条曲线
可以将serie看作一根线或者一根柱子: 9 | * 10 | *

11 | * 参照JS图表来描述数据:
series: [{ name: 'Tokyo', data: [7.0, 6.9, 9.5, 14.5] 12 | * },
{ name: 'New York', data: [-0.2, 0.8, 5.7, 11.3} ]
13 | *

14 | * 15 | * @author ccw 16 | * @date 2014-6-4 17 | */ 18 | public class Serie implements Serializable { 19 | 20 | private static final long serialVersionUID = 1L; 21 | private String name;// 名字 22 | private Vector data;// 数据值 23 | 24 | public Serie() { 25 | 26 | } 27 | 28 | /** 29 | * 30 | * @param name 31 | * 名称(线条名称) 32 | * @param data 33 | * 数据(线条上的所有数据值) 34 | */ 35 | public Serie(String name, Vector data) { 36 | 37 | this.name = name; 38 | this.data = data; 39 | } 40 | 41 | /** 42 | * 43 | * @param name 44 | * 名称(线条名称) 45 | * @param array 46 | * 数据(线条上的所有数据值) 47 | */ 48 | public Serie(String name, Object[] array) { 49 | this.name = name; 50 | if (array != null) { 51 | data = new Vector(array.length); 52 | for (int i = 0; i < array.length; i++) { 53 | data.add(array[i]); 54 | } 55 | } 56 | } 57 | 58 | public String getName() { 59 | return name; 60 | } 61 | 62 | public void setName(String name) { 63 | this.name = name; 64 | } 65 | 66 | public Vector getData() { 67 | return data; 68 | } 69 | 70 | public void setData(Vector data) { 71 | this.data = data; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/com/desay/utils/AnrCrashUtils.java: -------------------------------------------------------------------------------- 1 | package com.desay.utils; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.ObjectInputStream; 8 | import java.io.ObjectOutputStream; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | import org.eclipse.swt.SWT; 15 | import org.eclipse.swt.widgets.Table; 16 | import org.eclipse.swt.widgets.TableItem; 17 | 18 | import com.desaysv.bean.AnrCrash; 19 | 20 | public class AnrCrashUtils { 21 | 22 | 23 | public static final int COLUMN_COUNTER = 5; 24 | 25 | public static final int ANR_TYPE = 0; 26 | public static final int CRASH_TYPE = 1; 27 | 28 | public static final int DIR_UI = 0; 29 | public static final int CRASH_NUM_UI = 1; 30 | public static final int CRASH_NAME_UI = 2; 31 | public static final int ANR_NUM_UI = 3; 32 | public static final int ANR_NAME_UI = 4; 33 | 34 | public static final String ANR_FILE_NAME = "anrRecord.txt"; 35 | public static final String CRASH_FILE_NAME = "crashRecord.txt"; 36 | 37 | /** 38 | * 把list序列化到文件 39 | * @param list 泛型list 40 | * @param filePath 文件路径 41 | * @return 成功返回true 42 | */ 43 | public static boolean writeObject(List list, String filePath) 44 | { 45 | @SuppressWarnings("unchecked") 46 | T[] array = (T[]) list.toArray(); 47 | try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File(filePath)))) 48 | { 49 | out.writeObject(array); 50 | out.writeObject(null);//需要写个空对象,否则会报错 51 | out.flush(); 52 | out.close(); 53 | return true; 54 | } 55 | catch (IOException e) 56 | { 57 | e.printStackTrace(); 58 | return false; 59 | } 60 | } 61 | 62 | /** 63 | * 从文件反序列化对象到泛型的list 64 | * @param objPath 文件路径 65 | * @return 文件不存在返回size为0的list,存在则返回序列化的list 66 | */ 67 | @SuppressWarnings("unchecked") 68 | public static List readObjectForList(String objPath) 69 | { 70 | File file = new File(objPath); 71 | if (file.exists()) { 72 | E[] object; 73 | try(ObjectInputStream out = new ObjectInputStream(new FileInputStream(file))) 74 | { 75 | object = (E[]) out.readObject(); 76 | out.close(); 77 | return Collections.synchronizedList(new ArrayList(Arrays.asList(object))); 78 | } 79 | catch (IOException e) 80 | { 81 | e.printStackTrace(); 82 | } 83 | catch (ClassNotFoundException e) 84 | { 85 | e.printStackTrace(); 86 | } 87 | } 88 | return Collections.synchronizedList(new ArrayList()); 89 | } 90 | 91 | /** 92 | * 添加一条ANR或CRASH记录 93 | * @param type anr或carsh 94 | * @param packageName 包名 95 | * @param content 详细内容 96 | * @param pathStr 文件所在目录 97 | * @return 98 | */ 99 | public static boolean addAnrCrashRecord(int type, String packageName, String content, String pathStr) { 100 | String filePath = ""; 101 | if (type == ANR_TYPE) { 102 | filePath = pathStr + "\\" + ANR_FILE_NAME; 103 | } else { 104 | filePath = pathStr + "\\" + CRASH_FILE_NAME; 105 | } 106 | List recordList = readObjectForList(filePath); 107 | List tmpList = Collections.synchronizedList(new ArrayList());//临时list 108 | if (recordList.size() > 0) {//已有记录,先把记录读取出来添加到临时list 109 | for (AnrCrash anrCrash : recordList) { 110 | tmpList.add(anrCrash); 111 | } 112 | } 113 | AnrCrash anrCrashRecord = new AnrCrash(); 114 | anrCrashRecord.setPackageName(packageName); 115 | anrCrashRecord.setContentDetail(content); 116 | anrCrashRecord.setFileDir(pathStr); 117 | tmpList.add(anrCrashRecord); 118 | return writeObject(tmpList, filePath); 119 | } 120 | 121 | /** 122 | * 读取记录到列表 123 | * @param type 124 | * @param pathStr 125 | * @return 126 | */ 127 | public static List readAnrCrashRecord(int type, String pathStr) { 128 | String filePath = ""; 129 | if (type == ANR_TYPE) { 130 | filePath = pathStr + "\\" + ANR_FILE_NAME; 131 | } else { 132 | filePath = pathStr + "\\" + CRASH_FILE_NAME; 133 | } 134 | return readObjectForList(filePath); 135 | } 136 | 137 | /** 138 | * 更新anr和crash列表界面 139 | * @param table 列表 140 | * @param pathStr anr和crash记录文件所在目录 141 | */ 142 | public static void updateAnrCrashTableUi(Table table, String pathStr) { 143 | if (pathStr != null) { 144 | List crashList = readAnrCrashRecord(CRASH_TYPE, pathStr); 145 | List anrList = readAnrCrashRecord(ANR_TYPE, pathStr); 146 | 147 | int crashCounter = crashList.size(); 148 | int anrCounter = anrList.size(); 149 | 150 | int tableLineCounter = crashCounter>anrCounter ? crashCounter:anrCounter; 151 | TableItem[] items = table.getItems(); 152 | if (items.length > tableLineCounter) {//表格多了,把记录填入表格后多余的表格dispose掉 153 | for (int i = 0; i < items.length; i++) { 154 | if (i >= tableLineCounter) { 155 | items[i].dispose(); 156 | } 157 | } 158 | } else if (items.length <= tableLineCounter) {//表格少了或等于,需要增加表格的行再添加数据 159 | int counter = tableLineCounter - items.length; 160 | for (int i = 0; i < counter; i++) { 161 | new TableItem(table, SWT.CENTER); 162 | } 163 | } 164 | items = table.getItems();//删减表格后重新获取表格每一行 165 | 166 | if (crashCounter > 0 || anrCounter > 0) {//有anr或crash的情况 167 | if (crashCounter >= anrCounter) { //crash大于anr大于0 168 | for (int i = 0; i < crashCounter; i++) { 169 | if (i < anrCounter) {//同时有crash和anr记录的行 170 | AnrCrash crashRecord = crashList.get(i); 171 | AnrCrash anrRecord = anrList.get(i); 172 | 173 | items[i].setText(DIR_UI, crashRecord.getFileDir()); 174 | items[i].setText(CRASH_NUM_UI, (i + 1) + ""); 175 | items[i].setText(CRASH_NAME_UI, crashRecord.getPackageName()); 176 | 177 | items[i].setText(ANR_NUM_UI, (i + 1) + ""); 178 | items[i].setText(ANR_NAME_UI, anrRecord.getPackageName()); 179 | } else {//只有crash的行 180 | AnrCrash crashRecord = crashList.get(i); 181 | items[i].setText(DIR_UI, crashRecord.getFileDir()); 182 | items[i].setText(CRASH_NUM_UI, (i + 1) + ""); 183 | items[i].setText(CRASH_NAME_UI, crashRecord.getPackageName()); 184 | items[i].setText(ANR_NUM_UI, ""); 185 | items[i].setText(ANR_NAME_UI, ""); 186 | } 187 | } 188 | } else { //anr大于0大于crash 189 | for (int i = 0; i < anrCounter; i++) { 190 | if (i < crashCounter) {//同时有crash和anr记录的行 191 | AnrCrash crashRecord = crashList.get(i); 192 | AnrCrash anrRecord = anrList.get(i); 193 | 194 | items[i].setText(DIR_UI, anrRecord.getFileDir()); 195 | items[i].setText(CRASH_NUM_UI, (i + 1) + ""); 196 | items[i].setText(CRASH_NAME_UI, crashRecord.getPackageName()); 197 | 198 | items[i].setText(ANR_NUM_UI, (i + 1) + ""); 199 | items[i].setText(ANR_NAME_UI, anrRecord.getPackageName()); 200 | } else {//只有anr的行 201 | AnrCrash anrRecord = anrList.get(i); 202 | 203 | items[i].setText(DIR_UI, anrRecord.getFileDir()); 204 | items[i].setText(ANR_NUM_UI, (i + 1) + ""); 205 | items[i].setText(ANR_NAME_UI, anrRecord.getPackageName()); 206 | 207 | items[i].setText(CRASH_NUM_UI, ""); 208 | items[i].setText(CRASH_NAME_UI, ""); 209 | } 210 | } 211 | } 212 | } 213 | } else { 214 | TableItem[] items = table.getItems(); 215 | for (TableItem tableItem : items) { 216 | tableItem.dispose(); 217 | } 218 | } 219 | } 220 | 221 | /** 222 | * 获取某个记录的某个字段 223 | * @param index 第几条记录 224 | * @param type anr或crash 225 | * @param pathStr 记录所在的目录 226 | * @return 内容信息 227 | */ 228 | public static String getAnrCrashDetail(int index, int type, String pathStr) { 229 | String resultStr = ""; 230 | List list = readAnrCrashRecord(type, pathStr); 231 | if (list.size() > 0) { 232 | AnrCrash anrCrashRecord = list.get(index - 1); 233 | resultStr = anrCrashRecord.getContentDetail(); 234 | } 235 | 236 | return resultStr; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/com/desay/utils/EmailUtils.java: -------------------------------------------------------------------------------- 1 | package com.desay.utils; 2 | import java.util.Iterator; 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.Properties; 6 | 7 | import javax.activation.DataHandler; 8 | import javax.activation.FileDataSource; 9 | import javax.mail.BodyPart; 10 | import javax.mail.Message; 11 | import javax.mail.Multipart; 12 | import javax.mail.Session; 13 | import javax.mail.Transport; 14 | import javax.mail.internet.InternetAddress; 15 | import javax.mail.internet.MimeBodyPart; 16 | import javax.mail.internet.MimeMessage; 17 | import javax.mail.internet.MimeMultipart; 18 | import javax.mail.internet.MimeUtility; 19 | 20 | public class EmailUtils { 21 | private static String defaultSenderName = "";// 默认的发件人用户名,defaultEntity用得到 22 | private static String defaultSenderPass = "";// 默认的发件人密码,defaultEntity用得到 23 | private static String defaultSmtpHost = "";// 默认的邮件服务器地址,defaultEntity用得到 24 | 25 | private String smtpHost; // 邮件服务器地址 26 | private String sendUserName; // 发件人的用户名 27 | private String sendUserPass; // 发件人密码 28 | 29 | private MimeMessage mimeMsg; // 邮件对象 30 | private Session session; 31 | private Properties props; 32 | private Multipart mp;// 附件添加的组件 33 | private List files = new LinkedList();// 存放附件文件 34 | 35 | private void init() { 36 | if (props == null) { 37 | props = System.getProperties(); 38 | } 39 | props.put("mail.smtp.host", smtpHost); 40 | props.put("mail.smtp.auth", "true"); // 需要身份验证 41 | session = Session.getDefaultInstance(props, null); 42 | // 置true可以在控制台(console)上看到发送邮件的过程 43 | session.setDebug(true); 44 | // 用session对象来创建并初始化邮件对象 45 | mimeMsg = new MimeMessage(session); 46 | // 生成附件组件的实例 47 | mp = new MimeMultipart(); 48 | } 49 | 50 | private EmailUtils(String smtpHost, String sendUserName, String sendUserPass, String to, String cc, String mailSubject, String mailBody, 51 | List attachments) { 52 | this.smtpHost = smtpHost; 53 | this.sendUserName = sendUserName; 54 | this.sendUserPass = sendUserPass; 55 | 56 | init(); 57 | setFrom(sendUserName); 58 | setTo(to); 59 | setCC(cc); 60 | setBody(mailBody); 61 | setSubject(mailSubject); 62 | if (attachments != null) { 63 | for (String attachment : attachments) { 64 | addFileAffix(attachment); 65 | } 66 | } 67 | 68 | } 69 | 70 | /** 71 | * 邮件实体 72 | * 73 | * @param smtpHost 74 | * 邮件服务器地址 75 | * @param sendUserName 76 | * 发件邮件地址 77 | * @param sendUserPass 78 | * 发件邮箱密码 79 | * @param to 80 | * 收件人,多个邮箱地址以半角逗号分隔 81 | * @param cc 82 | * 抄送,多个邮箱地址以半角逗号分隔 83 | * @param mailSubject 84 | * 邮件主题 85 | * @param mailBody 86 | * 邮件正文 87 | * @param attachmentPath 88 | * 附件路径 89 | * @return 90 | */ 91 | public static EmailUtils entity(String smtpHost, String sendUserName, String sendUserPass, String to, String cc, String mailSubject, String mailBody, 92 | List attachments) { 93 | return new EmailUtils(smtpHost, sendUserName, sendUserPass, to, cc, mailSubject, mailBody, attachments); 94 | } 95 | 96 | /** 97 | * 默认邮件实体,用了默认的发送帐号和邮件服务器 98 | * 99 | * @param to 100 | * 收件人,多个邮箱地址以半角逗号分隔 101 | * @param cc 102 | * 抄送,多个邮箱地址以半角逗号分隔 103 | * @param subject 104 | * 邮件主题 105 | * @param body 106 | * 邮件正文 107 | * @param attachment 108 | * 附件全路径 109 | * @return 110 | */ 111 | public static EmailUtils defaultEntity(String to, String cc, String subject, String body, List attachments) { 112 | return new EmailUtils(defaultSmtpHost, defaultSenderName, defaultSenderPass, to, cc, subject, body, attachments); 113 | } 114 | 115 | /** 116 | * 设置邮件主题 117 | * 118 | * @param mailSubject 119 | * @return 120 | */ 121 | private boolean setSubject(String mailSubject) { 122 | try { 123 | mimeMsg.setSubject(mailSubject); 124 | } catch (Exception e) { 125 | return false; 126 | } 127 | return true; 128 | } 129 | 130 | /** 131 | * 设置邮件内容,并设置其为文本格式或HTML文件格式,编码方式为UTF-8 132 | * 133 | * @param mailBody 134 | * @return 135 | */ 136 | private boolean setBody(String mailBody) { 137 | try { 138 | BodyPart bp = new MimeBodyPart(); 139 | bp.setContent("" + mailBody, "text/html;charset=UTF-8"); 140 | // 在组件上添加邮件文本 141 | mp.addBodyPart(bp); 142 | } catch (Exception e) { 143 | System.err.println("设置邮件正文时发生错误!" + e); 144 | return false; 145 | } 146 | return true; 147 | } 148 | 149 | /** 150 | * 添加一个附件 151 | * 152 | * @param filename 153 | * 邮件附件的地址,只能是本机地址而不能是网络地址,否则抛出异常 154 | * @return 155 | */ 156 | public boolean addFileAffix(String filename) { 157 | try { 158 | if (filename != null && filename.length() > 0) { 159 | BodyPart bp = new MimeBodyPart(); 160 | FileDataSource fileds = new FileDataSource(filename); 161 | bp.setDataHandler(new DataHandler(fileds)); 162 | bp.setFileName(MimeUtility.encodeText(fileds.getName(), "utf-8", null)); // 解决附件名称乱码 163 | mp.addBodyPart(bp);// 添加附件 164 | files.add(fileds); 165 | } 166 | } catch (Exception e) { 167 | System.err.println("增加邮件附件:" + filename + "发生错误!" + e); 168 | return false; 169 | } 170 | return true; 171 | } 172 | 173 | /** 174 | * 删除所有附件 175 | * 176 | * @return 177 | */ 178 | public boolean delFileAffix() { 179 | try { 180 | FileDataSource fileds = null; 181 | for (Iterator it = files.iterator(); it.hasNext();) { 182 | fileds = it.next(); 183 | if (fileds != null && fileds.getFile() != null) { 184 | fileds.getFile().delete(); 185 | } 186 | } 187 | } catch (Exception e) { 188 | return false; 189 | } 190 | return true; 191 | } 192 | 193 | /** 194 | * 设置发件人地址 195 | * 196 | * @param from 197 | * 发件人地址 198 | * @return 199 | */ 200 | private boolean setFrom(String from) { 201 | try { 202 | mimeMsg.setFrom(new InternetAddress(from)); 203 | } catch (Exception e) { 204 | return false; 205 | } 206 | return true; 207 | } 208 | 209 | /** 210 | * 设置收件人地址 211 | * 212 | * @param to收件人的地址 213 | * @return 214 | */ 215 | private boolean setTo(String to) { 216 | if (to == null) 217 | return false; 218 | try { 219 | mimeMsg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to)); 220 | } catch (Exception e) { 221 | return false; 222 | } 223 | return true; 224 | } 225 | 226 | /** 227 | * 设置抄送 228 | * 229 | * @param cc 230 | * @return 231 | */ 232 | private boolean setCC(String cc) { 233 | if (cc == null) { 234 | return false; 235 | } 236 | try { 237 | mimeMsg.setRecipients(Message.RecipientType.CC, InternetAddress.parse(cc)); 238 | } catch (Exception e) { 239 | return false; 240 | } 241 | return true; 242 | } 243 | 244 | /** 245 | * 发送邮件 246 | * 247 | * @return 248 | */ 249 | public boolean send() throws Exception { 250 | mimeMsg.setContent(mp); 251 | mimeMsg.saveChanges(); 252 | System.out.println("正在发送邮件...."); 253 | Transport transport = session.getTransport("smtp"); 254 | // 连接邮件服务器并进行身份验证 255 | transport.connect(smtpHost, sendUserName, sendUserPass); 256 | // 发送邮件 257 | transport.sendMessage(mimeMsg, mimeMsg.getRecipients(Message.RecipientType.TO)); 258 | System.out.println("发送邮件成功!"); 259 | transport.close(); 260 | return true; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/com/desay/utils/JfreeChartUtil.java: -------------------------------------------------------------------------------- 1 | package com.desay.utils; 2 | 3 | import java.awt.Font; 4 | import java.awt.image.BufferedImage; 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Date; 10 | import java.util.List; 11 | 12 | import javax.imageio.ImageIO; 13 | 14 | import org.apache.poi.ss.usermodel.Drawing; 15 | import org.apache.poi.xssf.usermodel.XSSFClientAnchor; 16 | import org.apache.poi.xssf.usermodel.XSSFSheet; 17 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 18 | import org.jfree.chart.ChartFactory; 19 | import org.jfree.chart.ChartUtilities; 20 | import org.jfree.chart.JFreeChart; 21 | import org.jfree.chart.axis.CategoryAxis; 22 | import org.jfree.chart.axis.NumberAxis; 23 | import org.jfree.chart.axis.NumberTickUnit; 24 | import org.jfree.chart.labels.StandardCategoryItemLabelGenerator; 25 | import org.jfree.chart.plot.CategoryPlot; 26 | import org.jfree.chart.plot.PlotOrientation; 27 | import org.jfree.chart.renderer.category.BarRenderer; 28 | import org.jfree.data.category.DefaultCategoryDataset; 29 | import org.jfree.ui.RectangleEdge; 30 | 31 | /** 32 | * @author uidq0460 33 | * 生成excel柱状图的工具类 34 | */ 35 | public class JfreeChartUtil { 36 | 37 | /** 38 | * 生成图表文件(图片格式),需要插入excel中 39 | * @param list 数据对象 40 | * @param title 图表标题 41 | * @param xTitle X轴标题 42 | * @param yTitle Y轴标题 43 | * @param num Y轴刻度单位 44 | * @return 图片文件 45 | */ 46 | public static File createChartBar(List list,String title,String xTitle,String yTitle,String num){ 47 | 48 | DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 49 | for(int i=0;list!=null && i " + message.replaceAll("\r\n", "\r\n" + getNowTimeMills() + "> ")); 28 | } 29 | 30 | /** 31 | * 获取设备当前时间 32 | * */ 33 | public static String getNowTimeMills(){ 34 | return new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSS").format(new Date()); 35 | } 36 | 37 | /** 38 | * 获取设备当前时间 39 | * */ 40 | public static String getNowTime(){ 41 | return new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(new Date()); 42 | } 43 | 44 | /** 45 | * 获取设备当前时间 46 | * */ 47 | public static String getNowTimeSpace(){ 48 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); 49 | } 50 | 51 | /** 52 | * 把毫秒转成时间 XX天XX时XX分 53 | * @param time 毫秒数 54 | * @return 时分秒字符串 55 | */ 56 | public static String millisToTimeMinuteCn(long time) { 57 | String timeStr = null; 58 | long dateL = 0; 59 | long hour = 0; 60 | long minute = 0; 61 | long second = 0; 62 | if (time < 1000*60) 63 | return "0天0时0分"; 64 | else { 65 | second = time /1000; 66 | minute = second / 60; 67 | hour = minute /60; 68 | if (second < 60) { 69 | timeStr = "0天0时0分"; 70 | }else if (minute < 60) { 71 | timeStr = "0天0时" + unitFormat(minute) + "分"; 72 | } else if (hour < 24) { 73 | minute = minute - hour*60; 74 | timeStr = "0天" + unitFormat(hour) + "时" + unitFormat(minute) + "分"; 75 | } else {//数字>=3600 000的时候 (超过一天) 76 | dateL = hour / 24; //多少天 77 | hour = minute /60 - dateL*24; //多少小时 78 | minute = minute - dateL*24*60 - hour*60; 79 | timeStr = unitFormat(dateL) + "天" + unitFormat(hour) + "时" + unitFormat(minute) + "分"; 80 | } 81 | } 82 | return timeStr; 83 | } 84 | 85 | /** 86 | * 把毫秒转成时间 XX天XX时XX分XX秒 87 | * @param time 毫秒数 88 | * @return 天 时 分 秒字符串 89 | */ 90 | public static String millisToTimeSecondCn(long time) { 91 | String timeStr = null; 92 | long dateL = 0; 93 | long hour = 0; 94 | long minute = 0; 95 | long second = 0; 96 | if (time < 1000) 97 | return time + "毫秒"; 98 | else { 99 | second = time/1000; 100 | minute = second / 60; 101 | hour = minute /60; 102 | if (second < 60) { 103 | timeStr = second + "秒"; 104 | }else if (minute < 60) { 105 | second = second - minute * 60; 106 | timeStr = minute + "分" + unitFormat(second) + "秒"; 107 | } else if (hour < 24) { 108 | second = second - minute * 60; 109 | minute = minute - hour*60; 110 | timeStr = hour + "时" + unitFormat(minute) + "分" + unitFormat(second) + "秒"; 111 | } else {//数字>=3600 000的时候 (超过一天) 112 | dateL = hour / 24; //多少天 113 | hour = minute/60 - dateL*24; //多少小时 114 | minute = minute - dateL*24*60 - hour*60; 115 | second = second - dateL*24*60*60 - hour*60*60 - minute*60; 116 | timeStr = dateL + "天" + unitFormat(hour) + "时" + unitFormat(minute) + "分" + unitFormat(second) + "秒"; 117 | } 118 | } 119 | return timeStr; 120 | } 121 | 122 | /** 123 | * 把毫秒转成时间hh:mi:ss 124 | * @param time 毫秒数 125 | * @return 时分秒字符串 126 | */ 127 | public static String millisToTimeMinute(long time) { 128 | String timeStr = null; 129 | long hour = 0; 130 | long minute = 0; 131 | long second = 0; 132 | if (time < 1000*60) 133 | return "00:00"; 134 | else { 135 | second = time /1000; 136 | minute = second / 60; 137 | if (second < 60) { 138 | timeStr = "00:00"; 139 | }else if (minute < 60) { 140 | timeStr = "00:" + unitFormat(minute); 141 | }else{//数字>=3600 000的时候 142 | hour = minute /60; 143 | minute = minute % 60; 144 | timeStr = unitFormat(hour) + ":" + unitFormat(minute); 145 | } 146 | } 147 | return timeStr; 148 | } 149 | 150 | /** 151 | * 把毫秒转成时间hh:mi:ss 152 | * @param time 毫秒数 153 | * @return 时分秒字符串 154 | */ 155 | public static String millisToTimeSecond(long time) { 156 | String timeStr = null; 157 | long hour = 0; 158 | long minute = 0; 159 | long second = 0; 160 | if (time < 1000) 161 | return "00:00:00"; 162 | else { 163 | second = time /1000; 164 | minute = second / 60; 165 | if (second < 60) { 166 | timeStr = "00:00:" + unitFormat(second); 167 | }else if (minute < 60) { 168 | second = second % 60; 169 | timeStr = "00:" + unitFormat(minute) + ":" + unitFormat(second); 170 | }else{//数字>=3600 000的时候 171 | hour = minute /60; 172 | minute = minute % 60; 173 | second = second - hour * 3600 - minute * 60; 174 | timeStr = unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second); 175 | } 176 | } 177 | return timeStr; 178 | } 179 | 180 | /*把毫秒转成时间hh:mi:ss.xxx*/ 181 | public static String millisToTime(long time) { 182 | String timeStr = null; 183 | long hour = 0; 184 | long minute = 0; 185 | long second = 0; 186 | long millisecond = 0; 187 | if (time <= 0) 188 | return "00:00:00.000"; 189 | else { 190 | second = time /1000; 191 | minute = second / 60; 192 | millisecond = time % 1000; 193 | if (second < 60) { 194 | 195 | timeStr = "00:00:" + unitFormat(second) + "." + unitFormat2(millisecond); 196 | }else if (minute < 60) { 197 | second = second % 60; 198 | timeStr = "00:" + unitFormat(minute) + ":" + unitFormat(second) + "." + unitFormat2(millisecond); 199 | }else{//数字>=3600 000的时候 200 | hour = minute /60; 201 | minute = minute % 60; 202 | second = second - hour * 3600 - minute * 60; 203 | timeStr = unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second) + "." + unitFormat2(millisecond); 204 | } 205 | } 206 | return timeStr; 207 | } 208 | 209 | public static String unitFormat(long i) {//时分秒的格式转换 210 | String retStr = null; 211 | if (i >= 0 && i < 10) 212 | retStr = "0" + Long.toString(i); 213 | else 214 | retStr = "" + i; 215 | return retStr; 216 | } 217 | 218 | public static String unitFormat2(long i) {//毫秒的格式转换 219 | String retStr = null; 220 | if (i >= 0 && i < 10) 221 | retStr = "00" + Long.toString(i); 222 | else if (i >=10 && i < 100) { 223 | retStr = "0" + Long.toString(i); 224 | } 225 | else 226 | retStr = "" + i; 227 | return retStr; 228 | } 229 | 230 | 231 | /** 232 | * 文件拷贝 233 | * @param fromFile 原始文件 234 | * @param toFile 拷贝到的文件 235 | * @throws IOException 236 | */ 237 | public static void copyFile(File fromFile,File toFile) throws IOException{ 238 | FileInputStream ins = new FileInputStream(fromFile); 239 | FileOutputStream out = new FileOutputStream(toFile); 240 | byte[] b = new byte[1024]; 241 | int n=0; 242 | while((n=ins.read(b))!=-1){ 243 | out.write(b, 0, n); 244 | } 245 | ins.close(); 246 | out.close(); 247 | } 248 | 249 | 250 | /** 251 | * 文件拷贝 252 | * @param fromFile 原始文件 253 | * @param toFile 拷贝到的文件 254 | * @throws IOException 255 | */ 256 | public static void copyFileByPath(String fromPath, String toPath) throws IOException { 257 | File fromFile = new File(fromPath); 258 | File toFile = new File(toPath); 259 | if (fromFile.exists()) { 260 | if (toFile.exists()) { 261 | toFile.delete(); 262 | toFile.createNewFile(); 263 | } 264 | FileInputStream ins = new FileInputStream(fromFile); 265 | FileOutputStream out = new FileOutputStream(toFile); 266 | byte[] b = new byte[1024]; 267 | int n=0; 268 | while((n=ins.read(b))!=-1){ 269 | out.write(b, 0, n); 270 | } 271 | ins.close(); 272 | out.close(); 273 | } 274 | } 275 | 276 | 277 | //创建配置文件 278 | public static void createPropFile(String propFilePath) { 279 | File propFile = new File(propFilePath); 280 | if (propFile.exists()) {//文件存在 281 | propFilePath = propFile.getAbsolutePath(); 282 | System.out.println("propFile = exists"); 283 | } else { 284 | System.out.println("propFile = not exists"); 285 | try { 286 | if (propFile.createNewFile()) { 287 | propFilePath = propFile.getAbsolutePath(); 288 | System.out.println("创建成功"); 289 | } else { 290 | System.out.println("创建失败"); 291 | } 292 | } catch (IOException e1) { 293 | e1.printStackTrace(); 294 | } 295 | } 296 | } 297 | 298 | } 299 | -------------------------------------------------------------------------------- /src/com/desay/utils/RecordUtils.java: -------------------------------------------------------------------------------- 1 | package com.desay.utils; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.ObjectInputStream; 8 | import java.io.ObjectOutputStream; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | import org.eclipse.swt.SWT; 15 | import org.eclipse.swt.widgets.Table; 16 | import org.eclipse.swt.widgets.TableItem; 17 | 18 | import com.desaysv.SvMonkey; 19 | import com.desaysv.bean.Record; 20 | 21 | public class RecordUtils { 22 | 23 | public static final String RECORD_FILE_PATH = SvMonkey.recordFilePath; 24 | public static final String FINISHED_STATUS = "结束";//结束:未检测到Monkey finished(比如设备断电,或出现anr和crash) 25 | public static final String TESTING_STATUS = "进行中";//进行中:测试进行中 26 | public static final String INTERRUPT_STATUS = "中断";//中断:正在测试时软件被关闭后重新打开 27 | public static final String ERROR_STATUS = "执行出错";//执行出错:执行monkey命令时出现执行错误 28 | public static final String PASS_STATUS = "通过"; //通过:检测到Monkey finished且无anr和crash发生 29 | 30 | 31 | public static final int TYPE_COUNTER = 9; 32 | 33 | public static final int NUMBER_TYPE = 0; 34 | public static final int ID_TYPE = 1; 35 | public static final int TEST_STATUS_TYPE = 2; 36 | public static final int CRASH_TYPE = 3; 37 | public static final int ANR_TYPE = 4; 38 | public static final int RUNNING_TIME_TYPE = 5; 39 | public static final int LOG_PATH_TYPE = 6; 40 | public static final int MONKEY_CMD_TYPE = 7; 41 | public static final int ERROR_MESSAGE = 8; 42 | 43 | 44 | /** 45 | * 根据设备ID判断设备是否正在测试 46 | * @param deviceId 设备ID 47 | * @return 正在测试返回true 48 | */ 49 | public static boolean isRunning(String deviceId) { 50 | boolean isRun = false; 51 | 52 | List recordList = readObjectForList(RECORD_FILE_PATH); 53 | if (recordList.size() >0) { 54 | for (Record record : recordList) { 55 | String idStr = record.getDeviceId(); 56 | if (idStr.equals(deviceId)) { 57 | String statusStr = record.getTestStatus(); 58 | if (statusStr.equals(TESTING_STATUS)) { 59 | isRun = true; 60 | } 61 | } 62 | } 63 | } 64 | return isRun; 65 | } 66 | 67 | /** 68 | * 如果正在测试,程序关闭则把记录改为中断 69 | */ 70 | public static void interruptSaveRecord() { 71 | List recordList = readObjectForList(RECORD_FILE_PATH); 72 | List tmpList = Collections.synchronizedList(new ArrayList()); 73 | if (recordList.size() >0) { 74 | for (Record record : recordList) { 75 | String statusStr = record.getTestStatus(); 76 | if (statusStr.equals(TESTING_STATUS)) { 77 | record.setTestStatus(INTERRUPT_STATUS); 78 | } 79 | tmpList.add(record); 80 | } 81 | updateRecordFile(tmpList); 82 | } 83 | } 84 | /** 85 | * 更新某一条记录中的数据 86 | * @param index 第几条记录(从1开始) 87 | * @param type 更新哪一列 88 | * @param valueStr 更新后的值 89 | */ 90 | public static void updateOneRecordInFile(int index, int type, String valueStr) { 91 | List recordList = readObjectForList(RECORD_FILE_PATH);//先把数据读取出来 92 | if (recordList.size() > 0) {//有数据才能进行更新 93 | List tmpList = Collections.synchronizedList(new ArrayList()); 94 | for (int i = 0; i < recordList.size(); i++) {//遍历旧的记录 95 | Record record = recordList.get(i); 96 | if (i == (index-1)) {//找到该条记录 97 | switch (type) { //按类型修改 98 | case TEST_STATUS_TYPE: 99 | record.setTestStatus(valueStr); 100 | break; 101 | case CRASH_TYPE: 102 | record.setCrashCounter(valueStr); 103 | break; 104 | case ANR_TYPE: 105 | record.setAnrCounter(valueStr); 106 | break; 107 | case RUNNING_TIME_TYPE: 108 | record.setRunningTime(valueStr); 109 | break; 110 | case ERROR_MESSAGE: 111 | record.setErrorMessage(valueStr); 112 | break; 113 | default: 114 | break; 115 | } 116 | tmpList.add(record); 117 | } else { 118 | tmpList.add(record); 119 | } 120 | } 121 | updateRecordFile(tmpList);//更新新数据 122 | } 123 | } 124 | 125 | /** 126 | * duqu mou 127 | * @param index 第几条记录(从1开始) 128 | * @param type 更新哪一列 129 | * @param valueStr 更新后的值 130 | */ 131 | public static String readOneRecordInFile(int index, int type) { 132 | List recordList = readObjectForList(RECORD_FILE_PATH);//先把数据读取出来 133 | String recordItemStr = ""; 134 | if (recordList.size() > 0) {//有数据才能进行更新 135 | for (int i = 0; i < recordList.size(); i++) {//遍历旧的记录 136 | Record record = recordList.get(i); 137 | if (i == (index-1)) {//找到该条记录 138 | switch (type) { //按类型修改 139 | case TEST_STATUS_TYPE: 140 | recordItemStr = record.getTestStatus(); 141 | break; 142 | case CRASH_TYPE: 143 | recordItemStr = record.getCrashCounter(); 144 | break; 145 | case ANR_TYPE: 146 | recordItemStr = record.getAnrCounter(); 147 | break; 148 | case RUNNING_TIME_TYPE: 149 | recordItemStr = record.getRunningTime(); 150 | break; 151 | case ERROR_MESSAGE: 152 | recordItemStr = record.getErrorMessage(); 153 | break; 154 | default: 155 | break; 156 | } 157 | } 158 | } 159 | } 160 | return recordItemStr; 161 | } 162 | 163 | /** 164 | * 更新测试记录列表,序列化到本地(不更新界面,界面需要刷新) 165 | * @param table 列表 166 | * @param recordList 记录 167 | */ 168 | public static boolean updateRecordFile(List recordList) { 169 | return writeObject(recordList); 170 | } 171 | 172 | /** 173 | * 把list序列号到文件 174 | * @param list 泛型list 175 | * @param file 文件 176 | * @return 成功返回true 177 | */ 178 | public static boolean writeObject(List list) 179 | { 180 | @SuppressWarnings("unchecked") 181 | T[] array = (T[]) list.toArray(); 182 | try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File(RECORD_FILE_PATH)))) 183 | { 184 | out.writeObject(array); 185 | out.writeObject(null);//需要写个空对象,否则会报错 186 | out.flush(); 187 | out.close(); 188 | return true; 189 | } 190 | catch (IOException e) 191 | { 192 | e.printStackTrace(); 193 | return false; 194 | } 195 | } 196 | 197 | /** 198 | * 从文件反序列化对象到泛型的list 199 | * @param file 文件 200 | * @return 返回list 201 | */ 202 | @SuppressWarnings("unchecked") 203 | public static List readObjectForList(String objPath) 204 | { 205 | File file = new File(objPath); 206 | if (file.exists()) { 207 | E[] object; 208 | try(ObjectInputStream out = new ObjectInputStream(new FileInputStream(file))) 209 | { 210 | object = (E[]) out.readObject(); 211 | out.close(); 212 | return Collections.synchronizedList(new ArrayList(Arrays.asList(object))); 213 | } 214 | catch (IOException e) 215 | { 216 | e.printStackTrace(); 217 | } 218 | catch (ClassNotFoundException e) 219 | { 220 | e.printStackTrace(); 221 | } 222 | } 223 | return Collections.synchronizedList(new ArrayList()); 224 | } 225 | 226 | /** 227 | * 添加一条记录(没有文件则创建新文件) 228 | * @param deviceId 设备ID 229 | * @param monkeyWorkDir 日志路径 230 | * @param monkeyCmdStr monkey命令 231 | * @return 添加成功返回true 232 | */ 233 | public static List addRecord(String deviceId, String monkeyWorkDir, String monkeyCmdStr) { 234 | 235 | File recordFile = new File(RECORD_FILE_PATH); 236 | List recordList = Collections.synchronizedList(new ArrayList()); 237 | if (recordFile.exists()) {//文件已存在,读取原有文件的记录并在最后追加一条记录 238 | recordList = readObjectForList(RECORD_FILE_PATH); 239 | 240 | Record addRecord = new Record(); 241 | addRecord.setNumber((recordList.size() +1) + ""); 242 | addRecord.setDeviceId(deviceId); 243 | addRecord.setLogPath(monkeyWorkDir); 244 | addRecord.setMonkeyCmd(monkeyCmdStr); 245 | 246 | recordList.add(addRecord); 247 | writeObject(recordList); 248 | } else {//文件不存在,则创建文件并添加为第一条记录 249 | try { 250 | if (recordFile.createNewFile()) { 251 | Record addRecord = new Record(); 252 | addRecord.setNumber("1"); 253 | addRecord.setDeviceId(deviceId); 254 | addRecord.setLogPath(monkeyWorkDir); 255 | addRecord.setMonkeyCmd(monkeyCmdStr); 256 | 257 | recordList.add(addRecord); 258 | writeObject(recordList); 259 | } else { 260 | MyUtils.printWithTimeMill("创建record文件失败"); 261 | } 262 | } catch (IOException e) { 263 | // TODO Auto-generated catch block 264 | e.printStackTrace(); 265 | } 266 | } 267 | return recordList; 268 | } 269 | 270 | /** 271 | * 删除所有测试记录,进行中的记录除外. 272 | * @param table 记录所在表格 273 | */ 274 | public static void removeAllRecordFormFile() { 275 | 276 | List recordList = readObjectForList(RECORD_FILE_PATH); 277 | if (recordList.size() > 0) { 278 | List tmpList = Collections.synchronizedList(new ArrayList()); 279 | for (Record record : recordList) { 280 | if (record.getTestStatus().equals(TESTING_STATUS)) {//进行中的测试记录不能删除 281 | tmpList.add(record); 282 | } 283 | } 284 | //TODO 285 | updateRecordFile(tmpList);//直接把新的list写到文件,会覆盖旧的内容 286 | } 287 | } 288 | 289 | /** 290 | * 删除测试记录 291 | * @param table 记录所在表格 292 | * @param index 要删除的记录的序号(从1开始) 293 | */ 294 | public static void removeRecordFormFile(int index) { 295 | File recordFile = new File(RECORD_FILE_PATH); 296 | 297 | if (index > 0) { 298 | List recordList = readObjectForList(RECORD_FILE_PATH); 299 | if (recordList.size() > 1) { 300 | recordList.remove(index -1); 301 | for (int i = 0; i < recordList.size(); i++) { 302 | Record record = recordList.get(i); 303 | record.setNumber((i+1) + ""); 304 | } 305 | updateRecordFile(recordList); 306 | } else { 307 | if (recordFile.exists()) { 308 | recordFile.delete(); 309 | } 310 | } 311 | } 312 | } 313 | 314 | /** 315 | * 更新table显示 316 | * @param table 317 | */ 318 | public static void updateTableUi(Table table) { 319 | List recordList = readObjectForList(RECORD_FILE_PATH); 320 | if (recordList.size() > 0) { 321 | TableItem[] items = table.getItems();//当前table总共有多少行 322 | if (items.length > recordList.size()) {//表格多了,把记录填入表格后多余的表格dispose掉 323 | for (int i = 0; i < items.length; i++) { 324 | if (i 0) { 357 | for (TableItem tableItem : items) { 358 | tableItem.dispose(); 359 | } 360 | } 361 | } 362 | } 363 | 364 | 365 | } 366 | -------------------------------------------------------------------------------- /src/com/desaysv/KeyCodePc.java: -------------------------------------------------------------------------------- 1 | package com.desaysv; 2 | 3 | public class KeyCodePc { 4 | /** 5 | * 小键盘 + 6 | */ 7 | public static final int SMALL_KEY_BOARD_ADD = 16777259; 8 | /** 9 | * 小键盘 - 10 | */ 11 | public static final int SMALL_KEY_BOARD_SUB = 16777261; 12 | /** 13 | * 小键盘 * 14 | */ 15 | public static final int SMALL_KEY_BOARD_MUL = 16777258; 16 | /** 17 | * 小键盘 / 18 | */ 19 | public static final int SMALL_KEY_BOARD_DEV = 16777263; 20 | 21 | /** 22 | * 小键盘 0 23 | */ 24 | 25 | public static final int SMALL_KEY_BOARD_0 = 16777264; 26 | /** 27 | * 小键盘 1 28 | */ 29 | 30 | public static final int SMALL_KEY_BOARD_1 = 16777265; 31 | /** 32 | * 小键盘 2 33 | */ 34 | 35 | public static final int SMALL_KEY_BOARD_2 = 16777266; 36 | /** 37 | * 小键盘 3 38 | */ 39 | 40 | public static final int SMALL_KEY_BOARD_3 = 16777267; 41 | /** 42 | * 小键盘 4 43 | */ 44 | 45 | public static final int SMALL_KEY_BOARD_4 = 16777268; 46 | /** 47 | * 小键盘 5 48 | */ 49 | 50 | public static final int SMALL_KEY_BOARD_5 = 16777269; 51 | /** 52 | * 小键盘 6 53 | */ 54 | 55 | public static final int SMALL_KEY_BOARD_6 = 16777270; 56 | /** 57 | * 小键盘 7 58 | */ 59 | 60 | public static final int SMALL_KEY_BOARD_7 = 16777271; 61 | /** 62 | * 小键盘 8 63 | */ 64 | 65 | public static final int SMALL_KEY_BOARD_8 = 16777272; 66 | /** 67 | * 小键盘 9 68 | */ 69 | 70 | public static final int SMALL_KEY_BOARD_9 = 16777273; 71 | 72 | /** 73 | * 小键盘 回车 74 | */ 75 | public static final int SMALL_KEY_BOARD_ENTER = 16777296; 76 | 77 | /** 78 | * 小键盘 . 79 | */ 80 | public static final int SMALL_KEY_BOARD_DOT = 16777262; 81 | 82 | /** 83 | * 小键盘 num lock 84 | */ 85 | public static final int SMALL_KEY_BOARD_NUM_LOCK = 16777299; 86 | 87 | /** 88 | * 小键盘 Home 89 | */ 90 | public static final int SMALL_KEY_BOARD_HOME = 16777223; 91 | 92 | /** 93 | * 小键盘 End 94 | */ 95 | public static final int SMALL_KEY_BOARD_END = 16777224; 96 | 97 | /** 98 | * 小键盘 pause break 99 | */ 100 | public static final int SMALL_KEY_BOARD_PAUSE_BREAK = 16777301; 101 | 102 | /** 103 | * 小键盘 scroll lock 104 | */ 105 | public static final int SMALL_KEY_BOARD_SCROLL_LOCK = 16777300; 106 | 107 | /** 108 | * 退格 109 | */ 110 | public static final int BACK = 8; 111 | /** 112 | * 空格键 113 | */ 114 | public static final int SPACE = 32; 115 | 116 | /** 117 | * ESC键 118 | */ 119 | public static final int KEY_ESC = 27; 120 | /** 121 | * F1键 122 | */ 123 | public static final int KEY_F1 = 16777226; 124 | /** 125 | * F2键 126 | */ 127 | public static final int KEY_F2 = 16777227; 128 | /** 129 | * F3键 130 | */ 131 | public static final int KEY_F3 = 16777228; 132 | /** 133 | * F4键 134 | */ 135 | public static final int KEY_F4 = 16777229; 136 | /** 137 | * F5键 138 | */ 139 | public static final int KEY_F5 = 16777230; 140 | /** 141 | * F6键 142 | */ 143 | public static final int KEY_F6 = 16777231; 144 | /** 145 | * F7键 146 | */ 147 | public static final int KEY_F7 = 16777232; 148 | /** 149 | * F8键 150 | */ 151 | public static final int KEY_F8 = 16777233; 152 | /** 153 | * F9键 154 | */ 155 | public static final int KEY_F9 = 16777234; 156 | /** 157 | * F10键 158 | */ 159 | public static final int KEY_F10= 16777235; 160 | /** 161 | * F11键 162 | */ 163 | public static final int KEY_F11 = 16777236; 164 | /** 165 | * F12键 166 | */ 167 | public static final int KEY_F12 = 16777237; 168 | /** 169 | * `键 170 | */ 171 | public static final int KEY_DRIP = 96; 172 | /** 173 | * 1键 174 | */ 175 | public static final int NUM_1 = 49; 176 | /** 177 | * 2键 178 | */ 179 | public static final int NUM_2 = 50; 180 | /** 181 | * 3键 182 | */ 183 | public static final int NUM_3 = 51; 184 | /** 185 | * 4键 186 | */ 187 | public static final int NUM_4= 52; 188 | /** 189 | * 5键 190 | */ 191 | public static final int NUM_5= 53; 192 | /** 193 | * 6键 194 | */ 195 | public static final int NUM_6= 54; 196 | /** 197 | * 7键 198 | */ 199 | public static final int NUM_7= 55; 200 | /** 201 | * 8键 202 | */ 203 | public static final int NUM_8= 56; 204 | /** 205 | * 9键 206 | */ 207 | public static final int NUM_9= 57; 208 | /** 209 | * 0键 210 | */ 211 | public static final int NUM_0= 48; 212 | /** 213 | * -键 214 | */ 215 | public static final int KEY_SUB = 45; 216 | /** 217 | * =键 218 | */ 219 | public static final int KEY_ADD = 61; 220 | /** 221 | * tab键 222 | */ 223 | public static final int KEY_TAB = 9; 224 | /** 225 | * Q键 226 | */ 227 | public static final int KEY_Q = 113; 228 | /** 229 | * W键 230 | */ 231 | public static final int KEY_W = 119; 232 | /** 233 | * E键 234 | */ 235 | public static final int KEY_E = 101; 236 | /** 237 | * R键 238 | */ 239 | public static final int KEY_R = 114; 240 | /** 241 | * T键 242 | */ 243 | public static final int KEY_T = 116; 244 | /** 245 | * Y键 246 | */ 247 | public static final int KEY_Y = 121; 248 | /** 249 | * U键 250 | */ 251 | public static final int KEY_U = 117; 252 | /** 253 | * I键 254 | */ 255 | public static final int KEY_I = 105; 256 | /** 257 | * O键 258 | */ 259 | public static final int KEY_O = 111; 260 | /** 261 | * P键 262 | */ 263 | public static final int KEY_P = 112; 264 | /** 265 | * [键 266 | */ 267 | public static final int LEFT_BRACKETS = 91; 268 | /** 269 | * ]键 270 | */ 271 | public static final int RIGHT_BRACKETS = 93; 272 | /** 273 | * \键 274 | */ 275 | public static final int TURN_BEVEL = 92; 276 | /** 277 | * caps lock键 278 | */ 279 | public static final int CAPS_LOCK = 16777298; 280 | /** 281 | * A键 282 | */ 283 | public static final int KEY_A = 97; 284 | /** 285 | * S键 286 | */ 287 | public static final int KEY_S = 115; 288 | /** 289 | * D键 290 | */ 291 | public static final int KEY_D = 100; 292 | /** 293 | * F键 294 | */ 295 | public static final int KEY_F = 102; 296 | /** 297 | * G键 298 | */ 299 | public static final int KEY_G = 103; 300 | /** 301 | * H键 302 | */ 303 | public static final int KEY_H = 104; 304 | /** 305 | * J键 306 | */ 307 | public static final int KEY_J = 106; 308 | /** 309 | * K键 310 | */ 311 | public static final int KEY_K= 107; 312 | /** 313 | * L键 314 | */ 315 | public static final int KEY_L= 108; 316 | /** 317 | * ;键 318 | */ 319 | public static final int SEMICOLON= 59; 320 | /** 321 | * '键 322 | */ 323 | public static final int UP_COMMA= 39; 324 | /** 325 | * 回车键 326 | */ 327 | public static final int ENTER= 13; 328 | /** 329 | * SHIFT键 330 | */ 331 | public static final int SHIFT= 131072; 332 | /** 333 | * Z键 334 | */ 335 | public static final int KEY_Z= 122; 336 | /** 337 | * X键 338 | */ 339 | public static final int KEY_X= 120; 340 | /** 341 | * C键 342 | */ 343 | public static final int KEY_C= 99; 344 | /** 345 | * V键 346 | */ 347 | public static final int KEY_V= 118; 348 | /** 349 | * B键 350 | */ 351 | public static final int KEY_B= 98; 352 | /** 353 | * N键 354 | */ 355 | public static final int KEY_N= 110; 356 | /** 357 | * M键 358 | */ 359 | public static final int KEY_M= 109; 360 | /** 361 | * ,键 362 | */ 363 | public static final int COMMA = 44; 364 | /** 365 | * .键 366 | */ 367 | public static final int DOT = 46; 368 | /** 369 | * /键 370 | */ 371 | public static final int BEVEL= 47; 372 | /** 373 | * CTRL键 374 | */ 375 | public static final int KEY_CTRL = 262144; 376 | /** 377 | * ALT键 378 | */ 379 | public static final int KEY_ALT = 65536; 380 | /** 381 | * 上箭头 382 | */ 383 | public static final int KEY_UP = 16777217; 384 | /** 385 | * 下箭头 386 | */ 387 | public static final int KEY_DOWN = 16777218; 388 | /** 389 | * 左箭头 390 | */ 391 | public static final int KEY_LEFT = 16777219; 392 | /** 393 | * 右箭头 394 | */ 395 | public static final int KEY_RIGHT = 16777220; 396 | /** 397 | * SCROLL键 398 | */ 399 | public static final int KEY_SCROLL = 16777300; 400 | /** 401 | * PAUSE键 402 | */ 403 | public static final int KEY_PAUSE= 16777301; 404 | /** 405 | * INSERT键 406 | */ 407 | public static final int KEY_INSERT = 16777225; 408 | /** 409 | * DELETE键 410 | */ 411 | public static final int KEY_DELETE= 127; 412 | /** 413 | * HOME键 414 | */ 415 | public static final int KEY_HOME = 16777223; 416 | /** 417 | * END键 418 | */ 419 | public static final int KEY_END= 16777224; 420 | /** 421 | * PAGE UP键 422 | */ 423 | public static final int PAGE_UP = 16777221; 424 | /** 425 | * PAGE DOWN键 426 | */ 427 | public static final int PAGE_DOWN= 16777222; 428 | } 429 | -------------------------------------------------------------------------------- /src/com/desaysv/LogcatThread.java: -------------------------------------------------------------------------------- 1 | package com.desaysv; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.BufferedWriter; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.io.InputStreamReader; 8 | 9 | import com.desay.utils.MyUtils; 10 | 11 | public class LogcatThread extends Thread { 12 | /** 13 | * 停止线程 14 | */ 15 | volatile boolean stop = false; 16 | private String monkeyWorkDir; 17 | private String idStr; 18 | 19 | /** 20 | * 创建获取logcat日志的线程 21 | * @param idStr 要获取logcat日志的设备ID 22 | * @param dir 保存目录 23 | */ 24 | public LogcatThread(String idStr, String dir) { 25 | this.monkeyWorkDir = dir; 26 | this.idStr = idStr; 27 | this.setDaemon(true); 28 | } 29 | 30 | public void run() { 31 | FileWriter fw = null; 32 | BufferedWriter bw = null; 33 | BufferedReader inputStream = null; 34 | BufferedReader errorReader = null; 35 | try { 36 | try { 37 | // String logcatCmd = "adb -s " + idStr + " shell logcat -v threadTime -b all";//同时输出缓冲区日志 38 | String logcatCmd = "adb -s " + idStr + " shell logcat -v threadTime";//不输出缓冲区日志 39 | MyUtils.printWithTimeMill(logcatCmd); 40 | Process pLogcat = Runtime.getRuntime().exec(logcatCmd); 41 | inputStream = new BufferedReader(new InputStreamReader(pLogcat.getInputStream())); 42 | errorReader = new BufferedReader(new InputStreamReader(pLogcat.getErrorStream())); 43 | String line; 44 | String logcatPathStr = monkeyWorkDir + "\\logcat.txt"; 45 | fw = new FileWriter(logcatPathStr, true); 46 | bw = new BufferedWriter(fw); 47 | while((line = inputStream.readLine()) != null && stop==false) { 48 | if (!line.matches("\\s*")) {//一个或多个空格 49 | bw.append(MyUtils.getNowTimeMills() + "> " + line + "\r\n"); 50 | bw.flush(); 51 | } 52 | } 53 | while((line = errorReader.readLine()) != null && stop==false) { 54 | MyUtils.printWithTimeMill(logcatCmd); 55 | if (!line.matches("\\s*")) {//一个或多个空格 56 | bw.append(MyUtils.getNowTimeMills() + "> " + line + "\r\n"); 57 | bw.flush(); 58 | } 59 | } 60 | // pLogcat.waitFor(); 61 | bw.close(); 62 | fw.close(); 63 | inputStream.close(); 64 | errorReader.close(); 65 | Thread.sleep(1); 66 | MyUtils.printWithTimeMill(idStr + " LogcatThread finished in normal"); 67 | } catch (IOException e) { 68 | MyUtils.printWithTimeMill(idStr + " LogcatThread IOException" + e.getMessage()); 69 | if (fw != null) { 70 | try { 71 | fw.close(); 72 | } catch (IOException e1) { 73 | } 74 | } 75 | if (bw != null) { 76 | try { 77 | bw.close(); 78 | } catch (IOException e1) { 79 | } 80 | } 81 | if (inputStream != null) { 82 | try { 83 | inputStream.close(); 84 | } catch (IOException e1) { 85 | } 86 | } 87 | if (errorReader != null) { 88 | try { 89 | errorReader.close(); 90 | } catch (IOException e1) { 91 | } 92 | } 93 | stop = true; // 在异常处理代码中修改共享变量的状态 94 | } 95 | } catch (InterruptedException ie) { 96 | MyUtils.printWithTimeMill(idStr + " close logcat input stream in LogcatThread InterruptedException"); 97 | stop = true; // 在异常处理代码中修改共享变量的状态 98 | if (fw != null) { 99 | try { 100 | fw.close(); 101 | } catch (IOException e) { 102 | } 103 | } 104 | if (bw != null) { 105 | try { 106 | bw.close(); 107 | } catch (IOException e) { 108 | } 109 | } 110 | if (inputStream != null) { 111 | try { 112 | inputStream.close(); 113 | } catch (IOException e) { 114 | } 115 | } 116 | if (errorReader != null) { 117 | try { 118 | errorReader.close(); 119 | } catch (IOException e) { 120 | } 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/com/desaysv/MyDialog.java: -------------------------------------------------------------------------------- 1 | package com.desaysv; 2 | 3 | 4 | import org.eclipse.jface.dialogs.Dialog; 5 | import org.eclipse.jface.dialogs.IDialogConstants; 6 | import org.eclipse.swt.SWT; 7 | import org.eclipse.swt.layout.RowData; 8 | import org.eclipse.swt.layout.RowLayout; 9 | import org.eclipse.swt.widgets.Composite; 10 | import org.eclipse.swt.widgets.Control; 11 | import org.eclipse.swt.widgets.Label; 12 | import org.eclipse.swt.widgets.Shell; 13 | import org.eclipse.swt.widgets.Text; 14 | 15 | 16 | public class MyDialog extends Dialog { 17 | 18 | private String textValue;// 用来保存Text值的变量 19 | private Text text; // 将文本写为类实例变量,否则其他方法无法访问它 20 | private String lblStr; // 将文本写为类实例变量,否则其他方法无法访问它 21 | 22 | protected MyDialog(Shell parentShell) { 23 | super(parentShell); 24 | this.lblStr = "保存log路径:"; 25 | } 26 | 27 | protected MyDialog(Shell parentShell, String lableStr) { 28 | super(parentShell); 29 | this.lblStr = lableStr; 30 | } 31 | 32 | public String getTextValue() { 33 | return this.textValue; 34 | } 35 | 36 | public void setTextValue(String valueStr) { 37 | this.textValue = valueStr; 38 | } 39 | 40 | // 在这个方法里构建Dialog中的界面内容 41 | protected Control createDialogArea(Composite parent) { 42 | Composite topComp = new Composite(parent, SWT.None); 43 | RowLayout layout = new RowLayout(); 44 | layout.marginWidth = 10; 45 | layout.marginHeight = 10; 46 | 47 | topComp.setLayout(layout); 48 | new Label(topComp, SWT.NONE).setText(lblStr); 49 | text = new Text(topComp, SWT.BORDER); 50 | // 把textValue设给Text作为初值,这时要注意对textValue作空值判断,给文本框设置空值是会出错的 51 | text.setText(textValue == null ? "" : textValue); 52 | text.setLayoutData(new RowData(300, -1)); 53 | 54 | return topComp; 55 | } 56 | 57 | // 单击对话框底部按钮会执行此方法,参数buttonId是被单击按钮的ID值。 58 | protected void buttonPressed(int buttonId) { 59 | if (buttonId == IDialogConstants.OK_ID)// 如果单击确定按钮,则将值保存到变量 60 | { 61 | textValue = text.getText(); 62 | } 63 | super.buttonPressed(buttonId); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/com/desaysv/Settings.java: -------------------------------------------------------------------------------- 1 | package com.desaysv; 2 | 3 | import java.io.File; 4 | 5 | import org.eclipse.swt.SWT; 6 | import org.eclipse.swt.custom.StackLayout; 7 | import org.eclipse.swt.events.SelectionAdapter; 8 | import org.eclipse.swt.events.SelectionEvent; 9 | import org.eclipse.swt.layout.GridData; 10 | import org.eclipse.swt.layout.GridLayout; 11 | import org.eclipse.swt.widgets.Button; 12 | import org.eclipse.swt.widgets.Combo; 13 | import org.eclipse.swt.widgets.Composite; 14 | import org.eclipse.swt.widgets.Display; 15 | import org.eclipse.swt.widgets.Group; 16 | import org.eclipse.swt.widgets.Label; 17 | import org.eclipse.swt.widgets.List; 18 | import org.eclipse.swt.widgets.Shell; 19 | 20 | import com.desay.utils.PropUtil; 21 | 22 | public class Settings { 23 | 24 | private List list; 25 | 26 | private Composite composite; 27 | 28 | private StackLayout stacklayout; 29 | 30 | private Composite comp1; 31 | 32 | private Composite comp2; 33 | 34 | private Composite comp3; 35 | 36 | private Composite comp4; 37 | 38 | private Composite comp5; 39 | 40 | private String settingsPropFilePath = "";//配置文件路径 41 | 42 | private String endGetLogs = ""; 43 | private String sendEmail = ""; 44 | private String endTakePic = ""; 45 | private String getLogCmdFilePath = System.getProperty("user.dir") + "\\getLogCmds.txt"; 46 | 47 | //PropUtil.setProperties(propFilePath, "saveOutputStutes", "on", true); 48 | 49 | public Settings(String propPath) { 50 | settingsPropFilePath = propPath; 51 | if (settingsPropFilePath != null && !settingsPropFilePath.equals("")) { 52 | //读取之前的设置,如果为空则设置为默认 53 | endGetLogs = PropUtil.getValueOfProp("endGetLogs", settingsPropFilePath); 54 | if (endGetLogs != null && !endGetLogs.equals("")) { 55 | } else { 56 | PropUtil.setProperties(settingsPropFilePath, "endGetLogs", "false", true); 57 | endGetLogs = "false"; 58 | } 59 | sendEmail = PropUtil.getValueOfProp("sendEmail", settingsPropFilePath); 60 | if (sendEmail != null && !sendEmail.equals("")) { 61 | } else { 62 | PropUtil.setProperties(settingsPropFilePath, "sendEmail", "false", true); 63 | sendEmail = "false"; 64 | } 65 | endTakePic = PropUtil.getValueOfProp("endTakePic", settingsPropFilePath); 66 | if (endTakePic != null && !endTakePic.equals("")) { 67 | 68 | } else { 69 | PropUtil.setProperties(settingsPropFilePath, "endTakePic", "false", true); 70 | endTakePic = "false"; 71 | } 72 | 73 | getLogCmdFilePath = PropUtil.getValueOfProp("getLogCmdFilePath", settingsPropFilePath); 74 | if (getLogCmdFilePath != null && !getLogCmdFilePath.equals("")) { 75 | 76 | } else { 77 | String defaultPathStr = System.getProperty("user.dir") + "\\getLogCmds.txt"; 78 | PropUtil.setProperties(settingsPropFilePath, "getLogCmdFilePath", defaultPathStr, true); 79 | getLogCmdFilePath = defaultPathStr; 80 | } 81 | } 82 | 83 | final Display display = Display.getDefault(); 84 | final Shell shell = new Shell(new Shell(display.getActiveShell(), SWT.APPLICATION_MODAL)); 85 | shell.setLocation(600, 200); 86 | shell.setSize(500, 460); 87 | shell.setText("设置"); 88 | // 设置容器为7列 89 | shell.setLayout(new GridLayout(7, false)); 90 | // 在Shell上定义List,并对List进行布局 91 | { 92 | list = new List(shell, SWT.BORDER); 93 | // 设置列表项 94 | list.setItems(new String[] { "常规"}); 95 | // list.setItems(new String[] { "通知", "音频", "被拒", "常规通知", "链接" }); 96 | // 使List垂直充满 97 | GridData gridList = new GridData(GridData.FILL_VERTICAL); 98 | gridList.horizontalSpan = 3;//水平强占3列 99 | gridList.widthHint = 100; //使宽度为100个像素 100 | // 设置List距离shell容器左边框为5个像素 101 | gridList.horizontalIndent = 5; 102 | list.setLayoutData(gridList); 103 | } 104 | // 在Shell上定义Composite,并对Composite进行布局 105 | { 106 | composite = new Composite(shell, SWT.BORDER); 107 | // 使composite面板双向充满 108 | GridData gridComposite = new GridData(GridData.FILL_BOTH); 109 | gridComposite.horizontalSpan = 3;//水平强占3列 110 | composite.setLayoutData(gridComposite); 111 | // 定义一个堆栈布局 112 | stacklayout = new StackLayout(); 113 | // 在composite面板上应用堆栈布局 114 | composite.setLayout(stacklayout); 115 | // 调用自定义方法 116 | comp1Content(); 117 | // comp2Content(); 118 | // comp3Content(); 119 | // comp4Content(); 120 | // comp5Content(); 121 | 122 | } 123 | // 在Shell上定义单个按钮,并对其进行布局 124 | { 125 | final Button helpButton = new Button(shell, SWT.NONE); 126 | helpButton.setText("帮助"); 127 | GridData gridHelp = new GridData(); 128 | // 使help按钮水平抢占3列 129 | gridHelp.horizontalSpan = 3; 130 | // 使宽度为90个像素 131 | gridHelp.widthHint = 90; 132 | // 设置帮助按钮距离shell容器左边框为5个像素 133 | gridHelp.horizontalIndent = 5; 134 | helpButton.setLayoutData(gridHelp); 135 | 136 | final Button okButton = new Button(shell, SWT.NONE); 137 | okButton.setText("确定"); 138 | GridData gridOk = new GridData(); 139 | gridOk.horizontalIndent = 180; 140 | gridOk.horizontalSpan = 2; 141 | gridOk.widthHint = 90; 142 | okButton.setLayoutData(gridOk); 143 | okButton.addSelectionListener(new SelectionAdapter() { 144 | @Override 145 | public void widgetSelected(SelectionEvent e) { 146 | if (endGetLogs != null && !endGetLogs.equals("")) { 147 | //失败播放通知 148 | PropUtil.setProperties(settingsPropFilePath, "endGetLogs", endGetLogs, true); 149 | } 150 | if (sendEmail != null && !sendEmail.equals("")) { 151 | //失败播放通知 152 | PropUtil.setProperties(settingsPropFilePath, "sendEmail", sendEmail, true); 153 | } 154 | if (endTakePic != null && !endTakePic.equals("")) { 155 | //是否一致播放 156 | PropUtil.setProperties(settingsPropFilePath, "endTakePic", endTakePic, true); 157 | } 158 | if (getLogCmdFilePath != null && !getLogCmdFilePath.equals("")) { 159 | //是否一致播放 160 | PropUtil.setProperties(settingsPropFilePath, "getLogCmdFilePath", getLogCmdFilePath, true); 161 | } 162 | shell.dispose(); 163 | } 164 | }); 165 | 166 | final Button cancelButton = new Button(shell, SWT.NONE); 167 | cancelButton.setText("取消"); 168 | GridData gridCancel = new GridData(); 169 | gridCancel.horizontalSpan = 2; 170 | gridCancel.widthHint = 90; 171 | cancelButton.setLayoutData(gridCancel); 172 | cancelButton.addSelectionListener(new SelectionAdapter() { 173 | @Override 174 | public void widgetSelected(SelectionEvent e) { 175 | shell.dispose(); 176 | } 177 | }); 178 | 179 | } 180 | // 将comp1面板设置为最设置为堆栈的最顶端显示 181 | stacklayout.topControl = comp1; 182 | 183 | list.addSelectionListener(new SelectionAdapter() { 184 | public void widgetSelected(SelectionEvent e) { 185 | int selectionIndex = list.getSelectionIndex(); 186 | if (selectionIndex == 0) { 187 | stacklayout.topControl = comp1; 188 | } else if (selectionIndex == 1) { 189 | stacklayout.topControl = comp2; 190 | } else if (selectionIndex == 2) { 191 | stacklayout.topControl = comp3; 192 | } else if (selectionIndex == 3) 193 | stacklayout.topControl = comp4; 194 | else if (selectionIndex == 4) 195 | stacklayout.topControl = comp5; 196 | composite.layout(); 197 | } 198 | }); 199 | shell.open(); 200 | shell.layout(); 201 | while (!shell.isDisposed()) { 202 | if (!display.readAndDispatch()) 203 | display.sleep(); 204 | } 205 | } 206 | 207 | // 通知面板的布局设置 208 | private void comp1Content() { 209 | // 在composite面板上定义comp1面板 210 | comp1 = new Composite(composite, SWT.BORDER); 211 | // 在comp1上采用GridLayout布局,将comp1容器设置5列 212 | comp1.setLayout(new GridLayout(5, false)); 213 | // 定义常规标签,并对其进行布局 214 | { 215 | final Label labRoutine = new Label(comp1, SWT.NONE); 216 | labRoutine.setText("常规"); 217 | // 用GridData对labRoutine进行布局 218 | GridData gridRoutine = new GridData(); 219 | // 水平抢占2列 220 | gridRoutine.horizontalSpan = 2; 221 | // 距离comp1顶端10个像素 222 | gridRoutine.verticalIndent = 10; 223 | labRoutine.setLayoutData(gridRoutine); 224 | } 225 | // 定义labSeparator分隔符标签,并对其进行布局 226 | { 227 | final Label labSeparator = new Label(comp1, SWT.SEPARATOR 228 | | SWT.HORIZONTAL); 229 | GridData gridSeparator = new GridData(GridData.FILL_HORIZONTAL); 230 | gridSeparator.horizontalSpan = 5; 231 | // 垂直抢占5列 232 | gridSeparator.verticalSpan = 5; 233 | labSeparator.setLayoutData(gridSeparator); 234 | } 235 | // 定义button1、button2复选框按钮,并对其进行布局 236 | { 237 | /** 238 | * 因为button1、button2在comp1上的布局相同 239 | * 所以采用下面的同一布局方式设置相同的属性参数进行布局 240 | */ 241 | GridData gridButton = new GridData(GridData.FILL_HORIZONTAL); 242 | // 水平抢占5列 243 | gridButton.horizontalSpan = 5; 244 | // 垂直抢占5列 245 | gridButton.verticalSpan = 5; 246 | // 距离comp1左边框20个像素 247 | gridButton.horizontalIndent = 20; 248 | final Button button1 = new Button(comp1, SWT.CHECK); 249 | button1.setText("测试结束截图(&S)"); 250 | // 对button1进行布局 251 | button1.setLayoutData(gridButton); 252 | if (endTakePic.equals("true")) { 253 | button1.setSelection(true); 254 | } else { 255 | button1.setSelection(false); 256 | } 257 | button1.addSelectionListener(new SelectionAdapter() { 258 | @Override 259 | public void widgetSelected(SelectionEvent e) { 260 | if (button1.getSelection()) { 261 | endTakePic = "true"; 262 | } else { 263 | endTakePic = "false"; 264 | } 265 | } 266 | }); 267 | // 定义button2复选框按钮,并对其进行布局 268 | final Button button2 = new Button(comp1, SWT.CHECK); 269 | button2.setText("测试结束抓log(&U)"); 270 | if (endGetLogs.equals("true")) { 271 | button2.setSelection(true); 272 | } else { 273 | button2.setSelection(false); 274 | } 275 | button2.addSelectionListener(new SelectionAdapter() { 276 | @Override 277 | public void widgetSelected(SelectionEvent e) { 278 | if (button2.getSelection()) { 279 | endGetLogs = "true"; 280 | } else { 281 | endGetLogs = "false"; 282 | } 283 | } 284 | }); 285 | /** 286 | * 由于button1和button2布局完全相同所以可以通过 287 | * button2.setLayoutData(gridDataC)来设置button2 的布局 288 | */ 289 | button2.setLayoutData(gridButton); 290 | } 291 | // 定义Group分组框,并对其进行布局 292 | { 293 | final Group group = new Group(comp1, SWT.NONE); 294 | group.setText("抓log命令文件路径:"); 295 | GridData gridGroup = new GridData(GridData.FILL_HORIZONTAL); 296 | 297 | gridGroup.horizontalSpan = 3; 298 | gridGroup.verticalSpan = 5; 299 | gridGroup.horizontalIndent = 20; 300 | group.setLayoutData(gridGroup); 301 | // 设置group分组框上组件,并对组件进行布局 302 | { 303 | group.setLayout(new GridLayout()); 304 | /** 305 | * 四个复选按钮在group分组框上布局完全相同, 306 | * 故此可用下面同一种布局方式设置相同的属性参数进行布局 307 | */ 308 | GridData gridDataButton = new GridData(GridData.FILL_HORIZONTAL); 309 | gridDataButton.verticalSpan = 6; 310 | gridDataButton.horizontalIndent = 6; 311 | final org.eclipse.swt.widgets.Text cmdsPath = new org.eclipse.swt.widgets.Text(group, SWT.NONE); 312 | cmdsPath.setLayoutData(gridDataButton); 313 | cmdsPath.setText(getLogCmdFilePath); 314 | final Button buttonOne = new Button(group, SWT.NONE); 315 | buttonOne.setText("选择文件"); 316 | // buttonOne.setLayoutData(gridDataButton); 317 | buttonOne.addSelectionListener(new SelectionAdapter() { 318 | @Override 319 | public void widgetSelected(SelectionEvent e) { 320 | String filePath = WinUtil.fileSeleteDialog(Display.getDefault().getActiveShell(), System.getProperty("user.dir")); 321 | System.out.println(filePath); 322 | if (!filePath.equals("")) { 323 | cmdsPath.setText(filePath); 324 | getLogCmdFilePath = filePath; 325 | } 326 | //TODO: 327 | } 328 | }); 329 | final Button buttonTwo = new Button(group, SWT.NONE); 330 | buttonTwo.setText("跳转到该目录"); 331 | buttonTwo.addSelectionListener(new SelectionAdapter() { 332 | @Override 333 | public void widgetSelected(SelectionEvent e) { 334 | File logCmdFile = new File(getLogCmdFilePath); 335 | WinUtil.openDir(logCmdFile.getParent()); 336 | } 337 | }); 338 | } 339 | } 340 | 341 | { 342 | GridData gridButtonEmail = new GridData(GridData.FILL_HORIZONTAL); 343 | // 水平抢占5列 344 | gridButtonEmail.horizontalSpan = 5; 345 | // 垂直抢占5列 346 | gridButtonEmail.verticalSpan = 5; 347 | // 距离comp1左边框20个像素 348 | gridButtonEmail.horizontalIndent = 20; 349 | // 定义button2复选框按钮,并对其进行布局 350 | final Button btnSendEmail = new Button(comp1, SWT.CHECK); 351 | btnSendEmail.setText("测试结束发送邮件"); 352 | if (sendEmail.equals("true")) { 353 | btnSendEmail.setSelection(true); 354 | } else { 355 | btnSendEmail.setSelection(false); 356 | } 357 | btnSendEmail.setLayoutData(gridButtonEmail); 358 | btnSendEmail.setEnabled(false); 359 | btnSendEmail.addSelectionListener(new SelectionAdapter() { 360 | @Override 361 | public void widgetSelected(SelectionEvent e) { 362 | if (btnSendEmail.getSelection()) { 363 | sendEmail = "true"; 364 | } else { 365 | sendEmail = "false"; 366 | } 367 | } 368 | }); 369 | final Group groupEmailSettings = new Group(comp1, SWT.NONE); 370 | groupEmailSettings.setText("邮件设置"); 371 | GridData gridGroupEmail = new GridData(GridData.FILL_HORIZONTAL); 372 | 373 | gridGroupEmail.horizontalSpan = 3; 374 | gridGroupEmail.verticalSpan = 5; 375 | gridGroupEmail.horizontalIndent = 20; 376 | groupEmailSettings.setLayoutData(gridGroupEmail); 377 | } 378 | 379 | } 380 | 381 | // 音频面板的布局设置 382 | private void comp2Content() { 383 | comp2 = new Composite(composite, SWT.BORDER); 384 | comp2.setLayout(new GridLayout()); 385 | // 定义音频标签,并对其进行布局 386 | { 387 | final Label labelAudio = new Label(comp2, SWT.NONE); 388 | labelAudio.setText("音频"); 389 | GridData gridAudio = new GridData(); 390 | gridAudio.verticalIndent = 10; 391 | labelAudio.setLayoutData(gridAudio); 392 | } 393 | // 设置comp2面板上的分隔符标签 394 | { 395 | final Label labelSeparator = new Label(comp2, SWT.SEPARATOR 396 | | SWT.HORIZONTAL); 397 | 398 | GridData gridSeparator = new GridData(GridData.FILL_HORIZONTAL); 399 | gridSeparator.verticalSpan = 5; 400 | labelSeparator.setLayoutData(gridSeparator); 401 | } 402 | // 在comp2上定义 groupOne 分组框,并对其进行布局 403 | { 404 | Group groupOne = new Group(comp2, SWT.NONE); 405 | groupOne.setText("输入—麦克风或耳麦"); 406 | GridData gridGroupOne = new GridData(GridData.FILL_HORIZONTAL); 407 | gridGroupOne.horizontalIndent = 20; 408 | gridGroupOne.verticalSpan = 20; 409 | groupOne.setLayoutData(gridGroupOne); 410 | { // 设置groupa面板上组件的布局 411 | groupOne.setLayout(new GridLayout()); 412 | /** 413 | * comb1和check1复选框按钮可用相同的布局方式和相同的属性值 414 | */ 415 | GridData grid = new GridData(); 416 | grid.verticalIndent = 5; 417 | grid.horizontalIndent = 10; 418 | final Combo combo1 = new Combo(groupOne, SWT.NONE); 419 | // 在下拉框中设置下拉项 420 | combo1.setItems(new String[] { "默认设备", "Realtek AC97 Audio" }); 421 | combo1.setLayoutData(grid); 422 | 423 | final Button check1 = new Button(groupOne, SWT.CHECK); 424 | check1.setText("自动调整麦克风灵敏度(&A)"); 425 | check1.setLayoutData(grid); 426 | 427 | } 428 | } 429 | // 在comp2上定义groupTwo分组框,并对其进行布局 430 | { 431 | Group groupTwo = new Group(comp2, SWT.NONE); 432 | groupTwo.setText("输入—扬声器或耳麦"); 433 | 434 | GridData gridGroupTwo = new GridData(GridData.FILL_HORIZONTAL); 435 | gridGroupTwo.horizontalIndent = 20; 436 | gridGroupTwo.verticalSpan = 20; 437 | groupTwo.setLayoutData(gridGroupTwo); 438 | 439 | {// 设置groupTwo面板上组件,并对组件进行布局 440 | groupTwo.setLayout(new GridLayout()); 441 | GridData gridData = new GridData(); 442 | gridData.horizontalIndent = 10; 443 | gridData.widthHint = 138; 444 | gridData.verticalSpan = 5; 445 | final Label lab1 = new Label(groupTwo, SWT.NONE); 446 | lab1.setText("通知—铃声(&N)"); 447 | lab1.setLayoutData(gridData); 448 | final Combo combo2 = new Combo(groupTwo, SWT.NONE); 449 | combo2.setItems(new String[] { "所有设备", "默认设备", 450 | "Realtek AC97 Audio" }); 451 | combo2.setLayoutData(gridData); 452 | 453 | final Label lab2 = new Label(groupTwo, SWT.NONE); 454 | lab2.setText("呼叫(&C)"); 455 | lab2.setLayoutData(gridData); 456 | 457 | final Combo combo3 = new Combo(groupTwo, SWT.NONE); 458 | combo3.setItems(new String[] { "默认设备", "Realtek AC97 Audio" }); 459 | 460 | combo3.setLayoutData(gridData); 461 | 462 | final Button cancelButton = new Button(groupTwo, SWT.CHECK); 463 | cancelButton.setText("响铃时取消扬声器静音(&S)"); 464 | GridData gridDataCancel = new GridData(); 465 | gridDataCancel.verticalSpan = 6; 466 | cancelButton.setLayoutData(gridDataCancel); 467 | 468 | } 469 | } 470 | // 在comp2上设置callButton按钮,并对其进行布局 471 | { 472 | final Button callButton = new Button(comp2, SWT.CHECK); 473 | callButton.setText("呼叫时取消扬声器和麦克风静音(&U)"); 474 | GridData gridDataCall = new GridData(); 475 | gridDataCall.horizontalIndent = 20; 476 | gridDataCall.verticalSpan = 6; 477 | callButton.setLayoutData(gridDataCall); 478 | } 479 | 480 | } 481 | 482 | private void comp3Content() { 483 | comp3 = new Composite(composite, SWT.BORDER); 484 | comp3.setLayout(new GridLayout()); 485 | final Label labelRefuse = new Label(comp3, SWT.NONE); 486 | labelRefuse.setText("被拒"); 487 | 488 | } 489 | 490 | private void comp4Content() { 491 | comp4 = new Composite(composite, SWT.BORDER); 492 | comp4.setLayout(new GridLayout()); 493 | final Label labelNotice = new Label(comp4, SWT.NONE); 494 | labelNotice.setText("通知"); 495 | } 496 | 497 | private void comp5Content() { 498 | comp5 = new Composite(composite, SWT.BORDER); 499 | comp5.setLayout(new GridLayout()); 500 | final Label labelLink = new Label(comp5, SWT.NONE); 501 | labelLink.setText("链接"); 502 | 503 | } 504 | 505 | } 506 | -------------------------------------------------------------------------------- /src/com/desaysv/TimerThread.java: -------------------------------------------------------------------------------- 1 | package com.desaysv; 2 | 3 | import org.eclipse.swt.widgets.Display; 4 | import org.eclipse.swt.widgets.Table; 5 | import com.desay.utils.MyUtils; 6 | import com.desay.utils.RecordUtils; 7 | 8 | public class TimerThread extends Thread { 9 | /** 10 | * 停止线程 11 | */ 12 | volatile boolean stop = false; 13 | 14 | private Display display; 15 | private Table table; 16 | private int index; 17 | private long startTime; 18 | 19 | 20 | /** 21 | * 创建已运行时间的线程 22 | * @param display 23 | * @param table 表格 24 | * @param index 时间写在表格的第几行 25 | * @param startTime 开始计时的毫秒数 26 | */ 27 | public TimerThread(Display display, Table table, int index, long startTime) { 28 | this.display = display; 29 | this.table = table; 30 | this.index = index; 31 | this.startTime = startTime; 32 | } 33 | 34 | public void run() { 35 | long endTestTime = 0; 36 | try { 37 | while(stop == false) { 38 | endTestTime = System.currentTimeMillis();//测试结束时间点距离1970年1月1日0点0分0秒的毫秒数 39 | if ((endTestTime - startTime) > 60*1000) {//一分钟刷新一次 40 | String runTimeStr = MyUtils.millisToTimeMinuteCn(endTestTime - startTime); 41 | display.asyncExec(new Runnable() { 42 | public void run() { 43 | RecordUtils.updateOneRecordInFile(index, RecordUtils.RUNNING_TIME_TYPE, runTimeStr); 44 | RecordUtils.updateTableUi(table); 45 | } 46 | }); 47 | } 48 | Thread.sleep(1000); 49 | } 50 | } catch (InterruptedException e) { 51 | MyUtils.printWithTimeMill("timer Interrupted"); 52 | stop = true; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/com/desaysv/WinUtil.java: -------------------------------------------------------------------------------- 1 | package com.desaysv; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.Date; 9 | import java.util.List; 10 | 11 | import org.eclipse.swt.SWT; 12 | import org.eclipse.swt.graphics.Rectangle; 13 | import org.eclipse.swt.widgets.DirectoryDialog; 14 | import org.eclipse.swt.widgets.Display; 15 | import org.eclipse.swt.widgets.FileDialog; 16 | import org.eclipse.swt.widgets.MessageBox; 17 | import org.eclipse.swt.widgets.Shell; 18 | 19 | 20 | public class WinUtil { 21 | 22 | /** 23 | * 递归删除目录下的所有文件及子目录下所有文件 24 | * @param dir 将要删除的文件目录 25 | * @return boolean Returns "true" if all deletions were successful. 26 | * If a deletion fails, the method stops attempting to 27 | * delete and returns "false". 28 | */ 29 | public static boolean deleteDir(File dir, Shell shell) { 30 | if (dir.isDirectory()) { 31 | String[] children = dir.list(); 32 | //递归删除目录中的子目录 33 | for (int i=0; i apkFilesSeleteDialog(Shell shell, String selectFilePathStr){ 228 | List fileList = Collections.synchronizedList(new ArrayList()); 229 | 230 | //新建文件对话框,并设置为打开的方式 231 | FileDialog filedlg=new FileDialog(shell,SWT.MULTI); 232 | //设置文件对话框的标题 233 | filedlg.setText("文件选择"); 234 | filedlg.setFilterNames(new String[] { "apk文件 (*.apk)" });//设置扩展名 235 | filedlg.setFilterExtensions(new String[] {"*.apk"}); 236 | //设置初始路径 237 | File file = new File(selectFilePathStr); 238 | if (file.exists()) { 239 | filedlg.setFilterPath(file.getParent()); 240 | } else { 241 | filedlg.setFilterPath("SystemRoot"); 242 | } 243 | //打开文件对话框,返回选中文件的绝对路径 244 | String selected = filedlg.open(); 245 | System.out.println("Selete file:"+selected); 246 | if (selected == null) { 247 | selected = selectFilePathStr; 248 | } else { 249 | if (!selected.equals("")) { 250 | File firstSelectFile = new File(selected); 251 | String dir = firstSelectFile.getParent(); 252 | 253 | String[] selFilesStr = filedlg.getFileNames(); 254 | for (String string : selFilesStr) { 255 | if (dir.endsWith(File.separator)) { 256 | fileList.add(dir + string); 257 | } else { 258 | fileList.add(dir + File.separator + string); 259 | } 260 | } 261 | } 262 | } 263 | 264 | return fileList; 265 | } 266 | 267 | } 268 | -------------------------------------------------------------------------------- /src/com/desaysv/bean/AnrCrash.java: -------------------------------------------------------------------------------- 1 | package com.desaysv.bean; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author uidq0460 7 | * Crash和ANR信息 8 | */ 9 | public class AnrCrash implements Serializable { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | private String fileDir; //文件路径 17 | private String packageName; //包名 18 | private String contentDetail; //详细内容 19 | 20 | public AnrCrash() { 21 | this.fileDir = ""; 22 | this.packageName = ""; 23 | this.contentDetail = ""; 24 | } 25 | public String getFileDir() { 26 | return fileDir; 27 | } 28 | public void setFileDir(String filePath) { 29 | this.fileDir = filePath; 30 | } 31 | public String getPackageName() { 32 | String name = ""; 33 | String[] strings = packageName.split(" "); 34 | for (int i = 0; i < strings.length; i++) { 35 | String getName = strings[i]; 36 | if (getName.equals("CRASH:") || getName.equals("RESPONDING:")) { 37 | name = strings[i + 1]; 38 | if (name.contains(":")) { 39 | String[] names = name.split(":"); 40 | name = names[0]; 41 | } 42 | } 43 | } 44 | return name; 45 | } 46 | public void setPackageName(String packageName) { 47 | this.packageName = packageName; 48 | } 49 | public String getContentDetail() { 50 | return contentDetail; 51 | } 52 | public void setContentDetail(String contentDetail) { 53 | this.contentDetail = contentDetail; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/com/desaysv/bean/Device.java: -------------------------------------------------------------------------------- 1 | package com.desaysv.bean; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author uidq0460 7 | * 设备monkey参数 8 | */ 9 | public class Device { 10 | 11 | private String deviceId; //设备ID 12 | private List packageList; //设备程序列表 13 | private List systemPackageList; //设备系统程序列表 14 | private List thirdPackageList; //设备第三方程序列表 15 | 16 | private String throttleMillisec; //事件间的时间间隔 17 | private String logLevel; //日志等级 18 | private String seed; //随机种子值 19 | private String eventCount; //事件数量 20 | 21 | private String trackBallPercent; //轨迹事件的百分比(轨迹事件由一个或几个随机的移动组成,有时还伴随有点击) 22 | private String appSwitchPercent; //Activity启动事件百分比 23 | private String flipPercent; //键盘翻转事件百分比 24 | private String touchPercent; //触摸事件的百分比(触摸事件是一个down-up事件) 25 | private String sysKeyPercent; //系统按键事件的百分比(这些按键通常被保留,由系统使用,如Home、Back、Start Call、End Call及音量控制键) 26 | 27 | //其它类型事件的百分比。它包罗了所有其它类型的事件,如:按键、其它不常用的设备按钮、等等(这个按键可能会按到power,因此工具中强制设置为0) 28 | private String anyEventPercent; 29 | private String pinchZoonPercent; //缩放事件百分比 30 | private String navPercent; //基本导航事件的百分比(导航事件由来自方向输入设备的up/down/left/right组成) 31 | private String majorNavPercent; //主要导航事件的百分比(这些导航事件通常引发图形界面中的动作,如:5-way键盘的中间按键、回退按键、菜单按键) 32 | private String motionPercent; //滑动事件的百分比(滑动事件由屏幕上某处的一个down事件、一系列的伪随机事件和一个up事件组成) 33 | private String permissionPercent; //滑动事件的百分比(滑动事件由屏幕上某处的一个down事件、一系列的伪随机事件和一个up事件组成) 34 | 35 | private Boolean isIgnoreCrashes; //是否在出现crash时停止测试 36 | private Boolean isIgnoreTimeouts; //是否在出现anr时停止测试 37 | private Boolean isIgnoreSecurityExceptions;//是否在出现指定当应用程序发生许可错误时(如证书许可,网络许可等)停止测试 38 | private Boolean isMonitorNativeCrash;//监视系统中本地代码发生的崩溃 39 | private Boolean isIgnoreNativeCrashes;//忽略本地代码导致的崩溃。设置忽略后,Monkey将执行完所有的事件,不会因此停止 40 | private Boolean isPermissionTargetSystem;//忽略本地代码导致的崩溃。设置忽略后,Monkey将执行完所有的事件,不会因此停止 41 | private Boolean isbugreport;//忽略本地代码导致的崩溃。设置忽略后,Monkey将执行完所有的事件,不会因此停止 42 | 43 | 44 | public Device() { 45 | 46 | this.throttleMillisec = "300"; 47 | this.logLevel = "-v -v -v"; 48 | this.seed = "0"; 49 | this.eventCount = "100000"; 50 | 51 | this.trackBallPercent = ""; 52 | this.appSwitchPercent = ""; 53 | this.flipPercent = ""; 54 | this.touchPercent = ""; 55 | this.sysKeyPercent = ""; 56 | this.anyEventPercent = ""; 57 | this.pinchZoonPercent = ""; 58 | this.navPercent = ""; 59 | this.majorNavPercent = ""; 60 | this.motionPercent = ""; 61 | this.permissionPercent = ""; 62 | 63 | this.isIgnoreCrashes = false; 64 | this.isIgnoreTimeouts = false; 65 | this.isIgnoreSecurityExceptions = false; 66 | this.isMonitorNativeCrash = false; 67 | this.isIgnoreNativeCrashes = false; 68 | this.isPermissionTargetSystem = false; 69 | this.isbugreport = true; 70 | 71 | } 72 | 73 | public String getDeviceId() { 74 | return deviceId; 75 | } 76 | public void setDeviceId(String deviceId) { 77 | this.deviceId = deviceId; 78 | } 79 | public List getPackageList() { 80 | return packageList; 81 | } 82 | public void setPackageList(List packagesList) { 83 | this.packageList = packagesList; 84 | } 85 | public List getThirdPackageList() { 86 | return thirdPackageList; 87 | } 88 | 89 | public void setThirdPackageList(List thirdPackageList) { 90 | this.thirdPackageList = thirdPackageList; 91 | } 92 | 93 | public List getSystemPackageList() { 94 | return systemPackageList; 95 | } 96 | 97 | public void setSystemPackageList(List systemPackageList) { 98 | this.systemPackageList = systemPackageList; 99 | } 100 | 101 | public String getThrottleMillisec() { 102 | return throttleMillisec; 103 | } 104 | public void setThrottleMillisec(String throttleMillisec) { 105 | this.throttleMillisec = throttleMillisec; 106 | } 107 | public String getLogLevel() { 108 | return logLevel; 109 | } 110 | public void setLogLevel(String logLevel) { 111 | this.logLevel = logLevel; 112 | } 113 | public String getSeed() { 114 | return seed; 115 | } 116 | public void setSeed(String seed) { 117 | this.seed = seed; 118 | } 119 | public String getEventCount() { 120 | return eventCount; 121 | } 122 | public void setEventCount(String eventCount) { 123 | this.eventCount = eventCount; 124 | } 125 | public String getTrackBallPercent() { 126 | return trackBallPercent; 127 | } 128 | public void setTrackBallPercent(String trackBallPercent) { 129 | this.trackBallPercent = trackBallPercent; 130 | } 131 | public String getAppswitchPercent() { 132 | return appSwitchPercent; 133 | } 134 | public void setAppswitchPercent(String appswitchPercent) { 135 | this.appSwitchPercent = appswitchPercent; 136 | } 137 | public String getFlipPercent() { 138 | return flipPercent; 139 | } 140 | public void setFlipPercent(String flipPercent) { 141 | this.flipPercent = flipPercent; 142 | } 143 | public String getTouchPercent() { 144 | return touchPercent; 145 | } 146 | public void setTouchPercent(String touchPercent) { 147 | this.touchPercent = touchPercent; 148 | } 149 | public String getSyskeyPercent() { 150 | return sysKeyPercent; 151 | } 152 | public void setSyskeyPercent(String syskeyPercent) { 153 | this.sysKeyPercent = syskeyPercent; 154 | } 155 | public String getAnyeventPercent() { 156 | return anyEventPercent; 157 | } 158 | public void setAnyeventPercent(String anyeventPercent) { 159 | this.anyEventPercent = anyeventPercent; 160 | } 161 | public String getPinchzoonPercent() { 162 | return pinchZoonPercent; 163 | } 164 | public void setPinchzoonPercent(String pinchzoonPercent) { 165 | this.pinchZoonPercent = pinchzoonPercent; 166 | } 167 | public String getNavPercent() { 168 | return navPercent; 169 | } 170 | public void setNavPercent(String navPercent) { 171 | this.navPercent = navPercent; 172 | } 173 | public String getMajorNavPercent() { 174 | return majorNavPercent; 175 | } 176 | public void setMajorNavPercent(String majorNavPercent) { 177 | this.majorNavPercent = majorNavPercent; 178 | } 179 | public String getMotionPercent() { 180 | return motionPercent; 181 | } 182 | public void setMotionPercent(String motionPercent) { 183 | this.motionPercent = motionPercent; 184 | } 185 | public String getPermissionPercent() { 186 | return permissionPercent; 187 | } 188 | 189 | public void setPermissionPercent(String permissionPercent) { 190 | this.permissionPercent = permissionPercent; 191 | } 192 | 193 | public boolean isIgnoreCrashes() { 194 | return isIgnoreCrashes; 195 | } 196 | public void setIgnoreCrashes(Boolean isIgnoreCrashes) { 197 | this.isIgnoreCrashes = isIgnoreCrashes; 198 | } 199 | public boolean isIgnoreTimeouts() { 200 | return isIgnoreTimeouts; 201 | } 202 | public void setIgnoreTimeouts(Boolean isIgnoreTimeouts) { 203 | this.isIgnoreTimeouts = isIgnoreTimeouts; 204 | } 205 | public boolean isIgnoreSecurityExceptions() { 206 | return isIgnoreSecurityExceptions; 207 | } 208 | public void setIgnoreSecurityExceptions(Boolean isIgnoreSecurityExceptions) { 209 | this.isIgnoreSecurityExceptions = isIgnoreSecurityExceptions; 210 | } 211 | public boolean isMonitorNativeCrash() { 212 | return isMonitorNativeCrash; 213 | } 214 | public void setMonitorNativeCrash(Boolean isMonitorNativeCrash) { 215 | this.isMonitorNativeCrash = isMonitorNativeCrash; 216 | } 217 | public boolean isIgnoreNativeCrashes() { 218 | return isIgnoreNativeCrashes; 219 | } 220 | public void setIgnoreNativeCrashes(Boolean isIgnoreNativeCrashes) { 221 | this.isIgnoreNativeCrashes = isIgnoreNativeCrashes; 222 | } 223 | 224 | public Boolean isPermissionTargetSystem() { 225 | return isPermissionTargetSystem; 226 | } 227 | 228 | public void setIsPermissionTargetSystem(Boolean isPermissionTargetSystem) { 229 | this.isPermissionTargetSystem = isPermissionTargetSystem; 230 | } 231 | 232 | public Boolean isBugreport() { 233 | return isbugreport; 234 | } 235 | 236 | public void setIsBugreport(Boolean isbugreport) { 237 | this.isbugreport = isbugreport; 238 | } 239 | 240 | } 241 | -------------------------------------------------------------------------------- /src/com/desaysv/bean/DeviceInfo.java: -------------------------------------------------------------------------------- 1 | package com.desaysv.bean; 2 | 3 | 4 | public class DeviceInfo { 5 | 6 | private String deviceId; 7 | private String model; //ro.product.model=jacinto6evm 设备名称 8 | private String displayId; //ro.build.display.id=G5Android_5.1.47 内部名称//she bei mingcheng 9 | private String versionRelease; //ro.build.version.release=5.1.1 安卓版本 10 | private String buildDate; //ro.build.date=Fri Oct 27 10:42:54 CST 2017发布日期 11 | private String sdkVersion; //ro.build.date=Fri Oct 27 10:42:54 CST 2017发布日期 12 | private String startTestTime; //开始测试时间 13 | private String testCostTime; //测试用时 14 | private String testCmd; //monkey测试指令 15 | private String seed; //实际seed值 16 | private String logPath; //日志路径 17 | 18 | public DeviceInfo() { 19 | this.deviceId = "未知"; 20 | this.model = "未知"; 21 | this.displayId = "未知"; 22 | this.versionRelease = "未知"; 23 | this.buildDate = "未知"; 24 | this.sdkVersion = "未知"; 25 | this.startTestTime = "未知"; 26 | this.testCmd = "未知"; 27 | this.seed = "未知"; 28 | this.logPath = "未知"; 29 | this.testCostTime = "0"; 30 | } 31 | 32 | public DeviceInfo(String deviceId, String model, String displayId, String versionRelease, String BuildDate) { 33 | this.deviceId = deviceId; 34 | this.model = model; 35 | this.displayId = displayId; 36 | this.versionRelease = versionRelease; 37 | this.buildDate = BuildDate; 38 | this.sdkVersion = "未知"; 39 | this.startTestTime = "未知"; 40 | this.testCmd = "未知"; 41 | this.seed = "未知"; 42 | this.logPath = "未知"; 43 | this.testCostTime = "0"; 44 | } 45 | 46 | public String getDeviceId() { 47 | return deviceId; 48 | } 49 | 50 | public void setDeviceId(String deviceId) { 51 | this.deviceId = deviceId; 52 | } 53 | 54 | public String getModel() { 55 | return model; 56 | } 57 | 58 | public void setModel(String model) { 59 | this.model = model; 60 | } 61 | 62 | public String getDisplayId() { 63 | return displayId; 64 | } 65 | 66 | public void setDisplayId(String displayId) { 67 | this.displayId = displayId; 68 | } 69 | 70 | public String getVersionRelease() { 71 | return versionRelease; 72 | } 73 | 74 | public void setVersionRelease(String versionRelease) { 75 | this.versionRelease = versionRelease; 76 | } 77 | 78 | public String getBuildDate() { 79 | return buildDate; 80 | } 81 | 82 | public void setBuildDate(String buildDate) { 83 | this.buildDate = buildDate; 84 | } 85 | 86 | public String getSdkVersion() { 87 | return sdkVersion; 88 | } 89 | 90 | public void setSdkVersion(String sdkVersion) { 91 | this.sdkVersion = sdkVersion; 92 | } 93 | 94 | public String getStartTestTime() { 95 | return startTestTime; 96 | } 97 | 98 | public void setStartTestTime(String startTestTime) { 99 | this.startTestTime = startTestTime; 100 | } 101 | 102 | public String getTestCmd() { 103 | return testCmd; 104 | } 105 | 106 | public void setTestCmd(String testCmd) { 107 | this.testCmd = testCmd; 108 | } 109 | 110 | public String getSeed() { 111 | return seed; 112 | } 113 | 114 | public void setSeed(String seed) { 115 | this.seed = seed; 116 | } 117 | 118 | public String getLogPath() { 119 | return logPath; 120 | } 121 | 122 | public void setLogPath(String logPath) { 123 | this.logPath = logPath; 124 | } 125 | 126 | public String getTestCostTime() { 127 | return testCostTime; 128 | } 129 | 130 | public void setTestCostTime(String testCostTime) { 131 | this.testCostTime = testCostTime; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/com/desaysv/bean/DeviceRecord.java: -------------------------------------------------------------------------------- 1 | package com.desaysv.bean; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | /** 8 | * @author uidq0460 9 | * 某个设备的测试记录,用于导出表格中某个设备的所有记录到excel 10 | */ 11 | public class DeviceRecord { 12 | 13 | private DeviceInfo deviceInfo; 14 | private List packageInfoList; 15 | 16 | public DeviceRecord() { 17 | this.deviceInfo = new DeviceInfo(); 18 | this.packageInfoList = Collections.synchronizedList(new ArrayList()); 19 | } 20 | 21 | public DeviceInfo getDeviceInfo() { 22 | return deviceInfo; 23 | } 24 | public void setDeviceInfo(DeviceInfo deviceInfo) { 25 | this.deviceInfo = deviceInfo; 26 | } 27 | public List getPackageInfoList() { 28 | return packageInfoList; 29 | } 30 | public void setPackageInfoList(List packageInfoList) { 31 | this.packageInfoList = packageInfoList; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/com/desaysv/bean/MonkeyTaskBean.java: -------------------------------------------------------------------------------- 1 | package com.desaysv.bean; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.FileWriter; 5 | 6 | public class MonkeyTaskBean { 7 | 8 | private long threadPid; 9 | private boolean keepRunning; 10 | private Thread thread; 11 | private long logcatPid; 12 | private long timerThreadPid; 13 | private BufferedWriter bufferdWriter; 14 | private FileWriter fileWriter; 15 | 16 | public MonkeyTaskBean() { 17 | this.threadPid = -1; 18 | this.keepRunning = true; 19 | } 20 | 21 | /** 22 | * 构造函数,传入是否继续的值 23 | * @param keepRun 24 | */ 25 | public MonkeyTaskBean(long pid, boolean keepRun) { 26 | this.threadPid = pid; 27 | this.keepRunning = keepRun; 28 | } 29 | 30 | public long getThreadPid() { 31 | return threadPid; 32 | } 33 | 34 | public void setThreadPid(long threadPid) { 35 | this.threadPid = threadPid; 36 | } 37 | 38 | public boolean isKeepRunning() { 39 | return keepRunning; 40 | } 41 | 42 | public void setKeepRunning(boolean keepRunning) { 43 | this.keepRunning = keepRunning; 44 | } 45 | 46 | public Thread getThread() { 47 | return thread; 48 | } 49 | 50 | public void setThread(Thread thread) { 51 | this.thread = thread; 52 | } 53 | 54 | public long getLogcatPid() { 55 | return logcatPid; 56 | } 57 | 58 | public void setLogcatPid(long logcatPid) { 59 | this.logcatPid = logcatPid; 60 | } 61 | 62 | public long getTimerThreadPid() { 63 | return timerThreadPid; 64 | } 65 | 66 | public void setTimerThreadPid(long timerThreadPid) { 67 | this.timerThreadPid = timerThreadPid; 68 | } 69 | 70 | public BufferedWriter getBufferdWriter() { 71 | return bufferdWriter; 72 | } 73 | 74 | public void setBufferdWriter(BufferedWriter bufferdWriter) { 75 | this.bufferdWriter = bufferdWriter; 76 | } 77 | 78 | public FileWriter getFileWriter() { 79 | return fileWriter; 80 | } 81 | 82 | public void setFileWriter(FileWriter fileWriter) { 83 | this.fileWriter = fileWriter; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/com/desaysv/bean/PackageErrorInfo.java: -------------------------------------------------------------------------------- 1 | package com.desaysv.bean; 2 | 3 | /** 4 | * @author uidq0460 5 | * 包错误的信息内容,包括这个包发生anr的数量,crash的数量,log路径 6 | */ 7 | public class PackageErrorInfo { 8 | 9 | private String packageName; 10 | private int crashCount; 11 | private int anrCount; 12 | // private String logPath; 13 | 14 | public PackageErrorInfo() { 15 | this.packageName = ""; 16 | this.crashCount = 0; 17 | this.anrCount = 0; 18 | // this.logPath = ""; 19 | } 20 | 21 | public PackageErrorInfo(String packageName, int crashCount, int anrCount, String logPath) { 22 | this.packageName = packageName; 23 | this.crashCount = crashCount; 24 | this.anrCount = anrCount; 25 | // this.logPath = logPath; 26 | } 27 | 28 | public String getPackageName() { 29 | return packageName; 30 | } 31 | public void setPackageName(String packageName) { 32 | this.packageName = packageName; 33 | } 34 | public int getCrashCount() { 35 | return crashCount; 36 | } 37 | public void setCrashCount(int crashCount) { 38 | this.crashCount = crashCount; 39 | } 40 | public int getAnrCount() { 41 | return anrCount; 42 | } 43 | public void setAnrCount(int anrCount) { 44 | this.anrCount = anrCount; 45 | } 46 | // public String getLogPath() { 47 | // return logPath; 48 | // } 49 | // public void setLogPath(String logPath) { 50 | // this.logPath = logPath; 51 | // } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/com/desaysv/bean/Record.java: -------------------------------------------------------------------------------- 1 | package com.desaysv.bean; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author uidq0460 7 | * monkey测试记录 8 | */ 9 | public class Record implements Serializable { 10 | 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | 16 | private String number; //序号 17 | private String deviceId; //设备ID 18 | private String testStatus; //测试状态 19 | private String crashCounter; //crash数量 20 | private String anrCounter; //anr数量 21 | private String logPath; //日志路径 22 | private String runningTime; //已运行时间 23 | private String monkeyCmd; //执行的monkey指令 24 | private String errorMessage; //其他错误信息 25 | 26 | public Record() { 27 | this.number = "1"; 28 | this.deviceId = ""; 29 | this.testStatus = "进行中"; 30 | this.crashCounter = "0"; 31 | this.anrCounter = "0"; 32 | this.logPath = ""; 33 | this.runningTime = "0天0时0分"; 34 | this.monkeyCmd = ""; 35 | this.errorMessage = ""; 36 | } 37 | 38 | public String getNumber() { 39 | return number; 40 | } 41 | public void setNumber(String number) { 42 | this.number = number; 43 | } 44 | public String getDeviceId() { 45 | return deviceId; 46 | } 47 | public void setDeviceId(String deviceId) { 48 | this.deviceId = deviceId; 49 | } 50 | public String getTestStatus() { 51 | return testStatus; 52 | } 53 | public void setTestStatus(String testStatus) { 54 | this.testStatus = testStatus; 55 | } 56 | public String getCrashCounter() { 57 | return crashCounter; 58 | } 59 | public void setCrashCounter(String crashCounter) { 60 | this.crashCounter = crashCounter; 61 | } 62 | public String getAnrCounter() { 63 | return anrCounter; 64 | } 65 | public void setAnrCounter(String anrCounter) { 66 | this.anrCounter = anrCounter; 67 | } 68 | public String getLogPath() { 69 | return logPath; 70 | } 71 | public void setLogPath(String logPath) { 72 | this.logPath = logPath; 73 | } 74 | public String getMonkeyCmd() { 75 | return monkeyCmd; 76 | } 77 | public void setMonkeyCmd(String monkeyCmd) { 78 | this.monkeyCmd = monkeyCmd; 79 | } 80 | public String getRunningTime() { 81 | return runningTime; 82 | } 83 | public void setRunningTime(String runningTime) { 84 | this.runningTime = runningTime; 85 | } 86 | 87 | public String getErrorMessage() { 88 | return errorMessage; 89 | } 90 | 91 | public void setErrorMessage(String errorMessage) { 92 | this.errorMessage = errorMessage; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/images/monkey.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TonsenWei/SVmonkey/e7db879c26601f6fc412577359676a78f530cab2/src/images/monkey.ico -------------------------------------------------------------------------------- /src/org/eclipse/wb/swt/SWTResourceManager.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2011 Google, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Google, Inc. - initial API and implementation 10 | *******************************************************************************/ 11 | package org.eclipse.wb.swt; 12 | 13 | import java.io.FileInputStream; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | import org.eclipse.swt.SWT; 20 | import org.eclipse.swt.graphics.Color; 21 | import org.eclipse.swt.graphics.Cursor; 22 | import org.eclipse.swt.graphics.Font; 23 | import org.eclipse.swt.graphics.FontData; 24 | import org.eclipse.swt.graphics.GC; 25 | import org.eclipse.swt.graphics.Image; 26 | import org.eclipse.swt.graphics.ImageData; 27 | import org.eclipse.swt.graphics.RGB; 28 | import org.eclipse.swt.graphics.Rectangle; 29 | import org.eclipse.swt.widgets.Display; 30 | 31 | /** 32 | * Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc. 33 | *

34 | * !!! IMPORTANT !!! Application code must explicitly invoke the dispose() method to release the 35 | * operating system resources managed by cached objects when those objects and OS resources are no longer 36 | * needed (e.g. on application shutdown) 37 | *

38 | * This class may be freely distributed as part of any application or plugin. 39 | *

40 | * @author scheglov_ke 41 | * @author Dan Rubel 42 | */ 43 | public class SWTResourceManager { 44 | //////////////////////////////////////////////////////////////////////////// 45 | // 46 | // Color 47 | // 48 | //////////////////////////////////////////////////////////////////////////// 49 | private static Map m_colorMap = new HashMap(); 50 | /** 51 | * Returns the system {@link Color} matching the specific ID. 52 | * 53 | * @param systemColorID 54 | * the ID value for the color 55 | * @return the system {@link Color} matching the specific ID 56 | */ 57 | public static Color getColor(int systemColorID) { 58 | Display display = Display.getCurrent(); 59 | return display.getSystemColor(systemColorID); 60 | } 61 | /** 62 | * Returns a {@link Color} given its red, green and blue component values. 63 | * 64 | * @param r 65 | * the red component of the color 66 | * @param g 67 | * the green component of the color 68 | * @param b 69 | * the blue component of the color 70 | * @return the {@link Color} matching the given red, green and blue component values 71 | */ 72 | public static Color getColor(int r, int g, int b) { 73 | return getColor(new RGB(r, g, b)); 74 | } 75 | /** 76 | * Returns a {@link Color} given its RGB value. 77 | * 78 | * @param rgb 79 | * the {@link RGB} value of the color 80 | * @return the {@link Color} matching the RGB value 81 | */ 82 | public static Color getColor(RGB rgb) { 83 | Color color = m_colorMap.get(rgb); 84 | if (color == null) { 85 | Display display = Display.getCurrent(); 86 | color = new Color(display, rgb); 87 | m_colorMap.put(rgb, color); 88 | } 89 | return color; 90 | } 91 | /** 92 | * Dispose of all the cached {@link Color}'s. 93 | */ 94 | public static void disposeColors() { 95 | for (Color color : m_colorMap.values()) { 96 | color.dispose(); 97 | } 98 | m_colorMap.clear(); 99 | } 100 | //////////////////////////////////////////////////////////////////////////// 101 | // 102 | // Image 103 | // 104 | //////////////////////////////////////////////////////////////////////////// 105 | /** 106 | * Maps image paths to images. 107 | */ 108 | private static Map m_imageMap = new HashMap(); 109 | /** 110 | * Returns an {@link Image} encoded by the specified {@link InputStream}. 111 | * 112 | * @param stream 113 | * the {@link InputStream} encoding the image data 114 | * @return the {@link Image} encoded by the specified input stream 115 | */ 116 | protected static Image getImage(InputStream stream) throws IOException { 117 | try { 118 | Display display = Display.getCurrent(); 119 | ImageData data = new ImageData(stream); 120 | if (data.transparentPixel > 0) { 121 | return new Image(display, data, data.getTransparencyMask()); 122 | } 123 | return new Image(display, data); 124 | } finally { 125 | stream.close(); 126 | } 127 | } 128 | /** 129 | * Returns an {@link Image} stored in the file at the specified path. 130 | * 131 | * @param path 132 | * the path to the image file 133 | * @return the {@link Image} stored in the file at the specified path 134 | */ 135 | public static Image getImage(String path) { 136 | Image image = m_imageMap.get(path); 137 | if (image == null) { 138 | try { 139 | image = getImage(new FileInputStream(path)); 140 | m_imageMap.put(path, image); 141 | } catch (Exception e) { 142 | image = getMissingImage(); 143 | m_imageMap.put(path, image); 144 | } 145 | } 146 | return image; 147 | } 148 | /** 149 | * Returns an {@link Image} stored in the file at the specified path relative to the specified class. 150 | * 151 | * @param clazz 152 | * the {@link Class} relative to which to find the image 153 | * @param path 154 | * the path to the image file, if starts with '/' 155 | * @return the {@link Image} stored in the file at the specified path 156 | */ 157 | public static Image getImage(Class clazz, String path) { 158 | String key = clazz.getName() + '|' + path; 159 | Image image = m_imageMap.get(key); 160 | if (image == null) { 161 | try { 162 | image = getImage(clazz.getResourceAsStream(path)); 163 | m_imageMap.put(key, image); 164 | } catch (Exception e) { 165 | image = getMissingImage(); 166 | m_imageMap.put(key, image); 167 | } 168 | } 169 | return image; 170 | } 171 | private static final int MISSING_IMAGE_SIZE = 10; 172 | /** 173 | * @return the small {@link Image} that can be used as placeholder for missing image. 174 | */ 175 | private static Image getMissingImage() { 176 | Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE); 177 | // 178 | GC gc = new GC(image); 179 | gc.setBackground(getColor(SWT.COLOR_RED)); 180 | gc.fillRectangle(0, 0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE); 181 | gc.dispose(); 182 | // 183 | return image; 184 | } 185 | /** 186 | * Style constant for placing decorator image in top left corner of base image. 187 | */ 188 | public static final int TOP_LEFT = 1; 189 | /** 190 | * Style constant for placing decorator image in top right corner of base image. 191 | */ 192 | public static final int TOP_RIGHT = 2; 193 | /** 194 | * Style constant for placing decorator image in bottom left corner of base image. 195 | */ 196 | public static final int BOTTOM_LEFT = 3; 197 | /** 198 | * Style constant for placing decorator image in bottom right corner of base image. 199 | */ 200 | public static final int BOTTOM_RIGHT = 4; 201 | /** 202 | * Internal value. 203 | */ 204 | protected static final int LAST_CORNER_KEY = 5; 205 | /** 206 | * Maps images to decorated images. 207 | */ 208 | @SuppressWarnings("unchecked") 209 | private static Map>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY]; 210 | /** 211 | * Returns an {@link Image} composed of a base image decorated by another image. 212 | * 213 | * @param baseImage 214 | * the base {@link Image} that should be decorated 215 | * @param decorator 216 | * the {@link Image} to decorate the base image 217 | * @return {@link Image} The resulting decorated image 218 | */ 219 | public static Image decorateImage(Image baseImage, Image decorator) { 220 | return decorateImage(baseImage, decorator, BOTTOM_RIGHT); 221 | } 222 | /** 223 | * Returns an {@link Image} composed of a base image decorated by another image. 224 | * 225 | * @param baseImage 226 | * the base {@link Image} that should be decorated 227 | * @param decorator 228 | * the {@link Image} to decorate the base image 229 | * @param corner 230 | * the corner to place decorator image 231 | * @return the resulting decorated {@link Image} 232 | */ 233 | public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) { 234 | if (corner <= 0 || corner >= LAST_CORNER_KEY) { 235 | throw new IllegalArgumentException("Wrong decorate corner"); 236 | } 237 | Map> cornerDecoratedImageMap = m_decoratedImageMap[corner]; 238 | if (cornerDecoratedImageMap == null) { 239 | cornerDecoratedImageMap = new HashMap>(); 240 | m_decoratedImageMap[corner] = cornerDecoratedImageMap; 241 | } 242 | Map decoratedMap = cornerDecoratedImageMap.get(baseImage); 243 | if (decoratedMap == null) { 244 | decoratedMap = new HashMap(); 245 | cornerDecoratedImageMap.put(baseImage, decoratedMap); 246 | } 247 | // 248 | Image result = decoratedMap.get(decorator); 249 | if (result == null) { 250 | Rectangle bib = baseImage.getBounds(); 251 | Rectangle dib = decorator.getBounds(); 252 | // 253 | result = new Image(Display.getCurrent(), bib.width, bib.height); 254 | // 255 | GC gc = new GC(result); 256 | gc.drawImage(baseImage, 0, 0); 257 | if (corner == TOP_LEFT) { 258 | gc.drawImage(decorator, 0, 0); 259 | } else if (corner == TOP_RIGHT) { 260 | gc.drawImage(decorator, bib.width - dib.width, 0); 261 | } else if (corner == BOTTOM_LEFT) { 262 | gc.drawImage(decorator, 0, bib.height - dib.height); 263 | } else if (corner == BOTTOM_RIGHT) { 264 | gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height); 265 | } 266 | gc.dispose(); 267 | // 268 | decoratedMap.put(decorator, result); 269 | } 270 | return result; 271 | } 272 | /** 273 | * Dispose all of the cached {@link Image}'s. 274 | */ 275 | public static void disposeImages() { 276 | // dispose loaded images 277 | { 278 | for (Image image : m_imageMap.values()) { 279 | image.dispose(); 280 | } 281 | m_imageMap.clear(); 282 | } 283 | // dispose decorated images 284 | for (int i = 0; i < m_decoratedImageMap.length; i++) { 285 | Map> cornerDecoratedImageMap = m_decoratedImageMap[i]; 286 | if (cornerDecoratedImageMap != null) { 287 | for (Map decoratedMap : cornerDecoratedImageMap.values()) { 288 | for (Image image : decoratedMap.values()) { 289 | image.dispose(); 290 | } 291 | decoratedMap.clear(); 292 | } 293 | cornerDecoratedImageMap.clear(); 294 | } 295 | } 296 | } 297 | //////////////////////////////////////////////////////////////////////////// 298 | // 299 | // Font 300 | // 301 | //////////////////////////////////////////////////////////////////////////// 302 | /** 303 | * Maps font names to fonts. 304 | */ 305 | private static Map m_fontMap = new HashMap(); 306 | /** 307 | * Maps fonts to their bold versions. 308 | */ 309 | private static Map m_fontToBoldFontMap = new HashMap(); 310 | /** 311 | * Returns a {@link Font} based on its name, height and style. 312 | * 313 | * @param name 314 | * the name of the font 315 | * @param height 316 | * the height of the font 317 | * @param style 318 | * the style of the font 319 | * @return {@link Font} The font matching the name, height and style 320 | */ 321 | public static Font getFont(String name, int height, int style) { 322 | return getFont(name, height, style, false, false); 323 | } 324 | /** 325 | * Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline 326 | * flags are also supported. 327 | * 328 | * @param name 329 | * the name of the font 330 | * @param size 331 | * the size of the font 332 | * @param style 333 | * the style of the font 334 | * @param strikeout 335 | * the strikeout flag (warning: Windows only) 336 | * @param underline 337 | * the underline flag (warning: Windows only) 338 | * @return {@link Font} The font matching the name, height, style, strikeout and underline 339 | */ 340 | public static Font getFont(String name, int size, int style, boolean strikeout, boolean underline) { 341 | String fontName = name + '|' + size + '|' + style + '|' + strikeout + '|' + underline; 342 | Font font = m_fontMap.get(fontName); 343 | if (font == null) { 344 | FontData fontData = new FontData(name, size, style); 345 | if (strikeout || underline) { 346 | try { 347 | Class logFontClass = Class.forName("org.eclipse.swt.internal.win32.LOGFONT"); //$NON-NLS-1$ 348 | Object logFont = FontData.class.getField("data").get(fontData); //$NON-NLS-1$ 349 | if (logFont != null && logFontClass != null) { 350 | if (strikeout) { 351 | logFontClass.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$ 352 | } 353 | if (underline) { 354 | logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$ 355 | } 356 | } 357 | } catch (Throwable e) { 358 | System.err.println("Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e); //$NON-NLS-1$ //$NON-NLS-2$ 359 | } 360 | } 361 | font = new Font(Display.getCurrent(), fontData); 362 | m_fontMap.put(fontName, font); 363 | } 364 | return font; 365 | } 366 | /** 367 | * Returns a bold version of the given {@link Font}. 368 | * 369 | * @param baseFont 370 | * the {@link Font} for which a bold version is desired 371 | * @return the bold version of the given {@link Font} 372 | */ 373 | public static Font getBoldFont(Font baseFont) { 374 | Font font = m_fontToBoldFontMap.get(baseFont); 375 | if (font == null) { 376 | FontData fontDatas[] = baseFont.getFontData(); 377 | FontData data = fontDatas[0]; 378 | font = new Font(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD); 379 | m_fontToBoldFontMap.put(baseFont, font); 380 | } 381 | return font; 382 | } 383 | /** 384 | * Dispose all of the cached {@link Font}'s. 385 | */ 386 | public static void disposeFonts() { 387 | // clear fonts 388 | for (Font font : m_fontMap.values()) { 389 | font.dispose(); 390 | } 391 | m_fontMap.clear(); 392 | // clear bold fonts 393 | for (Font font : m_fontToBoldFontMap.values()) { 394 | font.dispose(); 395 | } 396 | m_fontToBoldFontMap.clear(); 397 | } 398 | //////////////////////////////////////////////////////////////////////////// 399 | // 400 | // Cursor 401 | // 402 | //////////////////////////////////////////////////////////////////////////// 403 | /** 404 | * Maps IDs to cursors. 405 | */ 406 | private static Map m_idToCursorMap = new HashMap(); 407 | /** 408 | * Returns the system cursor matching the specific ID. 409 | * 410 | * @param id 411 | * int The ID value for the cursor 412 | * @return Cursor The system cursor matching the specific ID 413 | */ 414 | public static Cursor getCursor(int id) { 415 | Integer key = Integer.valueOf(id); 416 | Cursor cursor = m_idToCursorMap.get(key); 417 | if (cursor == null) { 418 | cursor = new Cursor(Display.getDefault(), id); 419 | m_idToCursorMap.put(key, cursor); 420 | } 421 | return cursor; 422 | } 423 | /** 424 | * Dispose all of the cached cursors. 425 | */ 426 | public static void disposeCursors() { 427 | for (Cursor cursor : m_idToCursorMap.values()) { 428 | cursor.dispose(); 429 | } 430 | m_idToCursorMap.clear(); 431 | } 432 | //////////////////////////////////////////////////////////////////////////// 433 | // 434 | // General 435 | // 436 | //////////////////////////////////////////////////////////////////////////// 437 | /** 438 | * Dispose of cached objects and their underlying OS resources. This should only be called when the cached 439 | * objects are no longer needed (e.g. on application shutdown). 440 | */ 441 | public static void dispose() { 442 | disposeColors(); 443 | disposeImages(); 444 | disposeFonts(); 445 | disposeCursors(); 446 | } 447 | } --------------------------------------------------------------------------------