├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ ├── city.json
│ └── city2.json
│ ├── java
│ └── cn
│ │ └── qqtheme
│ │ └── androidpicker
│ │ ├── AddressInitTask.java
│ │ ├── AssetsUtils.java
│ │ ├── CustomHeaderAndFooterPicker.java
│ │ └── MainActivity.java
│ └── res
│ ├── anim
│ ├── popup_in.xml
│ └── popup_out.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── picker_footer.xml
│ └── picker_header.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ └── values
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── .gitignore
├── ColorPicker
│ ├── .gitignore
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── cn
│ │ │ └── qqtheme
│ │ │ └── framework
│ │ │ ├── picker
│ │ │ └── ColorPicker.java
│ │ │ └── widget
│ │ │ └── ColorPanelView.java
│ │ └── res
│ │ └── drawable-hdpi
│ │ ├── color_picker_cursor_bottom.png
│ │ └── color_picker_cursor_top.png
├── Common
│ ├── .gitignore
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── cn
│ │ │ └── qqtheme
│ │ │ └── framework
│ │ │ ├── AppConfig.java
│ │ │ ├── popup
│ │ │ ├── BottomPopup.java
│ │ │ ├── ConfirmPopup.java
│ │ │ └── Popup.java
│ │ │ └── util
│ │ │ ├── CompatUtils.java
│ │ │ ├── ConvertUtils.java
│ │ │ ├── DateUtils.java
│ │ │ ├── LogUtils.java
│ │ │ └── ScreenUtils.java
│ │ └── res
│ │ └── values
│ │ └── styles.xml
├── FilePicker
│ ├── .gitignore
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── cn
│ │ │ └── qqtheme
│ │ │ └── framework
│ │ │ ├── adapter
│ │ │ └── FileAdapter.java
│ │ │ ├── entity
│ │ │ └── FileItem.java
│ │ │ ├── picker
│ │ │ └── FilePicker.java
│ │ │ ├── util
│ │ │ ├── FileUtils.java
│ │ │ └── StorageUtils.java
│ │ │ └── widget
│ │ │ └── MarqueeTextView.java
│ │ └── res
│ │ ├── drawable-hdpi
│ │ ├── file_picker_file.png
│ │ ├── file_picker_folder.png
│ │ ├── file_picker_home.png
│ │ └── file_picker_updir.png
│ │ └── layout
│ │ └── file_item.xml
└── WheelPicker
│ ├── .gitignore
│ ├── build.gradle
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── cn
│ └── qqtheme
│ └── framework
│ ├── picker
│ ├── AddressPicker.java
│ ├── ChineseZodiacPicker.java
│ ├── ConstellationPicker.java
│ ├── DatePicker.java
│ ├── DateTimePicker.java
│ ├── LinkagePicker.java
│ ├── NumberPicker.java
│ ├── OptionPicker.java
│ ├── SexPicker.java
│ ├── TimePicker.java
│ └── WheelPicker.java
│ └── widget
│ └── WheelView.java
├── publish.cmd
├── screenshots
├── address.gif
├── address.png
├── chinesezodiac.gif
├── color.gif
├── constellation.gif
├── custom.gif
├── date.gif
├── dir.gif
├── file.gif
├── number.gif
├── option.gif
└── time.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | /*.iml
2 | /gradlew
3 | /*/*.iml
4 | /gradle
5 | /.idea
6 | /gradlew.bat
7 | bin/
8 | gen/
9 | .gradle/
10 | build/
11 | /*/build/
12 | local.properties
13 | proguard/
14 | *.log
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Summary
2 | [](https://github.com/gzu-liyujiang/AndroidPicker)
3 | [](http://jcenter.bintray.com/cn/qqtheme/framework/)
4 | 安卓选择器类库,包括日期选择器、时间选择器、单项选择器、城市选择器、颜色选择器、文件选择器、目录选择器、数字选择器、星座选择器、生肖选择器等,可自定义顶部及底部界面,可自定义窗口动画。
5 | 欢迎大伙儿在[Issues](https://github.com/gzu-liyujiang/AndroidPicker/issues)提交你的意见或建议。欢迎Fork & Pull requests贡献您的代码。
6 |
7 | # Change Log
8 | - v1.1.2 - 2016.05.06
9 | + 添加二三级联动选择器;
10 | + 文件选择器布局调整;
11 | - v1.1.1 - 2016.04.23
12 | + 合并@Wastrel及@lutas2000贡献的代码,地址选择器支持只选择省和市、不能混淆某些类;
13 | - v1.1.0 - 2016.01.29
14 | + 添加注解约束,如“setOffset()”只能是1至4;
15 | + 所有枚举类改为常量来表示,据说这样可以节约内存;
16 | + 支持自定义选择器的顶部及底部的视图;
17 | + 支持使用第三方动画库来实现窗口动画;
18 | - v1.0.3 - 2016.01.19
19 | + 日期时间、地址、单项、数字等选择器支持伪循环滚动。
20 | - v1.0.2 - 2016.01.15
21 | + 年或月变动时,保持之前选择的日不动:如果之前选择的日是之前年月的最大日,则日自动为该年月的最大日。
22 | - v1.0.1 - 2016.01.14
23 | + 精简文件选择器的数据适配器;
24 | + 添加选择器顶部确定、取消按钮所在容器的背景色设置。
25 | - v1.0.0 - 2016.01.13
26 | + 发布到jcenter,支持远程maven依赖。
27 |
28 | # Install
29 | “app”是测试用例;“library”包括WheelPicker、ColorPicker、FilePicker,
30 | WheelPicker包括DatePicker、TimePicker、OptionPicker、AddressPicker、NumberPicker等,
31 | 可下载本项目手动进行源代码集成,导入“library”下的相关module,然后依赖,如:
32 | ```groovy
33 | dependencies {
34 | compile project(':WheelPicker')
35 | compile project(':FilePicker')
36 | }
37 | ```
38 | 也可以直接远程加载jcenter里的,如:
39 | ```groovy
40 | dependencies {
41 | compile 'cn.qqtheme.framework:WheelPicker:1.1.2'
42 | compile 'cn.qqtheme.framework:FilePicker:1.1.2'
43 | }
44 | ```
45 | *注:*
46 | 本项目使用gradle来构建,迁移到Eclipse比较麻烦,建议换为Android Studio或Intellij IDEA。
47 | 由于地址选择器使用了FastJson来解析,混淆时候需要加入以下类似的规则,不混淆Province、City等实体类。
48 | ```
49 | -keep class cn.qqtheme.framework.entity.** { *;}
50 | -keep class cn.qqtheme.framework.picker.AddressPicker$* { *;}
51 | ```
52 |
53 | # Custom
54 | ### 自定义窗口进入退出动画(可选,默认动画为淡入淡出)
55 | ```xml
56 |
57 |
61 |
62 | ```
63 | ```java
64 | picker.setAnimationStyle(R.style.Animation_CustomPopup);
65 | ```
66 | 或者使用[ViewAnimator](https://github.com/gzu-liyujiang/ViewAnimator)这个动画库来实现:
67 | ```groovy
68 | dependencies {
69 | compile 'com.github.florent37:viewanimator:1.0.3'
70 | }
71 | ```
72 | ```java
73 | ViewAnimator.animate(picker.getRootView())
74 | .slideBottomIn()
75 | .interpolator(new AccelerateInterpolator())
76 | .start();
77 | ```
78 |
79 | ### 自定义顶部及底部界面
80 | 添加自己的类,继承自现有的选择器,覆盖makeHeaderView、makeFooterView,在确定选择时调用onSubmit,
81 | 取消选择时调用onCancel。详见示例:CustomHeaderAndFooterPicker.java。
82 | ```java
83 | public class CustomHeaderAndFooterPicker extends OptionPicker {
84 |
85 | @Nullable
86 | @Override
87 | protected View makeHeaderView() {
88 | return null;//顶部视图
89 | }
90 |
91 | @Nullable
92 | @Override
93 | protected View makeFooterView() {
94 | return null;//底部视图
95 | }
96 |
97 | }
98 | ```
99 |
100 | # Sample (更多用法详见示例项目)
101 | 日期选择器:
102 | ```java
103 | DatePicker picker = new DatePicker(this, DatePicker.YEAR_MONTH_DAY);
104 | picker.setRange(1990, 2015);//年份范围
105 | picker.setOnDatePickListener(new DatePicker.OnYearMonthDayPickListener() {
106 | @Override
107 | public void onDatePicked(String year, String month, String day) {
108 | showToast(year + "-" + month + "-" + day);
109 | }
110 | });
111 | picker.show();
112 | ```
113 |
114 | 时间选择器:
115 | ```java
116 | //默认选中当前时间
117 | TimePicker picker = new TimePicker(this, TimePicker.HOUR_OF_DAY);
118 | picker.setTopLineVisible(false);
119 | picker.setOnTimePickListener(new TimePicker.OnTimePickListener() {
120 | @Override
121 | public void onTimePicked(String hour, String minute) {
122 | showToast(hour + ":" + minute);
123 | }
124 | });
125 | picker.show();
126 | ```
127 |
128 | 单项选择器(可用于性别、学历、职业、星座等选择):
129 | ```java
130 | OptionPicker picker = new OptionPicker(this, new String[]{
131 | "第一项", "第二项", "这是一个很长很长很长很长很长很长很长很长很长的很长很长的很长很长的项"
132 | });
133 | picker.setOffset(2);
134 | picker.setSelectedIndex(1);
135 | picker.setTextSize(11);
136 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
137 | @Override
138 | public void onOptionPicked(String option) {
139 | showToast(option);
140 | }
141 | });
142 | picker.show();
143 | ```
144 |
145 | 数字选择器(可用于身高、体重、年龄等选择):
146 | ```java
147 | NumberPicker picker = new NumberPicker(this);
148 | picker.setOffset(2);//偏移量
149 | picker.setRange(145, 200);//数字范围
150 | picker.setSelectedItem(172);
151 | picker.setLabel("厘米");
152 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
153 | @Override
154 | public void onOptionPicked(String option) {
155 | showToast(option);
156 | }
157 | });
158 | picker.show();
159 | ```
160 |
161 | 二三级联动选择器(参见地址选择器)
162 |
163 | 地址选择器(含省级、地级、县级):
164 | ```java
165 | ArrayList data = new ArrayList();
166 | String json = AssetsUtils.readText(this, "city.json");
167 | data.addAll(JSON.parseArray(json, AddressPicker.Province.class));
168 | AddressPicker picker = new AddressPicker(this, result);
169 | picker.setSelectedItem("贵州", "贵阳", "花溪");
170 | //picker.setHideProvince(true);//加上此句举将只显示地级及县级
171 | //picker.setHideCounty(true);//加上此句举将只显示省级及地级
172 | picker.setOnAddressPickListener(new AddressPicker.OnAddressPickListener() {
173 | @Override
174 | public void onAddressPicked(String province, String city, String county) {
175 | showToast(province + city + county);
176 | }
177 | });
178 | picker.show();
179 | ```
180 |
181 | 星座选择器:
182 | ```java
183 | ConstellationPicker picker = new ConstellationPicker(this);
184 | picker.setTopBackgroundColor(0xFFEEEEEE);
185 | picker.setTopLineVisible(false);
186 | picker.setCancelTextColor(0xFF33B5E5);
187 | picker.setSubmitTextColor(0xFF33B5E5);
188 | picker.setTextColor(0xFFFF0000, 0xFFCCCCCC);
189 | picker.setLineColor(0xFFEE0000);
190 | picker.setSelectedItem("射手");
191 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
192 | @Override
193 | public void onOptionPicked(String option) {
194 | showToast(option);
195 | }
196 | });
197 | picker.show();
198 | ```
199 |
200 | 颜色选择器:
201 | ```java
202 | ColorPicker picker = new ColorPicker(this);
203 | picker.setInitColor(0xFFDD00DD);
204 | picker.setOnColorPickListener(new ColorPicker.OnColorPickListener() {
205 | @Override
206 | public void onColorPicked(int pickedColor) {
207 | showToast(ConvertUtils.toColorString(pickedColor));
208 | }
209 | });
210 | picker.show();
211 | ```
212 |
213 | 文件选择器(需要权限android.permission.READ_EXTERNAL_STORAGE):
214 | ```java
215 | //noinspection MissingPermission
216 | FilePicker picker = new FilePicker(this, FilePicker.FILE);
217 | picker.setShowHideDir(false);
218 | picker.setRootPath(StorageUtils.getRootPath(this) + "Download/");
219 | //picker.setAllowExtensions(new String[]{".apk"});
220 | picker.setOnFilePickListener(new FilePicker.OnFilePickListener() {
221 | @Override
222 | public void onFilePicked(String currentPath) {
223 | showToast(currentPath);
224 | }
225 | });
226 | picker.show();
227 | ```
228 |
229 | 目录选择器(需要权限android.permission.READ_EXTERNAL_STORAGE):
230 | ```java
231 | //noinspection MissingPermission
232 | FilePicker picker = new FilePicker(this, FilePicker.DIRECTORY);
233 | picker.setOnFilePickListener(new FilePicker.OnFilePickListener() {
234 | @Override
235 | public void onFilePicked(String currentPath) {
236 | showToast(currentPath);
237 | }
238 | });
239 | picker.show();
240 | ```
241 |
242 | # Thanks
243 | 库项目修改了使用以下项目:
244 | https://github.com/wangjiegulu/WheelView
245 | https://github.com/jbruchanov/AndroidColorPicker
246 |
247 | # Screenshots
248 | 
249 | 
250 | 
251 | 
252 | 
253 | 
254 | 
255 | 
256 | 
257 | 
258 | 
259 | 
260 |
261 | # Contact
262 |
263 |
264 |
265 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /*.iml
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 |
5 | buildTypes {
6 | release {
7 | //是否zip优化
8 | zipAlignEnabled true
9 | //是否移除无用的资源
10 | shrinkResources true
11 | //是否混淆
12 | minifyEnabled true
13 | //混淆配置文件
14 | proguardFile 'proguard-rules.pro'
15 | }
16 | }
17 |
18 | }
19 |
20 | dependencies {
21 | compile 'com.alibaba:fastjson:1.1.46.android'
22 | compile 'com.github.florent37:viewanimator:1.0.3'
23 | compile project(':library:WheelPicker')
24 | compile project(':library:FilePicker')
25 | compile project(':library:ColorPicker')
26 | // compile 'cn.qqtheme.framework:WheelPicker:1.1.1'
27 | // compile 'cn.qqtheme.framework:FilePicker:1.1.1'
28 | // compile 'cn.qqtheme.framework:ColorPicker:1.1.1'
29 | }
30 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -keep class cn.qqtheme.framework.entity.** { *;}
2 | -keep class cn.qqtheme.framework.picker.AddressPickerr$* { *;}
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/assets/city2.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "cities": [
4 | {
5 | "counties": [
6 | {
7 | "areaName": "南明区 ",
8 | "areaId": "520102"
9 | },
10 | {
11 | "areaName": "云岩区",
12 | "areaId": "520103"
13 | },
14 | {
15 | "areaName": "花溪区 ",
16 | "areaId": "520111"
17 | },
18 | {
19 | "areaName": "乌当区 ",
20 | "areaId": "520112"
21 | },
22 | {
23 | "areaName": "白云区",
24 | "areaId": "520113"
25 | },
26 | {
27 | "areaName": "观山湖区",
28 | "areaId": "520115"
29 | },
30 | {
31 | "areaName": "开阳县",
32 | "areaId": "520121"
33 | },
34 | {
35 | "areaName": "息烽县",
36 | "areaId": "520122"
37 | },
38 | {
39 | "areaName": "修文县 ",
40 | "areaId": "520123"
41 | },
42 | {
43 | "areaName": "清镇市",
44 | "areaId": "520181"
45 | }
46 | ],
47 | "areaName": "贵阳市 ",
48 | "areaId": "520100"
49 | },
50 | {
51 | "counties": [
52 | {
53 | "areaName": "钟山区",
54 | "areaId": "520201"
55 | },
56 | {
57 | "areaName": "六枝特区 ",
58 | "areaId": "520203"
59 | },
60 | {
61 | "areaName": "水城县 ",
62 | "areaId": "520221"
63 | },
64 | {
65 | "areaName": "盘县 ",
66 | "areaId": "520222"
67 | }
68 | ],
69 | "areaName": "六盘水市",
70 | "areaId": "520200"
71 | },
72 | {
73 | "counties": [
74 | {
75 | "areaName": "红花岗区 ",
76 | "areaId": "520302"
77 | },
78 | {
79 | "areaName": "汇川区 ",
80 | "areaId": "520303"
81 | },
82 | {
83 | "areaName": "遵义县 ",
84 | "areaId": "520321"
85 | },
86 | {
87 | "areaName": "桐梓县",
88 | "areaId": "520322"
89 | },
90 | {
91 | "areaName": "绥阳县",
92 | "areaId": "520323"
93 | },
94 | {
95 | "areaName": "正安县",
96 | "areaId": "520324"
97 | },
98 | {
99 | "areaName": "道真仡佬族苗族自治县",
100 | "areaId": "520325"
101 | },
102 | {
103 | "areaName": "务川仡佬族苗族自治县 ",
104 | "areaId": "520326"
105 | },
106 | {
107 | "areaName": "凤冈县 ",
108 | "areaId": "520327"
109 | },
110 | {
111 | "areaName": "湄潭县 ",
112 | "areaId": "520328"
113 | },
114 | {
115 | "areaName": "余庆县 ",
116 | "areaId": "520329"
117 | },
118 | {
119 | "areaName": "习水县 ",
120 | "areaId": "520330"
121 | },
122 | {
123 | "areaName": "赤水市",
124 | "areaId": "520381"
125 | },
126 | {
127 | "areaName": "仁怀市",
128 | "areaId": "520382"
129 | }
130 | ],
131 | "areaName": "遵义市 ",
132 | "areaId": "520300"
133 | },
134 | {
135 | "counties": [
136 | {
137 | "areaName": "西秀区 ",
138 | "areaId": "520402"
139 | },
140 | {
141 | "areaName": "平坝区 ",
142 | "areaId": "520403"
143 | },
144 | {
145 | "areaName": "普定县 ",
146 | "areaId": "520422"
147 | },
148 | {
149 | "areaName": "镇宁布依族苗族自治县 ",
150 | "areaId": "520423"
151 | },
152 | {
153 | "areaName": "关岭布依族苗族自治县 ",
154 | "areaId": "520424"
155 | },
156 | {
157 | "areaName": "紫云苗族布依族自治县",
158 | "areaId": "520425"
159 | }
160 | ],
161 | "areaName": "安顺市 ",
162 | "areaId": "520400"
163 | },
164 | {
165 | "counties": [
166 | {
167 | "areaName": "七星关区 ",
168 | "areaId": "520502"
169 | },
170 | {
171 | "areaName": "大方县",
172 | "areaId": "520521"
173 | },
174 | {
175 | "areaName": "黔西县 ",
176 | "areaId": "520522"
177 | },
178 | {
179 | "areaName": "金沙县 ",
180 | "areaId": "520523"
181 | },
182 | {
183 | "areaName": "织金县 ",
184 | "areaId": "520524"
185 | },
186 | {
187 | "areaName": "纳雍县 ",
188 | "areaId": "520525"
189 | },
190 | {
191 | "areaName": "威宁彝族回族苗族自治县 ",
192 | "areaId": "520526"
193 | },
194 | {
195 | "areaName": " 赫章县 ",
196 | "areaId": "520527"
197 | }
198 | ],
199 | "areaName": "毕节市 ",
200 | "areaId": "520500"
201 | },
202 | {
203 | "counties": [
204 | {
205 | "areaName": "碧江区 ",
206 | "areaId": "520602"
207 | },
208 | {
209 | "areaName": "万山区",
210 | "areaId": "520603"
211 | },
212 | {
213 | "areaName": "江口县 ",
214 | "areaId": "520621"
215 | },
216 | {
217 | "areaName": "玉屏侗族自治县",
218 | "areaId": "520622"
219 | },
220 | {
221 | "areaName": "石阡县 ",
222 | "areaId": "520623"
223 | },
224 | {
225 | "areaName": "思南县 ",
226 | "areaId": "520624"
227 | },
228 | {
229 | "areaName": "印江土家族苗族自治县 ",
230 | "areaId": "520625"
231 | },
232 | {
233 | "areaName": "德江县 ",
234 | "areaId": "520626"
235 | },
236 | {
237 | "areaName": "沿河土家族自治县 ",
238 | "areaId": "520627"
239 | },
240 | {
241 | "areaName": "松桃苗族自治县",
242 | "areaId": "520628"
243 | }
244 | ],
245 | "areaName": "铜仁市",
246 | "areaId": "520600"
247 | },
248 | {
249 | "counties": [
250 | {
251 | "areaName": "兴义市 ",
252 | "areaId": "522301"
253 | },
254 | {
255 | "areaName": "兴仁县 ",
256 | "areaId": "522322"
257 | },
258 | {
259 | "areaName": "普安县 ",
260 | "areaId": "522323"
261 | },
262 | {
263 | "areaName": "晴隆县 ",
264 | "areaId": "522324"
265 | },
266 | {
267 | "areaName": "贞丰县 ",
268 | "areaId": "522325"
269 | },
270 | {
271 | "areaName": "望谟县 ",
272 | "areaId": "522326"
273 | },
274 | {
275 | "areaName": "册亨县 ",
276 | "areaId": "522327"
277 | },
278 | {
279 | "areaName": "安龙县 ",
280 | "areaId": "522328"
281 | }
282 | ],
283 | "areaName": "黔西南布依族苗族自治州",
284 | "areaId": "522300"
285 | },
286 | {
287 | "counties": [
288 | {
289 | "areaName": "凯里市 ",
290 | "areaId": "522601"
291 | },
292 | {
293 | "areaName": "黄平县 ",
294 | "areaId": "522622"
295 | },
296 | {
297 | "areaName": "施秉县",
298 | "areaId": "522623"
299 | },
300 | {
301 | "areaName": "三穗县",
302 | "areaId": "522624"
303 | },
304 | {
305 | "areaName": "镇远县 ",
306 | "areaId": "522625"
307 | },
308 | {
309 | "areaName": "岑巩县",
310 | "areaId": "522626"
311 | },
312 | {
313 | "areaName": "天柱县 ",
314 | "areaId": "522627"
315 | },
316 | {
317 | "areaName": "锦屏县",
318 | "areaId": "522628"
319 | },
320 | {
321 | "areaName": "剑河县 ",
322 | "areaId": "522629"
323 | },
324 | {
325 | "areaName": "台江县 ",
326 | "areaId": "522630"
327 | },
328 | {
329 | "areaName": "黎平县 ",
330 | "areaId": "522631"
331 | },
332 | {
333 | "areaName": "榕江县 ",
334 | "areaId": "522632"
335 | },
336 | {
337 | "areaName": "从江县 ",
338 | "areaId": "522633"
339 | },
340 | {
341 | "areaName": "雷山县 ",
342 | "areaId": "522634"
343 | },
344 | {
345 | "areaName": "麻江县",
346 | "areaId": "522635"
347 | },
348 | {
349 | "areaName": "丹寨县",
350 | "areaId": "522636"
351 | }
352 | ],
353 | "areaName": "黔东南苗族侗族自治州",
354 | "areaId": "522600"
355 | },
356 | {
357 | "counties": [
358 | {
359 | "areaName": "都匀市 ",
360 | "areaId": "522701"
361 | },
362 | {
363 | "areaName": "福泉市 ",
364 | "areaId": "522702"
365 | },
366 | {
367 | "areaName": "荔波县 ",
368 | "areaId": "522722"
369 | },
370 | {
371 | "areaName": "贵定县 ",
372 | "areaId": "522723"
373 | },
374 | {
375 | "areaName": "瓮安县",
376 | "areaId": "522725"
377 | },
378 | {
379 | "areaName": "独山县 ",
380 | "areaId": "522726"
381 | },
382 | {
383 | "areaName": "平塘县",
384 | "areaId": "522727"
385 | },
386 | {
387 | "areaName": "罗甸县",
388 | "areaId": "522728"
389 | },
390 | {
391 | "areaName": "长顺县 ",
392 | "areaId": "522729"
393 | },
394 | {
395 | "areaName": "龙里县",
396 | "areaId": "522730"
397 | },
398 | {
399 | "areaName": "惠水县 ",
400 | "areaId": "522731"
401 | },
402 | {
403 | "areaName": "三都水族自治县 ",
404 | "areaId": "522732"
405 | }
406 | ],
407 | "areaName": "黔南布依族苗族自治州 ",
408 | "areaId": "522700"
409 | },
410 | {
411 | "counties": [
412 | {
413 | "areaName": "贵安新区",
414 | "areaId": "529900"
415 | }
416 | ],
417 | "areaName": "贵安新区",
418 | "areaId": "529900"
419 | }
420 | ],
421 | "areaName": "贵州省 ",
422 | "areaId": "520000"
423 | }
424 | ]
--------------------------------------------------------------------------------
/app/src/main/java/cn/qqtheme/androidpicker/AddressInitTask.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.androidpicker;
2 |
3 | import android.app.Activity;
4 | import android.app.ProgressDialog;
5 | import android.os.AsyncTask;
6 | import android.widget.Toast;
7 |
8 | import com.alibaba.fastjson.JSON;
9 |
10 | import java.util.ArrayList;
11 |
12 | import cn.qqtheme.framework.picker.AddressPicker;
13 |
14 | /**
15 | * 获取地址数据并显示地址选择器
16 | *
17 | * @author 李玉江[QQ:1032694760]
18 | * @version 2015/12/15
19 | */
20 | public class AddressInitTask extends AsyncTask> {
21 | private Activity activity;
22 | private ProgressDialog dialog;
23 | private String selectedProvince = "", selectedCity = "", selectedCounty = "";
24 | private boolean hideCounty=false;
25 |
26 | /**
27 | * 初始化为不显示区县的模式
28 | * @param activity
29 | * @param hideCounty is hide County
30 | */
31 | public AddressInitTask(Activity activity,boolean hideCounty) {
32 | this.activity = activity;
33 | this.hideCounty=hideCounty;
34 | dialog = ProgressDialog.show(activity, null, "正在初始化数据...", true, true);
35 | }
36 |
37 | public AddressInitTask(Activity activity) {
38 | this.activity = activity;
39 | dialog = ProgressDialog.show(activity, null, "正在初始化数据...", true, true);
40 | }
41 | @Override
42 | protected ArrayList doInBackground(String... params) {
43 | if (params != null) {
44 | switch (params.length) {
45 | case 1:
46 | selectedProvince = params[0];
47 | break;
48 | case 2:
49 | selectedProvince = params[0];
50 | selectedCity = params[1];
51 | break;
52 | case 3:
53 | selectedProvince = params[0];
54 | selectedCity = params[1];
55 | selectedCounty = params[2];
56 | break;
57 | default:
58 | break;
59 | }
60 | }
61 | ArrayList data = new ArrayList();
62 | try {
63 | String json = AssetsUtils.readText(activity, "city.json");
64 | data.addAll(JSON.parseArray(json, AddressPicker.Province.class));
65 | } catch (Exception e) {
66 | e.printStackTrace();
67 | }
68 | return data;
69 | }
70 |
71 | @Override
72 | protected void onPostExecute(ArrayList result) {
73 | dialog.dismiss();
74 | if (result.size() > 0) {
75 | AddressPicker picker = new AddressPicker(activity, result);
76 | picker.setHideCounty(hideCounty);
77 | picker.setSelectedItem(selectedProvince, selectedCity, selectedCounty);
78 | picker.setOnAddressPickListener(new AddressPicker.OnAddressPickListener() {
79 | @Override
80 | public void onAddressPicked(String province, String city, String county) {
81 | if (county==null){
82 | Toast.makeText(activity, province + city, Toast.LENGTH_LONG).show();
83 | } else {
84 | Toast.makeText(activity, province + city + county, Toast.LENGTH_LONG).show();
85 | }
86 | }
87 | });
88 | picker.show();
89 | } else {
90 | Toast.makeText(activity, "数据初始化失败", Toast.LENGTH_SHORT).show();
91 | }
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/qqtheme/androidpicker/AssetsUtils.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.androidpicker;
2 |
3 | import android.content.Context;
4 |
5 | import cn.qqtheme.framework.util.ConvertUtils;
6 | import cn.qqtheme.framework.util.LogUtils;
7 |
8 | /**
9 | * 操作安装包中的“assets”目录下的文件
10 | *
11 | * @author 李玉江[QQ:1023694760]
12 | * @version 2013-11-2
13 | */
14 | public class AssetsUtils {
15 |
16 | /**
17 | * read file content
18 | *
19 | * @param context the context
20 | * @param assetPath the asset path
21 | * @return String string
22 | */
23 | public static String readText(Context context, String assetPath) {
24 | LogUtils.debug("read assets file as text: " + assetPath);
25 | try {
26 | return ConvertUtils.toString(context.getAssets().open(assetPath));
27 | } catch (Exception e) {
28 | LogUtils.error(e);
29 | return "";
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/qqtheme/androidpicker/CustomHeaderAndFooterPicker.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.androidpicker;
2 |
3 | import android.app.Activity;
4 | import android.support.annotation.Nullable;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.animation.AccelerateInterpolator;
8 | import android.widget.Button;
9 | import android.widget.TextView;
10 |
11 | import com.github.florent37.viewanimator.AnimationListener;
12 | import com.github.florent37.viewanimator.ViewAnimator;
13 |
14 | import cn.qqtheme.framework.picker.OptionPicker;
15 |
16 | /**
17 | * 自定义顶部及底部
18 | *
19 | * Author:李玉江[QQ:1032694760]
20 | * Email:liyujiang_tk@yeah.net
21 | * DateTime:2016/1/29 14:47
22 | * Builder:Android Studio
23 | */
24 | public class CustomHeaderAndFooterPicker extends OptionPicker {
25 |
26 | public CustomHeaderAndFooterPicker(Activity activity) {
27 | super(activity, new String[]{
28 | "C/C++", "Java", "PHP", "Swift", "Node.js", "C#", "HTML5"
29 | });
30 | setTitleText("请选择你最擅长的语言");
31 | setSelectedItem("PHP");
32 | }
33 |
34 | @Override
35 | public void show() {
36 | super.show();
37 | ViewAnimator.animate(getRootView())
38 | .duration(2000)
39 | .interpolator(new AccelerateInterpolator())
40 | .slideBottom()
41 | .start();
42 | }
43 |
44 | @Override
45 | public void dismiss() {
46 | ViewAnimator.animate(getRootView())
47 | .duration(1000)
48 | .rollOut()
49 | .onStop(new AnimationListener.Stop() {
50 | @Override
51 | public void onStop() {
52 | CustomHeaderAndFooterPicker.super.dismiss();
53 | }
54 | })
55 | .start();
56 | }
57 |
58 | @Nullable
59 | @Override
60 | protected View makeHeaderView() {
61 | View view = LayoutInflater.from(activity).inflate(R.layout.picker_header, null);
62 | TextView titleView = (TextView) view.findViewById(R.id.picker_title);
63 | titleView.setText(titleText);
64 | return view;
65 | }
66 |
67 | @Nullable
68 | @Override
69 | protected View makeFooterView() {
70 | View view = LayoutInflater.from(activity).inflate(R.layout.picker_footer, null);
71 | Button submitView = (Button) view.findViewById(R.id.picker_submit);
72 | submitView.setText(submitText);
73 | submitView.setOnClickListener(new View.OnClickListener() {
74 | @Override
75 | public void onClick(View v) {
76 | dismiss();
77 | onSubmit();
78 | }
79 | });
80 | Button cancelView = (Button) view.findViewById(R.id.picker_cancel);
81 | cancelView.setText(cancelText);
82 | cancelView.setOnClickListener(new View.OnClickListener() {
83 | @Override
84 | public void onClick(View v) {
85 | dismiss();
86 | onCancel();
87 | }
88 | });
89 | return view;
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/qqtheme/androidpicker/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.androidpicker;
2 |
3 | import android.app.Activity;
4 | import android.content.DialogInterface;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.view.View;
9 | import android.widget.Toast;
10 |
11 | import com.alibaba.fastjson.JSON;
12 | import com.github.florent37.viewanimator.ViewAnimator;
13 |
14 | import java.util.ArrayList;
15 | import java.util.Calendar;
16 |
17 | import cn.qqtheme.framework.picker.AddressPicker;
18 | import cn.qqtheme.framework.picker.ChineseZodiacPicker;
19 | import cn.qqtheme.framework.picker.ColorPicker;
20 | import cn.qqtheme.framework.picker.ConstellationPicker;
21 | import cn.qqtheme.framework.picker.DatePicker;
22 | import cn.qqtheme.framework.picker.DateTimePicker;
23 | import cn.qqtheme.framework.picker.FilePicker;
24 | import cn.qqtheme.framework.picker.LinkagePicker;
25 | import cn.qqtheme.framework.picker.NumberPicker;
26 | import cn.qqtheme.framework.picker.OptionPicker;
27 | import cn.qqtheme.framework.picker.SexPicker;
28 | import cn.qqtheme.framework.picker.TimePicker;
29 | import cn.qqtheme.framework.util.ConvertUtils;
30 | import cn.qqtheme.framework.util.DateUtils;
31 | import cn.qqtheme.framework.util.StorageUtils;
32 |
33 | public class MainActivity extends Activity {
34 |
35 | private Calendar calendar = Calendar.getInstance();
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_main);
40 | }
41 |
42 | @Override
43 | public void onBackPressed() {
44 | System.exit(0);
45 | android.os.Process.killProcess(android.os.Process.myPid());
46 | finish();
47 | }
48 |
49 | private void showToast(String msg) {
50 | Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
51 | }
52 |
53 | public void onAnimationStyle(View view) {
54 | NumberPicker picker = new NumberPicker(this);
55 | picker.setAnimationStyle(R.style.Animation_CustomPopup);
56 | picker.setOffset(2);//偏移量
57 | picker.setRange(40, 100);//数字范围
58 | picker.setSelectedItem(65);
59 | picker.setLabel("Kg");
60 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
61 | @Override
62 | public void onOptionPicked(String option) {
63 | showToast(option);
64 | }
65 | });
66 | picker.show();
67 | }
68 |
69 | public void onAnimator(View view) {
70 | CustomHeaderAndFooterPicker picker = new CustomHeaderAndFooterPicker(this);
71 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
72 | @Override
73 | public void onOptionPicked(String option) {
74 | showToast(option);
75 | }
76 | });
77 | picker.show();
78 | }
79 |
80 | public void onYearMonthDayPicker(View view) {
81 | DatePicker picker = new DatePicker(this);
82 | picker.setRange(2000, 2030);
83 | picker.setSelectedItem(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH));
84 | picker.setOnDatePickListener(new DatePicker.OnYearMonthDayPickListener() {
85 | @Override
86 | public void onDatePicked(String year, String month, String day) {
87 | showToast(year + "-" + month + "-" + day);
88 | }
89 | });
90 | picker.show();
91 | }
92 |
93 |
94 | public void onYearMonthDayTimePicker(View view) {
95 | DateTimePicker picker = new DateTimePicker(this, DateTimePicker.HOUR_OF_DAY);
96 | picker.setRange(2000, 2030);
97 | picker.setSelectedItem(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH)+1, calendar.get(Calendar.DAY_OF_MONTH),
98 | calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE));
99 | picker.setOnDateTimePickListener(new DateTimePicker.OnYearMonthDayTimePickListener() {
100 | @Override
101 | public void onDateTimePicked(String year, String month, String day, String hour, String minute) {
102 | showToast(year + "-" + month + "-" + day + " " + hour + ":" + minute);
103 | }
104 | });
105 | picker.show();
106 | }
107 |
108 |
109 |
110 | public void onYearMonthPicker(View view) {
111 | DatePicker picker = new DatePicker(this, DatePicker.YEAR_MONTH);
112 | picker.setRange(1990, 2015);
113 | picker.setOnDatePickListener(new DatePicker.OnYearMonthPickListener() {
114 | @Override
115 | public void onDatePicked(String year, String month) {
116 | showToast(year + "-" + month);
117 | }
118 | });
119 | picker.show();
120 | }
121 |
122 | public void onMonthDayPicker(View view) {
123 | DatePicker picker = new DatePicker(this, DatePicker.MONTH_DAY);
124 | picker.setOnDatePickListener(new DatePicker.OnMonthDayPickListener() {
125 | @Override
126 | public void onDatePicked(String month, String day) {
127 | showToast(month + "-" + day);
128 | }
129 | });
130 | picker.show();
131 | }
132 |
133 | public void onTimePicker(View view) {
134 | //默认选中当前时间
135 | TimePicker picker = new TimePicker(this, TimePicker.HOUR_OF_DAY);
136 | picker.setTopLineVisible(false);
137 | picker.setOnTimePickListener(new TimePicker.OnTimePickListener() {
138 | @Override
139 | public void onTimePicked(String hour, String minute) {
140 | showToast(hour + ":" + minute);
141 | }
142 | });
143 | picker.show();
144 | }
145 |
146 | public void onOptionPicker(View view) {
147 | OptionPicker picker = new OptionPicker(this, new String[]{
148 | "第一项", "第二项", "这是一个很长很长很长很长很长很长很长很长很长的很长很长的很长很长的项"
149 | });
150 | picker.setOffset(2);
151 | picker.setSelectedIndex(1);
152 | picker.setTextSize(11);
153 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
154 | @Override
155 | public void onOptionPicked(String option) {
156 | showToast(option);
157 | }
158 | });
159 | picker.show();
160 | }
161 |
162 | public void onLinkagePicker(View view) {
163 | ArrayList firstList = new ArrayList();
164 | firstList.add("今天");
165 | firstList.add("明天");
166 | ArrayList> secondList = new ArrayList>();
167 | ArrayList secondListItem = new ArrayList();
168 | for (int i = 0; i < 24; i++) {
169 | secondListItem.add(DateUtils.fillZero(i) + "点");
170 | }
171 | secondList.add(secondListItem);//对应今天
172 | secondList.add(secondListItem);//对应明天
173 | ArrayList>> thirdList = new ArrayList>>();
174 | ArrayList> thirdListItem1 = new ArrayList>();
175 | ArrayList thirdListItem2 = new ArrayList();
176 | for (int i = 0; i < 60; i++) {
177 | thirdListItem2.add(DateUtils.fillZero(i) + "分");
178 | }
179 | for (int i = 0; i < 24; i++) {
180 | thirdListItem1.add(thirdListItem2);//对应0-23点
181 | }
182 | thirdList.add(thirdListItem1);//对应今天
183 | thirdList.add(thirdListItem1);//对应明天
184 | LinkagePicker picker = new LinkagePicker(this, firstList, secondList);
185 | picker.setSelectedItem("明天", "9点");
186 | picker.setOnLinkageListener(new LinkagePicker.OnLinkageListener() {
187 |
188 | @Override
189 | public void onPicked(String first, String second, String third) {
190 | showToast(first + "-" + second + "-" + third);
191 | }
192 | });
193 | picker.show();
194 | }
195 |
196 | public void onConstellationPicker(View view) {
197 | ConstellationPicker picker = new ConstellationPicker(this);
198 | picker.setTopBackgroundColor(0xFFEEEEEE);
199 | picker.setTopLineVisible(false);
200 | picker.setCancelTextColor(0xFF33B5E5);
201 | picker.setSubmitTextColor(0xFF33B5E5);
202 | picker.setTextColor(0xFFFF0000, 0xFFCCCCCC);
203 | picker.setLineColor(0xFFEE0000);
204 | picker.setSelectedItem("射手");
205 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
206 | @Override
207 | public void onOptionPicked(String option) {
208 | showToast(option);
209 | }
210 | });
211 | picker.show();
212 | }
213 |
214 | public void onChineseZodiacPicker(View view) {
215 | ChineseZodiacPicker picker = new ChineseZodiacPicker(this);
216 | picker.setLineVisible(false);
217 | picker.setSelectedItem("羊");
218 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
219 | @Override
220 | public void onOptionPicked(String option) {
221 | showToast(option);
222 | }
223 | });
224 | picker.show();
225 | }
226 |
227 | public void onNumberPicker(View view) {
228 | NumberPicker picker = new NumberPicker(this);
229 | picker.setOffset(2);//偏移量
230 | picker.setRange(145, 200);//数字范围
231 | picker.setSelectedItem(172);
232 | picker.setLabel("厘米");
233 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
234 | @Override
235 | public void onOptionPicked(String option) {
236 | showToast(option);
237 | }
238 | });
239 | picker.show();
240 | }
241 |
242 | public void onSexPicker(View view) {
243 | SexPicker picker = new SexPicker(this);
244 | //picker.onlyMaleAndFemale();
245 | picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
246 | @Override
247 | public void onOptionPicked(String option) {
248 | showToast(option);
249 | }
250 | });
251 | picker.show();
252 | }
253 |
254 | public void onAddressPicker(View view) {
255 | new AddressInitTask(this).execute("贵州", "毕节", "纳雍");
256 | }
257 |
258 | public void onAddress2Picker(View view) {
259 | try {
260 | ArrayList data = new ArrayList();
261 | String json = AssetsUtils.readText(this, "city2.json");
262 | data.addAll(JSON.parseArray(json, AddressPicker.Province.class));
263 | AddressPicker picker = new AddressPicker(this, data);
264 | picker.setHideProvince(true);
265 | picker.setSelectedItem("贵州", "贵阳", "花溪");
266 | picker.setOnAddressPickListener(new AddressPicker.OnAddressPickListener() {
267 | @Override
268 | public void onAddressPicked(String province, String city, String county) {
269 | showToast(city + county);
270 | }
271 | });
272 | picker.show();
273 | } catch (Exception e) {
274 | showToast(e.toString());
275 | }
276 | }
277 |
278 |
279 | public void onAddress3Picker(View view) {
280 | new AddressInitTask(this, true).execute("四川", "成都");
281 | }
282 |
283 | public void onColorPicker(View view) {
284 | ColorPicker picker = new ColorPicker(this);
285 | picker.setInitColor(0xDD00DD);
286 | picker.setOnColorPickListener(new ColorPicker.OnColorPickListener() {
287 | @Override
288 | public void onColorPicked(int pickedColor) {
289 | showToast(ConvertUtils.toColorString(pickedColor));
290 | }
291 | });
292 | picker.show();
293 | }
294 |
295 | public void onFilePicker(View view) {
296 | //noinspection MissingPermission
297 | FilePicker picker = new FilePicker(this, FilePicker.FILE);
298 | picker.setShowHideDir(false);
299 | //picker.setAllowExtensions(new String[]{".apk"});
300 | picker.setOnFilePickListener(new FilePicker.OnFilePickListener() {
301 | @Override
302 | public void onFilePicked(String currentPath) {
303 | showToast(currentPath);
304 | }
305 | });
306 | picker.show();
307 | }
308 |
309 | public void onDirPicker(View view) {
310 | //noinspection MissingPermission
311 | FilePicker picker = new FilePicker(this, FilePicker.DIRECTORY);
312 | picker.setRootPath(StorageUtils.getRootPath(this) + "Download/");
313 | picker.setOnFilePickListener(new FilePicker.OnFilePickListener() {
314 | @Override
315 | public void onFilePicked(String currentPath) {
316 | showToast(currentPath);
317 | }
318 | });
319 | picker.show();
320 | }
321 |
322 | public void onContact(View view) {
323 | Intent intent = new Intent(Intent.ACTION_SENDTO);
324 | intent.setData(Uri.parse("mailto:liyujiang_tk@yeah.net"));
325 | intent.putExtra(Intent.EXTRA_CC, new String[]
326 | {"1032694760@qq.com"});
327 | intent.putExtra(Intent.EXTRA_EMAIL, "");
328 | intent.putExtra(Intent.EXTRA_TEXT, "欢迎提供意您的见或建议");
329 | startActivity(Intent.createChooser(intent, "选择邮件客户端"));
330 | }
331 |
332 | }
333 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popup_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popup_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
16 |
17 |
22 |
23 |
28 |
29 |
34 |
35 |
40 |
41 |
46 |
47 |
52 |
53 |
58 |
59 |
64 |
65 |
70 |
71 |
76 |
77 |
82 |
83 |
88 |
89 |
94 |
95 |
100 |
105 |
106 |
111 |
112 |
117 |
118 |
123 |
124 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/picker_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
19 |
20 |
25 |
26 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/picker_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sevenli777/android-picker/a561012af9c737f43da4d3bc10990efa467a8d2c/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidPicker
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | //参考:https://raw.github.com/dm77/barcodescanner/master/build.gradle
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:${GRADLE_BUILD_VERSION}"
9 | classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:${GRADLE_BINTRAY_VERSION}"
10 | }
11 | }
12 |
13 | allprojects {
14 | group = PROJ_GROUP
15 | version = PROJ_VERSION
16 |
17 | repositories {
18 | jcenter() //bintray的maven库
19 | mavenCentral() //sonatype的maven库
20 | mavenLocal() //本地maven库
21 | flatDir {
22 | dirs 'libs' //本地.aar文件
23 | }
24 | }
25 |
26 | ext {
27 | isLibrary = false
28 | pomGroup = PROJ_GROUP
29 | pomArtifactId = "library"
30 | pomVersion = PROJ_VERSION
31 | pomDescription = 'This is library description'
32 | }
33 |
34 | }
35 |
36 | subprojects {
37 | afterEvaluate { Project project ->
38 | ext.pluginContainer = project.getPlugins()
39 | def hasAppPlugin = ext.pluginContainer.hasPlugin("com.android.application")
40 | def hasLibPlugin = ext.pluginContainer.hasPlugin("com.android.library")
41 | if (hasAppPlugin || hasLibPlugin) {
42 | android {
43 | compileSdkVersion COMPILE_SDK_VERSION as int
44 | buildToolsVersion BUILD_TOOL_VERSION
45 |
46 | defaultConfig {
47 | minSdkVersion MIN_SDK_VERSION as int
48 | targetSdkVersion COMPILE_SDK_VERSION as int
49 | versionCode VERSION_CODE as int
50 | versionName VERSION_NAME
51 | }
52 |
53 | buildTypes {
54 | release {
55 | debuggable false
56 | minifyEnabled false
57 | proguardFile getDefaultProguardFile('proguard-android.txt')
58 | }
59 | debug {
60 | debuggable true
61 | minifyEnabled false
62 | }
63 | }
64 |
65 | lintOptions {
66 | abortOnError false
67 | }
68 |
69 | packagingOptions {
70 | exclude 'META-INF/DEPENDENCIES.txt'
71 | exclude 'META-INF/LICENSE.txt'
72 | exclude 'META-INF/NOTICE.txt'
73 | exclude 'META-INF/NOTICE'
74 | exclude 'META-INF/LICENSE'
75 | exclude 'META-INF/DEPENDENCIES'
76 | exclude 'META-INF/notice.txt'
77 | exclude 'META-INF/license.txt'
78 | exclude 'META-INF/dependencies.txt'
79 | exclude 'META-INF/LGPL2.1'
80 | exclude 'META-INF/ASL2.0'
81 | }
82 |
83 | }
84 |
85 | dependencies {
86 | compile "com.android.support:support-v4:${ANDROID_SUPPORT_VERSION}"
87 | compile "com.android.support:support-annotations:${ANDROID_SUPPORT_VERSION}"
88 | }
89 | }
90 |
91 | if (project.isLibrary) {
92 | configure(project) {
93 | // 这个脚本是用来发布库项目到jcenter
94 | apply plugin: 'com.jfrog.bintray'
95 | apply plugin: 'maven-publish'
96 |
97 | version = project.pomVersion //版本号
98 | group = PROJ_GROUP // 包名
99 | project.archivesBaseName = project.pomArtifactId
100 |
101 | task sourcesJar(type: Jar) {
102 | from android.sourceSets.main.java.srcDirs
103 | classifier = 'sources'
104 | }
105 |
106 | task javadoc(type: Javadoc) {
107 | source = android.sourceSets.main.java.srcDirs
108 | classpath += configurations.compile
109 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
110 | exclude '**/BuildConfig.java'
111 | exclude '**/R.java'
112 | failOnError = false
113 | }
114 |
115 | task javadocJar(type: Jar, dependsOn: javadoc) {
116 | classifier = 'javadoc'
117 | from javadoc.destinationDir
118 | }
119 |
120 | javadoc {
121 | options {
122 | encoding "UTF-8"
123 | charSet 'UTF-8'
124 | author true
125 | version true
126 | links "http://docs.oracle.com/javase/7/docs/api"
127 | title project.pomArtifactId
128 | }
129 | }
130 |
131 | publishing {
132 | publications {
133 | mavenJava(MavenPublication) {
134 | artifactId project.pomArtifactId
135 | artifact "${buildDir}/outputs/aar/${project.pomArtifactId}-release.aar"
136 | artifact javadocJar
137 | artifact sourcesJar
138 |
139 | pom.withXml {
140 | Node root = asNode()
141 | root.appendNode('name', project.pomArtifactId)
142 | root.appendNode('description', project.pomDescription)
143 | root.appendNode('url', PROJ_WEBSITE_URL)
144 |
145 | def issues = root.appendNode('issueManagement')
146 | issues.appendNode('system', 'github')
147 | issues.appendNode('url', PROJ_ISSUE_URL)
148 |
149 | def scm = root.appendNode('scm')
150 | scm.appendNode('url', PROJ_GIT_URL)
151 | scm.appendNode('connection', "scm:git:${PROJ_GIT_URL}")
152 | scm.appendNode('developerConnection', "scm:git:${PROJ_GIT_URL}")
153 |
154 | def license = root.appendNode('licenses').appendNode('license')
155 | license.appendNode('name', "The Apache Software License, Version 2.0")
156 | license.appendNode('url', "http://www.apache.org/licenses/LICENSE-2.0.txt")
157 | license.appendNode('distribution', "repo")
158 |
159 | def developer = root.appendNode('developers').appendNode('developer')
160 | developer.appendNode('id', DEVELOPER_ID)
161 | developer.appendNode('name', DEVELOPER_NAME)
162 | developer.appendNode('email', DEVELOPER_EMAIL)
163 |
164 | def dependenciesNode = root.appendNode('dependencies')
165 | configurations.compile.allDependencies.each {
166 | if (it.group && it.name && it.version) {
167 | def dependencyNode = dependenciesNode.appendNode('dependency')
168 | dependencyNode.appendNode('groupId', it.group)
169 | dependencyNode.appendNode('artifactId', it.name)
170 | dependencyNode.appendNode('version', it.version)
171 | }
172 | }
173 | }
174 | }
175 | }
176 | }
177 |
178 | afterEvaluate {
179 | publishing.publications.mavenJava.artifact(bundleRelease)
180 | }
181 |
182 | bintray {
183 | Properties properties = new Properties()
184 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
185 | user = properties.getProperty("bintray.user")
186 | key = properties.getProperty("bintray.apikey")
187 |
188 | publications = ['mavenJava']
189 | publish = true //是否发布
190 |
191 | pkg {
192 | repo = "maven" //上传的中央仓库名称
193 | name = project.pomArtifactId //发布到中央仓库上的项目名字
194 | desc = project.pomDescription
195 | websiteUrl = PROJ_WEBSITE_URL //项目主页
196 | issueTrackerUrl = PROJ_ISSUE_URL //项目讨论页
197 | vcsUrl = PROJ_GIT_URL //项目GIT仓库
198 | licenses = ["Apache-2.0"]
199 | publicDownloadNumbers = true
200 | version {
201 | name = project.pomVersion
202 | desc = project.pomDescription
203 | gpg {
204 | sign = true //是否GPG签名,可使用Gpg4win创建密钥文件
205 | passphrase = properties.getProperty("bintray.gpg.password")
206 | //GPG签名所用密钥
207 | }
208 | mavenCentralSync {
209 | sync = false //是否同步到Maven Central
210 | user = properties.getProperty("sonatype.user") //sonatype用户名
211 | password = properties.getProperty("sonatype.password")
212 | //sonatype密码
213 | close = '1'
214 | }
215 | }
216 | }
217 | }
218 |
219 | }
220 | }
221 | }
222 | }
223 |
224 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sevenli777/android-picker/a561012af9c737f43da4d3bc10990efa467a8d2c/gradle.properties
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sevenli777/android-picker/a561012af9c737f43da4d3bc10990efa467a8d2c/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 | #distributionUrl=http://127.0.0.1/gradle-2.10-all.zip
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /*.iml
3 |
--------------------------------------------------------------------------------
/library/ColorPicker/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /*.iml
3 |
--------------------------------------------------------------------------------
/library/ColorPicker/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | ext {
4 | isLibrary = true
5 | pomArtifactId = "ColorPicker"
6 | pomDescription = "color picker for android"
7 | }
8 |
9 | dependencies {
10 | compile project(":library:Common")
11 | }
12 |
--------------------------------------------------------------------------------
/library/ColorPicker/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/library/ColorPicker/src/main/java/cn/qqtheme/framework/picker/ColorPicker.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.picker;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.res.ColorStateList;
6 | import android.graphics.Color;
7 | import android.support.annotation.ColorInt;
8 | import android.support.annotation.NonNull;
9 | import android.text.InputType;
10 | import android.view.Gravity;
11 | import android.view.KeyEvent;
12 | import android.view.View;
13 | import android.view.inputmethod.EditorInfo;
14 | import android.view.inputmethod.InputMethodManager;
15 | import android.widget.EditText;
16 | import android.widget.LinearLayout;
17 | import android.widget.TextView;
18 |
19 | import java.util.Locale;
20 |
21 | import cn.qqtheme.framework.colorpicker.R;
22 | import cn.qqtheme.framework.popup.ConfirmPopup;
23 | import cn.qqtheme.framework.util.CompatUtils;
24 | import cn.qqtheme.framework.util.ConvertUtils;
25 | import cn.qqtheme.framework.widget.ColorPanelView;
26 |
27 | /**
28 | * 颜色选择器。
29 | *
30 | * @author 李玉江[QQ :1032694760]
31 | * @version 2015 /9/29
32 | */
33 | public class ColorPicker extends ConfirmPopup implements TextView.OnEditorActionListener {
34 | private static final int MULTI_ID = 0x1;
35 | private static final int BLACK_ID = 0x2;
36 | private int initColor = Color.WHITE;
37 | private ColorPanelView multiColorView, blackColorView;
38 | private EditText hexValView;
39 | private ColorStateList hexValDefaultColor;
40 | private OnColorPickListener onColorPickListener;
41 |
42 | /**
43 | * Instantiates a new Color picker.
44 | *
45 | * @param activity the activity
46 | */
47 | public ColorPicker(Activity activity) {
48 | super(activity);
49 | setHalfScreen(true);
50 | }
51 |
52 | @Override
53 | @NonNull
54 | protected LinearLayout makeCenterView() {
55 | LinearLayout rootLayout = new LinearLayout(activity);
56 | rootLayout.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
57 | rootLayout.setOrientation(LinearLayout.VERTICAL);
58 | blackColorView = new ColorPanelView(activity);
59 | //noinspection ResourceType
60 | blackColorView.setId(BLACK_ID);
61 | blackColorView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, ConvertUtils.toPx(activity, 30)));
62 | blackColorView.setPointerDrawable(CompatUtils.getDrawable(activity, R.drawable.color_picker_cursor_bottom));
63 | blackColorView.setLockPointerInBounds(false);
64 | blackColorView.setOnColorChangedListener(new ColorPanelView.OnColorChangedListener() {
65 | @Override
66 | public void onColorChanged(ColorPanelView view, int color) {
67 | updateCurrentColor(color);
68 | }
69 | });
70 | rootLayout.addView(blackColorView);
71 | multiColorView = new ColorPanelView(activity);
72 | //noinspection ResourceType
73 | multiColorView.setId(MULTI_ID);
74 | multiColorView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f));
75 | multiColorView.setPointerDrawable(CompatUtils.getDrawable(activity, R.drawable.color_picker_cursor_top));
76 | multiColorView.setLockPointerInBounds(true);
77 | multiColorView.setOnColorChangedListener(new ColorPanelView.OnColorChangedListener() {
78 | @Override
79 | public void onColorChanged(ColorPanelView view, int color) {
80 | updateCurrentColor(color);
81 | }
82 | });
83 | rootLayout.addView(multiColorView);
84 | LinearLayout previewLayout = new LinearLayout(activity);
85 | previewLayout.setOrientation(LinearLayout.HORIZONTAL);
86 | previewLayout.setGravity(Gravity.CENTER);
87 | previewLayout.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, ConvertUtils.toPx(activity, 30)));
88 | hexValView = new EditText(activity);
89 | hexValView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
90 | hexValView.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
91 | hexValView.setImeOptions(EditorInfo.IME_ACTION_DONE);
92 | hexValView.setGravity(Gravity.CENTER);
93 | hexValView.setBackgroundColor(initColor);
94 | hexValView.setTextColor(Color.BLACK);
95 | hexValView.setShadowLayer(3, 0, 2, Color.WHITE);//设置阴影,以便背景色为黑色系列时仍然看得见
96 | hexValView.setMinEms(6);
97 | hexValView.setMaxEms(8);
98 | hexValView.setPadding(0, 0, 0, 0);
99 | hexValView.setSingleLine(true);
100 | hexValView.setOnEditorActionListener(this);
101 | hexValDefaultColor = hexValView.getTextColors();
102 | previewLayout.addView(hexValView);
103 | rootLayout.addView(previewLayout);
104 | return rootLayout;
105 | }
106 |
107 | @Override
108 | protected void setContentViewAfter(View contentView) {
109 | multiColorView.setColor(initColor);//将触发onColorChanged,故必须先待其他控件初始化完成后才能调用
110 | multiColorView.setBrightnessGradientView(blackColorView);
111 | }
112 |
113 | @Override
114 | protected void onSubmit() {
115 | if (onColorPickListener != null) {
116 | onColorPickListener.onColorPicked(getCurrentColor());
117 | }
118 | }
119 |
120 | /**
121 | * Gets current color.
122 | *
123 | * @return the current color
124 | */
125 | @ColorInt
126 | public int getCurrentColor() {
127 | return Color.parseColor("#" + hexValView.getText());
128 | }
129 |
130 | private void updateCurrentColor(int color) {
131 | String hexColorString = ConvertUtils.toColorString(color, false).toUpperCase(Locale.getDefault());
132 | hexValView.setText(hexColorString);
133 | hexValView.setTextColor(hexValDefaultColor);
134 | hexValView.setBackgroundColor(color);
135 | }
136 |
137 | /**
138 | * Sets init color.
139 | *
140 | * @param initColor the init color
141 | */
142 | public void setInitColor(int initColor) {
143 | this.initColor = initColor;
144 | }
145 |
146 | /**
147 | * Sets on color pick listener.
148 | *
149 | * @param onColorPickListener the on color pick listener
150 | */
151 | public void setOnColorPickListener(OnColorPickListener onColorPickListener) {
152 | this.onColorPickListener = onColorPickListener;
153 | }
154 |
155 | @Override
156 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
157 | if (actionId == EditorInfo.IME_ACTION_DONE) {
158 | InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
159 | imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
160 | String hexString = hexValView.getText().toString();
161 | int length = hexString.length();
162 | if (length == 6 || length == 8) {
163 | try {
164 | int color = Color.parseColor("#" + hexString);
165 | multiColorView.setColor(color);
166 | hexValView.setTextColor(hexValDefaultColor);
167 | } catch (IllegalArgumentException e) {
168 | hexValView.setTextColor(Color.RED);
169 | }
170 | } else {
171 | hexValView.setTextColor(Color.RED);
172 | }
173 | return true;
174 | }
175 | return false;
176 | }
177 |
178 | /**
179 | * The interface On color pick listener.
180 | */
181 | public interface OnColorPickListener {
182 |
183 | /**
184 | * On color picked.
185 | *
186 | * @param pickedColor the picked color
187 | */
188 | void onColorPicked(@ColorInt int pickedColor);
189 |
190 | }
191 |
192 | }
193 |
--------------------------------------------------------------------------------
/library/ColorPicker/src/main/res/drawable-hdpi/color_picker_cursor_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sevenli777/android-picker/a561012af9c737f43da4d3bc10990efa467a8d2c/library/ColorPicker/src/main/res/drawable-hdpi/color_picker_cursor_bottom.png
--------------------------------------------------------------------------------
/library/ColorPicker/src/main/res/drawable-hdpi/color_picker_cursor_top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sevenli777/android-picker/a561012af9c737f43da4d3bc10990efa467a8d2c/library/ColorPicker/src/main/res/drawable-hdpi/color_picker_cursor_top.png
--------------------------------------------------------------------------------
/library/Common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /*.iml
--------------------------------------------------------------------------------
/library/Common/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | ext {
4 | isLibrary = true
5 | pomArtifactId = "Common"
6 | pomDescription = "Common of android picker"
7 | }
8 |
9 | dependencies {
10 | compile "com.android.support:support-v4:${BUILD_TOOL_VERSION}"
11 | compile "com.android.support:support-annotations:${BUILD_TOOL_VERSION}"
12 | }
13 |
14 |
15 |
--------------------------------------------------------------------------------
/library/Common/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/library/Common/src/main/java/cn/qqtheme/framework/AppConfig.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework;
2 |
3 | /**
4 | * ************************************************************************
5 | * ** _oo0oo_ **
6 | * ** o8888888o **
7 | * ** 88" . "88 **
8 | * ** (| -_- |) **
9 | * ** 0\ = /0 **
10 | * ** ___/'---'\___ **
11 | * ** .' \\\| |// '. **
12 | * ** / \\\||| : |||// \\ **
13 | * ** / _ ||||| -:- |||||- \\ **
14 | * ** | | \\\\ - /// | | **
15 | * ** | \_| ''\---/'' |_/ | **
16 | * ** \ .-\__ '-' __/-. / **
17 | * ** ___'. .' /--.--\ '. .'___ **
18 | * ** ."" '< '.___\_<|>_/___.' >' "". **
19 | * ** | | : '- \'.;'\ _ /';.'/ - ' : | | **
20 | * ** \ \ '_. \_ __\ /__ _/ .-' / / **
21 | * ** ====='-.____'.___ \_____/___.-'____.-'===== **
22 | * ** '=---=' **
23 | * ************************************************************************
24 | * ** 佛祖保佑 镇类之宝 **
25 | * ************************************************************************
26 | *
27 | * @author 李玉江[QQ :1032694760]
28 | * @version 2014-09-05 11:49
29 | */
30 | public class AppConfig {
31 | /**
32 | * The constant DEBUG_ENABLE.
33 | */
34 | public static final boolean DEBUG_ENABLE = BuildConfig.DEBUG;// 是否调试模式
35 | /**
36 | * The constant DEBUG_TAG.
37 | */
38 | public static final String DEBUG_TAG = "liyujiang";// LogCat的标记
39 | }
40 |
--------------------------------------------------------------------------------
/library/Common/src/main/java/cn/qqtheme/framework/popup/BottomPopup.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.popup;
2 |
3 | import android.app.Activity;
4 | import android.content.DialogInterface;
5 | import android.support.annotation.CallSuper;
6 | import android.support.annotation.StyleRes;
7 | import android.util.DisplayMetrics;
8 | import android.view.KeyEvent;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.view.Window;
12 |
13 | import cn.qqtheme.framework.util.ScreenUtils;
14 | import cn.qqtheme.framework.util.LogUtils;
15 |
16 | /**
17 | * 底部弹窗基类
18 | *
19 | * @param the type parameter
20 | * @author 李玉江[QQ :1023694760]
21 | * @version 2015/7/19
22 | */
23 | public abstract class BottomPopup implements DialogInterface.OnKeyListener {
24 | /**
25 | * The constant MATCH_PARENT.
26 | */
27 | public static final int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
28 | /**
29 | * The constant WRAP_CONTENT.
30 | */
31 | public static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
32 | protected Activity activity;
33 | protected int screenWidthPixels;
34 | protected int screenHeightPixels;
35 | private Popup popup;
36 | private int width = 0, height = 0;
37 | private boolean isFillScreen = false;
38 | private boolean isHalfScreen = false;
39 |
40 | /**
41 | * Instantiates a new Bottom popup.
42 | *
43 | * @param activity the activity
44 | */
45 | public BottomPopup(Activity activity) {
46 | this.activity = activity;
47 | DisplayMetrics displayMetrics = ScreenUtils.displayMetrics(activity);
48 | screenWidthPixels = displayMetrics.widthPixels;
49 | screenHeightPixels = displayMetrics.heightPixels;
50 | popup = new Popup(activity);
51 | popup.setOnKeyListener(this);
52 | }
53 |
54 | /**
55 | * Gets view.
56 | *
57 | * @return the view
58 | */
59 | protected abstract V makeContentView();
60 |
61 | /**
62 | * 弹出窗显示之前调用
63 | */
64 | private void onShowPrepare() {
65 | setContentViewBefore();
66 | V view = makeContentView();
67 | popup.setContentView(view);// 设置弹出窗体的布局
68 | setContentViewAfter(view);
69 | LogUtils.debug("do something before popup show");
70 | if (width == 0 && height == 0) {
71 | //未明确指定宽高,优先考虑全屏再考虑半屏然后再考虑包裹内容
72 | width = screenWidthPixels;
73 | if (isFillScreen) {
74 | height = MATCH_PARENT;
75 | } else if (isHalfScreen) {
76 | height = screenHeightPixels / 2;
77 | } else {
78 | height = WRAP_CONTENT;
79 | }
80 | }
81 | popup.setSize(width, height);
82 | }
83 |
84 | /**
85 | * 固定高度为屏幕的高
86 | *
87 | * @param fillScreen the fill screen
88 | */
89 | public void setFillScreen(boolean fillScreen) {
90 | isFillScreen = fillScreen;
91 | }
92 |
93 | /**
94 | * 固定高度为屏幕的一半
95 | *
96 | * @param halfScreen the half screen
97 | */
98 | public void setHalfScreen(boolean halfScreen) {
99 | isHalfScreen = halfScreen;
100 | }
101 |
102 | /**
103 | * Sets content view before.
104 | */
105 | protected void setContentViewBefore() {
106 | }
107 |
108 | /**
109 | * Sets content view after.
110 | *
111 | * @param contentView the content view
112 | */
113 | protected void setContentViewAfter(V contentView) {
114 | }
115 |
116 | /**
117 | * Sets animation.
118 | *
119 | * @param animRes the anim res
120 | */
121 | public void setAnimationStyle(@StyleRes int animRes) {
122 | popup.setAnimationStyle(animRes);
123 | }
124 |
125 | /**
126 | * Sets on dismiss listener.
127 | *
128 | * @param onDismissListener the on dismiss listener
129 | */
130 | public void setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) {
131 | popup.setOnDismissListener(onDismissListener);
132 | LogUtils.debug("popup setOnDismissListener");
133 | }
134 |
135 | /**
136 | * Sets size.
137 | *
138 | * @param width the width
139 | * @param height the height
140 | */
141 | public void setSize(int width, int height) {
142 | // fixed: 2016/1/26 修复显示之前设置宽高无效问题
143 | this.width = width;
144 | this.height = height;
145 | }
146 |
147 | /**
148 | * Sets width.
149 | *
150 | * @param width the width
151 | * @see #setSize(int, int) #setSize(int, int)
152 | */
153 | public void setWidth(int width) {
154 | this.width = width;
155 | }
156 |
157 | /**
158 | * Sets height.
159 | *
160 | * @param height the height
161 | * @see #setSize(int, int) #setSize(int, int)
162 | */
163 | public void setHeight(int height) {
164 | this.height = height;
165 | }
166 |
167 | /**
168 | * Is showing boolean.
169 | *
170 | * @return the boolean
171 | */
172 | public boolean isShowing() {
173 | return popup.isShowing();
174 | }
175 |
176 | /**
177 | * Show.
178 | */
179 | @CallSuper
180 | public void show() {
181 | onShowPrepare();
182 | popup.show();
183 | LogUtils.debug("popup show");
184 | }
185 |
186 | /**
187 | * Dismiss.
188 | */
189 | public void dismiss() {
190 | popup.dismiss();
191 | LogUtils.debug("popup dismiss");
192 | }
193 |
194 | /**
195 | * On key down boolean.
196 | *
197 | * @param keyCode the key code
198 | * @param event the event
199 | * @return the boolean
200 | */
201 | public boolean onKeyDown(int keyCode, KeyEvent event) {
202 | return false;
203 | }
204 |
205 | @Override
206 | public final boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
207 | if (event.getAction() == KeyEvent.ACTION_DOWN) {
208 | return onKeyDown(keyCode, event);
209 | }
210 | return false;
211 | }
212 |
213 | /**
214 | * Gets window.
215 | *
216 | * @return the window
217 | */
218 | public Window getWindow() {
219 | return popup.getWindow();
220 | }
221 |
222 | /**
223 | * Gets root view.
224 | *
225 | * @return the root view
226 | */
227 | public ViewGroup getRootView() {
228 | return popup.getRootView();
229 | }
230 |
231 | }
232 |
--------------------------------------------------------------------------------
/library/Common/src/main/java/cn/qqtheme/framework/popup/ConfirmPopup.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.popup;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Color;
5 | import android.support.annotation.ColorInt;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.annotation.StringRes;
9 | import android.text.TextUtils;
10 | import android.view.Gravity;
11 | import android.view.View;
12 | import android.widget.Button;
13 | import android.widget.LinearLayout;
14 | import android.widget.RelativeLayout;
15 | import android.widget.TextView;
16 |
17 | import cn.qqtheme.framework.util.ConvertUtils;
18 |
19 | /**
20 | * 带确定及取消按钮的
21 | *
22 | * @param the type parameter
23 | * @author 李玉江[QQ:1032694760]
24 | * @since 2015/10/21
25 | */
26 | public abstract class ConfirmPopup extends BottomPopup {
27 | protected boolean topLineVisible = true;
28 | protected int topLineColor = 0xFFDDDDDD;
29 | protected int topBackgroundColor = Color.WHITE;
30 | protected boolean cancelVisible = true;
31 | protected CharSequence cancelText = "";
32 | protected CharSequence submitText = "";
33 | protected CharSequence titleText = "";
34 | protected int cancelTextColor = Color.BLACK;
35 | protected int submitTextColor = Color.BLACK;
36 | protected int titleTextColor = Color.BLACK;
37 |
38 | /**
39 | * Instantiates a new Confirm popup.
40 | *
41 | * @param activity the activity
42 | */
43 | public ConfirmPopup(Activity activity) {
44 | super(activity);
45 | cancelText = activity.getString(android.R.string.cancel);
46 | submitText = activity.getString(android.R.string.ok);
47 | }
48 |
49 | /**
50 | * Sets top line color.
51 | *
52 | * @param topLineColor the top line color
53 | */
54 | public void setTopLineColor(@ColorInt int topLineColor) {
55 | this.topLineColor = topLineColor;
56 | }
57 |
58 | /**
59 | * Sets top background color.
60 | *
61 | * @param topBackgroundColor the top background color
62 | */
63 | public void setTopBackgroundColor(@ColorInt int topBackgroundColor) {
64 | this.topBackgroundColor = topBackgroundColor;
65 | }
66 |
67 | /**
68 | * Sets top line visible.
69 | *
70 | * @param topLineVisible the top line visible
71 | */
72 | public void setTopLineVisible(boolean topLineVisible) {
73 | this.topLineVisible = topLineVisible;
74 | }
75 |
76 | /**
77 | * Sets cancel visible.
78 | *
79 | * @param cancelVisible the cancel visible
80 | */
81 | public void setCancelVisible(boolean cancelVisible) {
82 | this.cancelVisible = cancelVisible;
83 | }
84 |
85 | /**
86 | * Sets cancel text.
87 | *
88 | * @param cancelText the cancel text
89 | */
90 | public void setCancelText(CharSequence cancelText) {
91 | this.cancelText = cancelText;
92 | }
93 |
94 | /**
95 | * Sets cancel text.
96 | *
97 | * @param textRes the text res
98 | */
99 | public void setCancelText(@StringRes int textRes) {
100 | this.cancelText = activity.getString(textRes);
101 | }
102 |
103 | /**
104 | * Sets submit text.
105 | *
106 | * @param submitText the submit text
107 | */
108 | public void setSubmitText(CharSequence submitText) {
109 | this.submitText = submitText;
110 | }
111 |
112 | /**
113 | * Sets submit text.
114 | *
115 | * @param textRes the text res
116 | */
117 | public void setSubmitText(@StringRes int textRes) {
118 | this.submitText = activity.getString(textRes);
119 | }
120 |
121 | /**
122 | * Sets title text.
123 | *
124 | * @param titleText the title text
125 | */
126 | public void setTitleText(CharSequence titleText) {
127 | this.titleText = titleText;
128 | }
129 |
130 | /**
131 | * Sets title text.
132 | *
133 | * @param textRes the text res
134 | */
135 | public void setTitleText(@StringRes int textRes) {
136 | this.titleText = activity.getString(textRes);
137 | }
138 |
139 | /**
140 | * Sets cancel text color.
141 | *
142 | * @param cancelTextColor the cancel text color
143 | */
144 | public void setCancelTextColor(@ColorInt int cancelTextColor) {
145 | this.cancelTextColor = cancelTextColor;
146 | }
147 |
148 | /**
149 | * Sets submit text color.
150 | *
151 | * @param submitTextColor the submit text color
152 | */
153 | public void setSubmitTextColor(@ColorInt int submitTextColor) {
154 | this.submitTextColor = submitTextColor;
155 | }
156 |
157 | /**
158 | * Sets title text color.
159 | *
160 | * @param titleTextColor the title text color
161 | */
162 | public void setTitleTextColor(@ColorInt int titleTextColor) {
163 | this.titleTextColor = titleTextColor;
164 | }
165 |
166 | /**
167 | * @see #makeHeaderView()
168 | * @see #makeCenterView()
169 | * @see #makeFooterView()
170 | */
171 | @Override
172 | protected final View makeContentView() {
173 | LinearLayout rootLayout = new LinearLayout(activity);
174 | rootLayout.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
175 | rootLayout.setBackgroundColor(Color.WHITE);
176 | rootLayout.setOrientation(LinearLayout.VERTICAL);
177 | rootLayout.setGravity(Gravity.CENTER);
178 | rootLayout.setPadding(0, 0, 0, 0);
179 | rootLayout.setClipToPadding(false);
180 | View headerView = makeHeaderView();
181 | if (headerView != null) {
182 | rootLayout.addView(headerView);
183 | }
184 | if (topLineVisible) {
185 | View lineView = new View(activity);
186 | lineView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 1));
187 | lineView.setBackgroundColor(topLineColor);
188 | rootLayout.addView(lineView);
189 | }
190 | rootLayout.addView(makeCenterView(), new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f));
191 | View footerView = makeFooterView();
192 | if (footerView != null) {
193 | rootLayout.addView(footerView);
194 | }
195 | return rootLayout;
196 | }
197 |
198 | /**
199 | * Make header view view.
200 | *
201 | * @return the view
202 | */
203 | @Nullable
204 | protected View makeHeaderView() {
205 | RelativeLayout topButtonLayout = new RelativeLayout(activity);
206 | topButtonLayout.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, ConvertUtils.toPx(activity, 40)));
207 | topButtonLayout.setBackgroundColor(topBackgroundColor);
208 | topButtonLayout.setGravity(Gravity.CENTER_VERTICAL);
209 |
210 | Button cancelButton = new Button(activity);
211 | cancelButton.setVisibility(cancelVisible ? View.VISIBLE : View.GONE);
212 | RelativeLayout.LayoutParams cancelButtonLayoutParams = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
213 | cancelButtonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
214 | cancelButtonLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
215 | cancelButton.setLayoutParams(cancelButtonLayoutParams);
216 | cancelButton.setBackgroundColor(Color.TRANSPARENT);
217 | cancelButton.setGravity(Gravity.CENTER);
218 | if (!TextUtils.isEmpty(cancelText)) {
219 | cancelButton.setText(cancelText);
220 | }
221 | cancelButton.setTextColor(cancelTextColor);
222 | cancelButton.setOnClickListener(new View.OnClickListener() {
223 | @Override
224 | public void onClick(View v) {
225 | dismiss();
226 | onCancel();
227 | }
228 | });
229 | topButtonLayout.addView(cancelButton);
230 |
231 | TextView titleView = new TextView(activity);
232 | RelativeLayout.LayoutParams titleLayoutParams = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
233 | int margin = ConvertUtils.toPx(activity, 20);
234 | titleLayoutParams.leftMargin = margin;
235 | titleLayoutParams.rightMargin = margin;
236 | titleLayoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
237 | titleLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
238 | titleView.setLayoutParams(titleLayoutParams);
239 | titleView.setGravity(Gravity.CENTER);
240 | if (!TextUtils.isEmpty(titleText)) {
241 | titleView.setText(titleText);
242 | }
243 | titleView.setTextColor(titleTextColor);
244 | topButtonLayout.addView(titleView);
245 |
246 | Button submitButton = new Button(activity);
247 | RelativeLayout.LayoutParams submitButtonLayoutParams = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
248 | submitButtonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
249 | submitButtonLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
250 | submitButton.setLayoutParams(submitButtonLayoutParams);
251 | submitButton.setBackgroundColor(Color.TRANSPARENT);
252 | submitButton.setGravity(Gravity.CENTER);
253 | if (!TextUtils.isEmpty(submitText)) {
254 | submitButton.setText(submitText);
255 | }
256 | submitButton.setTextColor(submitTextColor);
257 | submitButton.setOnClickListener(new View.OnClickListener() {
258 | @Override
259 | public void onClick(View v) {
260 | dismiss();
261 | onSubmit();
262 | }
263 | });
264 | topButtonLayout.addView(submitButton);
265 |
266 | return topButtonLayout;
267 | }
268 |
269 | /**
270 | * Init center view v.
271 | *
272 | * @return the v
273 | */
274 | @NonNull
275 | protected abstract V makeCenterView();
276 |
277 | /**
278 | * Make footer view view.
279 | *
280 | * @return the view
281 | */
282 | @Nullable
283 | protected View makeFooterView() {
284 | return null;
285 | }
286 |
287 | /**
288 | * On submit.
289 | */
290 | protected void onSubmit() {
291 |
292 | }
293 |
294 | /**
295 | * On cancel.
296 | */
297 | protected void onCancel() {
298 |
299 | }
300 |
301 | }
302 |
--------------------------------------------------------------------------------
/library/Common/src/main/java/cn/qqtheme/framework/popup/Popup.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.popup;
2 |
3 | import android.content.Context;
4 | import android.content.DialogInterface;
5 | import android.graphics.Color;
6 | import android.graphics.drawable.ColorDrawable;
7 | import android.support.annotation.CallSuper;
8 | import android.support.annotation.StyleRes;
9 | import android.view.Gravity;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.view.Window;
13 | import android.widget.FrameLayout;
14 |
15 | import cn.qqtheme.framework.R;
16 | import cn.qqtheme.framework.util.LogUtils;
17 |
18 | /**
19 | * 弹窗
20 | *
21 | * @author 李玉江[QQ :1023694760]
22 | * @version 2015 -10-19
23 | * @see android.widget.PopupWindow
24 | */
25 | public class Popup {
26 | private android.app.Dialog dialog;
27 | private FrameLayout contentLayout;
28 |
29 | /**
30 | * Instantiates a new Popup.
31 | *
32 | * @param context the context
33 | */
34 | public Popup(Context context) {
35 | init(context);
36 | }
37 |
38 | private void init(Context context) {
39 | contentLayout = new FrameLayout(context);
40 | contentLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
41 | contentLayout.setFocusable(true);
42 | contentLayout.setFocusableInTouchMode(true);
43 | dialog = new android.app.Dialog(context);
44 | dialog.setCanceledOnTouchOutside(true);//触摸屏幕取消窗体
45 | dialog.setCancelable(true);//按返回键取消窗体
46 | Window window = dialog.getWindow();
47 | window.setGravity(Gravity.BOTTOM);//位于屏幕底部
48 | window.setWindowAnimations(R.style.Animation_Popup);
49 | window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
50 | //android.util.AndroidRuntimeException: requestFeature() must be called before adding content
51 | window.requestFeature(Window.FEATURE_NO_TITLE);
52 | window.setContentView(contentLayout);
53 | }
54 |
55 | /**
56 | * Gets context.
57 | *
58 | * @return the context
59 | */
60 | public Context getContext() {
61 | return contentLayout.getContext();
62 | }
63 |
64 | /**
65 | * Sets animation.
66 | *
67 | * @param animRes the anim res
68 | */
69 | public void setAnimationStyle(@StyleRes int animRes) {
70 | Window window = dialog.getWindow();
71 | window.setWindowAnimations(animRes);
72 | }
73 |
74 | /**
75 | * Is showing boolean.
76 | *
77 | * @return the boolean
78 | */
79 | public boolean isShowing() {
80 | return dialog.isShowing();
81 | }
82 |
83 | /**
84 | * Show.
85 | */
86 | @CallSuper
87 | public void show() {
88 | dialog.show();
89 | }
90 |
91 | /**
92 | * Dismiss.
93 | */
94 | @CallSuper
95 | public void dismiss() {
96 | dialog.dismiss();
97 | }
98 |
99 | /**
100 | * Sets content view.
101 | *
102 | * @param view the view
103 | */
104 | public void setContentView(View view) {
105 | contentLayout.removeAllViews();
106 | contentLayout.addView(view);
107 | }
108 |
109 | /**
110 | * Gets content view.
111 | *
112 | * @return the content view
113 | */
114 | public View getContentView() {
115 | return contentLayout.getChildAt(0);
116 | }
117 |
118 | /**
119 | * Sets size.
120 | *
121 | * @param width the width
122 | * @param height the height
123 | */
124 | public void setSize(int width, int height) {
125 | LogUtils.debug(String.format("will set popup width/height to: %s/%s", width, height));
126 | ViewGroup.LayoutParams params = contentLayout.getLayoutParams();
127 | if (params == null) {
128 | params = new ViewGroup.LayoutParams(width, height);
129 | } else {
130 | params.width = width;
131 | params.height = height;
132 | }
133 | contentLayout.setLayoutParams(params);
134 | }
135 |
136 | /**
137 | * Sets on dismiss listener.
138 | *
139 | * @param onDismissListener the on dismiss listener
140 | */
141 | public void setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) {
142 | dialog.setOnDismissListener(onDismissListener);
143 | }
144 |
145 | /**
146 | * Sets on key listener.
147 | *
148 | * @param onKeyListener the on key listener
149 | */
150 | public void setOnKeyListener(DialogInterface.OnKeyListener onKeyListener) {
151 | dialog.setOnKeyListener(onKeyListener);
152 | }
153 |
154 | /**
155 | * Gets window.
156 | *
157 | * @return the window
158 | */
159 | public Window getWindow() {
160 | return dialog.getWindow();
161 | }
162 |
163 | /**
164 | * Gets root view.
165 | *
166 | * @return the root view
167 | */
168 | public ViewGroup getRootView() {
169 | return contentLayout;
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/library/Common/src/main/java/cn/qqtheme/framework/util/CompatUtils.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.util;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.drawable.Drawable;
6 | import android.os.Build;
7 | import android.support.annotation.ColorInt;
8 | import android.support.annotation.ColorRes;
9 | import android.support.annotation.DrawableRes;
10 | import android.support.annotation.StringRes;
11 | import android.support.annotation.StyleRes;
12 | import android.view.View;
13 | import android.widget.TextView;
14 |
15 | /**
16 | * 兼容旧版&新版
17 | *
18 | * @author 李玉江[QQ :1032694760]
19 | * @since 2015 /10/19 Created By Android Studio
20 | */
21 | public class CompatUtils {
22 |
23 | /**
24 | * Sets background.
25 | *
26 | * @param view the view
27 | * @param drawable the drawable
28 | */
29 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
30 | public static void setBackground(View view, Drawable drawable) {
31 | if (Build.VERSION.SDK_INT < 16) {
32 | //noinspection deprecation
33 | view.setBackgroundDrawable(drawable);
34 | } else {
35 | view.setBackground(drawable);
36 | }
37 | }
38 |
39 | /**
40 | * Sets text appearance.
41 | *
42 | * @param view the view
43 | * @param appearanceRes the appearance res
44 | */
45 | @TargetApi(Build.VERSION_CODES.M)
46 | public static void setTextAppearance(TextView view, @StyleRes int appearanceRes) {
47 | if (Build.VERSION.SDK_INT < 23) {
48 | //noinspection deprecation
49 | view.setTextAppearance(view.getContext(), appearanceRes);
50 | } else {
51 | view.setTextAppearance(appearanceRes);
52 | }
53 | }
54 |
55 | /**
56 | * Gets drawable.
57 | *
58 | * @param context the context
59 | * @param drawableRes the drawable res
60 | * @return the drawable
61 | */
62 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
63 | public static Drawable getDrawable(Context context, @DrawableRes int drawableRes) {
64 | if (Build.VERSION.SDK_INT < 21) {
65 | //noinspection deprecation
66 | return context.getResources().getDrawable(drawableRes);
67 | } else {
68 | return context.getDrawable(drawableRes);
69 | }
70 | }
71 |
72 | /**
73 | * Gets string.
74 | *
75 | * @param context the context
76 | * @param stringRes the string res
77 | * @return the string
78 | */
79 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
80 | public static String getString(Context context, @StringRes int stringRes) {
81 | if (Build.VERSION.SDK_INT < 21) {
82 | //noinspection deprecation
83 | return context.getResources().getString(stringRes);
84 | } else {
85 | return context.getString(stringRes);
86 | }
87 | }
88 |
89 | /**
90 | * Gets color.
91 | *
92 | * @param context the context
93 | * @param colorRes the color res
94 | * @return the color
95 | */
96 | @ColorInt
97 | public static int getColor(Context context, @ColorRes int colorRes) {
98 | if (Build.VERSION.SDK_INT < 21) {
99 | //noinspection deprecation
100 | return context.getResources().getColor(colorRes);
101 | } else {
102 | return context.getResources().getColor(colorRes, null);
103 | }
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/library/Common/src/main/java/cn/qqtheme/framework/util/DateUtils.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.util;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.support.annotation.NonNull;
5 |
6 | import java.text.SimpleDateFormat;
7 | import java.util.Arrays;
8 | import java.util.Calendar;
9 | import java.util.Date;
10 | import java.util.List;
11 |
12 | /**
13 | * 日期时间工具类
14 | *
15 | * @author 李玉江[QQ :1023694760]
16 | * @version 2015 /8/5 Created by IntelliJ IDEA 14.1
17 | */
18 | public class DateUtils extends android.text.format.DateUtils {
19 |
20 | /**
21 | * The enum Difference mode.
22 | */
23 | public enum DifferenceMode {
24 | /**
25 | * Second difference mode.
26 | */
27 | Second, /**
28 | * Minute difference mode.
29 | */
30 | Minute, /**
31 | * Hour difference mode.
32 | */
33 | Hour, /**
34 | * Day difference mode.
35 | */
36 | Day
37 | }
38 |
39 | /**
40 | * Calculate different second long.
41 | *
42 | * @param startDate the start date
43 | * @param endDate the end date
44 | * @return the long
45 | */
46 | public static long calculateDifferentSecond(Date startDate, Date endDate) {
47 | return calculateDifference(startDate, endDate, DifferenceMode.Second);
48 | }
49 |
50 | /**
51 | * Calculate different minute long.
52 | *
53 | * @param startDate the start date
54 | * @param endDate the end date
55 | * @return the long
56 | */
57 | public static long calculateDifferentMinute(Date startDate, Date endDate) {
58 | return calculateDifference(startDate, endDate, DifferenceMode.Minute);
59 | }
60 |
61 | /**
62 | * Calculate different hour long.
63 | *
64 | * @param startDate the start date
65 | * @param endDate the end date
66 | * @return the long
67 | */
68 | public static long calculateDifferentHour(Date startDate, Date endDate) {
69 | return calculateDifference(startDate, endDate, DifferenceMode.Hour);
70 | }
71 |
72 | /**
73 | * Calculate different day long.
74 | *
75 | * @param startDate the start date
76 | * @param endDate the end date
77 | * @return the long
78 | */
79 | public static long calculateDifferentDay(Date startDate, Date endDate) {
80 | return calculateDifference(startDate, endDate, DifferenceMode.Day);
81 | }
82 |
83 | /**
84 | * Calculate different second long.
85 | *
86 | * @param startTimeMillis the start time millis
87 | * @param endTimeMillis the end time millis
88 | * @return the long
89 | */
90 | public static long calculateDifferentSecond(long startTimeMillis, long endTimeMillis) {
91 | return calculateDifference(startTimeMillis, endTimeMillis, DifferenceMode.Second);
92 | }
93 |
94 | /**
95 | * Calculate different minute long.
96 | *
97 | * @param startTimeMillis the start time millis
98 | * @param endTimeMillis the end time millis
99 | * @return the long
100 | */
101 | public static long calculateDifferentMinute(long startTimeMillis, long endTimeMillis) {
102 | return calculateDifference(startTimeMillis, endTimeMillis, DifferenceMode.Minute);
103 | }
104 |
105 | /**
106 | * Calculate different hour long.
107 | *
108 | * @param startTimeMillis the start time millis
109 | * @param endTimeMillis the end time millis
110 | * @return the long
111 | */
112 | public static long calculateDifferentHour(long startTimeMillis, long endTimeMillis) {
113 | return calculateDifference(startTimeMillis, endTimeMillis, DifferenceMode.Hour);
114 | }
115 |
116 | /**
117 | * Calculate different day long.
118 | *
119 | * @param startTimeMillis the start time millis
120 | * @param endTimeMillis the end time millis
121 | * @return the long
122 | */
123 | public static long calculateDifferentDay(long startTimeMillis, long endTimeMillis) {
124 | return calculateDifference(startTimeMillis, endTimeMillis, DifferenceMode.Day);
125 | }
126 |
127 | /**
128 | * Calculate difference long.
129 | *
130 | * @param startTimeMillis the start time millis
131 | * @param endTimeMillis the end time millis
132 | * @param mode the mode
133 | * @return the long
134 | */
135 | public static long calculateDifference(long startTimeMillis, long endTimeMillis, DifferenceMode mode) {
136 | return calculateDifference(new Date(startTimeMillis), new Date(endTimeMillis), mode);
137 | }
138 |
139 | /**
140 | * Calculate difference long.
141 | *
142 | * @param startDate the start date
143 | * @param endDate the end date
144 | * @param mode the mode
145 | * @return the long
146 | */
147 | public static long calculateDifference(Date startDate, Date endDate, DifferenceMode mode) {
148 | long[] different = calculateDifference(startDate, endDate);
149 | if (mode.equals(DifferenceMode.Minute)) {
150 | return different[2];
151 | } else if (mode.equals(DifferenceMode.Hour)) {
152 | return different[1];
153 | } else if (mode.equals(DifferenceMode.Day)) {
154 | return different[0];
155 | } else {
156 | return different[3];
157 | }
158 | }
159 |
160 | /**
161 | * Calculate difference long [ ].
162 | *
163 | * @param startDate the start date
164 | * @param endDate the end date
165 | * @return the long [ ]
166 | */
167 | public static long[] calculateDifference(Date startDate, Date endDate) {
168 | return calculateDifference(endDate.getTime() - startDate.getTime());
169 | }
170 |
171 | /**
172 | * Calculate difference long [ ].
173 | *
174 | * @param differentMilliSeconds the different milli seconds
175 | * @return the long [ ]
176 | */
177 | public static long[] calculateDifference(long differentMilliSeconds) {
178 | long secondsInMilli = 1000;//1s==1000ms
179 | long minutesInMilli = secondsInMilli * 60;
180 | long hoursInMilli = minutesInMilli * 60;
181 | long daysInMilli = hoursInMilli * 24;
182 | long elapsedDays = differentMilliSeconds / daysInMilli;
183 | differentMilliSeconds = differentMilliSeconds % daysInMilli;
184 | long elapsedHours = differentMilliSeconds / hoursInMilli;
185 | differentMilliSeconds = differentMilliSeconds % hoursInMilli;
186 | long elapsedMinutes = differentMilliSeconds / minutesInMilli;
187 | differentMilliSeconds = differentMilliSeconds % minutesInMilli;
188 | long elapsedSeconds = differentMilliSeconds / secondsInMilli;
189 | LogUtils.debug(String.format("different: %d ms, %d days, %d hours, %d minutes, %d seconds",
190 | differentMilliSeconds, elapsedDays, elapsedHours, elapsedMinutes, elapsedSeconds));
191 | return new long[]{elapsedDays, elapsedHours, elapsedMinutes, elapsedSeconds};
192 | }
193 |
194 | /**
195 | * Calculate days in month int.
196 | *
197 | * @param month the month
198 | * @return the int
199 | */
200 | public static int calculateDaysInMonth(int month) {
201 | return calculateDaysInMonth(0, month);
202 | }
203 |
204 | /**
205 | * Calculate days in month int.
206 | *
207 | * @param year the year
208 | * @param month the month
209 | * @return the int
210 | */
211 | public static int calculateDaysInMonth(int year, int month) {
212 | // 添加大小月月份并将其转换为list,方便之后的判断
213 | String[] bigMonths = {"1", "3", "5", "7", "8", "10", "12"};
214 | String[] littleMonths = {"4", "6", "9", "11"};
215 | List bigList = Arrays.asList(bigMonths);
216 | List littleList = Arrays.asList(littleMonths);
217 | // 判断大小月及是否闰年,用来确定"日"的数据
218 | if (bigList.contains(String.valueOf(month))) {
219 | return 31;
220 | } else if (littleList.contains(String.valueOf(month))) {
221 | return 30;
222 | } else {
223 | if (year <= 0) {
224 | return 29;
225 | }
226 | // 是否闰年
227 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
228 | return 29;
229 | } else {
230 | return 28;
231 | }
232 | }
233 | }
234 |
235 | /**
236 | * 月日时分秒,0-9前补0
237 | *
238 | * @param number the number
239 | * @return the string
240 | */
241 | @NonNull
242 | public static String fillZero(int number) {
243 | return number < 10 ? "0" + number : "" + number;
244 | }
245 |
246 | /**
247 | * 功能:判断日期是否和当前date对象在同一天。
248 | * 参见:http://www.cnblogs.com/myzhijie/p/3330970.html
249 | *
250 | * @param date 比较的日期
251 | * @return boolean 如果在返回true,否则返回false。
252 | * @author 沙琪玛 QQ:862990787 Aug 21, 2013 7:15:53 AM
253 | */
254 | public static boolean isSameDay(Date date) {
255 | if (date == null) {
256 | throw new IllegalArgumentException("date is null");
257 | }
258 | Calendar nowCalendar = Calendar.getInstance();
259 | Calendar newCalendar = Calendar.getInstance();
260 | newCalendar.setTime(date);
261 | return (nowCalendar.get(Calendar.ERA) == newCalendar.get(Calendar.ERA) &&
262 | nowCalendar.get(Calendar.YEAR) == newCalendar.get(Calendar.YEAR) &&
263 | nowCalendar.get(Calendar.DAY_OF_YEAR) == newCalendar.get(Calendar.DAY_OF_YEAR));
264 | }
265 |
266 | /**
267 | * 将yyyy-MM-dd HH:mm:ss字符串转换成日期
268 | *
269 | * @param dateStr 时间字符串
270 | * @param dataFormat 当前时间字符串的格式。
271 | * @return Date 日期 ,转换异常时返回null。
272 | */
273 | public static Date parseDate(String dateStr, String dataFormat) {
274 | try {
275 | @SuppressLint("SimpleDateFormat")
276 | SimpleDateFormat dateFormat = new SimpleDateFormat(dataFormat);
277 | Date date = dateFormat.parse(dateStr);
278 | return new Date(date.getTime());
279 | } catch (Exception e) {
280 | LogUtils.warn(e);
281 | return null;
282 | }
283 | }
284 |
285 | /**
286 | * 将yyyy-MM-dd HH:mm:ss字符串转换成日期
287 | *
288 | * @param dateStr yyyy-MM-dd HH:mm:ss字符串
289 | * @return Date 日期 ,转换异常时返回null。
290 | */
291 | public static Date parseDate(String dateStr) {
292 | return parseDate(dateStr, "yyyy-MM-dd HH:mm:ss");
293 | }
294 |
295 | }
296 |
--------------------------------------------------------------------------------
/library/Common/src/main/java/cn/qqtheme/framework/util/LogUtils.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.util;
2 |
3 | import android.os.Debug;
4 | import android.os.Environment;
5 | import android.util.Log;
6 |
7 | import java.io.File;
8 | import java.io.PrintWriter;
9 | import java.io.StringWriter;
10 |
11 | import cn.qqtheme.framework.AppConfig;
12 |
13 | /**
14 | * 如果用于android平台,将信息记录到“LogCat”。如果用于java平台,将信息记录到“Console”
15 | *
16 | * @author 李玉江[QQ :1023694760]
17 | * @version 2013 -11-2
18 | */
19 | public class LogUtils {
20 | private static final int MAX_STACK_TRACE_SIZE = 131071; //128 KB - 1
21 | // 是否开启日志输出,在Debug状态下开启,在Release状态下关闭以提高程序性能,避免日志被人抓取
22 | private static boolean isDebug = AppConfig.DEBUG_ENABLE;
23 | private static String debugTag = AppConfig.DEBUG_TAG;
24 |
25 | /**
26 | * Debug.
27 | *
28 | * @param message the message
29 | */
30 | public static void debug(String message) {
31 | debug(debugTag, message);
32 | }
33 |
34 | /**
35 | * Debug.
36 | *
37 | * @param object the object
38 | * @param message the message
39 | */
40 | public static void debug(Object object, String message) {
41 | debug(object.getClass().getSimpleName(), message);
42 | }
43 |
44 | /**
45 | * 记录“debug”级别的信息
46 | *
47 | * @param tag the tag
48 | * @param message the message
49 | */
50 | public static void debug(String tag, String message) {
51 | if (isDebug) {
52 | try {
53 | Log.d(debugTag + "-" + tag, message);
54 | } catch (Exception e) {
55 | System.out.println(tag + ">>>" + message);
56 | }
57 | }
58 | }
59 |
60 | /**
61 | * Warn.
62 | *
63 | * @param e the e
64 | */
65 | public static void warn(Throwable e) {
66 | warn(toStackTraceString(e));
67 | }
68 |
69 | /**
70 | * Warn.
71 | *
72 | * @param message the message
73 | */
74 | public static void warn(String message) {
75 | warn(debugTag, message);
76 | }
77 |
78 | /**
79 | * Warn.
80 | *
81 | * @param object the object
82 | * @param message the message
83 | */
84 | public static void warn(Object object, String message) {
85 | warn(object.getClass().getSimpleName(), message);
86 | }
87 |
88 | /**
89 | * Warn.
90 | *
91 | * @param object the object
92 | * @param e the e
93 | */
94 | public static void warn(Object object, Throwable e) {
95 | warn(object.getClass().getSimpleName(), toStackTraceString(e));
96 | }
97 |
98 | /**
99 | * 记录“warn”级别的信息
100 | *
101 | * @param tag the tag
102 | * @param message the message
103 | */
104 | public static void warn(String tag, String message) {
105 | if (isDebug) {
106 | try {
107 | Log.w(debugTag + tag, message);
108 | } catch (Exception e) {
109 | System.out.println(debugTag + ">>>" + message);
110 | }
111 | }
112 | }
113 |
114 | /**
115 | * Error.
116 | *
117 | * @param e the e
118 | */
119 | public static void error(Throwable e) {
120 | error(toStackTraceString(e));
121 | }
122 |
123 | /**
124 | * Error.
125 | *
126 | * @param message the message
127 | */
128 | public static void error(String message) {
129 | error(debugTag, message);
130 | }
131 |
132 | /**
133 | * Error.
134 | *
135 | * @param object the object
136 | * @param message the message
137 | */
138 | public static void error(Object object, String message) {
139 | error(object.getClass().getSimpleName(), message);
140 | }
141 |
142 | /**
143 | * Error.
144 | *
145 | * @param object the object
146 | * @param e the e
147 | */
148 | public static void error(Object object, Throwable e) {
149 | error(object.getClass().getSimpleName(), toStackTraceString(e));
150 | }
151 |
152 | /**
153 | * 记录“error”级别的信息
154 | *
155 | * @param tag the tag
156 | * @param message the message
157 | */
158 | public static void error(String tag, String message) {
159 | if (isDebug) {
160 | try {
161 | Log.e(debugTag + tag, message);
162 | } catch (Exception e) {
163 | System.out.println(debugTag + ">>>" + message);
164 | }
165 | }
166 | }
167 |
168 | /**
169 | * 在某个方法中调用生成.trace文件。然后拿到电脑上用DDMS工具打开分析
170 | *
171 | * @see #stopMethodTracing() #stopMethodTracing()#stopMethodTracing()
172 | */
173 | public static void startMethodTracing() {
174 | if (isDebug) {
175 | Debug.startMethodTracing(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + debugTag + ".trace");
176 | }
177 | }
178 |
179 | /**
180 | * Stop method tracing.
181 | */
182 | public static void stopMethodTracing() {
183 | if (isDebug) {
184 | Debug.stopMethodTracing();
185 | }
186 | }
187 |
188 | /**
189 | * To stack trace string string.
190 | *
191 | * @param throwable the throwable
192 | * @return the string
193 | */
194 | public static String toStackTraceString(Throwable throwable) {
195 | StringWriter sw = new StringWriter();
196 | PrintWriter pw = new PrintWriter(sw);
197 | throwable.printStackTrace(pw);
198 | String stackTraceString = sw.toString();
199 | //Reduce data to 128KB so we don't get a TransactionTooLargeException when sending the intent.
200 | //The limit is 1MB on Android but some devices seem to have it lower.
201 | //See: http://developer.android.com/reference/android/os/TransactionTooLargeException.html
202 | //And: http://stackoverflow.com/questions/11451393/what-to-do-on-transactiontoolargeexception#comment46697371_12809171
203 | if (stackTraceString.length() > MAX_STACK_TRACE_SIZE) {
204 | String disclaimer = " [stack trace too large]";
205 | stackTraceString = stackTraceString.substring(0, MAX_STACK_TRACE_SIZE - disclaimer.length()) + disclaimer;
206 | }
207 | return stackTraceString;
208 | }
209 |
210 | }
211 |
--------------------------------------------------------------------------------
/library/Common/src/main/java/cn/qqtheme/framework/util/ScreenUtils.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.util;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.util.DisplayMetrics;
6 | import android.view.Window;
7 | import android.view.WindowManager;
8 |
9 | /**
10 | * 获取屏幕宽高等信息、全屏切换、保持屏幕常亮、截屏等
11 | *
12 | * @author liyujiang[QQ:1032694760]
13 | * @since 2015/11/26
14 | */
15 | public final class ScreenUtils {
16 | private static boolean isFullScreen = false;
17 |
18 | /**
19 | * Display metrics display metrics.
20 | *
21 | * @param context the context
22 | * @return the display metrics
23 | */
24 | public static DisplayMetrics displayMetrics(Context context) {
25 | DisplayMetrics dm = new DisplayMetrics();
26 | WindowManager windowManager = (WindowManager) context
27 | .getSystemService(Context.WINDOW_SERVICE);
28 | windowManager.getDefaultDisplay().getMetrics(dm);
29 | LogUtils.debug("screen width=" + dm.widthPixels + "px, screen height=" + dm.heightPixels
30 | + "px, densityDpi=" + dm.densityDpi + ", density=" + dm.density);
31 | return dm;
32 | }
33 |
34 | /**
35 | * Width pixels int.
36 | *
37 | * @param context the context
38 | * @return the int
39 | */
40 | public static int widthPixels(Context context) {
41 | return displayMetrics(context).widthPixels;
42 | }
43 |
44 | /**
45 | * Height pixels int.
46 | *
47 | * @param context the context
48 | * @return the int
49 | */
50 | public static int heightPixels(Context context) {
51 | return displayMetrics(context).heightPixels;
52 | }
53 |
54 | /**
55 | * Density float.
56 | *
57 | * @param context the context
58 | * @return the float
59 | */
60 | public static float density(Context context) {
61 | return displayMetrics(context).density;
62 | }
63 |
64 | /**
65 | * Density dpi int.
66 | *
67 | * @param context the context
68 | * @return the int
69 | */
70 | public static int densityDpi(Context context) {
71 | return displayMetrics(context).densityDpi;
72 | }
73 |
74 | /**
75 | * Is full screen boolean.
76 | *
77 | * @return the boolean
78 | */
79 | public static boolean isFullScreen() {
80 | return isFullScreen;
81 | }
82 |
83 | /**
84 | * Toggle full displayMetrics.
85 | *
86 | * @param activity the activity
87 | */
88 | public static void toggleFullScreen(Activity activity) {
89 | Window window = activity.getWindow();
90 | int flagFullscreen = WindowManager.LayoutParams.FLAG_FULLSCREEN;
91 | if (isFullScreen) {
92 | window.clearFlags(flagFullscreen);
93 | isFullScreen = false;
94 | } else {
95 | window.setFlags(flagFullscreen, flagFullscreen);
96 | isFullScreen = true;
97 | }
98 | }
99 |
100 | /**
101 | * 保持屏幕常亮
102 | *
103 | * @param activity the activity
104 | */
105 | public static void keepBright(Activity activity) {
106 | //需在setContentView前调用
107 | int keepScreenOn = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
108 | activity.getWindow().setFlags(keepScreenOn, keepScreenOn);
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/library/Common/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/library/FilePicker/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /*.iml
3 |
--------------------------------------------------------------------------------
/library/FilePicker/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | ext {
4 | isLibrary = true
5 | pomArtifactId = "FilePicker"
6 | pomDescription = "file picker for android"
7 | }
8 |
9 | dependencies {
10 | compile project(":library:Common")
11 | }
12 |
--------------------------------------------------------------------------------
/library/FilePicker/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/library/FilePicker/src/main/java/cn/qqtheme/framework/adapter/FileAdapter.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.adapter;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.BaseAdapter;
9 | import android.widget.ImageView;
10 | import android.widget.TextView;
11 |
12 | import java.io.File;
13 | import java.util.ArrayList;
14 |
15 | import cn.qqtheme.framework.entity.FileItem;
16 | import cn.qqtheme.framework.filepicker.R;
17 | import cn.qqtheme.framework.util.CompatUtils;
18 | import cn.qqtheme.framework.util.ConvertUtils;
19 | import cn.qqtheme.framework.util.FileUtils;
20 | import cn.qqtheme.framework.util.LogUtils;
21 |
22 | /**
23 | * The type File adapter.
24 | */
25 | public class FileAdapter extends BaseAdapter {
26 | /**
27 | * The constant DIR_ROOT.
28 | */
29 | public static final String DIR_ROOT = "..";
30 | /**
31 | * The constant DIR_PARENT.
32 | */
33 | public static final String DIR_PARENT = "";
34 | private Context context;
35 | private ArrayList data = new ArrayList();
36 | private String rootPath = null;
37 | private String currentPath = null;
38 | private String[] allowExtensions = null;//允许的扩展名
39 | private boolean onlyListDir = false;//是否仅仅读取目录
40 | private boolean showHomeDir = false;//是否显示返回主目录
41 | private boolean showUpDir = true;//是否显示返回上一级
42 | private boolean showHideDir = true;//是否显示隐藏的目录(以“.”开头)
43 | private int homeIcon = R.drawable.file_picker_home;
44 | private int upIcon = R.drawable.file_picker_updir;
45 | private int folderIcon = R.drawable.file_picker_folder;
46 | private int fileIcon = R.drawable.file_picker_file;
47 |
48 | /**
49 | * Instantiates a new File adapter.
50 | *
51 | * @param context the context
52 | */
53 | public FileAdapter(Context context) {
54 | this.context = context;
55 | }
56 |
57 | /**
58 | * Gets current path.
59 | *
60 | * @return the current path
61 | */
62 | public String getCurrentPath() {
63 | return currentPath;
64 | }
65 |
66 | /**
67 | * Sets allow extensions.
68 | *
69 | * @param allowExtensions the allow extensions
70 | */
71 | public void setAllowExtensions(String[] allowExtensions) {
72 | this.allowExtensions = allowExtensions;
73 | }
74 |
75 | /**
76 | * Sets only list dir.
77 | *
78 | * @param onlyListDir the only list dir
79 | */
80 | public void setOnlyListDir(boolean onlyListDir) {
81 | this.onlyListDir = onlyListDir;
82 | }
83 |
84 | /**
85 | * Sets show home dir.
86 | *
87 | * @param showHomeDir the show home dir
88 | */
89 | public void setShowHomeDir(boolean showHomeDir) {
90 | this.showHomeDir = showHomeDir;
91 | }
92 |
93 | /**
94 | * Sets show up dir.
95 | *
96 | * @param showUpDir the show up dir
97 | */
98 | public void setShowUpDir(boolean showUpDir) {
99 | this.showUpDir = showUpDir;
100 | }
101 |
102 | /**
103 | * Sets show hide dir.
104 | *
105 | * @param showHideDir the show hide dir
106 | */
107 | public void setShowHideDir(boolean showHideDir) {
108 | this.showHideDir = showHideDir;
109 | }
110 |
111 | /**
112 | * Load data array list.
113 | *
114 | * @param path the path
115 | */
116 | public void loadData(String path) {
117 | if (path == null) {
118 | LogUtils.warn("current directory is null");
119 | return;
120 | }
121 | ArrayList datas = new ArrayList();
122 | if (rootPath == null) {
123 | rootPath = path;
124 | }
125 | LogUtils.debug("current directory path: " + path);
126 | currentPath = path;
127 | if (showHomeDir) {
128 | //添加“返回主目录”
129 | FileItem fileRoot = new FileItem();
130 | fileRoot.setDirectory(true);
131 | fileRoot.setIcon(homeIcon);
132 | fileRoot.setName(DIR_ROOT);
133 | fileRoot.setSize(0);
134 | fileRoot.setPath(rootPath);
135 | datas.add(fileRoot);
136 | }
137 | if (showUpDir && !path.equals("/")) {
138 | //添加“返回上一级目录”
139 | FileItem fileParent = new FileItem();
140 | fileParent.setDirectory(true);
141 | fileParent.setIcon(upIcon);
142 | fileParent.setName(DIR_PARENT);
143 | fileParent.setSize(0);
144 | fileParent.setPath(new File(path).getParent());
145 | datas.add(fileParent);
146 | }
147 | File[] files;
148 | if (allowExtensions == null) {
149 | if (onlyListDir) {
150 | files = FileUtils.listDirs(currentPath);
151 | } else {
152 | files = FileUtils.listDirsAndFiles(currentPath);
153 | }
154 | } else {
155 | if (onlyListDir) {
156 | files = FileUtils.listDirs(currentPath, allowExtensions);
157 | } else {
158 | files = FileUtils.listDirsAndFiles(currentPath, allowExtensions);
159 | }
160 | }
161 | if (files != null) {
162 | for (File file : files) {
163 | if (!showHideDir && file.getName().startsWith(".")) {
164 | continue;
165 | }
166 | FileItem fileItem = new FileItem();
167 | boolean isDirectory = file.isDirectory();
168 | fileItem.setDirectory(isDirectory);
169 | if (isDirectory) {
170 | fileItem.setIcon(folderIcon);
171 | fileItem.setSize(0);
172 | } else {
173 | fileItem.setIcon(fileIcon);
174 | fileItem.setSize(file.length());
175 | }
176 | fileItem.setName(file.getName());
177 | fileItem.setPath(file.getAbsolutePath());
178 | datas.add(fileItem);
179 | }
180 | }
181 | data.clear();
182 | data.addAll(datas);
183 | notifyDataSetChanged();
184 | }
185 |
186 | @Override
187 | public int getCount() {
188 | return data.size();
189 | }
190 |
191 | @Override
192 | public FileItem getItem(int position) {
193 | return data.get(position);
194 | }
195 |
196 | @Override
197 | public long getItemId(int position) {
198 | return position;
199 | }
200 |
201 | @Override
202 | public View getView(int position, View convertView, ViewGroup parent) {
203 | ViewHolder holder;
204 | if (convertView == null) {
205 | convertView = LayoutInflater.from(context).inflate(R.layout.file_item, null);
206 | CompatUtils.setBackground(convertView, ConvertUtils.toStateListDrawable(Color.WHITE, Color.LTGRAY));
207 | holder = new ViewHolder();
208 | holder.imageView = (ImageView) convertView.findViewById(R.id.file_icon);
209 | holder.textView = (TextView) convertView.findViewById(R.id.file_name);
210 | convertView.setTag(holder);
211 | } else {
212 | holder = (ViewHolder) convertView.getTag();
213 | }
214 | FileItem item = data.get(position);
215 | holder.imageView.setImageResource(item.getIcon());
216 | holder.textView.setText(item.getName());
217 | return convertView;
218 | }
219 |
220 | private class ViewHolder {
221 | ImageView imageView;
222 | TextView textView;
223 | }
224 |
225 | }
226 |
--------------------------------------------------------------------------------
/library/FilePicker/src/main/java/cn/qqtheme/framework/entity/FileItem.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.entity;
2 |
3 | /**
4 | * 文件项信息
5 | *
6 | * @author 李玉江[QQ :1032694760]
7 | * @version 2014 -05-23 18:02
8 | */
9 | public class FileItem {
10 | private int icon;
11 | private String name;
12 | private String path = "/";
13 | private long size = 0;
14 | private boolean isDirectory = false;
15 |
16 | /**
17 | * Sets icon.
18 | *
19 | * @param icon the icon
20 | */
21 | public void setIcon(int icon) {
22 | this.icon = icon;
23 | }
24 |
25 | /**
26 | * Gets icon.
27 | *
28 | * @return the icon
29 | */
30 | public int getIcon() {
31 | return icon;
32 | }
33 |
34 | /**
35 | * Gets name.
36 | *
37 | * @return the name
38 | */
39 | public String getName() {
40 | return name;
41 | }
42 |
43 | /**
44 | * Sets name.
45 | *
46 | * @param name the name
47 | */
48 | public void setName(String name) {
49 | this.name = name;
50 | }
51 |
52 | /**
53 | * Gets path.
54 | *
55 | * @return the path
56 | */
57 | public String getPath() {
58 | return path;
59 | }
60 |
61 | /**
62 | * Sets path.
63 | *
64 | * @param path the path
65 | */
66 | public void setPath(String path) {
67 | this.path = path;
68 | }
69 |
70 | /**
71 | * Gets size.
72 | *
73 | * @return the size
74 | */
75 | public long getSize() {
76 | return size;
77 | }
78 |
79 | /**
80 | * Sets size.
81 | *
82 | * @param size the size
83 | */
84 | public void setSize(long size) {
85 | this.size = size;
86 | }
87 |
88 | /**
89 | * Is directory boolean.
90 | *
91 | * @return the boolean
92 | */
93 | public boolean isDirectory() {
94 | return isDirectory;
95 | }
96 |
97 | /**
98 | * Sets directory.
99 | *
100 | * @param isDirectory the is directory
101 | */
102 | public void setDirectory(boolean isDirectory) {
103 | this.isDirectory = isDirectory;
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/library/FilePicker/src/main/java/cn/qqtheme/framework/picker/FilePicker.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.picker;
2 |
3 | import android.Manifest;
4 | import android.annotation.TargetApi;
5 | import android.app.Activity;
6 | import android.graphics.Color;
7 | import android.graphics.drawable.ColorDrawable;
8 | import android.os.Build;
9 | import android.support.annotation.IntDef;
10 | import android.support.annotation.NonNull;
11 | import android.support.annotation.Nullable;
12 | import android.support.annotation.RequiresPermission;
13 | import android.view.Gravity;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.widget.AdapterView;
17 | import android.widget.LinearLayout;
18 | import android.widget.ListView;
19 |
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 |
23 | import cn.qqtheme.framework.adapter.FileAdapter;
24 | import cn.qqtheme.framework.entity.FileItem;
25 | import cn.qqtheme.framework.popup.ConfirmPopup;
26 | import cn.qqtheme.framework.util.ConvertUtils;
27 | import cn.qqtheme.framework.util.LogUtils;
28 | import cn.qqtheme.framework.util.StorageUtils;
29 | import cn.qqtheme.framework.widget.MarqueeTextView;
30 |
31 | /**
32 | * 文件目录选择器
33 | *
34 | * @author 李玉江[QQ :1032694760]
35 | * @version 2015 /9/29
36 | */
37 | public class FilePicker extends ConfirmPopup implements AdapterView.OnItemClickListener {
38 | /**
39 | * Directory mode.
40 | */
41 | public static final int DIRECTORY = 0;
42 | /**
43 | * File mode.
44 | */
45 | public static final int FILE = 1;
46 |
47 | private String initPath;
48 | private FileAdapter adapter;
49 | private MarqueeTextView textView;
50 | private OnFilePickListener onFilePickListener;
51 | private int mode;
52 |
53 | @IntDef(flag = false, value = {DIRECTORY, FILE})
54 | @Retention(RetentionPolicy.SOURCE)
55 | public @interface Mode {
56 | }
57 |
58 | /**
59 | * Instantiates a new File picker.
60 | *
61 | * @param activity the activity
62 | * @param mode data mode
63 | * @see #FILE #FILE#FILE
64 | * @see #DIRECTORY #DIRECTORY#DIRECTORY
65 | */
66 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
67 | @RequiresPermission(anyOf = {Manifest.permission.READ_EXTERNAL_STORAGE})
68 | public FilePicker(Activity activity, @Mode int mode) {
69 | super(activity);
70 | setHalfScreen(true);
71 | this.initPath = StorageUtils.getRootPath(activity);
72 | this.mode = mode;
73 | this.adapter = new FileAdapter(activity);
74 | adapter.setOnlyListDir(mode == DIRECTORY);
75 | }
76 |
77 | @Override
78 | @NonNull
79 | protected LinearLayout makeCenterView() {
80 | LinearLayout rootLayout = new LinearLayout(activity);
81 | rootLayout.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
82 | rootLayout.setBackgroundColor(Color.WHITE);
83 | rootLayout.setOrientation(LinearLayout.VERTICAL);
84 | ListView listView = new ListView(activity);
85 | listView.setBackgroundColor(Color.WHITE);
86 | listView.setDivider(new ColorDrawable(0xFFDDDDDD));
87 | listView.setDividerHeight(1);
88 | listView.setCacheColorHint(Color.TRANSPARENT);
89 | listView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
90 | listView.setAdapter(adapter);
91 | listView.setOnItemClickListener(this);
92 | rootLayout.addView(listView);
93 | return rootLayout;
94 | }
95 |
96 | @Nullable
97 | @Override
98 | protected View makeFooterView() {
99 | textView = new MarqueeTextView(activity);
100 | textView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
101 | textView.setTextColor(Color.BLACK);
102 | textView.setGravity(Gravity.CENTER_VERTICAL);
103 | int padding = ConvertUtils.toPx(activity, 10);
104 | textView.setPadding(padding, padding, padding, padding);
105 | return textView;
106 | }
107 |
108 | /**
109 | * Sets root path.
110 | *
111 | * @param initPath the init path
112 | */
113 | public void setRootPath(String initPath) {
114 | this.initPath = initPath;
115 | }
116 |
117 | /**
118 | * Sets allow extensions.
119 | *
120 | * @param allowExtensions the allow extensions
121 | */
122 | public void setAllowExtensions(String[] allowExtensions) {
123 | adapter.setAllowExtensions(allowExtensions);
124 | }
125 |
126 | /**
127 | * Sets show up dir.
128 | *
129 | * @param showUpDir the show up dir
130 | */
131 | public void setShowUpDir(boolean showUpDir) {
132 | adapter.setShowUpDir(showUpDir);
133 | }
134 |
135 | /**
136 | * Sets show home dir.
137 | *
138 | * @param showHomeDir the show home dir
139 | */
140 | public void setShowHomeDir(boolean showHomeDir) {
141 | adapter.setShowHomeDir(showHomeDir);
142 | }
143 |
144 | /**
145 | * Sets show hide dir.
146 | *
147 | * @param showHideDir the show hide dir
148 | */
149 | public void setShowHideDir(boolean showHideDir) {
150 | adapter.setShowHideDir(showHideDir);
151 | }
152 |
153 | @Override
154 | protected void setContentViewBefore() {
155 | boolean isPickFile = mode == FILE;
156 | setCancelVisible(!isPickFile);
157 | setSubmitText(isPickFile ? "取消" : "确定");
158 | }
159 |
160 | @Override
161 | protected void setContentViewAfter(View contentView) {
162 | refreshCurrentDirPath(initPath);
163 | }
164 |
165 | @Override
166 | protected void onSubmit() {
167 | if (mode == FILE) {
168 | LogUtils.debug("已放弃选择!");
169 | } else {
170 | String currentPath = adapter.getCurrentPath();
171 | LogUtils.debug("已选择目录:" + currentPath);
172 | if (onFilePickListener != null) {
173 | onFilePickListener.onFilePicked(currentPath);
174 | }
175 | }
176 | }
177 |
178 | /**
179 | * Gets current path.
180 | *
181 | * @return the current path
182 | */
183 | public String getCurrentPath() {
184 | return adapter.getCurrentPath();
185 | }
186 |
187 | /**
188 | * 响应选择器的列表项点击事件
189 | */
190 | @Override
191 | public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
192 | FileItem fileItem = adapter.getItem(position);
193 | if (fileItem.isDirectory()) {
194 | refreshCurrentDirPath(fileItem.getPath());
195 | } else {
196 | String clickPath = fileItem.getPath();
197 | if (mode == DIRECTORY) {
198 | LogUtils.debug("选择的不是有效的目录: " + clickPath);
199 | } else {
200 | dismiss();
201 | LogUtils.debug("已选择文件:" + clickPath);
202 | if (onFilePickListener != null) {
203 | onFilePickListener.onFilePicked(clickPath);
204 | }
205 | }
206 | }
207 | }
208 |
209 | private void refreshCurrentDirPath(String currentPath) {
210 | if (currentPath.equals("/")) {
211 | textView.setText("根目录");
212 | } else {
213 | textView.setText(currentPath);
214 | }
215 | adapter.loadData(currentPath);
216 | }
217 |
218 | /**
219 | * Sets on file pick listener.
220 | *
221 | * @param listener the listener
222 | */
223 | public void setOnFilePickListener(OnFilePickListener listener) {
224 | this.onFilePickListener = listener;
225 | }
226 |
227 | /**
228 | * The interface On file pick listener.
229 | */
230 | public interface OnFilePickListener {
231 |
232 | /**
233 | * On file picked.
234 | *
235 | * @param currentPath the current path
236 | */
237 | void onFilePicked(String currentPath);
238 |
239 | }
240 |
241 | }
242 |
--------------------------------------------------------------------------------
/library/FilePicker/src/main/java/cn/qqtheme/framework/util/StorageUtils.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.util;
2 |
3 | import android.content.Context;
4 | import android.os.Environment;
5 | import android.text.TextUtils;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 |
10 | /**
11 | * 存储设备工具类
12 | *
13 | * @author 李玉江[QQ :1023694760]
14 | * @version 2013 -11-2
15 | */
16 | public final class StorageUtils {
17 |
18 | /**
19 | * 判断外置存储是否可用
20 | *
21 | * @return the boolean
22 | */
23 | public static boolean externalMounted() {
24 | String state = Environment.getExternalStorageState();
25 | if (state.equals(Environment.MEDIA_MOUNTED)) {
26 | return true;
27 | }
28 | LogUtils.warn("external storage unmounted");
29 | return false;
30 | }
31 |
32 | /**
33 | * 返回以“/”结尾的外置存储根目录,可选若外置存储不可用则是否使用内部存储
34 | *
35 | * @param context the context
36 | * @param onlyExternalStorage the only external storage
37 | * @param subdir the subdir
38 | * @return 诸如 :/mnt/sdcard/subdir
39 | */
40 | public static String getRootPath(Context context, boolean onlyExternalStorage, String subdir) {
41 | File file;
42 | if (externalMounted()) {
43 | file = Environment.getExternalStorageDirectory();
44 | } else {
45 | if (onlyExternalStorage) {
46 | file = new File("/");
47 | } else {
48 | file = context.getFilesDir();
49 | }
50 | }
51 | if (!TextUtils.isEmpty(subdir)) {
52 | file = new File(file, subdir);
53 | //noinspection ResultOfMethodCallIgnored
54 | file.mkdirs();
55 | }
56 | String path = "/";
57 | if (file != null) {
58 | path = FileUtils.separator(file.getAbsolutePath());
59 | }
60 | LogUtils.debug("storage root path: " + path);
61 | return path;
62 | }
63 |
64 | /**
65 | * 返回以“/”结尾的外置存储根目录,可选若外置存储不可用则是否使用内部存储
66 | *
67 | * @param context the context
68 | * @param subdir the subdir
69 | * @return 诸如 :/mnt/sdcard/
70 | */
71 | public static String getRootPath(Context context, String subdir) {
72 | return getRootPath(context, false, subdir);
73 | }
74 |
75 | /**
76 | * 返回以“/”结尾的外置存储根目录,可选若外置存储不可用则是否使用内部存储
77 | *
78 | * @param context the context
79 | * @param onlyExternalStorage the only external storage
80 | * @return 诸如 :/mnt/sdcard/
81 | */
82 | public static String getRootPath(Context context, boolean onlyExternalStorage) {
83 | return getRootPath(context, onlyExternalStorage, null);
84 | }
85 |
86 | /**
87 | * 返回以“/”结尾的外置存储根目录,若外置存储不可用则使用内部存储
88 | *
89 | * @param context the context
90 | * @return 诸如 :/mnt/sdcard/
91 | */
92 | public static String getRootPath(Context context) {
93 | return getRootPath(context, false);
94 | }
95 |
96 | /**
97 | * 下载的文件的保存路径,以“/”结尾
98 | *
99 | * @param context the context
100 | * @return 诸如 :/mnt/sdcard/Download/
101 | * @throws Exception the exception
102 | */
103 | public static String getDownloadPath(Context context) throws Exception {
104 | File file;
105 | if (externalMounted()) {
106 | file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
107 | } else {
108 | throw new Exception("外置存储不可用!");
109 | }
110 | return FileUtils.separator(file.getAbsolutePath());
111 | }
112 |
113 | /**
114 | * 各种类型的文件的专用的保存路径,以“/”结尾
115 | *
116 | * @param context the context
117 | * @param type the type
118 | * @return 诸如 :/mnt/sdcard/Android/data/[package]/files/[type]/
119 | */
120 | public static String getPrivatePath(Context context, String type) {
121 | File file = null;
122 | if (externalMounted()) {
123 | file = context.getExternalFilesDir(type);
124 | }
125 | if (file == null) {
126 | //SD卡不可用或暂时繁忙?
127 | if (type == null) {
128 | file = context.getFilesDir();
129 | } else {
130 | file = new File(FileUtils.separator(context.getFilesDir().getAbsolutePath()) + type);
131 | //noinspection ResultOfMethodCallIgnored
132 | file.mkdirs();
133 | }
134 | }
135 | return FileUtils.separator(file.getAbsolutePath());
136 | }
137 |
138 | /**
139 | * Gets internal root path.
140 | *
141 | * @param context the context
142 | * @return the internal root path
143 | */
144 | public static String getInternalRootPath(Context context) {
145 | return FileUtils.separator(context.getFilesDir().getAbsolutePath());
146 | }
147 |
148 | /**
149 | * Gets private root path.
150 | *
151 | * @param context the context
152 | * @return the private root path
153 | */
154 | public static String getPrivateRootPath(Context context) {
155 | return getPrivatePath(context, null);
156 | }
157 |
158 | /**
159 | * Gets plugin path.
160 | *
161 | * @param context the context
162 | * @return the plugin path
163 | */
164 | public static String getPluginPath(Context context) {
165 | return getPrivatePath(context, "plugin");
166 | }
167 |
168 | /**
169 | * Gets temporary dir path.
170 | *
171 | * @param context the context
172 | * @return the temporary dir path
173 | */
174 | public static String getTemporaryDirPath(Context context) {
175 | return getPrivatePath(context, "temporary");
176 | }
177 |
178 | /**
179 | * Gets temporary file path.
180 | *
181 | * @param context the context
182 | * @return the temporary file path
183 | */
184 | public static String getTemporaryFilePath(Context context) {
185 | try {
186 | return File.createTempFile("lyj_", ".tmp", context.getCacheDir()).getAbsolutePath();
187 | } catch (IOException e) {
188 | return getTemporaryDirPath(context) + "lyj.tmp";
189 | }
190 | }
191 |
192 | /**
193 | * Gets cache path.
194 | *
195 | * @param context the context
196 | * @param type the type
197 | * @return the cache path
198 | */
199 | public static String getCachePath(Context context, String type) {
200 | File file;
201 | if (externalMounted()) {
202 | file = context.getExternalCacheDir();
203 | } else {
204 | file = context.getCacheDir();
205 | }
206 | if (type != null) {
207 | file = new File(file, type);
208 | //noinspection ResultOfMethodCallIgnored
209 | file.mkdirs();
210 | }
211 | return FileUtils.separator(file != null ? file.getAbsolutePath() : null);
212 | }
213 |
214 | /**
215 | * Gets cache root path.
216 | *
217 | * @param context the context
218 | * @return the cache root path
219 | */
220 | public static String getCacheRootPath(Context context) {
221 | return getCachePath(context, null);
222 | }
223 |
224 | }
225 |
--------------------------------------------------------------------------------
/library/FilePicker/src/main/java/cn/qqtheme/framework/widget/MarqueeTextView.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.widget;
2 |
3 | import android.content.Context;
4 | import android.text.TextUtils;
5 | import android.util.AttributeSet;
6 | import android.view.Gravity;
7 | import android.widget.TextView;
8 |
9 | /**
10 | * 文本水平自动滚动(从右到左)。
11 | *
12 | * @author 李玉江[QQ :1023694760]
13 | * @version 2013 -1-11
14 | */
15 | public class MarqueeTextView extends TextView {
16 |
17 | /**
18 | * Instantiates a new Marquee text view.
19 | *
20 | * @param context the context
21 | */
22 | public MarqueeTextView(Context context) {
23 | this(context, null);
24 | }
25 |
26 | /**
27 | * Instantiates a new Marquee text view.
28 | *
29 | * @param context the context
30 | * @param attr the attr
31 | */
32 | public MarqueeTextView(Context context, AttributeSet attr) {
33 | super(context, attr);
34 | setEllipsize(TextUtils.TruncateAt.MARQUEE);
35 | setMarqueeRepeatLimit(-1);
36 | setSingleLine(true);
37 | setHorizontallyScrolling(true);
38 | setClickable(true);
39 | setFocusable(true);
40 | setFocusableInTouchMode(true);
41 | setGravity(Gravity.CENTER);
42 | }
43 |
44 | /**
45 | * 必须已获取到焦点,才能即显滚动
46 | */
47 | @Override
48 | public boolean isFocused() {
49 | return true;
50 | }
51 |
52 | @Override
53 | public boolean isPressed() {
54 | return true;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/library/FilePicker/src/main/res/drawable-hdpi/file_picker_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sevenli777/android-picker/a561012af9c737f43da4d3bc10990efa467a8d2c/library/FilePicker/src/main/res/drawable-hdpi/file_picker_file.png
--------------------------------------------------------------------------------
/library/FilePicker/src/main/res/drawable-hdpi/file_picker_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sevenli777/android-picker/a561012af9c737f43da4d3bc10990efa467a8d2c/library/FilePicker/src/main/res/drawable-hdpi/file_picker_folder.png
--------------------------------------------------------------------------------
/library/FilePicker/src/main/res/drawable-hdpi/file_picker_home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sevenli777/android-picker/a561012af9c737f43da4d3bc10990efa467a8d2c/library/FilePicker/src/main/res/drawable-hdpi/file_picker_home.png
--------------------------------------------------------------------------------
/library/FilePicker/src/main/res/drawable-hdpi/file_picker_updir.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sevenli777/android-picker/a561012af9c737f43da4d3bc10990efa467a8d2c/library/FilePicker/src/main/res/drawable-hdpi/file_picker_updir.png
--------------------------------------------------------------------------------
/library/FilePicker/src/main/res/layout/file_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/library/WheelPicker/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /*.iml
3 |
--------------------------------------------------------------------------------
/library/WheelPicker/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | ext {
4 | isLibrary = true
5 | pomArtifactId = "WheelPicker"
6 | pomDescription = "wheel picker for android, include date picker, time picker, option picker, address picker, etc."
7 | }
8 |
9 | dependencies {
10 | compile project(":library:Common")
11 | }
12 |
--------------------------------------------------------------------------------
/library/WheelPicker/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/library/WheelPicker/src/main/java/cn/qqtheme/framework/picker/AddressPicker.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.picker;
2 |
3 | import android.app.Activity;
4 | import android.support.annotation.NonNull;
5 | import android.view.Gravity;
6 | import android.view.View;
7 | import android.widget.LinearLayout;
8 |
9 | import java.util.ArrayList;
10 |
11 | import cn.qqtheme.framework.widget.WheelView;
12 |
13 | /**
14 | * 地址选择器(包括省级、地级、县级)。
15 | * 地址数据见示例项目的“city.json”,来源于国家统计局官网(http://www.stats.gov.cn/tjsj/tjbz/xzqhdm)
16 | *
17 | * @author 李玉江[QQ:1032694760]
18 | * @since 2015/12/15
19 | */
20 | public class AddressPicker extends LinkagePicker {
21 | private OnAddressPickListener onAddressPickListener;
22 | private boolean hideProvince = false;
23 | private boolean hideCounty = false;
24 |
25 | /**
26 | * Instantiates a new Address picker.
27 | *
28 | * @param activity the activity
29 | * @param data the data
30 | */
31 | public AddressPicker(Activity activity, ArrayList data) {
32 | super(activity);
33 | int provinceSize = data.size();
34 | //添加省
35 | for (int x = 0; x < provinceSize; x++) {
36 | Province pro = data.get(x);
37 | firstList.add(pro.getAreaName());
38 | ArrayList cities = pro.getCities();
39 | ArrayList xCities = new ArrayList();
40 | ArrayList> xCounties = new ArrayList>();
41 | int citySize = cities.size();
42 | //添加地市
43 | for (int y = 0; y < citySize; y++) {
44 | City cit = cities.get(y);
45 | xCities.add(cit.getAreaName());
46 | ArrayList counties = cit.getCounties();
47 | ArrayList yCounties = new ArrayList();
48 | int countySize = counties.size();
49 | //添加区县
50 | if (countySize == 0) {
51 | yCounties.add(cit.getAreaName());
52 | } else {
53 | for (int z = 0; z < countySize; z++) {
54 | yCounties.add(counties.get(z).getAreaName());
55 | }
56 | }
57 | xCounties.add(yCounties);
58 | }
59 | secondList.add(xCities);
60 | thirdList.add(xCounties);
61 | }
62 | }
63 |
64 | /**
65 | * Sets selected item.
66 | *
67 | * @param province the province
68 | * @param city the city
69 | * @param county the county
70 | */
71 | public void setSelectedItem(String province, String city, String county) {
72 | super.setSelectedItem(province, city, county);
73 | }
74 |
75 | /**
76 | * 隐藏省级行政区,只显示地市级和区县级。
77 | * 设置为true的话,地址数据中只需要某个省份的即可
78 | * 参见示例中的“city2.json”
79 | *
80 | * @param hideProvince the hide province
81 | */
82 | public void setHideProvince(boolean hideProvince) {
83 | this.hideProvince = hideProvince;
84 | }
85 |
86 | /**
87 | * 隐藏县级行政区,只显示省级和市级。
88 | * 设置为true的话,hideProvince将强制为false
89 | * 数据源依然使用“city.json” 仅在逻辑上隐藏县级选择框。
90 | *
91 | * @param hideCounty the hide county
92 | */
93 | public void setHideCounty(boolean hideCounty) {
94 | this.hideCounty = hideCounty;
95 | }
96 |
97 | /**
98 | * Sets on address pick listener.
99 | *
100 | * @param listener the listener
101 | */
102 | public void setOnAddressPickListener(OnAddressPickListener listener) {
103 | this.onAddressPickListener = listener;
104 | }
105 |
106 | @Deprecated
107 | @Override
108 | public void setOnLinkageListener(OnLinkageListener onLinkageListener) {
109 | throw new UnsupportedOperationException("Please use setOnAddressPickListener instead.");
110 | }
111 |
112 | @Override
113 | @NonNull
114 | protected View makeCenterView() {
115 | if (hideCounty) {
116 | hideProvince = false;
117 | }
118 | if (firstList.size() == 0) {
119 | throw new IllegalArgumentException("please initial data at first, can't be empty");
120 | }
121 | LinearLayout layout = new LinearLayout(activity);
122 | layout.setOrientation(LinearLayout.HORIZONTAL);
123 | layout.setGravity(Gravity.CENTER);
124 | final WheelView provinceView = new WheelView(activity);
125 | final int width = screenWidthPixels / 3;
126 | provinceView.setLayoutParams(new LinearLayout.LayoutParams(width, WRAP_CONTENT));
127 | provinceView.setTextSize(textSize);
128 | provinceView.setTextColor(textColorNormal, textColorFocus);
129 | provinceView.setLineVisible(lineVisible);
130 | provinceView.setLineColor(lineColor);
131 | provinceView.setOffset(offset);
132 | layout.addView(provinceView);
133 | if (hideProvince) {
134 | provinceView.setVisibility(View.GONE);
135 | }
136 | final WheelView cityView = new WheelView(activity);
137 | cityView.setLayoutParams(new LinearLayout.LayoutParams(width, WRAP_CONTENT));
138 | cityView.setTextSize(textSize);
139 | cityView.setTextColor(textColorNormal, textColorFocus);
140 | cityView.setLineVisible(lineVisible);
141 | cityView.setLineColor(lineColor);
142 | cityView.setOffset(offset);
143 | layout.addView(cityView);
144 | final WheelView countyView = new WheelView(activity);
145 | countyView.setLayoutParams(new LinearLayout.LayoutParams(width, WRAP_CONTENT));
146 | countyView.setTextSize(textSize);
147 | countyView.setTextColor(textColorNormal, textColorFocus);
148 | countyView.setLineVisible(lineVisible);
149 | countyView.setLineColor(lineColor);
150 | countyView.setOffset(offset);
151 | layout.addView(countyView);
152 | if (hideCounty) {
153 | countyView.setVisibility(View.GONE);
154 | }
155 | provinceView.setItems(firstList, selectedFirstIndex);
156 | provinceView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
157 | @Override
158 | public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
159 | selectedFirstText = item;
160 | selectedFirstIndex = selectedIndex;
161 | selectedThirdIndex = 0;
162 | //根据省份获取地市
163 | cityView.setItems(secondList.get(selectedFirstIndex), isUserScroll ? 0 : selectedSecondIndex);
164 | //根据地市获取区县
165 | countyView.setItems(thirdList.get(selectedFirstIndex).get(0), isUserScroll ? 0 : selectedThirdIndex);
166 | }
167 | });
168 | cityView.setItems(secondList.get(selectedFirstIndex), selectedSecondIndex);
169 | cityView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
170 | @Override
171 | public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
172 | selectedSecondText = item;
173 | selectedSecondIndex = selectedIndex;
174 | //根据地市获取区县
175 | countyView.setItems(thirdList.get(selectedFirstIndex).get(selectedSecondIndex), isUserScroll ? 0 : selectedThirdIndex);
176 | }
177 | });
178 | countyView.setItems(thirdList.get(selectedFirstIndex).get(selectedSecondIndex), selectedThirdIndex);
179 | countyView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
180 | @Override
181 | public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
182 | selectedThirdText = item;
183 | selectedThirdIndex = selectedIndex;
184 | }
185 | });
186 | return layout;
187 | }
188 |
189 | @Override
190 | public void onSubmit() {
191 | if (onAddressPickListener != null) {
192 | if (hideCounty) {
193 | onAddressPickListener.onAddressPicked(selectedFirstText, selectedSecondText, null);
194 | } else {
195 | onAddressPickListener.onAddressPicked(selectedFirstText, selectedSecondText, selectedThirdText);
196 | }
197 | }
198 | }
199 |
200 | /**
201 | * The interface On address pick listener.
202 | */
203 | public interface OnAddressPickListener {
204 |
205 | /**
206 | * On address picked.
207 | *
208 | * @param province the province
209 | * @param city the city
210 | * @param county the county ,if {@hideCounty} is true,this is null
211 | */
212 | void onAddressPicked(String province, String city, String county);
213 |
214 | }
215 |
216 | /**
217 | * The type Area.
218 | */
219 | public abstract static class Area {
220 | /**
221 | * The Area id.
222 | */
223 | String areaId;
224 | /**
225 | * The Area name.
226 | */
227 | String areaName;
228 |
229 | /**
230 | * Gets area id.
231 | *
232 | * @return the area id
233 | */
234 | public String getAreaId() {
235 | return areaId;
236 | }
237 |
238 | /**
239 | * Sets area id.
240 | *
241 | * @param areaId the area id
242 | */
243 | public void setAreaId(String areaId) {
244 | this.areaId = areaId;
245 | }
246 |
247 | /**
248 | * Gets area name.
249 | *
250 | * @return the area name
251 | */
252 | public String getAreaName() {
253 | return areaName;
254 | }
255 |
256 | /**
257 | * Sets area name.
258 | *
259 | * @param areaName the area name
260 | */
261 | public void setAreaName(String areaName) {
262 | this.areaName = areaName;
263 | }
264 |
265 | @Override
266 | public String toString() {
267 | return "areaId=" + areaId + ",areaName=" + areaName;
268 | }
269 |
270 | }
271 |
272 | /**
273 | * The type Province.
274 | */
275 | public static class Province extends Area {
276 | /**
277 | * The Cities.
278 | */
279 | ArrayList cities = new ArrayList();
280 |
281 | /**
282 | * Gets cities.
283 | *
284 | * @return the cities
285 | */
286 | public ArrayList getCities() {
287 | return cities;
288 | }
289 |
290 | /**
291 | * Sets cities.
292 | *
293 | * @param cities the cities
294 | */
295 | public void setCities(ArrayList cities) {
296 | this.cities = cities;
297 | }
298 |
299 | }
300 |
301 | /**
302 | * The type City.
303 | */
304 | public static class City extends Area {
305 | private ArrayList counties = new ArrayList();
306 |
307 | /**
308 | * Gets counties.
309 | *
310 | * @return the counties
311 | */
312 | public ArrayList getCounties() {
313 | return counties;
314 | }
315 |
316 | /**
317 | * Sets counties.
318 | *
319 | * @param counties the counties
320 | */
321 | public void setCounties(ArrayList counties) {
322 | this.counties = counties;
323 | }
324 |
325 | }
326 |
327 | /**
328 | * The type County.
329 | */
330 | public static class County extends Area {
331 | }
332 |
333 | }
334 |
--------------------------------------------------------------------------------
/library/WheelPicker/src/main/java/cn/qqtheme/framework/picker/ChineseZodiacPicker.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.picker;
2 |
3 | import android.app.Activity;
4 |
5 | /**
6 | * 生肖选择器
7 | *
8 | * @author 李玉江[QQ :1032694760]
9 | * @version 2015 /12/15
10 | */
11 | public class ChineseZodiacPicker extends OptionPicker {
12 |
13 | /**
14 | * Instantiates a new Chinese zodiac picker.
15 | *
16 | * @param activity the activity
17 | */
18 | public ChineseZodiacPicker(Activity activity) {
19 | super(activity, new String[]{
20 | "鼠",
21 | "牛",
22 | "虎",
23 | "兔",
24 | "龙",
25 | "蛇",
26 | "马",
27 | "羊",
28 | "猴",
29 | "鸡",
30 | "狗",
31 | "猪",
32 | });
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/library/WheelPicker/src/main/java/cn/qqtheme/framework/picker/ConstellationPicker.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.picker;
2 |
3 | import android.app.Activity;
4 |
5 | /**
6 | * 星座选择器
7 | *
8 | * @author 李玉江[QQ :1032694760]
9 | * @version 2015 /12/15
10 | */
11 | public class ConstellationPicker extends OptionPicker {
12 |
13 | /**
14 | * Instantiates a new Constellation picker.
15 | *
16 | * @param activity the activity
17 | */
18 | public ConstellationPicker(Activity activity) {
19 | super(activity, new String[]{
20 | "水瓶",
21 | "双鱼",
22 | "白羊",
23 | "金牛",
24 | "双子",
25 | "巨蟹",
26 | "狮子",
27 | "处女",
28 | "天秤",
29 | "天蝎",
30 | "射手",
31 | "摩羯",
32 | });
33 | setLabel("座");
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/library/WheelPicker/src/main/java/cn/qqtheme/framework/picker/DatePicker.java:
--------------------------------------------------------------------------------
1 | package cn.qqtheme.framework.picker;
2 |
3 | import android.app.Activity;
4 | import android.support.annotation.IntDef;
5 | import android.support.annotation.NonNull;
6 | import android.text.TextUtils;
7 | import android.view.Gravity;
8 | import android.view.View;
9 | import android.widget.LinearLayout;
10 | import android.widget.TextView;
11 |
12 | import java.lang.annotation.Retention;
13 | import java.lang.annotation.RetentionPolicy;
14 | import java.util.ArrayList;
15 | import java.util.Collections;
16 | import java.util.Comparator;
17 |
18 | import cn.qqtheme.framework.util.DateUtils;
19 | import cn.qqtheme.framework.widget.WheelView;
20 |
21 | /**
22 | * 日期选择器
23 | *
24 | * @author 李玉江[QQ :1032694760]
25 | * @version 2015 /12/14
26 | */
27 | public class DatePicker extends WheelPicker {
28 | /**
29 | * 年月日
30 | */
31 | public static final int YEAR_MONTH_DAY = 0;
32 | /**
33 | * 年月
34 | */
35 | public static final int YEAR_MONTH = 1;
36 | /**
37 | * 月日
38 | */
39 | public static final int MONTH_DAY = 2;
40 | private ArrayList years = new ArrayList();
41 | private ArrayList months = new ArrayList();
42 | private ArrayList days = new ArrayList();
43 | private OnDatePickListener onDatePickListener;
44 | private String yearLabel = "年", monthLabel = "月", dayLabel = "日";
45 | private int selectedYearIndex = 0, selectedMonthIndex = 0, selectedDayIndex = 0;
46 | private int mode = YEAR_MONTH_DAY;
47 |
48 | /**
49 | * 安卓开发应避免使用枚举类(enum),因为相比于静态常量enum会花费两倍以上的内存。
50 | *
51 | * @link http ://developer.android.com/training/articles/memory.html#Overhead
52 | */
53 | @IntDef(flag = false, value = {YEAR_MONTH_DAY, YEAR_MONTH, MONTH_DAY})
54 | @Retention(RetentionPolicy.SOURCE)
55 | public @interface Mode {
56 | }
57 |
58 | /**
59 | * Instantiates a new Date picker.
60 | *
61 | * @param activity the activity
62 | */
63 | public DatePicker(Activity activity) {
64 | this(activity, YEAR_MONTH_DAY);
65 | }
66 |
67 | /**
68 | * Instantiates a new Date picker.
69 | *
70 | * @param activity the activity
71 | * @param mode the mode
72 | * @see #YEAR_MONTH_DAY #YEAR_MONTH_DAY#YEAR_MONTH_DAY
73 | * @see #YEAR_MONTH #YEAR_MONTH#YEAR_MONTH
74 | * @see #MONTH_DAY #MONTH_DAY#MONTH_DAY
75 | */
76 | public DatePicker(Activity activity, @Mode int mode) {
77 | super(activity);
78 | this.mode = mode;
79 | for (int i = 2000; i <= 2050; i++) {
80 | years.add(String.valueOf(i));
81 | }
82 | for (int i = 1; i <= 12; i++) {
83 | months.add(DateUtils.fillZero(i));
84 | }
85 | for (int i = 1; i <= 31; i++) {
86 | days.add(DateUtils.fillZero(i));
87 | }
88 | }
89 |
90 | /**
91 | * Sets label.
92 | *
93 | * @param yearLabel the year label
94 | * @param monthLabel the month label
95 | * @param dayLabel the day label
96 | */
97 | public void setLabel(String yearLabel, String monthLabel, String dayLabel) {
98 | this.yearLabel = yearLabel;
99 | this.monthLabel = monthLabel;
100 | this.dayLabel = dayLabel;
101 | }
102 |
103 | /**
104 | * Sets range.
105 | *
106 | * @param startYear the start year
107 | * @param endYear the end year
108 | */
109 | public void setRange(int startYear, int endYear) {
110 | years.clear();
111 | for (int i = startYear; i <= endYear; i++) {
112 | years.add(String.valueOf(i));
113 | }
114 | }
115 |
116 | private int findItemIndex(ArrayList items, int item) {
117 | //折半查找有序元素的索引
118 | int index = Collections.binarySearch(items, item, new Comparator