Initial commit

This commit is contained in:
linglongxin24
2016-06-14 13:49:26 +08:00
commit ceed245fb3
154 changed files with 16198 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
/*.iml
/gradlew
/*/*.iml
/gradle
/.idea
/gradlew.bat
bin/
gen/
.gradle/
build/
/*/build/
local.properties
proguard/
*.log

286
README.md Normal file
View File

@@ -0,0 +1,286 @@
# Summary
[![API](https://img.shields.io/badge/API-9%2B-green.svg)](https://github.com/gzu-liyujiang/AndroidPicker)
[![Download](https://api.bintray.com/packages/gzu-liyujiang/maven/AndroidPicker/images/download.svg)](http://jcenter.bintray.com/cn/qqtheme/framework/AndroidPicker)
安卓选择器类库,包括日期选择器、时间选择器、单项选择器、城市选择器、颜色选择器、文件选择器、目录选择器、数字选择器、星座选择器、生肖选择器等,可自定义顶部及底部界面,可自定义窗口动画。
欢迎大伙儿在[Issues](https://github.com/gzu-liyujiang/AndroidPicker/issues)提交你的意见或建议。欢迎Fork & Pull requests贡献您的代码。
状态|链接|备注
-----|------|----
~~Deprecated~~|[~~Branch_OldAndroidPicker~~](https://github.com/gzu-liyujiang/AndroidPicker/tree/Branch_OldAndroidPicker)|~~基于android-wheel的旧版选择器不再维护~~
*Develop*|[dev](https://github.com/gzu-liyujiang/AndroidPicker/tree/dev)|WheelPicker、ColorPicker及FilePicker合并为一个模块来开发
**Release**|[master](https://github.com/gzu-liyujiang/AndroidPicker/tree/master)|主分支WheelPicker、ColorPicker及FilePicker分为三个模块。
# Change Log
- v1.1.0 - 2016.01.29
+ 添加注解约束如“setOffset()”只能是1至4
+ 所有枚举类改为常量来表示,据说这样可以节约内存;
+ 支持自定义选择器的顶部及底部的视图;
+ 支持使用第三方动画库来实现窗口动画;
- v1.0.3 - 2016.01.19
+ 日期时间、地址、单项、数字等选择器支持伪循环滚动。
- v1.0.2 - 2016.01.15
+ 年或月变动时,保持之前选择的日不动:如果之前选择的日是之前年月的最大日,则日自动为该年月的最大日。
- v1.0.1 - 2016.01.14
+ 精简文件选择器的数据适配器;
+ 添加选择器顶部确定、取消按钮所在容器的背景色设置。
- v1.0.0 - 2016.01.13
+ 发布到jcenter支持远程maven依赖。
# Install
“app”是测试用例“library”包括WheelPicker、ColorPicker、FilePicker
WheelPicker包括DatePicker、TimePicker、OptionPicker、AddressPicker、NumberPicker等
如果需要所有的的选择器的话建议依赖“AndroidPicker”。
```groovy
dependencies {
compile 'cn.qqtheme.framework:AndroidPicker:latest.integration'
//compile 'cn.qqtheme.framework:WheelPicker:latest.integration'
//compile 'cn.qqtheme.framework:ColorPicker:latest.integration'
//compile 'cn.qqtheme.framework:FilePicker:latest.integration'
}
```
也可以手动下载本项目复制“library”命名为“AndroidPicker”然后
```groovy
dependencies {
compile project('AndroidPicker')
}
```
*注本项目使用gradle来构建Eclipse用户建议换为Android Studio或Intellij IDEA。*
# Custom
### 自定义窗口进入退出动画(可选,默认动画为淡入淡出)
```xml
<resources>
<style name="Animation.CustomPopup" parent="@android:style/Animation">
<item name="android:windowEnterAnimation">@anim/popup_in</item>
<item name="android:windowExitAnimation">@anim/popup_out</item>
</style>
</resources>
```
```java
picker.setAnimationStyle(R.style.Animation_CustomPopup);
```
或者使用[ViewAnimator](https://github.com/gzu-liyujiang/ViewAnimator)这个动画库来实现:
```groovy
dependencies {
compile 'com.github.florent37:viewanimator:1.0.2'
}
```
```java
ViewAnimator.animate(picker.getRootView())
.slideBottomIn()
.interpolator(new AccelerateInterpolator())
.start();
```
### 自定义顶部及底部界面
添加自己的类继承自现有的选择器覆盖makeHeaderView、makeFooterView在确定选择时调用onSubmit
取消选择时调用onCancel。详见示例CustomHeaderAndFooterPicker.java。
```java
public class CustomHeaderAndFooterPicker extends OptionPicker {
@Nullable
@Override
protected View makeHeaderView() {
return null;//顶部视图
}
@Nullable
@Override
protected View makeFooterView() {
return null;//底部视图
}
}
```
# Sample
日期选择器:
```java
DatePicker picker = new DatePicker(this, DatePicker.YEAR_MONTH_DAY);
picker.setRange(1990, 2015);//年份范围
picker.setOnDatePickListener(new DatePicker.OnYearMonthDayPickListener() {
@Override
public void onDatePicked(String year, String month, String day) {
showToast(year + "-" + month + "-" + day);
}
});
picker.show();
```
时间选择器:
```java
//默认选中当前时间
TimePicker picker = new TimePicker(this, TimePicker.HOUR_OF_DAY);
picker.setTopLineVisible(false);
picker.setOnTimePickListener(new TimePicker.OnTimePickListener() {
@Override
public void onTimePicked(String hour, String minute) {
showToast(hour + ":" + minute);
}
});
picker.show();
```
单项选择器(可用于性别、学历、职业、星座等选择):
```java
OptionPicker picker = new OptionPicker(this, new String[]{
"第一项", "第二项", "这是一个很长很长很长很长很长很长很长很长很长的很长很长的很长很长的项"
});
picker.setOffset(2);
picker.setSelectedIndex(1);
picker.setTextSize(11);
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
```
数字选择器(可用于身高、体重、年龄等选择)
```java
NumberPicker picker = new NumberPicker(this);
picker.setOffset(2);//偏移量
picker.setRange(145, 200);//数字范围
picker.setSelectedItem(172);
picker.setLabel("厘米");
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
```
地址选择器(含省级、地级、县级):
```java
ArrayList<AddressPicker.Province> data = new ArrayList<AddressPicker.Province>();
String json = AssetsUtils.readText(this, "city.json");
data.addAll(JSON.parseArray(json, AddressPicker.Province.class));
AddressPicker picker = new AddressPicker(this, result);
picker.setSelectedItem("贵州", "贵阳", "花溪");
picker.setOnAddressPickListener(new AddressPicker.OnAddressPickListener() {
@Override
public void onAddressPicked(String province, String city, String county) {
showToast(province + city + county);
}
});
picker.show();
```
地址选择器(含地级、县级):
```java
ArrayList<AddressPicker.Province> data = new ArrayList<AddressPicker.Province>();
String json = AssetsUtils.readText(this, "city2.json");
data.addAll(JSON.parseArray(json, AddressPicker.Province.class));
AddressPicker picker = new AddressPicker(this, data);
picker.setHideProvince(true);
picker.setOnAddressPickListener(new AddressPicker.OnAddressPickListener() {
@Override
public void onAddressPicked(String province, String city, String county) {
showToast(province + city + county);
}
});
picker.show();
```
星座选择器:
```java
ConstellationPicker picker = new ConstellationPicker(this);
picker.setTopBackgroundColor(0xFFEEEEEE);
picker.setTopLineVisible(false);
picker.setCancelTextColor(0xFF33B5E5);
picker.setSubmitTextColor(0xFF33B5E5);
picker.setTextColor(0xFFFF0000, 0xFFCCCCCC);
picker.setLineColor(0xFFEE0000);
picker.setSelectedItem("射手");
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
```
生肖选择器:
```java
ChineseZodiacPicker picker = new ChineseZodiacPicker(this);
picker.setLineVisible(false);
picker.setSelectedItem("羊");
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
```
颜色选择器:
```java
ColorPicker picker = new ColorPicker(this);
picker.setInitColor(0xFFDD00DD);
picker.setOnColorPickListener(new ColorPicker.OnColorPickListener() {
@Override
public void onColorPicked(int pickedColor) {
showToast(ConvertUtils.toColorString(pickedColor));
}
});
picker.show();
```
文件选择器需要权限android.permission.READ_EXTERNAL_STORAGE
```java
//noinspection MissingPermission
FilePicker picker = new FilePicker(this, FilePicker.FILE);
picker.setShowHideDir(false);
picker.setRootPath(StorageUtils.getRootPath(this) + "Download/");
//picker.setAllowExtensions(new String[]{".apk"});
picker.setOnFilePickListener(new FilePicker.OnFilePickListener() {
@Override
public void onFilePicked(String currentPath) {
showToast(currentPath);
}
});
picker.show();
```
目录选择器需要权限android.permission.READ_EXTERNAL_STORAGE
```java
//noinspection MissingPermission
FilePicker picker = new FilePicker(this, FilePicker.DIRECTORY);
picker.setOnFilePickListener(new FilePicker.OnFilePickListener() {
@Override
public void onFilePicked(String currentPath) {
showToast(currentPath);
}
});
picker.show();
```
# Thanks
库项目修改了使用以下项目:
https://github.com/wangjiegulu/WheelView
https://github.com/jbruchanov/AndroidColorPicker
# Screenshots
![自定义选择器效果图](/screenshots/custom.gif)
![日期选择器效果图](/screenshots/date.gif)
![时间选择器效果图](/screenshots/time.gif)
![单项选择器效果图](/screenshots/option.gif)
![地址选择器效果图](/screenshots/address.gif)
![地址选择器效果图](/screenshots/address.png)
![数字选择器效果图](/screenshots/number.gif)
![星座选择器效果图](/screenshots/constellation.gif)
![生肖选择器效果图](/screenshots/chinesezodiac.gif)
![颜色选择器效果图](/screenshots/color.gif)
![文件选择器效果图](/screenshots/file.gif)
![目录选择器效果图](/screenshots/dir.gif)
# Contact
<a target="_blank" href="http://wpa.qq.com/msgrd?v=3&uin=1032694760&site=穿青人&menu=yes"><img border="0" src="http://wpa.qq.com/pa?p=2:1032694760:51" alt="点击这里给我发消息" title="点击这里给我发消息"/></a>
<a target="_blank" href="http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=q8fC0t7BwsrFzIXfwOva2oXIxMY" style="text-decoration:none;"><img src="http://rescdn.qqmail.com/zh_CN/htmledition/images/function/qm_open/ico_mailme_02.png"/></a>

2
app/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
/*.iml

29
app/build.gradle Normal file
View File

@@ -0,0 +1,29 @@
apply plugin: 'com.android.application'
android {
buildTypes {
release {
//是否zip优化
zipAlignEnabled true
//是否移除无用的资源
shrinkResources true
//是否混淆
minifyEnabled true
//混淆配置文件
proguardFile 'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.alibaba:fastjson:1.1.46.android'
compile 'com.github.florent37:viewanimator:1.0.2'
compile project(':library:WheelPicker')
compile project(':library:FilePicker')
compile project(':library:ColorPicker')
// compile 'cn.qqtheme.framework:WheelPicker:1.1.0'
// compile 'cn.qqtheme.framework:FilePicker:1.1.0'
// compile 'cn.qqtheme.framework:ColorPicker:1.1.0'
}

17
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\android-develop\android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="cn.qqtheme.androidpicker">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light"
tools:ignore="AllowBackup,GoogleAppIndexingWarning,NewApi">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,424 @@
[
{
"cities": [
{
"counties": [
{
"areaName": "南明区 ",
"areaId": "520102"
},
{
"areaName": "云岩区",
"areaId": "520103"
},
{
"areaName": "花溪区 ",
"areaId": "520111"
},
{
"areaName": "乌当区 ",
"areaId": "520112"
},
{
"areaName": "白云区",
"areaId": "520113"
},
{
"areaName": "观山湖区",
"areaId": "520115"
},
{
"areaName": "开阳县",
"areaId": "520121"
},
{
"areaName": "息烽县",
"areaId": "520122"
},
{
"areaName": "修文县 ",
"areaId": "520123"
},
{
"areaName": "清镇市",
"areaId": "520181"
}
],
"areaName": "贵阳市 ",
"areaId": "520100"
},
{
"counties": [
{
"areaName": "钟山区",
"areaId": "520201"
},
{
"areaName": "六枝特区 ",
"areaId": "520203"
},
{
"areaName": "水城县 ",
"areaId": "520221"
},
{
"areaName": "盘县 ",
"areaId": "520222"
}
],
"areaName": "六盘水市",
"areaId": "520200"
},
{
"counties": [
{
"areaName": "红花岗区 ",
"areaId": "520302"
},
{
"areaName": "汇川区 ",
"areaId": "520303"
},
{
"areaName": "遵义县 ",
"areaId": "520321"
},
{
"areaName": "桐梓县",
"areaId": "520322"
},
{
"areaName": "绥阳县",
"areaId": "520323"
},
{
"areaName": "正安县",
"areaId": "520324"
},
{
"areaName": "道真仡佬族苗族自治县",
"areaId": "520325"
},
{
"areaName": "务川仡佬族苗族自治县 ",
"areaId": "520326"
},
{
"areaName": "凤冈县 ",
"areaId": "520327"
},
{
"areaName": "湄潭县 ",
"areaId": "520328"
},
{
"areaName": "余庆县 ",
"areaId": "520329"
},
{
"areaName": "习水县 ",
"areaId": "520330"
},
{
"areaName": "赤水市",
"areaId": "520381"
},
{
"areaName": "仁怀市",
"areaId": "520382"
}
],
"areaName": "遵义市 ",
"areaId": "520300"
},
{
"counties": [
{
"areaName": "西秀区 ",
"areaId": "520402"
},
{
"areaName": "平坝区 ",
"areaId": "520403"
},
{
"areaName": "普定县 ",
"areaId": "520422"
},
{
"areaName": "镇宁布依族苗族自治县 ",
"areaId": "520423"
},
{
"areaName": "关岭布依族苗族自治县 ",
"areaId": "520424"
},
{
"areaName": "紫云苗族布依族自治县",
"areaId": "520425"
}
],
"areaName": "安顺市 ",
"areaId": "520400"
},
{
"counties": [
{
"areaName": "七星关区 ",
"areaId": "520502"
},
{
"areaName": "大方县",
"areaId": "520521"
},
{
"areaName": "黔西县 ",
"areaId": "520522"
},
{
"areaName": "金沙县 ",
"areaId": "520523"
},
{
"areaName": "织金县 ",
"areaId": "520524"
},
{
"areaName": "纳雍县 ",
"areaId": "520525"
},
{
"areaName": "威宁彝族回族苗族自治县 ",
"areaId": "520526"
},
{
"areaName": " 赫章县 ",
"areaId": "520527"
}
],
"areaName": "毕节市 ",
"areaId": "520500"
},
{
"counties": [
{
"areaName": "碧江区 ",
"areaId": "520602"
},
{
"areaName": "万山区",
"areaId": "520603"
},
{
"areaName": "江口县 ",
"areaId": "520621"
},
{
"areaName": "玉屏侗族自治县",
"areaId": "520622"
},
{
"areaName": "石阡县 ",
"areaId": "520623"
},
{
"areaName": "思南县 ",
"areaId": "520624"
},
{
"areaName": "印江土家族苗族自治县 ",
"areaId": "520625"
},
{
"areaName": "德江县 ",
"areaId": "520626"
},
{
"areaName": "沿河土家族自治县 ",
"areaId": "520627"
},
{
"areaName": "松桃苗族自治县",
"areaId": "520628"
}
],
"areaName": "铜仁市",
"areaId": "520600"
},
{
"counties": [
{
"areaName": "兴义市 ",
"areaId": "522301"
},
{
"areaName": "兴仁县 ",
"areaId": "522322"
},
{
"areaName": "普安县 ",
"areaId": "522323"
},
{
"areaName": "晴隆县 ",
"areaId": "522324"
},
{
"areaName": "贞丰县 ",
"areaId": "522325"
},
{
"areaName": "望谟县 ",
"areaId": "522326"
},
{
"areaName": "册亨县 ",
"areaId": "522327"
},
{
"areaName": "安龙县 ",
"areaId": "522328"
}
],
"areaName": "黔西南布依族苗族自治州",
"areaId": "522300"
},
{
"counties": [
{
"areaName": "凯里市 ",
"areaId": "522601"
},
{
"areaName": "黄平县 ",
"areaId": "522622"
},
{
"areaName": "施秉县",
"areaId": "522623"
},
{
"areaName": "三穗县",
"areaId": "522624"
},
{
"areaName": "镇远县 ",
"areaId": "522625"
},
{
"areaName": "岑巩县",
"areaId": "522626"
},
{
"areaName": "天柱县 ",
"areaId": "522627"
},
{
"areaName": "锦屏县",
"areaId": "522628"
},
{
"areaName": "剑河县 ",
"areaId": "522629"
},
{
"areaName": "台江县 ",
"areaId": "522630"
},
{
"areaName": "黎平县 ",
"areaId": "522631"
},
{
"areaName": "榕江县 ",
"areaId": "522632"
},
{
"areaName": "从江县 ",
"areaId": "522633"
},
{
"areaName": "雷山县 ",
"areaId": "522634"
},
{
"areaName": "麻江县",
"areaId": "522635"
},
{
"areaName": "丹寨县",
"areaId": "522636"
}
],
"areaName": "黔东南苗族侗族自治州",
"areaId": "522600"
},
{
"counties": [
{
"areaName": "都匀市 ",
"areaId": "522701"
},
{
"areaName": "福泉市 ",
"areaId": "522702"
},
{
"areaName": "荔波县 ",
"areaId": "522722"
},
{
"areaName": "贵定县 ",
"areaId": "522723"
},
{
"areaName": "瓮安县",
"areaId": "522725"
},
{
"areaName": "独山县 ",
"areaId": "522726"
},
{
"areaName": "平塘县",
"areaId": "522727"
},
{
"areaName": "罗甸县",
"areaId": "522728"
},
{
"areaName": "长顺县 ",
"areaId": "522729"
},
{
"areaName": "龙里县",
"areaId": "522730"
},
{
"areaName": "惠水县 ",
"areaId": "522731"
},
{
"areaName": "三都水族自治县 ",
"areaId": "522732"
}
],
"areaName": "黔南布依族苗族自治州 ",
"areaId": "522700"
},
{
"counties": [
{
"areaName": "贵安新区",
"areaId": "529900"
}
],
"areaName": "贵安新区",
"areaId": "529900"
}
],
"areaName": "贵州省 ",
"areaId": "520000"
}
]

View File

@@ -0,0 +1,94 @@
package cn.qqtheme.androidpicker;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.widget.Toast;
import com.alibaba.fastjson.JSON;
import java.util.ArrayList;
import cn.qqtheme.framework.picker.AddressPicker;
/**
* 获取地址数据并显示地址选择器
*
* @author 李玉江[QQ:1032694760]
* @version 2015/12/15
*/
public class AddressInitTask extends AsyncTask<String, Void, ArrayList<AddressPicker.Province>> {
private Activity activity;
private ProgressDialog dialog;
private String selectedProvince = "", selectedCity = "", selectedCounty = "";
private boolean hideCounty=false;
/**
* 初始化为不显示区县的模式
* @param activity
* @param hideCounty is hide County
*/
public AddressInitTask(Activity activity,boolean hideCounty) {
this.activity = activity;
this.hideCounty=hideCounty;
dialog = ProgressDialog.show(activity, null, "正在初始化数据...", true, true);
}
public AddressInitTask(Activity activity) {
this.activity = activity;
dialog = ProgressDialog.show(activity, null, "正在初始化数据...", true, true);
}
@Override
protected ArrayList<AddressPicker.Province> doInBackground(String... params) {
if (params != null) {
switch (params.length) {
case 1:
selectedProvince = params[0];
break;
case 2:
selectedProvince = params[0];
selectedCity = params[1];
break;
case 3:
selectedProvince = params[0];
selectedCity = params[1];
selectedCounty = params[2];
break;
default:
break;
}
}
ArrayList<AddressPicker.Province> data = new ArrayList<AddressPicker.Province>();
try {
String json = AssetsUtils.readText(activity, "city.json");
data.addAll(JSON.parseArray(json, AddressPicker.Province.class));
} catch (Exception e) {
e.printStackTrace();
}
return data;
}
@Override
protected void onPostExecute(ArrayList<AddressPicker.Province> result) {
dialog.dismiss();
if (result.size() > 0) {
AddressPicker picker = new AddressPicker(activity, result);
picker.setHideCounty(hideCounty);
picker.setSelectedItem(selectedProvince, selectedCity, selectedCounty);
picker.setOnAddressPickListener(new AddressPicker.OnAddressPickListener() {
@Override
public void onAddressPicked(String province, String city, String county) {
if (county==null){
Toast.makeText(activity, province + city, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(activity, province + city + county, Toast.LENGTH_LONG).show();
}
}
});
picker.show();
} else {
Toast.makeText(activity, "数据初始化失败", Toast.LENGTH_SHORT).show();
}
}
}

View File

@@ -0,0 +1,33 @@
package cn.qqtheme.androidpicker;
import android.content.Context;
import cn.qqtheme.framework.util.ConvertUtils;
import cn.qqtheme.framework.util.LogUtils;
/**
* 操作安装包中的“assets”目录下的文件
*
* @author 李玉江[QQ:1023694760]
* @version 2013-11-2
*/
public class AssetsUtils {
/**
* read file content
*
* @param context the context
* @param assetPath the asset path
* @return String string
*/
public static String readText(Context context, String assetPath) {
LogUtils.debug("read assets file as text: " + assetPath);
try {
return ConvertUtils.toString(context.getAssets().open(assetPath));
} catch (Exception e) {
LogUtils.error(e);
return "";
}
}
}

View File

@@ -0,0 +1,92 @@
package cn.qqtheme.androidpicker;
import android.app.Activity;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.widget.Button;
import android.widget.TextView;
import com.github.florent37.viewanimator.AnimationListener;
import com.github.florent37.viewanimator.ViewAnimator;
import cn.qqtheme.framework.picker.OptionPicker;
/**
* 自定义顶部及底部
* <p/>
* Author:李玉江[QQ:1032694760]
* Email:liyujiang_tk@yeah.net
* DateTime:2016/1/29 14:47
* Builder:Android Studio
*/
public class CustomHeaderAndFooterPicker extends OptionPicker {
public CustomHeaderAndFooterPicker(Activity activity) {
super(activity, new String[]{
"C/C++", "Java", "PHP", "Swift", "Node.js", "C#", "HTML5"
});
setTitleText("请选择你最擅长的语言");
setSelectedItem("PHP");
}
@Override
public void show() {
super.show();
ViewAnimator.animate(getRootView())
.duration(2000)
.interpolator(new AccelerateInterpolator())
.slideBottom()
.start();
}
@Override
public void dismiss() {
ViewAnimator.animate(getRootView())
.duration(1000)
.rollOut()
.onStop(new AnimationListener.Stop() {
@Override
public void onStop() {
CustomHeaderAndFooterPicker.super.dismiss();
}
})
.start();
}
@Nullable
@Override
protected View makeHeaderView() {
View view = LayoutInflater.from(activity).inflate(R.layout.picker_header, null);
TextView titleView = (TextView) view.findViewById(R.id.picker_title);
titleView.setText(titleText);
return view;
}
@Nullable
@Override
protected View makeFooterView() {
View view = LayoutInflater.from(activity).inflate(R.layout.picker_footer, null);
Button submitView = (Button) view.findViewById(R.id.picker_submit);
submitView.setText(submitText);
submitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
onSubmit();
}
});
Button cancelView = (Button) view.findViewById(R.id.picker_cancel);
cancelView.setText(cancelText);
cancelView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
onCancel();
}
});
return view;
}
}

View File

@@ -0,0 +1,345 @@
package cn.qqtheme.androidpicker;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.alibaba.fastjson.JSON;
import com.github.florent37.viewanimator.ViewAnimator;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import cn.qqtheme.framework.picker.AddressPicker;
import cn.qqtheme.framework.picker.ChineseZodiacPicker;
import cn.qqtheme.framework.picker.ColorPicker;
import cn.qqtheme.framework.picker.ConstellationPicker;
import cn.qqtheme.framework.picker.DatePicker;
import cn.qqtheme.framework.picker.FilePicker;
import cn.qqtheme.framework.picker.MyTimePicker;
import cn.qqtheme.framework.picker.NumberPicker;
import cn.qqtheme.framework.picker.OptionPicker;
import cn.qqtheme.framework.picker.SexPicker;
import cn.qqtheme.framework.picker.TimePicker;
import cn.qqtheme.framework.util.ConvertUtils;
import cn.qqtheme.framework.util.StorageUtils;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onBackPressed() {
System.exit(0);
android.os.Process.killProcess(android.os.Process.myPid());
finish();
}
private void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
public void onAnimationStyle(View view) {
NumberPicker picker = new NumberPicker(this);
picker.setAnimationStyle(R.style.Animation_CustomPopup);
picker.setOffset(2);//偏移量
picker.setRange(40, 100);//数字范围
picker.setSelectedItem(65);
picker.setLabel("Kg");
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
}
public void onAnimator(View view) {
CustomHeaderAndFooterPicker picker = new CustomHeaderAndFooterPicker(this);
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
}
public void onYearMonthDayPicker(View view) {
DatePicker picker = new DatePicker(this);
picker.setRange(2000, 2016);
picker.setSelectedItem(2015, 10, 10);
picker.setOnDatePickListener(new DatePicker.OnYearMonthDayPickListener() {
@Override
public void onDatePicked(String year, String month, String day) {
showToast(year + "-" + month + "-" + day);
}
});
picker.show();
}
public void onYearMonthPicker(View view) {
DatePicker picker = new DatePicker(this, DatePicker.YEAR_MONTH);
picker.setRange(1990, 2015);
picker.setOnDatePickListener(new DatePicker.OnYearMonthPickListener() {
@Override
public void onDatePicked(String year, String month) {
showToast(year + "-" + month);
}
});
picker.show();
}
public void onMonthDayPicker(View view) {
DatePicker picker = new DatePicker(this, DatePicker.MONTH_DAY);
picker.setOnDatePickListener(new DatePicker.OnMonthDayPickListener() {
@Override
public void onDatePicked(String month, String day) {
showToast(month + "-" + day);
}
});
picker.show();
}
public void onTimePicker(View view) {
//默认选中当前时间
TimePicker picker = new TimePicker(this, TimePicker.HOUR_OF_DAY);
picker.setTopLineVisible(false);
picker.setOnTimePickListener(new TimePicker.OnTimePickListener() {
@Override
public void onTimePicked(String hour, String minute) {
showToast(hour + ":" + minute);
}
});
picker.show();
}
public void onOptionPicker(View view) {
OptionPicker picker = new OptionPicker(this, new String[]{
"第一项", "第二项", "这是一个很长很长很长很长很长很长很长很长很长的很长很长的很长很长的项"
});
picker.setOffset(2);
picker.setSelectedIndex(1);
picker.setTextSize(11);
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
}
public void onConstellationPicker(View view) {
ConstellationPicker picker = new ConstellationPicker(this);
picker.setTopBackgroundColor(0xFFEEEEEE);
picker.setTopLineVisible(false);
picker.setCancelTextColor(0xFF33B5E5);
picker.setSubmitTextColor(0xFF33B5E5);
picker.setTextColor(0xFFFF0000, 0xFFCCCCCC);
picker.setLineColor(0xFFEE0000);
picker.setSelectedItem("射手");
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
}
public void onChineseZodiacPicker(View view) {
ChineseZodiacPicker picker = new ChineseZodiacPicker(this);
picker.setLineVisible(false);
picker.setSelectedItem("");
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
}
public void onNumberPicker(View view) {
NumberPicker picker = new NumberPicker(this);
picker.setOffset(2);//偏移量
picker.setRange(145, 200);//数字范围
picker.setSelectedItem(172);
picker.setLabel("厘米");
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
}
public void onSexPicker(View view) {
SexPicker picker = new SexPicker(this);
//picker.onlyMaleAndFemale();
picker.setOnOptionPickListener(new OptionPicker.OnOptionPickListener() {
@Override
public void onOptionPicked(String option) {
showToast(option);
}
});
picker.show();
}
public void onAddressPicker(View view) {
new AddressInitTask(this).execute("贵州", "毕节", "纳雍");
}
public void onAddress2Picker(View view) {
try {
ArrayList<AddressPicker.Province> data = new ArrayList<AddressPicker.Province>();
String json = AssetsUtils.readText(this, "city2.json");
data.addAll(JSON.parseArray(json, AddressPicker.Province.class));
AddressPicker picker = new AddressPicker(this, data);
picker.setHideProvince(true);
picker.setSelectedItem("贵州", "贵阳", "花溪");
picker.setOnAddressPickListener(new AddressPicker.OnAddressPickListener() {
@Override
public void onAddressPicked(String province, String city, String county) {
showToast(city + county);
}
});
picker.show();
} catch (Exception e) {
showToast(e.toString());
}
}
public void onAddress3Picker(View view) {
new AddressInitTask(this,true).execute("四川", "成都");
}
public void onColorPicker(View view) {
ColorPicker picker = new ColorPicker(this);
picker.setInitColor(0xDD00DD);
picker.setOnColorPickListener(new ColorPicker.OnColorPickListener() {
@Override
public void onColorPicked(int pickedColor) {
showToast(ConvertUtils.toColorString(pickedColor));
}
});
picker.show();
}
public void onFilePicker(View view) {
//noinspection MissingPermission
FilePicker picker = new FilePicker(this, FilePicker.FILE);
picker.setShowHideDir(false);
//picker.setAllowExtensions(new String[]{".apk"});
picker.setOnFilePickListener(new FilePicker.OnFilePickListener() {
@Override
public void onFilePicked(String currentPath) {
showToast(currentPath);
}
});
picker.show();
}
public void onDirPicker(View view) {
//noinspection MissingPermission
FilePicker picker = new FilePicker(this, FilePicker.DIRECTORY);
picker.setRootPath(StorageUtils.getRootPath(this) + "Download/");
picker.setOnFilePickListener(new FilePicker.OnFilePickListener() {
@Override
public void onFilePicked(String currentPath) {
showToast(currentPath);
}
});
picker.show();
}
public void onContact(View view) {
final ArrayList<String> minutes = get64minutes();
final ArrayList<String> hours = get24hours();
final ArrayList<String> dates = get7Date();
MyTimePicker myTimePicker=new MyTimePicker(this,dates,hours,minutes);
myTimePicker.setSelectedItem(0,0,0);
myTimePicker.setTitleText("选择时间");
myTimePicker.setTitleTextSize(16);
myTimePicker.setTopBackgroundColor(Color.parseColor("#FFEAEAEB"));
myTimePicker.setTextSize(21);
myTimePicker.setCancelText("取消");
myTimePicker.setSubmitText("完成");
myTimePicker.setSubmitTextColor(Color.parseColor("#F77B55"));
myTimePicker.setLineColor(Color.parseColor("#FFEAEAEB"));
myTimePicker.setTextColor(Color.parseColor("#000000"));
myTimePicker.show();
myTimePicker.setOnAddressPickListener(new MyTimePicker.OnAddressPickListener() {
@Override
public void onAddressPicked(String province, String city, String county) {
System.out.println("province="+province);
System.out.println("city="+city);
System.out.println("county="+county);
}
});
// Intent intent = new Intent(Intent.ACTION_SENDTO);
// intent.setData(Uri.parse("mailto:liyujiang_tk@yeah.net"));
// intent.putExtra(Intent.EXTRA_CC, new String[]
// {"1032694760@qq.com"});
// intent.putExtra(Intent.EXTRA_EMAIL, "");
// intent.putExtra(Intent.EXTRA_TEXT, "欢迎提供意您的见或建议");
// startActivity(Intent.createChooser(intent, "选择邮件客户端"));
}
private ArrayList<String> get7Date() {
ArrayList<String> dates = new ArrayList<String>();
for (int i = 0; i < 7; i++) {
dates.add(getStatetime(i));
}
return dates;
}
private ArrayList<String> get24hours() {
ArrayList<String> dates = new ArrayList<String>();
for (int i = 0; i < 24; i++) {
dates.add((i < 10 ? "0" + i : i) + "");
}
return dates;
}
private ArrayList<String> get64minutes() {
ArrayList<String> dates = new ArrayList<String>();
for (int i = 0; i < 60; i++) {
dates.add((i < 10 ? "0" + i : i) + "");
}
return dates;
}
private String getStatetime(int i) {
SimpleDateFormat sdf = new SimpleDateFormat("MM月dd日");
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE, i);
Date monday = c.getTime();
String preMonday = sdf.format(monday);
return preMonday;
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="1000"
android:fromYDelta="100%p"
android:toYDelta="0" />
<alpha
android:duration="500"
android:fromAlpha="0"
android:toAlpha="1" />
</set>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="1000"
android:fromYDelta="0"
android:toYDelta="50%p" />
<alpha
android:duration="500"
android:fromAlpha="1"
android:toAlpha="0" />
</set>

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onAnimationStyle"
android:text="窗口动画(基于XML)" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onAnimator"
android:text="窗口动画基于Java" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onYearMonthDayPicker"
android:text="年月日选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onYearMonthPicker"
android:text="年月选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onMonthDayPicker"
android:text="月日选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onTimePicker"
android:text="时间选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onOptionPicker"
android:text="单项选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onConstellationPicker"
android:text="星座选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onChineseZodiacPicker"
android:text="生肖选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onNumberPicker"
android:text="数字选择(如身高、体重、年龄)" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onSexPicker"
android:text="性别选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onAddressPicker"
android:text="地址选择(包括省级、地级、县级)" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onAddress2Picker"
android:text="地址选择(只包括地级、县级)" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onAddress3Picker"
android:text="地址选择(只包括省级、地级)" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onColorPicker"
android:text="颜色选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onFilePicker"
android:text="文件选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onDirPicker"
android:text="目录选择" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onContact"
android:text="建议收集" />
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="10dp"
android:background="#FFEEEEEE" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/picker_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="submit" />
<Button
android:id="@+id/picker_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="cancel"
android:textColor="#FFFF0000" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/picker_title"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:text="New Text" />
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">AndroidPicker</string>
</resources>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Animation.CustomPopup" parent="@android:style/Animation">
<item name="android:windowEnterAnimation">@anim/popup_in</item>
<item name="android:windowExitAnimation">@anim/popup_out</item>
</style>
</resources>

223
build.gradle Normal file
View File

@@ -0,0 +1,223 @@
//参考https://raw.github.com/dm77/barcodescanner/master/build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:${GRADLE_BUILD_VERSION}"
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:${GRADLE_BINTRAY_VERSION}"
}
}
allprojects {
group = PROJ_GROUP
version = PROJ_VERSION
repositories {
jcenter() //bintray的maven库
mavenCentral() //sonatype的maven库
mavenLocal() //本地maven库
flatDir {
dirs 'libs' //本地.aar文件
}
}
ext {
isLibrary = false
pomGroup = PROJ_GROUP
pomArtifactId = "library"
pomVersion = PROJ_VERSION
pomDescription = 'This is library description'
}
}
subprojects {
afterEvaluate { Project project ->
ext.pluginContainer = project.getPlugins()
def hasAppPlugin = ext.pluginContainer.hasPlugin("com.android.application")
def hasLibPlugin = ext.pluginContainer.hasPlugin("com.android.library")
if (hasAppPlugin || hasLibPlugin) {
android {
compileSdkVersion COMPILE_SDK_VERSION as int
buildToolsVersion BUILD_TOOL_VERSION
defaultConfig {
minSdkVersion MIN_SDK_VERSION as int
targetSdkVersion COMPILE_SDK_VERSION as int
versionCode VERSION_CODE as int
versionName VERSION_NAME
}
buildTypes {
release {
debuggable false
minifyEnabled false
proguardFile getDefaultProguardFile('proguard-android.txt')
}
debug {
debuggable true
minifyEnabled false
}
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES.txt'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/notice.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/dependencies.txt'
exclude 'META-INF/LGPL2.1'
exclude 'META-INF/ASL2.0'
}
}
dependencies {
compile "com.android.support:support-v4:${ANDROID_SUPPORT_VERSION}"
compile "com.android.support:support-annotations:${ANDROID_SUPPORT_VERSION}"
}
}
if (project.isLibrary) {
configure(project) {
// 这个脚本是用来发布库项目到jcenter
apply plugin: 'com.jfrog.bintray'
apply plugin: 'maven-publish'
version = project.pomVersion //版本号
group = PROJ_GROUP // 包名
project.archivesBaseName = project.pomArtifactId
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += configurations.compile
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
exclude '**/BuildConfig.java'
exclude '**/R.java'
failOnError = false
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
javadoc {
options {
encoding "UTF-8"
charSet 'UTF-8'
author true
version true
links "http://docs.oracle.com/javase/7/docs/api"
title project.pomArtifactId
}
}
publishing {
publications {
mavenJava(MavenPublication) {
artifactId project.pomArtifactId
artifact "${buildDir}/outputs/aar/${project.pomArtifactId}-release.aar"
artifact javadocJar
artifact sourcesJar
pom.withXml {
Node root = asNode()
root.appendNode('name', project.pomArtifactId)
root.appendNode('description', project.pomDescription)
root.appendNode('url', PROJ_WEBSITE_URL)
def issues = root.appendNode('issueManagement')
issues.appendNode('system', 'github')
issues.appendNode('url', PROJ_ISSUE_URL)
def scm = root.appendNode('scm')
scm.appendNode('url', PROJ_GIT_URL)
scm.appendNode('connection', "scm:git:${PROJ_GIT_URL}")
scm.appendNode('developerConnection', "scm:git:${PROJ_GIT_URL}")
def license = root.appendNode('licenses').appendNode('license')
license.appendNode('name', "The Apache Software License, Version 2.0")
license.appendNode('url', "http://www.apache.org/licenses/LICENSE-2.0.txt")
license.appendNode('distribution', "repo")
def developer = root.appendNode('developers').appendNode('developer')
developer.appendNode('id', DEVELOPER_ID)
developer.appendNode('name', DEVELOPER_NAME)
developer.appendNode('email', DEVELOPER_EMAIL)
def dependenciesNode = root.appendNode('dependencies')
configurations.compile.allDependencies.each {
if (it.group && it.name && it.version) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}
}
afterEvaluate {
publishing.publications.mavenJava.artifact(bundleRelease)
}
bintray {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
user = properties.getProperty("bintray.user")
key = properties.getProperty("bintray.apikey")
publications = ['mavenJava']
publish = true //是否发布
pkg {
repo = "maven" //上传的中央仓库名称
name = project.pomArtifactId //发布到中央仓库上的项目名字
desc = project.pomDescription
websiteUrl = PROJ_WEBSITE_URL //项目主页
issueTrackerUrl = PROJ_ISSUE_URL //项目讨论页
vcsUrl = PROJ_GIT_URL //项目GIT仓库
licenses = ["Apache-2.0"]
publicDownloadNumbers = true
version {
name = project.pomVersion
desc = project.pomDescription
gpg {
sign = true //是否GPG签名可使用Gpg4win创建密钥文件
passphrase = properties.getProperty("bintray.gpg.password")
//GPG签名所用密钥
}
mavenCentralSync {
sync = false //是否同步到Maven Central
user = properties.getProperty("sonatype.user") //sonatype用户名
password = properties.getProperty("sonatype.password")
//sonatype密码
close = '1'
}
}
}
}
}
}
}
}

24
gradle.properties Normal file
View File

@@ -0,0 +1,24 @@
#<23><>Ŀ·<C4BF><C2B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϴ˾<CFB4>
com.android.build.gradle.overridePathCheck=true
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#<23><><EFBFBD><EFBFBD><E6B6A8>һЩ<D2BB><D0A9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>gradle<6C>ű<EFBFBD>ʹ<EFBFBD><CAB9>
VERSION_NAME=1.1.0
VERSION_CODE=110
COMPILE_SDK_VERSION=23
MIN_SDK_VERSION=9
BUILD_TOOL_VERSION=23.0.1
ANDROID_SUPPORT_VERSION=23.1.1
GRADLE_BUILD_VERSION=1.5.0
GRADLE_BINTRAY_VERSION=1.5
#<23><><EFBFBD><EFBFBD><E6B6A8>һЩ<D2BB><D0A9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>gradle<6C>ű<EFBFBD>ʹ<EFBFBD><CAB9>
PROJ_GROUP=cn.qqtheme.framework
PROJ_VERSION=1.1.0
PROJ_WEBSITE_URL=https://github.com/gzu-liyujiang/AndroidPicker
PROJ_ISSUE_URL=https://github.com/gzu-liyujiang/AndroidPicker/issues
PROJ_GIT_URL=git@github.com/gzu-liyujiang/AndroidPicker.git
DEVELOPER_ID=gzu-liyujiang
DEVELOPER_NAME=liyujiang
DEVELOPER_EMAIL=liyujiang_tk@yeah.net

62
import-summary.txt Normal file
View File

@@ -0,0 +1,62 @@
ECLIPSE ANDROID PROJECT IMPORT SUMMARY
======================================
Ignored Files:
--------------
The following files were *not* copied into the new Gradle project; you
should evaluate whether these are still needed in your project and if
so manually move them:
* ic_launcher-web.png
* proguard-project.txt
Replaced Jars with Dependencies:
--------------------------------
The importer recognized the following .jar files as third party
libraries and replaced them with Gradle dependencies instead. This has
the advantage that more explicit version information is known, and the
libraries can be updated automatically. However, it is possible that
the .jar file in your project was of an older version than the
dependency we picked, which could render the project not compileable.
You can disable the jar replacement in the import wizard and try again:
android-support-v4.jar => com.android.support:support-v4:18.+
Moved Files:
------------
Android Gradle projects use a different directory structure than ADT
Eclipse projects. Here's how the projects were restructured:
* AndroidManifest.xml => 仿ios弹出框效果\src\main\AndroidManifest.xml
* assets\ => 仿ios弹出框效果\src\main\assets
* res\ => 仿ios弹出框效果\src\main\res\
* src\ => 仿ios弹出框效果\src\main\java\
Missing Android Support Repository:
-----------------------------------
Some useful libraries, such as the Android Support Library, are
installed from a special Maven repository, which should be installed
via the SDK manager.
It looks like this library is missing from your SDK installation at:
null
To install it, open the SDK manager, and in the Extras category,
select "Android Support Repository". You may also want to install the
"Google Repository" if you want to use libraries like Google Play
Services.
Next Steps:
-----------
You can now build the project. The Gradle project needs network
connectivity to download dependencies.
Bugs:
-----
If for some reason your project does not build, and you determine that
it is due to a bug or limitation of the Eclipse to Gradle importer,
please file a bug at http://b.android.com with category
Component-Tools.
(This import summary is for your information only, and can be deleted
after import once you are satisfied with the results.)

2
library/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
/*.iml

2
library/ColorPicker/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
/*.iml

View File

@@ -0,0 +1,11 @@
apply plugin: 'com.android.library'
ext {
isLibrary = true
pomArtifactId = "ColorPicker"
pomDescription = "color picker for android"
}
dependencies {
compile project(":library:Common")
}

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="cn.qqtheme.framework.colorpicker" />

View File

@@ -0,0 +1,192 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.text.InputType;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.Locale;
import cn.qqtheme.framework.colorpicker.R;
import cn.qqtheme.framework.popup.ConfirmPopup;
import cn.qqtheme.framework.util.CompatUtils;
import cn.qqtheme.framework.util.ConvertUtils;
import cn.qqtheme.framework.widget.ColorPanelView;
/**
* 颜色选择器。
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /9/29
*/
public class ColorPicker extends ConfirmPopup<LinearLayout> implements TextView.OnEditorActionListener {
private static final int MULTI_ID = 0x1;
private static final int BLACK_ID = 0x2;
private int initColor = Color.WHITE;
private ColorPanelView multiColorView, blackColorView;
private EditText hexValView;
private ColorStateList hexValDefaultColor;
private OnColorPickListener onColorPickListener;
/**
* Instantiates a new Color picker.
*
* @param activity the activity
*/
public ColorPicker(Activity activity) {
super(activity);
setHalfScreen(true);
}
@Override
@NonNull
protected LinearLayout makeCenterView() {
LinearLayout rootLayout = new LinearLayout(activity);
rootLayout.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
rootLayout.setOrientation(LinearLayout.VERTICAL);
blackColorView = new ColorPanelView(activity);
//noinspection ResourceType
blackColorView.setId(BLACK_ID);
blackColorView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, ConvertUtils.toPx(activity, 30)));
blackColorView.setPointerDrawable(CompatUtils.getDrawable(activity, R.drawable.color_picker_cursor_bottom));
blackColorView.setLockPointerInBounds(false);
blackColorView.setOnColorChangedListener(new ColorPanelView.OnColorChangedListener() {
@Override
public void onColorChanged(ColorPanelView view, int color) {
updateCurrentColor(color);
}
});
rootLayout.addView(blackColorView);
multiColorView = new ColorPanelView(activity);
//noinspection ResourceType
multiColorView.setId(MULTI_ID);
multiColorView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f));
multiColorView.setPointerDrawable(CompatUtils.getDrawable(activity, R.drawable.color_picker_cursor_top));
multiColorView.setLockPointerInBounds(true);
multiColorView.setOnColorChangedListener(new ColorPanelView.OnColorChangedListener() {
@Override
public void onColorChanged(ColorPanelView view, int color) {
updateCurrentColor(color);
}
});
rootLayout.addView(multiColorView);
LinearLayout previewLayout = new LinearLayout(activity);
previewLayout.setOrientation(LinearLayout.HORIZONTAL);
previewLayout.setGravity(Gravity.CENTER);
previewLayout.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, ConvertUtils.toPx(activity, 30)));
hexValView = new EditText(activity);
hexValView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
hexValView.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
hexValView.setImeOptions(EditorInfo.IME_ACTION_DONE);
hexValView.setGravity(Gravity.CENTER);
hexValView.setBackgroundColor(initColor);
hexValView.setTextColor(Color.BLACK);
hexValView.setShadowLayer(3, 0, 2, Color.WHITE);//设置阴影,以便背景色为黑色系列时仍然看得见
hexValView.setMinEms(6);
hexValView.setMaxEms(8);
hexValView.setPadding(0, 0, 0, 0);
hexValView.setSingleLine(true);
hexValView.setOnEditorActionListener(this);
hexValDefaultColor = hexValView.getTextColors();
previewLayout.addView(hexValView);
rootLayout.addView(previewLayout);
return rootLayout;
}
@Override
protected void setContentViewAfter(View contentView) {
multiColorView.setColor(initColor);//将触发onColorChanged故必须先待其他控件初始化完成后才能调用
multiColorView.setBrightnessGradientView(blackColorView);
}
@Override
protected void onSubmit() {
if (onColorPickListener != null) {
onColorPickListener.onColorPicked(getCurrentColor());
}
}
/**
* Gets current color.
*
* @return the current color
*/
@ColorInt
public int getCurrentColor() {
return Color.parseColor("#" + hexValView.getText());
}
private void updateCurrentColor(int color) {
String hexColorString = ConvertUtils.toColorString(color, false).toUpperCase(Locale.getDefault());
hexValView.setText(hexColorString);
hexValView.setTextColor(hexValDefaultColor);
hexValView.setBackgroundColor(color);
}
/**
* Sets init color.
*
* @param initColor the init color
*/
public void setInitColor(int initColor) {
this.initColor = initColor;
}
/**
* Sets on color pick listener.
*
* @param onColorPickListener the on color pick listener
*/
public void setOnColorPickListener(OnColorPickListener onColorPickListener) {
this.onColorPickListener = onColorPickListener;
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
String hexString = hexValView.getText().toString();
int length = hexString.length();
if (length == 6 || length == 8) {
try {
int color = Color.parseColor("#" + hexString);
multiColorView.setColor(color);
hexValView.setTextColor(hexValDefaultColor);
} catch (IllegalArgumentException e) {
hexValView.setTextColor(Color.RED);
}
} else {
hexValView.setTextColor(Color.RED);
}
return true;
}
return false;
}
/**
* The interface On color pick listener.
*/
public interface OnColorPickListener {
/**
* On color picked.
*
* @param pickedColor the picked color
*/
void onColorPicked(@ColorInt int pickedColor);
}
}

View File

@@ -0,0 +1,553 @@
package cn.qqtheme.framework.widget;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ComposeShader;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 颜色选择面板
*
* @author 李玉江[QQ :1023694760]
* @version 2015/7/20
* @link https ://github.com/jbruchanov/AndroidColorPicker
*/
public class ColorPanelView extends View {
private static final int[] GRAD_COLORS = new int[]{Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.RED};
private static final int[] GRAD_ALPHA = new int[]{Color.WHITE, Color.TRANSPARENT};
private ColorPanelView mBrightnessGradientView;
private Shader mShader;
private Drawable mPointerDrawable;
private Paint mPaint;
private Paint mPaintBackground;
private RectF mGradientRect = new RectF();
private float[] mHSV = new float[]{1f, 1f, 1f};
private int[] mSelectedColorGradient = new int[]{0, Color.BLACK};
private float mRadius = 0;
private int mSelectedColor = 0;
private boolean mIsBrightnessGradient = false;
private int mLastX = Integer.MIN_VALUE;
private int mLastY;
private int mPointerHeight;
private int mPointerWidth;
private boolean mLockPointerInBounds = false;
private OnColorChangedListener mOnColorChangedListener;
/**
* The interface On color changed listener.
*/
public interface OnColorChangedListener {
/**
* On color changed.
*
* @param view the view
* @param color the color
*/
void onColorChanged(ColorPanelView view, int color);
}
/**
* Instantiates a new Color panel view.
*
* @param context the context
*/
public ColorPanelView(Context context) {
super(context);
init();
}
/**
* Instantiates a new Color panel view.
*
* @param context the context
* @param attrs the attrs
*/
public ColorPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
/**
* Instantiates a new Color panel view.
*
* @param context the context
* @param attrs the attrs
* @param defStyleAttr the def style attr
*/
public ColorPanelView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void init() {
setClickable(true);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintBackground = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintBackground.setColor(Color.WHITE);
if (Build.VERSION.SDK_INT >= 11) {
setLayerType(View.LAYER_TYPE_SOFTWARE, isInEditMode() ? null : mPaint);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = 0;
int desiredHeight = 0;
if (mPointerDrawable != null) {
desiredHeight = mPointerDrawable.getIntrinsicHeight();
//this is nonsense, but at least have something than 0
desiredWidth = mPointerDrawable.getIntrinsicWidth();
}
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(desiredWidth, widthSize);
} else {
//Be whatever you want
width = desiredWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(desiredHeight, heightSize);
} else {
//Be whatever you want
height = desiredHeight;
}
//MUST CALL THIS
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mShader != null) {
canvas.drawRoundRect(mGradientRect, mRadius, mRadius, mPaintBackground);
canvas.drawRoundRect(mGradientRect, mRadius, mRadius, mPaint);
}
onDrawPointer(canvas);
}
private void onDrawPointer(Canvas canvas) {
if (mPointerDrawable != null) {
int vh = getHeight();
int pwh = mPointerWidth >> 1;
int phh = mPointerHeight >> 1;
float tx, ty;
if (!mIsBrightnessGradient) {
tx = mLastX - pwh;
ty = mLastY - phh;
if (mLockPointerInBounds) {
tx = Math.max(mGradientRect.left, Math.min(tx, mGradientRect.right - mPointerWidth));
ty = Math.max(mGradientRect.top, Math.min(ty, mGradientRect.bottom - mPointerHeight));
} else {
tx = Math.max(mGradientRect.left - pwh, Math.min(tx, mGradientRect.right - pwh));
ty = Math.max(mGradientRect.top - pwh, Math.min(ty, mGradientRect.bottom - phh));
}
} else {//vertical lock
tx = mLastX - pwh;
ty = mPointerHeight != mPointerDrawable.getIntrinsicHeight() ? (vh >> 1) - phh : 0;
if (mLockPointerInBounds) {
tx = Math.max(mGradientRect.left, Math.min(tx, mGradientRect.right - mPointerWidth));
ty = Math.max(mGradientRect.top, Math.min(ty, mGradientRect.bottom - mPointerHeight));
} else {
tx = Math.max(mGradientRect.left - pwh, Math.min(tx, mGradientRect.right - pwh));
ty = Math.max(mGradientRect.top - pwh, Math.min(ty, mGradientRect.bottom - phh));
}
}
canvas.translate(tx, ty);
mPointerDrawable.draw(canvas);
canvas.translate(-tx, -ty);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mGradientRect.set(getPaddingLeft(), getPaddingTop(), right - left - getPaddingRight(), bottom - top - getPaddingBottom());
if (changed) {
buildShader();
}
if (mPointerDrawable != null) {
int h = (int) mGradientRect.height();
int ph = mPointerDrawable.getIntrinsicHeight();
int pw = mPointerDrawable.getIntrinsicWidth();
mPointerHeight = ph;
mPointerWidth = pw;
if (h < ph) {
mPointerHeight = h;
mPointerWidth = (int) (pw * (h / (float) ph));
}
mPointerDrawable.setBounds(0, 0, mPointerWidth, mPointerHeight);
updatePointerPosition();
}
}
private void buildShader() {
if (mIsBrightnessGradient) {
mShader = new LinearGradient(mGradientRect.left, mGradientRect.top, mGradientRect.right, mGradientRect.top, mSelectedColorGradient, null, Shader.TileMode.CLAMP);
} else {
LinearGradient gradientShader = new LinearGradient(mGradientRect.left, mGradientRect.top, mGradientRect.right, mGradientRect.top, GRAD_COLORS, null, Shader.TileMode.CLAMP);
LinearGradient alphaShader = new LinearGradient(0, mGradientRect.top + (mGradientRect.height() / 3), 0, mGradientRect.bottom, GRAD_ALPHA, null, Shader.TileMode.CLAMP);
mShader = new ComposeShader(alphaShader, gradientShader, PorterDuff.Mode.MULTIPLY);
}
mPaint.setShader(mShader);
}
/**
* Set radius for gradient rectangle
*
* @param radius the radius
*/
public void setRadius(float radius) {
if (radius != mRadius) {
mRadius = radius;
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mLastX = (int) event.getX();
mLastY = (int) event.getY();
onUpdateColorSelection(mLastX, mLastY);
invalidate();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return super.onTouchEvent(event);
}
/**
* Update color based on touch events
*
* @param x the x
* @param y the y
*/
protected void onUpdateColorSelection(int x, int y) {
x = (int) Math.max(mGradientRect.left, Math.min(x, mGradientRect.right));
y = (int) Math.max(mGradientRect.top, Math.min(y, mGradientRect.bottom));
if (mIsBrightnessGradient) {
float b = pointToValueBrightness(x);
mHSV[2] = b;
mSelectedColor = Color.HSVToColor(mHSV);
} else {
float hue = pointToHue(x);
float sat = pointToSaturation(y);
mHSV[0] = hue;
mHSV[1] = sat;
mHSV[2] = 1f;
mSelectedColor = Color.HSVToColor(mHSV);
}
dispatchColorChanged(mSelectedColor);
}
/**
* Dispatch color changed.
*
* @param color the color
*/
protected void dispatchColorChanged(int color) {
if (mBrightnessGradientView != null) {
mBrightnessGradientView.setColor(color, false);
}
if (mOnColorChangedListener != null) {
mOnColorChangedListener.onColorChanged(this, color);
}
}
/**
* Switch view into brightness gradient only
*
* @param isBrightnessGradient the is brightness gradient
*/
public void setIsBrightnessGradient(boolean isBrightnessGradient) {
mIsBrightnessGradient = isBrightnessGradient;
}
/**
* Add reference for brightness view
*
* @param brightnessGradient the brightness gradient
*/
public void setBrightnessGradientView(ColorPanelView brightnessGradient) {
if (mBrightnessGradientView != brightnessGradient) {
mBrightnessGradientView = brightnessGradient;
if (mBrightnessGradientView != null) {
mBrightnessGradientView.setIsBrightnessGradient(true);
mBrightnessGradientView.setColor(mSelectedColor);
}
}
}
/**
* Get current selectec color
*
* @return selected color
*/
public int getSelectedColor() {
return mSelectedColor;
}
/**
* Update current color
*
* @param selectedColor the selected color
*/
public void setColor(int selectedColor) {
setColor(selectedColor, true);
}
/**
* Sets color.
*
* @param selectedColor the selected color
* @param updatePointers the update pointers
*/
protected void setColor(int selectedColor, boolean updatePointers) {
Color.colorToHSV(selectedColor, mHSV);
if (mIsBrightnessGradient) {
mSelectedColorGradient[0] = getColorForGradient(mHSV);
mSelectedColor = Color.HSVToColor(mHSV);
buildShader();
if (mLastX != Integer.MIN_VALUE) {
mHSV[2] = pointToValueBrightness(mLastX);
}
selectedColor = Color.HSVToColor(mHSV);
}
if (updatePointers) {
updatePointerPosition();
}
mSelectedColor = selectedColor;
invalidate();
dispatchColorChanged(mSelectedColor);
}
/**
* Get start color for gradient
*
* @param hsv
* @return
*/
private int getColorForGradient(float[] hsv) {
if (hsv[2] != 1f) {
float oldV = hsv[2];
hsv[2] = 1;
int color = Color.HSVToColor(hsv);
hsv[2] = oldV;
return color;
} else {
return Color.HSVToColor(hsv);
}
}
private void updatePointerPosition() {
if (mGradientRect.width() != 0 && mGradientRect.height() != 0) {
if (!mIsBrightnessGradient) {
mLastX = hueToPoint(mHSV[0]);
mLastY = saturationToPoint(mHSV[1]);
} else {
mLastX = brightnessToPoint(mHSV[2]);
}
}
}
/**
* Sets on color changed listener.
*
* @param onColorChangedListener the on color changed listener
*/
public void setOnColorChangedListener(OnColorChangedListener onColorChangedListener) {
mOnColorChangedListener = onColorChangedListener;
}
//region HSL math
/**
* @param x x coordinate of gradient
* @return
*/
private float pointToHue(float x) {
x = x - mGradientRect.left;
return x * 360f / mGradientRect.width();
}
private int hueToPoint(float hue) {
return (int) (mGradientRect.left + ((hue * mGradientRect.width()) / 360));
}
/**
* Get saturation
*
* @param y
* @return
*/
private float pointToSaturation(float y) {
y = y - mGradientRect.top;
return 1 - (1.f / mGradientRect.height() * y);
}
private int saturationToPoint(float sat) {
sat = 1 - sat;
return (int) (mGradientRect.top + (mGradientRect.height() * sat));
}
/**
* Get value of brightness
*
* @param x
* @return
*/
private float pointToValueBrightness(float x) {
x = x - mGradientRect.left;
return 1 - (1.f / mGradientRect.width() * x);
}
private int brightnessToPoint(float val) {
val = 1 - val;
return (int) (mGradientRect.left + (mGradientRect.width() * val));
}
//endregion HSL math
/**
* Sets pointer drawable.
*
* @param pointerDrawable the pointer drawable
*/
public void setPointerDrawable(Drawable pointerDrawable) {
if (mPointerDrawable != pointerDrawable) {
mPointerDrawable = pointerDrawable;
requestLayout();
}
}
/**
* Sets lock pointer in bounds.
*
* @param lockPointerInBounds the lock pointer in bounds
*/
public void setLockPointerInBounds(boolean lockPointerInBounds) {
if (lockPointerInBounds != mLockPointerInBounds) {
mLockPointerInBounds = lockPointerInBounds;
invalidate();
}
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.isBrightnessGradient = mIsBrightnessGradient;
ss.color = mSelectedColor;
return ss;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
mIsBrightnessGradient = ss.isBrightnessGradient;
setColor(ss.color, true);
}
private static class SavedState extends BaseSavedState {
/**
* The Color.
*/
int color;
/**
* The Is brightness gradient.
*/
boolean isBrightnessGradient;
/**
* Instantiates a new Saved state.
*
* @param superState the super state
*/
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
color = in.readInt();
isBrightnessGradient = in.readInt() == 1;
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(color);
out.writeInt(isBrightnessGradient ? 1 : 0);
}
/**
* The constant CREATOR.
*/
//required field that makes Parcelables from a Parcel
public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

2
library/Common/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
/*.iml

View File

@@ -0,0 +1,14 @@
apply plugin: 'com.android.library'
ext {
isLibrary = true
pomArtifactId = "Common"
pomDescription = "Common of android picker"
}
dependencies {
compile "com.android.support:support-v4:${BUILD_TOOL_VERSION}"
compile "com.android.support:support-annotations:${BUILD_TOOL_VERSION}"
}

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="cn.qqtheme.framework" />

View File

@@ -0,0 +1,39 @@
package cn.qqtheme.framework;
/**
* ************************************************************************
* ** _oo0oo_ **
* ** o8888888o **
* ** 88" . "88 **
* ** (| -_- |) **
* ** 0\ = /0 **
* ** ___/'---'\___ **
* ** .' \\\| |// '. **
* ** / \\\||| : |||// \\ **
* ** / _ ||||| -:- |||||- \\ **
* ** | | \\\\ - /// | | **
* ** | \_| ''\---/'' |_/ | **
* ** \ .-\__ '-' __/-. / **
* ** ___'. .' /--.--\ '. .'___ **
* ** ."" '< '.___\_<|>_/___.' >' "". **
* ** | | : '- \'.;'\ _ /';.'/ - ' : | | **
* ** \ \ '_. \_ __\ /__ _/ .-' / / **
* ** ====='-.____'.___ \_____/___.-'____.-'===== **
* ** '=---=' **
* ************************************************************************
* ** 佛祖保佑 镇类之宝 **
* ************************************************************************
*
* @author 李玉江[QQ :1032694760]
* @version 2014-09-05 11:49
*/
public class AppConfig {
/**
* The constant DEBUG_ENABLE.
*/
public static final boolean DEBUG_ENABLE = BuildConfig.DEBUG;// 是否调试模式
/**
* The constant DEBUG_TAG.
*/
public static final String DEBUG_TAG = "liyujiang";// LogCat的标记
}

View File

@@ -0,0 +1,231 @@
package cn.qqtheme.framework.popup;
import android.app.Activity;
import android.content.DialogInterface;
import android.support.annotation.CallSuper;
import android.support.annotation.StyleRes;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import cn.qqtheme.framework.util.ScreenUtils;
import cn.qqtheme.framework.util.LogUtils;
/**
* 底部弹窗基类
*
* @param <V> the type parameter
* @author 李玉江[QQ :1023694760]
* @version 2015/7/19
*/
public abstract class BottomPopup<V extends View> implements DialogInterface.OnKeyListener {
/**
* The constant MATCH_PARENT.
*/
public static final int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
/**
* The constant WRAP_CONTENT.
*/
public static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
protected Activity activity;
protected int screenWidthPixels;
protected int screenHeightPixels;
private Popup popup;
private int width = 0, height = 0;
private boolean isFillScreen = false;
private boolean isHalfScreen = false;
/**
* Instantiates a new Bottom popup.
*
* @param activity the activity
*/
public BottomPopup(Activity activity) {
this.activity = activity;
DisplayMetrics displayMetrics = ScreenUtils.displayMetrics(activity);
screenWidthPixels = displayMetrics.widthPixels;
screenHeightPixels = displayMetrics.heightPixels;
popup = new Popup(activity);
popup.setOnKeyListener(this);
}
/**
* Gets view.
*
* @return the view
*/
protected abstract V makeContentView();
/**
* 弹出窗显示之前调用
*/
private void onShowPrepare() {
setContentViewBefore();
V view = makeContentView();
popup.setContentView(view);// 设置弹出窗体的布局
setContentViewAfter(view);
LogUtils.debug("do something before popup show");
if (width == 0 && height == 0) {
//未明确指定宽高,优先考虑全屏再考虑半屏然后再考虑包裹内容
width = screenWidthPixels;
if (isFillScreen) {
height = MATCH_PARENT;
} else if (isHalfScreen) {
height = screenHeightPixels / 2;
} else {
height = WRAP_CONTENT;
}
}
popup.setSize(width, height);
}
/**
* 固定高度为屏幕的高
*
* @param fillScreen the fill screen
*/
public void setFillScreen(boolean fillScreen) {
isFillScreen = fillScreen;
}
/**
* 固定高度为屏幕的一半
*
* @param halfScreen the half screen
*/
public void setHalfScreen(boolean halfScreen) {
isHalfScreen = halfScreen;
}
/**
* Sets content view before.
*/
protected void setContentViewBefore() {
}
/**
* Sets content view after.
*
* @param contentView the content view
*/
protected void setContentViewAfter(V contentView) {
}
/**
* Sets animation.
*
* @param animRes the anim res
*/
public void setAnimationStyle(@StyleRes int animRes) {
popup.setAnimationStyle(animRes);
}
/**
* Sets on dismiss listener.
*
* @param onDismissListener the on dismiss listener
*/
public void setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) {
popup.setOnDismissListener(onDismissListener);
LogUtils.debug("popup setOnDismissListener");
}
/**
* Sets size.
*
* @param width the width
* @param height the height
*/
public void setSize(int width, int height) {
// fixed: 2016/1/26 修复显示之前设置宽高无效问题
this.width = width;
this.height = height;
}
/**
* Sets width.
*
* @param width the width
* @see #setSize(int, int) #setSize(int, int)
*/
public void setWidth(int width) {
this.width = width;
}
/**
* Sets height.
*
* @param height the height
* @see #setSize(int, int) #setSize(int, int)
*/
public void setHeight(int height) {
this.height = height;
}
/**
* Is showing boolean.
*
* @return the boolean
*/
public boolean isShowing() {
return popup.isShowing();
}
/**
* Show.
*/
@CallSuper
public void show() {
onShowPrepare();
popup.show();
LogUtils.debug("popup show");
}
/**
* Dismiss.
*/
public void dismiss() {
popup.dismiss();
LogUtils.debug("popup dismiss");
}
/**
* On key down boolean.
*
* @param keyCode the key code
* @param event the event
* @return the boolean
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
return false;
}
@Override
public final boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
return onKeyDown(keyCode, event);
}
return false;
}
/**
* Gets window.
*
* @return the window
*/
public Window getWindow() {
return popup.getWindow();
}
/**
* Gets root view.
*
* @return the root view
*/
public ViewGroup getRootView() {
return popup.getRootView();
}
}

View File

@@ -0,0 +1,311 @@
package cn.qqtheme.framework.popup;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import cn.qqtheme.framework.util.ConvertUtils;
/**
* 带确定及取消按钮的
*
* @param <V> the type parameter
* @author 李玉江[QQ:1032694760]
* @since 2015/10/21
*/
public abstract class ConfirmPopup<V extends View> extends BottomPopup<View> {
protected boolean topLineVisible = true;
protected int topLineColor = 0xFFDDDDDD;
protected int topBackgroundColor = Color.WHITE;
protected boolean cancelVisible = true;
protected CharSequence cancelText = "";
protected CharSequence submitText = "";
protected CharSequence titleText = "";
protected int cancelTextColor = Color.BLACK;
protected int submitTextColor = Color.BLACK;
protected int titleTextColor = Color.BLACK;
protected int textSize = 18;
/**
* Instantiates a new Confirm popup.
*
* @param activity the activity
*/
public ConfirmPopup(Activity activity) {
super(activity);
cancelText = activity.getString(android.R.string.cancel);
submitText = activity.getString(android.R.string.ok);
}
/**
* Sets top line color.
*
* @param topLineColor the top line color
*/
public void setTopLineColor(@ColorInt int topLineColor) {
this.topLineColor = topLineColor;
}
/**
* Sets top background color.
*
* @param topBackgroundColor the top background color
*/
public void setTopBackgroundColor(@ColorInt int topBackgroundColor) {
this.topBackgroundColor = topBackgroundColor;
}
/**
* Sets top line visible.
*
* @param topLineVisible the top line visible
*/
public void setTopLineVisible(boolean topLineVisible) {
this.topLineVisible = topLineVisible;
}
/**
* Sets cancel visible.
*
* @param cancelVisible the cancel visible
*/
public void setCancelVisible(boolean cancelVisible) {
this.cancelVisible = cancelVisible;
}
/**
* Sets cancel text.
*
* @param cancelText the cancel text
*/
public void setCancelText(CharSequence cancelText) {
this.cancelText = cancelText;
}
/**
* Sets cancel text.
*
* @param textRes the text res
*/
public void setCancelText(@StringRes int textRes) {
this.cancelText = activity.getString(textRes);
}
/**
* Sets submit text.
*
* @param submitText the submit text
*/
public void setSubmitText(CharSequence submitText) {
this.submitText = submitText;
}
/**
* Sets submit text.
*
* @param textRes the text res
*/
public void setSubmitText(@StringRes int textRes) {
this.submitText = activity.getString(textRes);
}
/**
* Sets title text.
*
* @param titleText the title text
*/
public void setTitleText(CharSequence titleText) {
this.titleText = titleText;
}
/**
* Sets title text.
*
* @param textSize the title text
*/
public void setTitleTextSize(int textSize) {
this.textSize = textSize;
}
/**
* Sets title text.
*
* @param textRes the text res
*/
public void setTitleText(@StringRes int textRes) {
this.titleText = activity.getString(textRes);
}
/**
* Sets cancel text color.
*
* @param cancelTextColor the cancel text color
*/
public void setCancelTextColor(@ColorInt int cancelTextColor) {
this.cancelTextColor = cancelTextColor;
}
/**
* Sets submit text color.
*
* @param submitTextColor the submit text color
*/
public void setSubmitTextColor(@ColorInt int submitTextColor) {
this.submitTextColor = submitTextColor;
}
/**
* Sets title text color.
*
* @param titleTextColor the title text color
*/
public void setTitleTextColor(@ColorInt int titleTextColor) {
this.titleTextColor = titleTextColor;
}
/**
* @see #makeHeaderView()
* @see #makeCenterView()
* @see #makeFooterView()
*/
@Override
protected final View makeContentView() {
LinearLayout rootLayout = new LinearLayout(activity);
rootLayout.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
rootLayout.setBackgroundColor(Color.WHITE);
rootLayout.setOrientation(LinearLayout.VERTICAL);
rootLayout.setGravity(Gravity.CENTER);
rootLayout.setPadding(0, 0, 0, 0);
rootLayout.setClipToPadding(false);
View headerView = makeHeaderView();
if (headerView != null) {
rootLayout.addView(headerView);
}
if (topLineVisible) {
View lineView = new View(activity);
lineView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 1));
lineView.setBackgroundColor(topLineColor);
rootLayout.addView(lineView);
}
rootLayout.addView(makeCenterView(), new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f));
View footerView = makeFooterView();
if (footerView != null) {
rootLayout.addView(footerView);
}
return rootLayout;
}
/**
* Make header view view.
*
* @return the view
*/
@Nullable
protected View makeHeaderView() {
RelativeLayout topButtonLayout = new RelativeLayout(activity);
topButtonLayout.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, ConvertUtils.toPx(activity, 40)));
topButtonLayout.setBackgroundColor(topBackgroundColor);
topButtonLayout.setGravity(Gravity.CENTER_VERTICAL);
Button cancelButton = new Button(activity);
cancelButton.setVisibility(cancelVisible ? View.VISIBLE : View.GONE);
RelativeLayout.LayoutParams cancelButtonLayoutParams = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
cancelButtonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
cancelButtonLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
cancelButton.setLayoutParams(cancelButtonLayoutParams);
cancelButton.setBackgroundColor(Color.TRANSPARENT);
cancelButton.setGravity(Gravity.CENTER);
if (!TextUtils.isEmpty(cancelText)) {
cancelButton.setText(cancelText);
}
cancelButton.setTextColor(cancelTextColor);
cancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
onCancel();
}
});
topButtonLayout.addView(cancelButton);
TextView titleView = new TextView(activity);
RelativeLayout.LayoutParams titleLayoutParams = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
int margin = ConvertUtils.toPx(activity, 20);
titleLayoutParams.leftMargin = margin;
titleLayoutParams.rightMargin = margin;
titleLayoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
titleLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
titleView.setLayoutParams(titleLayoutParams);
titleView.setGravity(Gravity.CENTER);
if (!TextUtils.isEmpty(titleText)) {
titleView.setText(titleText);
titleView.setTextSize(textSize);
}
titleView.setTextColor(titleTextColor);
topButtonLayout.addView(titleView);
Button submitButton = new Button(activity);
RelativeLayout.LayoutParams submitButtonLayoutParams = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
submitButtonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
submitButtonLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
submitButton.setLayoutParams(submitButtonLayoutParams);
submitButton.setBackgroundColor(Color.TRANSPARENT);
submitButton.setGravity(Gravity.CENTER);
if (!TextUtils.isEmpty(submitText)) {
submitButton.setText(submitText);
}
submitButton.setTextColor(submitTextColor);
submitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
onSubmit();
}
});
topButtonLayout.addView(submitButton);
return topButtonLayout;
}
/**
* Init center view v.
*
* @return the v
*/
@NonNull
protected abstract V makeCenterView();
/**
* Make footer view view.
*
* @return the view
*/
@Nullable
protected View makeFooterView() {
return null;
}
/**
* On submit.
*/
protected void onSubmit() {
}
/**
* On cancel.
*/
protected void onCancel() {
}
}

View File

@@ -0,0 +1,172 @@
package cn.qqtheme.framework.popup;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.annotation.CallSuper;
import android.support.annotation.StyleRes;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;
import cn.qqtheme.framework.R;
import cn.qqtheme.framework.util.LogUtils;
/**
* 弹窗
*
* @author 李玉江[QQ :1023694760]
* @version 2015 -10-19
* @see android.widget.PopupWindow
*/
public class Popup {
private android.app.Dialog dialog;
private FrameLayout contentLayout;
/**
* Instantiates a new Popup.
*
* @param context the context
*/
public Popup(Context context) {
init(context);
}
private void init(Context context) {
contentLayout = new FrameLayout(context);
contentLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
contentLayout.setFocusable(true);
contentLayout.setFocusableInTouchMode(true);
dialog = new android.app.Dialog(context);
dialog.setCanceledOnTouchOutside(true);//触摸屏幕取消窗体
dialog.setCancelable(true);//按返回键取消窗体
Window window = dialog.getWindow();
window.setGravity(Gravity.BOTTOM);//位于屏幕底部
window.setWindowAnimations(R.style.Animation_Popup);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
//android.util.AndroidRuntimeException: requestFeature() must be called before adding content
window.requestFeature(Window.FEATURE_NO_TITLE);
window.setContentView(contentLayout);
}
/**
* Gets context.
*
* @return the context
*/
public Context getContext() {
return contentLayout.getContext();
}
/**
* Sets animation.
*
* @param animRes the anim res
*/
public void setAnimationStyle(@StyleRes int animRes) {
Window window = dialog.getWindow();
window.setWindowAnimations(animRes);
}
/**
* Is showing boolean.
*
* @return the boolean
*/
public boolean isShowing() {
return dialog.isShowing();
}
/**
* Show.
*/
@CallSuper
public void show() {
dialog.show();
}
/**
* Dismiss.
*/
@CallSuper
public void dismiss() {
dialog.dismiss();
}
/**
* Sets content view.
*
* @param view the view
*/
public void setContentView(View view) {
contentLayout.removeAllViews();
contentLayout.addView(view);
}
/**
* Gets content view.
*
* @return the content view
*/
public View getContentView() {
return contentLayout.getChildAt(0);
}
/**
* Sets size.
*
* @param width the width
* @param height the height
*/
public void setSize(int width, int height) {
LogUtils.debug(String.format("will set popup width/height to: %s/%s", width, height));
ViewGroup.LayoutParams params = contentLayout.getLayoutParams();
if (params == null) {
params = new ViewGroup.LayoutParams(width, height);
} else {
params.width = width;
params.height = height;
}
contentLayout.setLayoutParams(params);
}
/**
* Sets on dismiss listener.
*
* @param onDismissListener the on dismiss listener
*/
public void setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) {
dialog.setOnDismissListener(onDismissListener);
}
/**
* Sets on key listener.
*
* @param onKeyListener the on key listener
*/
public void setOnKeyListener(DialogInterface.OnKeyListener onKeyListener) {
dialog.setOnKeyListener(onKeyListener);
}
/**
* Gets window.
*
* @return the window
*/
public Window getWindow() {
return dialog.getWindow();
}
/**
* Gets root view.
*
* @return the root view
*/
public ViewGroup getRootView() {
return contentLayout;
}
}

View File

@@ -0,0 +1,106 @@
package cn.qqtheme.framework.util;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import android.support.annotation.StyleRes;
import android.view.View;
import android.widget.TextView;
/**
* 兼容旧版&新版
*
* @author 李玉江[QQ :1032694760]
* @since 2015 /10/19 Created By Android Studio
*/
public class CompatUtils {
/**
* Sets background.
*
* @param view the view
* @param drawable the drawable
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static void setBackground(View view, Drawable drawable) {
if (Build.VERSION.SDK_INT < 16) {
//noinspection deprecation
view.setBackgroundDrawable(drawable);
} else {
view.setBackground(drawable);
}
}
/**
* Sets text appearance.
*
* @param view the view
* @param appearanceRes the appearance res
*/
@TargetApi(Build.VERSION_CODES.M)
public static void setTextAppearance(TextView view, @StyleRes int appearanceRes) {
if (Build.VERSION.SDK_INT < 23) {
//noinspection deprecation
view.setTextAppearance(view.getContext(), appearanceRes);
} else {
view.setTextAppearance(appearanceRes);
}
}
/**
* Gets drawable.
*
* @param context the context
* @param drawableRes the drawable res
* @return the drawable
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static Drawable getDrawable(Context context, @DrawableRes int drawableRes) {
if (Build.VERSION.SDK_INT < 21) {
//noinspection deprecation
return context.getResources().getDrawable(drawableRes);
} else {
return context.getDrawable(drawableRes);
}
}
/**
* Gets string.
*
* @param context the context
* @param stringRes the string res
* @return the string
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static String getString(Context context, @StringRes int stringRes) {
if (Build.VERSION.SDK_INT < 21) {
//noinspection deprecation
return context.getResources().getString(stringRes);
} else {
return context.getString(stringRes);
}
}
/**
* Gets color.
*
* @param context the context
* @param colorRes the color res
* @return the color
*/
@ColorInt
public static int getColor(Context context, @ColorRes int colorRes) {
if (Build.VERSION.SDK_INT < 21) {
//noinspection deprecation
return context.getResources().getColor(colorRes);
} else {
return context.getResources().getColor(colorRes, null);
}
}
}

View File

@@ -0,0 +1,925 @@
package cn.qqtheme.framework.util;
import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.*;
import android.graphics.drawable.*;
import android.graphics.drawable.shapes.RoundRectShape;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.View;
import android.widget.ListView;
import android.widget.ScrollView;
import java.io.*;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Arrays;
/**
* 数据类型转换、单位转换
*
* @author 李玉江[QQ :1023694760]
* @version 2014 -4-18
*/
public class ConvertUtils {
/**
* The constant GB.
*/
public static final long GB = 1073741824;
/**
* The constant MB.
*/
public static final long MB = 1048576;
/**
* The constant KB.
*/
public static final long KB = 1024;
/**
* To int int.
*
* @param obj the obj
* @return the int
*/
public static int toInt(Object obj) {
try {
return Integer.parseInt(obj.toString());
} catch (Exception e) {
return -1;
}
}
/**
* To int int.
*
* @param bytes the bytes
* @return the int
*/
public static int toInt(byte[] bytes) {
int result = 0;
byte abyte;
for (int i = 0; i < bytes.length; i++) {
abyte = bytes[i];
result += (abyte & 0xFF) << (8 * i);
}
return result;
}
/**
* To short int.
*
* @param first the first
* @param second the second
* @return the int
*/
public static int toShort(byte first, byte second) {
return (first << 8) + (second & 0xFF);
}
/**
* To long long.
*
* @param obj the obj
* @return the long
*/
public static long toLong(Object obj) {
try {
return Long.parseLong(obj.toString());
} catch (Exception e) {
return -1L;
}
}
/**
* To float float.
*
* @param obj the obj
* @return the float
*/
public static float toFloat(Object obj) {
try {
return Float.parseFloat(obj.toString());
} catch (Exception e) {
return -1f;
}
}
/**
* int占4字节
*
* @param i the
* @return byte [ ]
*/
public static byte[] toByteArray(int i) {
// byte[] bytes = new byte[4];
// bytes[0] = (byte) (0xff & i);
// bytes[1] = (byte) ((0xff00 & i) >> 8);
// bytes[2] = (byte) ((0xff0000 & i) >> 16);
// bytes[3] = (byte) ((0xff000000 & i) >> 24);
// return bytes;
return ByteBuffer.allocate(4).putInt(i).array();
}
/**
* To byte array byte [ ].
*
* @param hexData the hex data
* @param isHex the is hex
* @return the byte [ ]
*/
public static byte[] toByteArray(String hexData, boolean isHex) {
if (hexData == null || hexData.equals("")) {
return null;
}
if (!isHex) {
return hexData.getBytes();
}
hexData = hexData.replaceAll("\\s+", "");
String hexDigits = "0123456789ABCDEF";
ByteArrayOutputStream baos = new ByteArrayOutputStream(
hexData.length() / 2);
// 将每2位16进制整数组装成一个字节
for (int i = 0; i < hexData.length(); i += 2) {
baos.write((hexDigits.indexOf(hexData.charAt(i)) << 4 | hexDigits
.indexOf(hexData.charAt(i + 1))));
}
byte[] bytes = baos.toByteArray();
try {
baos.close();
} catch (Exception e) {
e.printStackTrace();
}
return bytes;
}
/**
* To hex string string.
*
* @param str the str
* @return the string
*/
public static String toHexString(String str) {
if (TextUtils.isEmpty(str))
return "";
StringBuilder buffer = new StringBuilder();
byte[] bytes = str.getBytes();
for (byte aByte : bytes) {
buffer.append(Integer.toHexString(Integer.valueOf(0xFF & aByte).intValue()));
buffer.append(" ");
}
return buffer.toString();
}
/**
* To hex string string.
*
* @param bytes the bytes
* @return the string
*/
public static String toHexString(byte... bytes) {
char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
// 参见http://www.oschina.net/code/snippet_116768_9019
char[] buffer = new char[bytes.length * 2];
for (int i = 0, j = 0; i < bytes.length; ++i) {
int u = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];//转无符号整型
buffer[j++] = DIGITS[u >>> 4];
buffer[j++] = DIGITS[u & 0xf];
}
return new String(buffer);
}
/**
* To hex string string.
*
* @param num the num
* @return the string
*/
public static String toHexString(int num) {
String hexString = Integer.toHexString(num);
LogUtils.debug(String.format("%d to hex string is %s", num, hexString));
return hexString;
}
/**
* To binary string string.
*
* @param bytes the bytes
* @return the string
*/
public static String toBinaryString(byte... bytes) {
char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
// 参见http://www.oschina.net/code/snippet_116768_9019
char[] buffer = new char[bytes.length * 8];
for (int i = 0, j = 0; i < bytes.length; ++i) {
int u = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];//转无符号整型
buffer[j++] = DIGITS[(u >>> 7) & 0x1];
buffer[j++] = DIGITS[(u >>> 6) & 0x1];
buffer[j++] = DIGITS[(u >>> 5) & 0x1];
buffer[j++] = DIGITS[(u >>> 4) & 0x1];
buffer[j++] = DIGITS[(u >>> 3) & 0x1];
buffer[j++] = DIGITS[(u >>> 2) & 0x1];
buffer[j++] = DIGITS[(u >>> 1) & 0x1];
buffer[j++] = DIGITS[u & 0x1];
}
return new String(buffer);
}
/**
* To binary string string.
*
* @param num the num
* @return the string
*/
public static String toBinaryString(int num) {
String binaryString = Integer.toBinaryString(num);
LogUtils.debug(String.format("%d to binary string is %s", num, binaryString));
return binaryString;
}
/**
* 转换为6位十六进制颜色代码不含“#”
*
* @param color the color
* @return string string
*/
public static String toColorString(int color) {
return toColorString(color, false);
}
/**
* 转换为6位十六进制颜色代码不含“#”
*
* @param color the color
* @param includeAlpha the include alpha
* @return string string
*/
public static String toColorString(int color, boolean includeAlpha) {
String alpha = Integer.toHexString(Color.alpha(color));
String red = Integer.toHexString(Color.red(color));
String green = Integer.toHexString(Color.green(color));
String blue = Integer.toHexString(Color.blue(color));
if (alpha.length() == 1) {
alpha = "0" + alpha;
}
if (red.length() == 1) {
red = "0" + red;
}
if (green.length() == 1) {
green = "0" + green;
}
if (blue.length() == 1) {
blue = "0" + blue;
}
String colorString;
if (includeAlpha) {
colorString = alpha + red + green + blue;
LogUtils.debug(String.format("%d to color string is %s", color, colorString));
} else {
colorString = red + green + blue;
LogUtils.debug(String.format("%d to color string is %s%s%s%s, exclude alpha is %s", color, alpha, red, green, blue, colorString));
}
return colorString;
}
/**
* 将指定的日期转换为一定格式的字符串
*
* @param date the date
* @param format the format
* @return string string
*/
public static String toDateString(Date date, String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.CHINA);
return sdf.format(date);
}
/**
* 将当前的日期转换为一定格式的字符串
*
* @param format the format
* @return string string
*/
public static String toDateString(String format) {
return toDateString(Calendar.getInstance(Locale.CHINA).getTime(), format);
}
/**
* 将指定的日期字符串转换为日期时间
*
* @param dateStr 如2014-04-08 23:02
* @return date date
*/
public static Date toDate(String dateStr) {
return DateUtils.parseDate(dateStr);
}
/**
* 将指定的日期字符串转换为时间戳
*
* @param dateStr 如2014-04-08 23:02
* @return long long
*/
public static long toTimemillis(String dateStr) {
return toDate(dateStr).getTime();
}
/**
* To slash string string.
*
* @param str the str
* @return the string
*/
public static String toSlashString(String str) {
String result = "";
char[] chars = str.toCharArray();
for (char chr : chars) {
if (chr == '"' || chr == '\'' || chr == '\\') {
result += "\\";//符合"、'、\这三个符号的前面加一个\
}
result += chr;
}
return result;
}
/**
* To array t [ ].
*
* @param <T> the type parameter
* @param list the list
* @return the t [ ]
*/
public static <T> T[] toArray(List<T> list) {
//noinspection unchecked
return (T[]) list.toArray();
}
/**
* To list list.
*
* @param <T> the type parameter
* @param array the array
* @return the list
*/
public static <T> List<T> toList(T[] array) {
return Arrays.asList(array);
}
/**
* To string string.
*
* @param objects the objects
* @return the string
*/
public static String toString(Object[] objects) {
return Arrays.deepToString(objects);
}
/**
* To string string.
*
* @param objects the objects
* @param tag the tag
* @return the string
*/
public static String toString(Object[] objects, String tag) {
StringBuilder sb = new StringBuilder();
for (Object object : objects) {
sb.append(object);
sb.append(tag);
}
return sb.toString();
}
/**
* To byte array byte [ ].
*
* @param is the is
* @return the byte [ ]
*/
public static byte[] toByteArray(InputStream is) {
if (is == null) {
return null;
}
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buff = new byte[100];
int rc = 0;
while ((rc = is.read(buff, 0, 100)) > 0) {
os.write(buff, 0, rc);
}
byte[] bytes = os.toByteArray();
os.close();
return bytes;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* To byte array byte [ ].
*
* @param bitmap the bitmap
* @return the byte [ ]
*/
public static byte[] toByteArray(Bitmap bitmap) {
if (bitmap == null) {
return null;
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
// 将Bitmap压缩成PNG编码质量为100%存储除了PNG还有很多常见格式如jpeg等。
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
byte[] bytes = os.toByteArray();
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
/**
* To bitmap bitmap.
*
* @param bytes the bytes
* @param width the width
* @param height the height
* @return the bitmap
*/
public static Bitmap toBitmap(byte[] bytes, int width, int height) {
Bitmap bitmap = null;
if (bytes.length != 0) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
// 不进行图片抖动处理
options.inDither = false;
// 设置让解码器以最佳方式解码
options.inPreferredConfig = null;
if (width > 0 && height > 0) {
options.outWidth = width;
options.outHeight = height;
}
bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
} catch (Exception e) {
LogUtils.error(e);
}
}
return bitmap;
}
/**
* To bitmap bitmap.
*
* @param bytes the bytes
* @return the bitmap
*/
public static Bitmap toBitmap(byte[] bytes) {
return toBitmap(bytes, -1, -1);
}
/**
* convert Drawable to Bitmap
* 参考http://kylines.iteye.com/blog/1660184
*
* @param drawable the drawable
* @return bitmap bitmap
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static Bitmap toBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} else if (drawable instanceof ColorDrawable) {
//color
Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
if (Build.VERSION.SDK_INT >= 11) {
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(((ColorDrawable) drawable).getColor());
}
return bitmap;
} else if (drawable instanceof NinePatchDrawable) {
//.9.png
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.draw(canvas);
return bitmap;
}
return null;
}
/**
* 从第三方文件选择器获取路径。
* 参见http://blog.csdn.net/zbjdsbj/article/details/42387551
*
* @param context the context
* @param uri the uri
* @return string string
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String toPath(Context context, Uri uri) {
LogUtils.debug("uri: " + uri.toString());
String path = uri.getPath();
String scheme = uri.getScheme();
String authority = uri.getAuthority();
//是否是4.4及以上版本
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (authority.equals("com.android.externalstorage.documents")) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
}
// DownloadsProvider
else if (authority.equals("com.android.providers.downloads.documents")) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return _queryPathFromMediaStore(context, contentUri, null, null);
}
// MediaProvider
else if (authority.equals("com.android.providers.media.documents")) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
return _queryPathFromMediaStore(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else {
if ("content".equalsIgnoreCase(scheme)) {
// Return the remote address
if (authority.equals("com.google.android.apps.photos.content")) {
return uri.getLastPathSegment();
}
return _queryPathFromMediaStore(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(scheme)) {
return uri.getPath();
}
}
LogUtils.debug("uri to path: " + path);
return path;
}
private static String _queryPathFromMediaStore(Context context, Uri uri, String selection, String[] selectionArgs) {
String filePath = null;
try {
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
filePath = cursor.getString(column_index);
cursor.close();
} catch (Exception e) {
LogUtils.error(e);
}
return filePath;
}
/**
* convert View to Bitmap.
*
* @param view the view
* @return the bitmap
* @link http ://www.cnblogs.com/lee0oo0/p/3355468.html
*/
public static Bitmap toBitmap(View view) {
//以下代码用于把当前view转化为bitmap截图
int width = view.getWidth();
int height = view.getHeight();
if (view instanceof ListView) {
height = 0;
// 获取listView实际高度
ListView listView = (ListView) view;
for (int i = 0; i < listView.getChildCount(); i++) {
height += listView.getChildAt(i).getHeight();
}
} else if (view instanceof ScrollView) {
height = 0;
// 获取scrollView实际高度
ScrollView scrollView = (ScrollView) view;
for (int i = 0; i < scrollView.getChildCount(); i++) {
height += scrollView.getChildAt(i).getHeight();
}
}
view.setDrawingCacheEnabled(true);
view.clearFocus();
view.setPressed(false);
boolean willNotCache = view.willNotCacheDrawing();
view.setWillNotCacheDrawing(false);
// Reset the drawing cache background color to fully transparent for the duration of this operation
int color = view.getDrawingCacheBackgroundColor();
view.setDrawingCacheBackgroundColor(Color.WHITE);//截图去黑色背景(透明像素)
if (color != Color.WHITE) {
view.destroyDrawingCache();
}
view.buildDrawingCache();
Bitmap cacheBitmap = view.getDrawingCache();
if (cacheBitmap == null) {
return null;
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(cacheBitmap, 0, 0, null);
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
cacheBitmap.recycle();
// Restore the view
view.destroyDrawingCache();
view.setWillNotCacheDrawing(willNotCache);
view.setDrawingCacheBackgroundColor(color);
return bitmap;
}
/**
* convert Bitmap to Drawable
*
* @param bitmap the bitmap
* @return drawable drawable
*/
public static Drawable toDrawable(Bitmap bitmap) {
return bitmap == null ? null : new BitmapDrawable(null, bitmap);
}
/**
* convert Drawable to byte array
*
* @param drawable the drawable
* @return byte [ ]
*/
public static byte[] toByteArray(Drawable drawable) {
return toByteArray(toBitmap(drawable));
}
/**
* convert byte array to Drawable
*
* @param bytes the bytes
* @return drawable drawable
*/
public static Drawable toDrawable(byte[] bytes) {
return toDrawable(toBitmap(bytes));
}
/**
* dp转换为px
*
* @param context the context
* @param dpValue the dp value
* @return int int
*/
public static int toPx(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
int pxValue = (int) (dpValue * scale + 0.5f);
LogUtils.debug(dpValue + " dp == " + pxValue + " px");
return pxValue;
}
/**
* To px int.
*
* @param dpValue the dp value
* @return the int
*/
public static int toPx(float dpValue) {
Resources resources = Resources.getSystem();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, resources.getDisplayMetrics());
return (int) px;
}
/**
* px转换为dp
*
* @param context the context
* @param pxValue the px value
* @return int int
*/
public static int toDp(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
int dpValue = (int) (pxValue / scale + 0.5f);
LogUtils.debug(pxValue + " px == " + dpValue + " dp");
return dpValue;
}
/**
* px转换为sp
*
* @param context the context
* @param pxValue the px value
* @return int int
*/
public static int toSp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
int spValue = (int) (pxValue / fontScale + 0.5f);
LogUtils.debug(pxValue + " px == " + spValue + " sp");
return spValue;
}
/**
* To gbk string.
*
* @param str the str
* @return the string
*/
public static String toGbk(String str) {
try {
return new String(str.getBytes("utf-8"), "gbk");
} catch (UnsupportedEncodingException e) {
LogUtils.warn(e.getMessage());
return str;
}
}
/**
* To file size string string.
*
* @param fileSize the file size
* @return the string
*/
public static String toFileSizeString(long fileSize) {
DecimalFormat df = new DecimalFormat("0.00");
String fileSizeString;
if (fileSize < KB) {
fileSizeString = fileSize + "B";
} else if (fileSize < MB) {
fileSizeString = df.format((double) fileSize / KB) + "K";
} else if (fileSize < GB) {
fileSizeString = df.format((double) fileSize / MB) + "M";
} else {
fileSizeString = df.format((double) fileSize / GB) + "G";
}
return fileSizeString;
}
/**
* To string string.
*
* @param is the is
* @return the string
*/
public static String toString(InputStream is) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
is.close();
} catch (IOException e) {
LogUtils.error(e);
}
return sb.toString();
}
/**
* To round drawable shape drawable.
*
* @param color the color
* @param radius the radius
* @return the shape drawable
*/
public static ShapeDrawable toRoundDrawable(int color, int radius) {
int r = toPx(radius);
float[] outerR = new float[]{r, r, r, r, r, r, r, r};
RoundRectShape shape = new RoundRectShape(outerR, null, null);
ShapeDrawable drawable = new ShapeDrawable(shape);
drawable.getPaint().setColor(color);
return drawable;
}
/**
* 对TextView、Button等设置不同状态时其文字颜色。
* 参见http://blog.csdn.net/sodino/article/details/6797821
* Modified by liyujiang at 2015.08.13
*
* @param normalColor the normal color
* @param pressedColor the pressed color
* @param focusedColor the focused color
* @param unableColor the unable color
* @return the color state list
*/
public static ColorStateList toColorStateList(int normalColor, int pressedColor, int focusedColor, int unableColor) {
int[] colors = new int[]{pressedColor, focusedColor, normalColor, focusedColor, unableColor, normalColor};
int[][] states = new int[6][];
states[0] = new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled};
states[1] = new int[]{android.R.attr.state_enabled, android.R.attr.state_focused};
states[2] = new int[]{android.R.attr.state_enabled};
states[3] = new int[]{android.R.attr.state_focused};
states[4] = new int[]{android.R.attr.state_window_focused};
states[5] = new int[]{};
return new ColorStateList(states, colors);
}
/**
* To color state list color state list.
*
* @param normalColor the normal color
* @param pressedColor the pressed color
* @return the color state list
*/
public static ColorStateList toColorStateList(int normalColor, int pressedColor) {
return toColorStateList(normalColor, pressedColor, pressedColor, normalColor);
}
/**
* To state list drawable state list drawable.
*
* @param normal the normal
* @param pressed the pressed
* @param focused the focused
* @param unable the unable
* @return the state list drawable
*/
public static StateListDrawable toStateListDrawable(Drawable normal, Drawable pressed, Drawable focused, Drawable unable) {
StateListDrawable drawable = new StateListDrawable();
drawable.addState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}, pressed);
drawable.addState(new int[]{android.R.attr.state_enabled, android.R.attr.state_focused}, focused);
drawable.addState(new int[]{android.R.attr.state_enabled}, normal);
drawable.addState(new int[]{android.R.attr.state_focused}, focused);
drawable.addState(new int[]{android.R.attr.state_window_focused}, unable);
drawable.addState(new int[]{}, normal);
return drawable;
}
/**
* To state list drawable state list drawable.
*
* @param normalColor the normal color
* @param pressedColor the pressed color
* @param focusedColor the focused color
* @param unableColor the unable color
* @return the state list drawable
*/
public static StateListDrawable toStateListDrawable(int normalColor, int pressedColor, int focusedColor, int unableColor) {
StateListDrawable drawable = new StateListDrawable();
Drawable normal = new ColorDrawable(normalColor);
Drawable pressed = new ColorDrawable(pressedColor);
Drawable focused = new ColorDrawable(focusedColor);
Drawable unable = new ColorDrawable(unableColor);
drawable.addState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}, pressed);
drawable.addState(new int[]{android.R.attr.state_enabled, android.R.attr.state_focused}, focused);
drawable.addState(new int[]{android.R.attr.state_enabled}, normal);
drawable.addState(new int[]{android.R.attr.state_focused}, focused);
drawable.addState(new int[]{android.R.attr.state_window_focused}, unable);
drawable.addState(new int[]{}, normal);
return drawable;
}
/**
* To state list drawable state list drawable.
*
* @param normal the normal
* @param pressed the pressed
* @return the state list drawable
*/
public static StateListDrawable toStateListDrawable(Drawable normal, Drawable pressed) {
return toStateListDrawable(normal, pressed, pressed, normal);
}
/**
* To state list drawable state list drawable.
*
* @param normalColor the normal color
* @param pressedColor the pressed color
* @return the state list drawable
*/
public static StateListDrawable toStateListDrawable(int normalColor, int pressedColor) {
return toStateListDrawable(normalColor, pressedColor, pressedColor, normalColor);
}
}

View File

@@ -0,0 +1,295 @@
package cn.qqtheme.framework.util;
import android.annotation.SuppressLint;
import android.support.annotation.NonNull;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* 日期时间工具类
*
* @author 李玉江[QQ :1023694760]
* @version 2015 /8/5 Created by IntelliJ IDEA 14.1
*/
public class DateUtils extends android.text.format.DateUtils {
/**
* The enum Difference mode.
*/
public enum DifferenceMode {
/**
* Second difference mode.
*/
Second, /**
* Minute difference mode.
*/
Minute, /**
* Hour difference mode.
*/
Hour, /**
* Day difference mode.
*/
Day
}
/**
* Calculate different second long.
*
* @param startDate the start date
* @param endDate the end date
* @return the long
*/
public static long calculateDifferentSecond(Date startDate, Date endDate) {
return calculateDifference(startDate, endDate, DifferenceMode.Second);
}
/**
* Calculate different minute long.
*
* @param startDate the start date
* @param endDate the end date
* @return the long
*/
public static long calculateDifferentMinute(Date startDate, Date endDate) {
return calculateDifference(startDate, endDate, DifferenceMode.Minute);
}
/**
* Calculate different hour long.
*
* @param startDate the start date
* @param endDate the end date
* @return the long
*/
public static long calculateDifferentHour(Date startDate, Date endDate) {
return calculateDifference(startDate, endDate, DifferenceMode.Hour);
}
/**
* Calculate different day long.
*
* @param startDate the start date
* @param endDate the end date
* @return the long
*/
public static long calculateDifferentDay(Date startDate, Date endDate) {
return calculateDifference(startDate, endDate, DifferenceMode.Day);
}
/**
* Calculate different second long.
*
* @param startTimeMillis the start time millis
* @param endTimeMillis the end time millis
* @return the long
*/
public static long calculateDifferentSecond(long startTimeMillis, long endTimeMillis) {
return calculateDifference(startTimeMillis, endTimeMillis, DifferenceMode.Second);
}
/**
* Calculate different minute long.
*
* @param startTimeMillis the start time millis
* @param endTimeMillis the end time millis
* @return the long
*/
public static long calculateDifferentMinute(long startTimeMillis, long endTimeMillis) {
return calculateDifference(startTimeMillis, endTimeMillis, DifferenceMode.Minute);
}
/**
* Calculate different hour long.
*
* @param startTimeMillis the start time millis
* @param endTimeMillis the end time millis
* @return the long
*/
public static long calculateDifferentHour(long startTimeMillis, long endTimeMillis) {
return calculateDifference(startTimeMillis, endTimeMillis, DifferenceMode.Hour);
}
/**
* Calculate different day long.
*
* @param startTimeMillis the start time millis
* @param endTimeMillis the end time millis
* @return the long
*/
public static long calculateDifferentDay(long startTimeMillis, long endTimeMillis) {
return calculateDifference(startTimeMillis, endTimeMillis, DifferenceMode.Day);
}
/**
* Calculate difference long.
*
* @param startTimeMillis the start time millis
* @param endTimeMillis the end time millis
* @param mode the mode
* @return the long
*/
public static long calculateDifference(long startTimeMillis, long endTimeMillis, DifferenceMode mode) {
return calculateDifference(new Date(startTimeMillis), new Date(endTimeMillis), mode);
}
/**
* Calculate difference long.
*
* @param startDate the start date
* @param endDate the end date
* @param mode the mode
* @return the long
*/
public static long calculateDifference(Date startDate, Date endDate, DifferenceMode mode) {
long[] different = calculateDifference(startDate, endDate);
if (mode.equals(DifferenceMode.Minute)) {
return different[2];
} else if (mode.equals(DifferenceMode.Hour)) {
return different[1];
} else if (mode.equals(DifferenceMode.Day)) {
return different[0];
} else {
return different[3];
}
}
/**
* Calculate difference long [ ].
*
* @param startDate the start date
* @param endDate the end date
* @return the long [ ]
*/
public static long[] calculateDifference(Date startDate, Date endDate) {
return calculateDifference(endDate.getTime() - startDate.getTime());
}
/**
* Calculate difference long [ ].
*
* @param differentMilliSeconds the different milli seconds
* @return the long [ ]
*/
public static long[] calculateDifference(long differentMilliSeconds) {
long secondsInMilli = 1000;//1s==1000ms
long minutesInMilli = secondsInMilli * 60;
long hoursInMilli = minutesInMilli * 60;
long daysInMilli = hoursInMilli * 24;
long elapsedDays = differentMilliSeconds / daysInMilli;
differentMilliSeconds = differentMilliSeconds % daysInMilli;
long elapsedHours = differentMilliSeconds / hoursInMilli;
differentMilliSeconds = differentMilliSeconds % hoursInMilli;
long elapsedMinutes = differentMilliSeconds / minutesInMilli;
differentMilliSeconds = differentMilliSeconds % minutesInMilli;
long elapsedSeconds = differentMilliSeconds / secondsInMilli;
LogUtils.debug(String.format("different: %d ms, %d days, %d hours, %d minutes, %d seconds",
differentMilliSeconds, elapsedDays, elapsedHours, elapsedMinutes, elapsedSeconds));
return new long[]{elapsedDays, elapsedHours, elapsedMinutes, elapsedSeconds};
}
/**
* Calculate days in month int.
*
* @param month the month
* @return the int
*/
public static int calculateDaysInMonth(int month) {
return calculateDaysInMonth(0, month);
}
/**
* Calculate days in month int.
*
* @param year the year
* @param month the month
* @return the int
*/
public static int calculateDaysInMonth(int year, int month) {
// 添加大小月月份并将其转换为list,方便之后的判断
String[] bigMonths = {"1", "3", "5", "7", "8", "10", "12"};
String[] littleMonths = {"4", "6", "9", "11"};
List<String> bigList = Arrays.asList(bigMonths);
List<String> littleList = Arrays.asList(littleMonths);
// 判断大小月及是否闰年,用来确定"日"的数据
if (bigList.contains(String.valueOf(month))) {
return 31;
} else if (littleList.contains(String.valueOf(month))) {
return 30;
} else {
if (year <= 0) {
return 29;
}
// 是否闰年
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
return 29;
} else {
return 28;
}
}
}
/**
* 月日时分秒0-9前补0
*
* @param number the number
* @return the string
*/
@NonNull
public static String fillZero(int number) {
return number < 10 ? "0" + number : "" + number;
}
/**
* 功能判断日期是否和当前date对象在同一天。
* 参见http://www.cnblogs.com/myzhijie/p/3330970.html
*
* @param date 比较的日期
* @return boolean 如果在返回true否则返回false。
* @author 沙琪玛 QQ862990787 Aug 21, 2013 7:15:53 AM
*/
public static boolean isSameDay(Date date) {
if (date == null) {
throw new IllegalArgumentException("date is null");
}
Calendar nowCalendar = Calendar.getInstance();
Calendar newCalendar = Calendar.getInstance();
newCalendar.setTime(date);
return (nowCalendar.get(Calendar.ERA) == newCalendar.get(Calendar.ERA) &&
nowCalendar.get(Calendar.YEAR) == newCalendar.get(Calendar.YEAR) &&
nowCalendar.get(Calendar.DAY_OF_YEAR) == newCalendar.get(Calendar.DAY_OF_YEAR));
}
/**
* 将yyyy-MM-dd HH:mm:ss字符串转换成日期<br/>
*
* @param dateStr 时间字符串
* @param dataFormat 当前时间字符串的格式。
* @return Date 日期 ,转换异常时返回null。
*/
public static Date parseDate(String dateStr, String dataFormat) {
try {
@SuppressLint("SimpleDateFormat")
SimpleDateFormat dateFormat = new SimpleDateFormat(dataFormat);
Date date = dateFormat.parse(dateStr);
return new Date(date.getTime());
} catch (Exception e) {
LogUtils.warn(e);
return null;
}
}
/**
* 将yyyy-MM-dd HH:mm:ss字符串转换成日期<br/>
*
* @param dateStr yyyy-MM-dd HH:mm:ss字符串
* @return Date 日期 ,转换异常时返回null。
*/
public static Date parseDate(String dateStr) {
return parseDate(dateStr, "yyyy-MM-dd HH:mm:ss");
}
}

View File

@@ -0,0 +1,210 @@
package cn.qqtheme.framework.util;
import android.os.Debug;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import cn.qqtheme.framework.AppConfig;
/**
* 如果用于android平台将信息记录到“LogCat”。如果用于java平台将信息记录到“Console”
*
* @author 李玉江[QQ :1023694760]
* @version 2013 -11-2
*/
public class LogUtils {
private static final int MAX_STACK_TRACE_SIZE = 131071; //128 KB - 1
// 是否开启日志输出,在Debug状态下开启在Release状态下关闭以提高程序性能避免日志被人抓取
private static boolean isDebug = AppConfig.DEBUG_ENABLE;
private static String debugTag = AppConfig.DEBUG_TAG;
/**
* Debug.
*
* @param message the message
*/
public static void debug(String message) {
debug(debugTag, message);
}
/**
* Debug.
*
* @param object the object
* @param message the message
*/
public static void debug(Object object, String message) {
debug(object.getClass().getSimpleName(), message);
}
/**
* 记录“debug”级别的信息
*
* @param tag the tag
* @param message the message
*/
public static void debug(String tag, String message) {
if (isDebug) {
try {
Log.d(debugTag + "-" + tag, message);
} catch (Exception e) {
System.out.println(tag + ">>>" + message);
}
}
}
/**
* Warn.
*
* @param e the e
*/
public static void warn(Throwable e) {
warn(toStackTraceString(e));
}
/**
* Warn.
*
* @param message the message
*/
public static void warn(String message) {
warn(debugTag, message);
}
/**
* Warn.
*
* @param object the object
* @param message the message
*/
public static void warn(Object object, String message) {
warn(object.getClass().getSimpleName(), message);
}
/**
* Warn.
*
* @param object the object
* @param e the e
*/
public static void warn(Object object, Throwable e) {
warn(object.getClass().getSimpleName(), toStackTraceString(e));
}
/**
* 记录“warn”级别的信息
*
* @param tag the tag
* @param message the message
*/
public static void warn(String tag, String message) {
if (isDebug) {
try {
Log.w(debugTag + tag, message);
} catch (Exception e) {
System.out.println(debugTag + ">>>" + message);
}
}
}
/**
* Error.
*
* @param e the e
*/
public static void error(Throwable e) {
error(toStackTraceString(e));
}
/**
* Error.
*
* @param message the message
*/
public static void error(String message) {
error(debugTag, message);
}
/**
* Error.
*
* @param object the object
* @param message the message
*/
public static void error(Object object, String message) {
error(object.getClass().getSimpleName(), message);
}
/**
* Error.
*
* @param object the object
* @param e the e
*/
public static void error(Object object, Throwable e) {
error(object.getClass().getSimpleName(), toStackTraceString(e));
}
/**
* 记录“error”级别的信息
*
* @param tag the tag
* @param message the message
*/
public static void error(String tag, String message) {
if (isDebug) {
try {
Log.e(debugTag + tag, message);
} catch (Exception e) {
System.out.println(debugTag + ">>>" + message);
}
}
}
/**
* 在某个方法中调用生成.trace文件。然后拿到电脑上用DDMS工具打开分析
*
* @see #stopMethodTracing() #stopMethodTracing()#stopMethodTracing()
*/
public static void startMethodTracing() {
if (isDebug) {
Debug.startMethodTracing(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + debugTag + ".trace");
}
}
/**
* Stop method tracing.
*/
public static void stopMethodTracing() {
if (isDebug) {
Debug.stopMethodTracing();
}
}
/**
* To stack trace string string.
*
* @param throwable the throwable
* @return the string
*/
public static String toStackTraceString(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
throwable.printStackTrace(pw);
String stackTraceString = sw.toString();
//Reduce data to 128KB so we don't get a TransactionTooLargeException when sending the intent.
//The limit is 1MB on Android but some devices seem to have it lower.
//See: http://developer.android.com/reference/android/os/TransactionTooLargeException.html
//And: http://stackoverflow.com/questions/11451393/what-to-do-on-transactiontoolargeexception#comment46697371_12809171
if (stackTraceString.length() > MAX_STACK_TRACE_SIZE) {
String disclaimer = " [stack trace too large]";
stackTraceString = stackTraceString.substring(0, MAX_STACK_TRACE_SIZE - disclaimer.length()) + disclaimer;
}
return stackTraceString;
}
}

View File

@@ -0,0 +1,111 @@
package cn.qqtheme.framework.util;
import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.Window;
import android.view.WindowManager;
/**
* 获取屏幕宽高等信息、全屏切换、保持屏幕常亮、截屏等
*
* @author liyujiang[QQ:1032694760]
* @since 2015/11/26
*/
public final class ScreenUtils {
private static boolean isFullScreen = false;
/**
* Display metrics display metrics.
*
* @param context the context
* @return the display metrics
*/
public static DisplayMetrics displayMetrics(Context context) {
DisplayMetrics dm = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(dm);
LogUtils.debug("screen width=" + dm.widthPixels + "px, screen height=" + dm.heightPixels
+ "px, densityDpi=" + dm.densityDpi + ", density=" + dm.density);
return dm;
}
/**
* Width pixels int.
*
* @param context the context
* @return the int
*/
public static int widthPixels(Context context) {
return displayMetrics(context).widthPixels;
}
/**
* Height pixels int.
*
* @param context the context
* @return the int
*/
public static int heightPixels(Context context) {
return displayMetrics(context).heightPixels;
}
/**
* Density float.
*
* @param context the context
* @return the float
*/
public static float density(Context context) {
return displayMetrics(context).density;
}
/**
* Density dpi int.
*
* @param context the context
* @return the int
*/
public static int densityDpi(Context context) {
return displayMetrics(context).densityDpi;
}
/**
* Is full screen boolean.
*
* @return the boolean
*/
public static boolean isFullScreen() {
return isFullScreen;
}
/**
* Toggle full displayMetrics.
*
* @param activity the activity
*/
public static void toggleFullScreen(Activity activity) {
Window window = activity.getWindow();
int flagFullscreen = WindowManager.LayoutParams.FLAG_FULLSCREEN;
if (isFullScreen) {
window.clearFlags(flagFullscreen);
isFullScreen = false;
} else {
window.setFlags(flagFullscreen, flagFullscreen);
isFullScreen = true;
}
}
/**
* 保持屏幕常亮
*
* @param activity the activity
*/
public static void keepBright(Activity activity) {
//需在setContentView前调用
int keepScreenOn = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
activity.getWindow().setFlags(keepScreenOn, keepScreenOn);
}
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Animation.Popup" parent="@android:style/Animation">
<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
</style>
</resources>

2
library/FilePicker/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
/*.iml

View File

@@ -0,0 +1,11 @@
apply plugin: 'com.android.library'
ext {
isLibrary = true
pomArtifactId = "FilePicker"
pomDescription = "file picker for android"
}
dependencies {
compile project(":library:Common")
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.qqtheme.framework.filepicker">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</manifest>

View File

@@ -0,0 +1,225 @@
package cn.qqtheme.framework.adapter;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.File;
import java.util.ArrayList;
import cn.qqtheme.framework.entity.FileItem;
import cn.qqtheme.framework.filepicker.R;
import cn.qqtheme.framework.util.CompatUtils;
import cn.qqtheme.framework.util.ConvertUtils;
import cn.qqtheme.framework.util.FileUtils;
import cn.qqtheme.framework.util.LogUtils;
/**
* The type File adapter.
*/
public class FileAdapter extends BaseAdapter {
/**
* The constant DIR_ROOT.
*/
public static final String DIR_ROOT = "..";
/**
* The constant DIR_PARENT.
*/
public static final String DIR_PARENT = "";
private Context context;
private ArrayList<FileItem> data = new ArrayList<FileItem>();
private String rootPath = null;
private String currentPath = null;
private String[] allowExtensions = null;//允许的扩展名
private boolean onlyListDir = false;//是否仅仅读取目录
private boolean showHomeDir = false;//是否显示返回主目录
private boolean showUpDir = true;//是否显示返回上一级
private boolean showHideDir = true;//是否显示隐藏的目录(以“.”开头)
private int homeIcon = R.drawable.file_picker_home;
private int upIcon = R.drawable.file_picker_updir;
private int folderIcon = R.drawable.file_picker_folder;
private int fileIcon = R.drawable.file_picker_file;
/**
* Instantiates a new File adapter.
*
* @param context the context
*/
public FileAdapter(Context context) {
this.context = context;
}
/**
* Gets current path.
*
* @return the current path
*/
public String getCurrentPath() {
return currentPath;
}
/**
* Sets allow extensions.
*
* @param allowExtensions the allow extensions
*/
public void setAllowExtensions(String[] allowExtensions) {
this.allowExtensions = allowExtensions;
}
/**
* Sets only list dir.
*
* @param onlyListDir the only list dir
*/
public void setOnlyListDir(boolean onlyListDir) {
this.onlyListDir = onlyListDir;
}
/**
* Sets show home dir.
*
* @param showHomeDir the show home dir
*/
public void setShowHomeDir(boolean showHomeDir) {
this.showHomeDir = showHomeDir;
}
/**
* Sets show up dir.
*
* @param showUpDir the show up dir
*/
public void setShowUpDir(boolean showUpDir) {
this.showUpDir = showUpDir;
}
/**
* Sets show hide dir.
*
* @param showHideDir the show hide dir
*/
public void setShowHideDir(boolean showHideDir) {
this.showHideDir = showHideDir;
}
/**
* Load data array list.
*
* @param path the path
*/
public void loadData(String path) {
if (path == null) {
LogUtils.warn("current directory is null");
return;
}
ArrayList<FileItem> datas = new ArrayList<FileItem>();
if (rootPath == null) {
rootPath = path;
}
LogUtils.debug("current directory path: " + path);
currentPath = path;
if (showHomeDir) {
//添加“返回主目录”
FileItem fileRoot = new FileItem();
fileRoot.setDirectory(true);
fileRoot.setIcon(homeIcon);
fileRoot.setName(DIR_ROOT);
fileRoot.setSize(0);
fileRoot.setPath(rootPath);
datas.add(fileRoot);
}
if (showUpDir && !path.equals("/")) {
//添加“返回上一级目录”
FileItem fileParent = new FileItem();
fileParent.setDirectory(true);
fileParent.setIcon(upIcon);
fileParent.setName(DIR_PARENT);
fileParent.setSize(0);
fileParent.setPath(new File(path).getParent());
datas.add(fileParent);
}
File[] files;
if (allowExtensions == null) {
if (onlyListDir) {
files = FileUtils.listDirs(currentPath);
} else {
files = FileUtils.listDirsAndFiles(currentPath);
}
} else {
if (onlyListDir) {
files = FileUtils.listDirs(currentPath, allowExtensions);
} else {
files = FileUtils.listDirsAndFiles(currentPath, allowExtensions);
}
}
if (files != null) {
for (File file : files) {
if (!showHideDir && file.getName().startsWith(".")) {
continue;
}
FileItem fileItem = new FileItem();
boolean isDirectory = file.isDirectory();
fileItem.setDirectory(isDirectory);
if (isDirectory) {
fileItem.setIcon(folderIcon);
fileItem.setSize(0);
} else {
fileItem.setIcon(fileIcon);
fileItem.setSize(file.length());
}
fileItem.setName(file.getName());
fileItem.setPath(file.getAbsolutePath());
datas.add(fileItem);
}
}
data.clear();
data.addAll(datas);
notifyDataSetChanged();
}
@Override
public int getCount() {
return data.size();
}
@Override
public FileItem getItem(int position) {
return data.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(android.R.layout.activity_list_item, null);
CompatUtils.setBackground(convertView, ConvertUtils.toStateListDrawable(Color.WHITE, Color.LTGRAY));
holder = new ViewHolder();
holder.imageView = (ImageView) convertView.findViewById(android.R.id.icon);
holder.textView = (TextView) convertView.findViewById(android.R.id.text1);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
FileItem item = data.get(position);
holder.imageView.setImageResource(item.getIcon());
holder.textView.setText(item.getName());
return convertView;
}
private class ViewHolder {
ImageView imageView;
TextView textView;
}
}

View File

@@ -0,0 +1,106 @@
package cn.qqtheme.framework.entity;
/**
* 文件项信息
*
* @author 李玉江[QQ :1032694760]
* @version 2014 -05-23 18:02
*/
public class FileItem {
private int icon;
private String name;
private String path = "/";
private long size = 0;
private boolean isDirectory = false;
/**
* Sets icon.
*
* @param icon the icon
*/
public void setIcon(int icon) {
this.icon = icon;
}
/**
* Gets icon.
*
* @return the icon
*/
public int getIcon() {
return icon;
}
/**
* Gets name.
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Sets name.
*
* @param name the name
*/
public void setName(String name) {
this.name = name;
}
/**
* Gets path.
*
* @return the path
*/
public String getPath() {
return path;
}
/**
* Sets path.
*
* @param path the path
*/
public void setPath(String path) {
this.path = path;
}
/**
* Gets size.
*
* @return the size
*/
public long getSize() {
return size;
}
/**
* Sets size.
*
* @param size the size
*/
public void setSize(long size) {
this.size = size;
}
/**
* Is directory boolean.
*
* @return the boolean
*/
public boolean isDirectory() {
return isDirectory;
}
/**
* Sets directory.
*
* @param isDirectory the is directory
*/
public void setDirectory(boolean isDirectory) {
this.isDirectory = isDirectory;
}
}

View File

@@ -0,0 +1,241 @@
package cn.qqtheme.framework.picker;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresPermission;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.ListView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import cn.qqtheme.framework.adapter.FileAdapter;
import cn.qqtheme.framework.entity.FileItem;
import cn.qqtheme.framework.popup.ConfirmPopup;
import cn.qqtheme.framework.util.ConvertUtils;
import cn.qqtheme.framework.util.LogUtils;
import cn.qqtheme.framework.util.StorageUtils;
import cn.qqtheme.framework.widget.MarqueeTextView;
/**
* 文件目录选择器
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /9/29
*/
public class FilePicker extends ConfirmPopup<LinearLayout> implements AdapterView.OnItemClickListener {
/**
* Directory mode.
*/
public static final int DIRECTORY = 0;
/**
* File mode.
*/
public static final int FILE = 1;
private String initPath;
private FileAdapter adapter;
private MarqueeTextView textView;
private OnFilePickListener onFilePickListener;
private int mode;
@IntDef(flag = false, value = {DIRECTORY, FILE})
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {
}
/**
* Instantiates a new File picker.
*
* @param activity the activity
* @param mode data mode
* @see #FILE #FILE#FILE
* @see #DIRECTORY #DIRECTORY#DIRECTORY
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@RequiresPermission(anyOf = {Manifest.permission.READ_EXTERNAL_STORAGE})
public FilePicker(Activity activity, @Mode int mode) {
super(activity);
setHalfScreen(true);
this.initPath = StorageUtils.getRootPath(activity);
this.mode = mode;
this.adapter = new FileAdapter(activity);
adapter.setOnlyListDir(mode == DIRECTORY);
}
@Override
@NonNull
protected LinearLayout makeCenterView() {
LinearLayout rootLayout = new LinearLayout(activity);
rootLayout.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
rootLayout.setBackgroundColor(Color.WHITE);
rootLayout.setOrientation(LinearLayout.VERTICAL);
ListView listView = new ListView(activity);
listView.setBackgroundColor(Color.WHITE);
listView.setDivider(new ColorDrawable(0xFFDDDDDD));
listView.setDividerHeight(1);
listView.setCacheColorHint(Color.TRANSPARENT);
listView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
rootLayout.addView(listView);
return rootLayout;
}
@Nullable
@Override
protected View makeFooterView() {
textView = new MarqueeTextView(activity);
textView.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
textView.setTextColor(Color.BLACK);
textView.setGravity(Gravity.CENTER_VERTICAL);
int padding = ConvertUtils.toPx(activity, 10);
textView.setPadding(padding, padding, padding, padding);
return textView;
}
/**
* Sets root path.
*
* @param initPath the init path
*/
public void setRootPath(String initPath) {
this.initPath = initPath;
}
/**
* Sets allow extensions.
*
* @param allowExtensions the allow extensions
*/
public void setAllowExtensions(String[] allowExtensions) {
adapter.setAllowExtensions(allowExtensions);
}
/**
* Sets show up dir.
*
* @param showUpDir the show up dir
*/
public void setShowUpDir(boolean showUpDir) {
adapter.setShowUpDir(showUpDir);
}
/**
* Sets show home dir.
*
* @param showHomeDir the show home dir
*/
public void setShowHomeDir(boolean showHomeDir) {
adapter.setShowHomeDir(showHomeDir);
}
/**
* Sets show hide dir.
*
* @param showHideDir the show hide dir
*/
public void setShowHideDir(boolean showHideDir) {
adapter.setShowHideDir(showHideDir);
}
@Override
protected void setContentViewBefore() {
boolean isPickFile = mode == FILE;
setCancelVisible(!isPickFile);
setSubmitText(isPickFile ? "取消" : "确定");
}
@Override
protected void setContentViewAfter(View contentView) {
refreshCurrentDirPath(initPath);
}
@Override
protected void onSubmit() {
if (mode == FILE) {
LogUtils.debug("已放弃选择!");
} else {
String currentPath = adapter.getCurrentPath();
LogUtils.debug("已选择目录:" + currentPath);
if (onFilePickListener != null) {
onFilePickListener.onFilePicked(currentPath);
}
}
}
/**
* Gets current path.
*
* @return the current path
*/
public String getCurrentPath() {
return adapter.getCurrentPath();
}
/**
* 响应选择器的列表项点击事件
*/
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
FileItem fileItem = adapter.getItem(position);
if (fileItem.isDirectory()) {
refreshCurrentDirPath(fileItem.getPath());
} else {
String clickPath = fileItem.getPath();
if (mode == DIRECTORY) {
LogUtils.debug("选择的不是有效的目录: " + clickPath);
} else {
dismiss();
LogUtils.debug("已选择文件:" + clickPath);
if (onFilePickListener != null) {
onFilePickListener.onFilePicked(clickPath);
}
}
}
}
private void refreshCurrentDirPath(String currentPath) {
if (currentPath.equals("/")) {
textView.setText("根目录");
} else {
textView.setText(currentPath);
}
adapter.loadData(currentPath);
}
/**
* Sets on file pick listener.
*
* @param listener the listener
*/
public void setOnFilePickListener(OnFilePickListener listener) {
this.onFilePickListener = listener;
}
/**
* The interface On file pick listener.
*/
public interface OnFilePickListener {
/**
* On file picked.
*
* @param currentPath the current path
*/
void onFilePicked(String currentPath);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,224 @@
package cn.qqtheme.framework.util;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import java.io.File;
import java.io.IOException;
/**
* 存储设备工具类
*
* @author 李玉江[QQ :1023694760]
* @version 2013 -11-2
*/
public final class StorageUtils {
/**
* 判断外置存储是否可用
*
* @return the boolean
*/
public static boolean externalMounted() {
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
return true;
}
LogUtils.warn("external storage unmounted");
return false;
}
/**
* 返回以“/”结尾的外置存储根目录,可选若外置存储不可用则是否使用内部存储
*
* @param context the context
* @param onlyExternalStorage the only external storage
* @param subdir the subdir
* @return 诸如 /mnt/sdcard/subdir
*/
public static String getRootPath(Context context, boolean onlyExternalStorage, String subdir) {
File file;
if (externalMounted()) {
file = Environment.getExternalStorageDirectory();
} else {
if (onlyExternalStorage) {
file = new File("/");
} else {
file = context.getFilesDir();
}
}
if (!TextUtils.isEmpty(subdir)) {
file = new File(file, subdir);
//noinspection ResultOfMethodCallIgnored
file.mkdirs();
}
String path = "/";
if (file != null) {
path = FileUtils.separator(file.getAbsolutePath());
}
LogUtils.debug("storage root path: " + path);
return path;
}
/**
* 返回以“/”结尾的外置存储根目录,可选若外置存储不可用则是否使用内部存储
*
* @param context the context
* @param subdir the subdir
* @return 诸如 /mnt/sdcard/
*/
public static String getRootPath(Context context, String subdir) {
return getRootPath(context, false, subdir);
}
/**
* 返回以“/”结尾的外置存储根目录,可选若外置存储不可用则是否使用内部存储
*
* @param context the context
* @param onlyExternalStorage the only external storage
* @return 诸如 /mnt/sdcard/
*/
public static String getRootPath(Context context, boolean onlyExternalStorage) {
return getRootPath(context, onlyExternalStorage, null);
}
/**
* 返回以“/”结尾的外置存储根目录,若外置存储不可用则使用内部存储
*
* @param context the context
* @return 诸如 /mnt/sdcard/
*/
public static String getRootPath(Context context) {
return getRootPath(context, false);
}
/**
* 下载的文件的保存路径,以“/”结尾
*
* @param context the context
* @return 诸如 /mnt/sdcard/Download/
* @throws Exception the exception
*/
public static String getDownloadPath(Context context) throws Exception {
File file;
if (externalMounted()) {
file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
} else {
throw new Exception("外置存储不可用!");
}
return FileUtils.separator(file.getAbsolutePath());
}
/**
* 各种类型的文件的专用的保存路径,以“/”结尾
*
* @param context the context
* @param type the type
* @return 诸如 /mnt/sdcard/Android/data/[package]/files/[type]/
*/
public static String getPrivatePath(Context context, String type) {
File file = null;
if (externalMounted()) {
file = context.getExternalFilesDir(type);
}
if (file == null) {
//SD卡不可用或暂时繁忙
if (type == null) {
file = context.getFilesDir();
} else {
file = new File(FileUtils.separator(context.getFilesDir().getAbsolutePath()) + type);
//noinspection ResultOfMethodCallIgnored
file.mkdirs();
}
}
return FileUtils.separator(file.getAbsolutePath());
}
/**
* Gets internal root path.
*
* @param context the context
* @return the internal root path
*/
public static String getInternalRootPath(Context context) {
return FileUtils.separator(context.getFilesDir().getAbsolutePath());
}
/**
* Gets private root path.
*
* @param context the context
* @return the private root path
*/
public static String getPrivateRootPath(Context context) {
return getPrivatePath(context, null);
}
/**
* Gets plugin path.
*
* @param context the context
* @return the plugin path
*/
public static String getPluginPath(Context context) {
return getPrivatePath(context, "plugin");
}
/**
* Gets temporary dir path.
*
* @param context the context
* @return the temporary dir path
*/
public static String getTemporaryDirPath(Context context) {
return getPrivatePath(context, "temporary");
}
/**
* Gets temporary file path.
*
* @param context the context
* @return the temporary file path
*/
public static String getTemporaryFilePath(Context context) {
try {
return File.createTempFile("lyj_", ".tmp", context.getCacheDir()).getAbsolutePath();
} catch (IOException e) {
return getTemporaryDirPath(context) + "lyj.tmp";
}
}
/**
* Gets cache path.
*
* @param context the context
* @param type the type
* @return the cache path
*/
public static String getCachePath(Context context, String type) {
File file;
if (externalMounted()) {
file = context.getExternalCacheDir();
} else {
file = context.getCacheDir();
}
if (type != null) {
file = new File(file, type);
//noinspection ResultOfMethodCallIgnored
file.mkdirs();
}
return FileUtils.separator(file != null ? file.getAbsolutePath() : null);
}
/**
* Gets cache root path.
*
* @param context the context
* @return the cache root path
*/
public static String getCacheRootPath(Context context) {
return getCachePath(context, null);
}
}

View File

@@ -0,0 +1,57 @@
package cn.qqtheme.framework.widget;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.widget.TextView;
/**
* 文本水平自动滚动(从右到左)。
*
* @author 李玉江[QQ :1023694760]
* @version 2013 -1-11
*/
public class MarqueeTextView extends TextView {
/**
* Instantiates a new Marquee text view.
*
* @param context the context
*/
public MarqueeTextView(Context context) {
this(context, null);
}
/**
* Instantiates a new Marquee text view.
*
* @param context the context
* @param attr the attr
*/
public MarqueeTextView(Context context, AttributeSet attr) {
super(context, attr);
setEllipsize(TextUtils.TruncateAt.MARQUEE);
setMarqueeRepeatLimit(-1);
setSingleLine(true);
setHorizontallyScrolling(true);
setClickable(true);
setFocusable(true);
setFocusableInTouchMode(true);
setGravity(Gravity.CENTER);
}
/**
* 必须已获取到焦点,才能即显滚动
*/
@Override
public boolean isFocused() {
return true;
}
@Override
public boolean isPressed() {
return true;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

2
library/WheelPicker/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
/*.iml

View File

@@ -0,0 +1,11 @@
apply plugin: 'com.android.library'
ext {
isLibrary = true
pomArtifactId = "WheelPicker"
pomDescription = "wheel picker for android, include date picker, time picker, option picker, address picker, etc."
}
dependencies {
compile project(":library:Common")
}

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="cn.qqtheme.framework.wheelpicker" />

View File

@@ -0,0 +1,358 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import java.util.ArrayList;
import cn.qqtheme.framework.util.LogUtils;
import cn.qqtheme.framework.widget.WheelView;
/**
* 地址选择器(包括省级、地级、县级)。
* 地址数据见示例项目的“city.json”来源于国家统计局官网http://www.stats.gov.cn/tjsj/tjbz/xzqhdm
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /12/15
*/
public class AddressPicker extends WheelPicker {
private ArrayList<String> provinceList = new ArrayList<String>();
private ArrayList<ArrayList<String>> cityList = new ArrayList<ArrayList<String>>();
private ArrayList<ArrayList<ArrayList<String>>> countyList = new ArrayList<ArrayList<ArrayList<String>>>();
private OnAddressPickListener onAddressPickListener;
private String selectedProvince = "", selectedCity = "", selectedCounty = "";
private int selectedProvinceIndex = 0, selectedCityIndex = 0, selectedCountyIndex = 0;
private boolean hideProvince = false;
private boolean hideCounty = false;
/**
* Instantiates a new Address picker.
*
* @param activity the activity
* @param data the data
*/
public AddressPicker(Activity activity, ArrayList<Province> data) {
super(activity);
int provinceSize = data.size();
//添加省
for (int x = 0; x < provinceSize; x++) {
Province pro = data.get(x);
provinceList.add(pro.getAreaName());
ArrayList<City> cities = pro.getCities();
ArrayList<String> xCities = new ArrayList<String>();
ArrayList<ArrayList<String>> xCounties = new ArrayList<ArrayList<String>>();
int citySize = cities.size();
//添加地市
for (int y = 0; y < citySize; y++) {
City cit = cities.get(y);
xCities.add(cit.getAreaName());
ArrayList<County> counties = cit.getCounties();
ArrayList<String> yCounties = new ArrayList<String>();
int countySize = counties.size();
//添加区县
if (countySize == 0) {
yCounties.add(cit.getAreaName());
} else {
for (int z = 0; z < countySize; z++) {
yCounties.add(counties.get(z).getAreaName());
}
}
xCounties.add(yCounties);
}
cityList.add(xCities);
countyList.add(xCounties);
}
}
/**
* Sets selected item.
*
* @param province the province
* @param city the city
* @param county the county
*/
public void setSelectedItem(String province, String city, String county) {
for (int i = 0; i < provinceList.size(); i++) {
String pro = provinceList.get(i);
if (pro.contains(province)) {
selectedProvinceIndex = i;
LogUtils.debug("init select province: " + pro);
break;
}
}
ArrayList<String> cities = cityList.get(selectedProvinceIndex);
for (int j = 0; j < cities.size(); j++) {
String cit = cities.get(j);
if (cit.contains(city)) {
selectedCityIndex = j;
LogUtils.debug("init select city: " + cit);
break;
}
}
ArrayList<String> counties = countyList.get(selectedProvinceIndex).get(selectedCityIndex);
for (int k = 0; k < counties.size(); k++) {
String cou = counties.get(k);
if (cou.contains(county)) {
selectedCountyIndex = k;
LogUtils.debug("init select county: " + cou);
break;
}
}
LogUtils.debug(String.format("init select index: %s-%s-%s", selectedProvinceIndex, selectedCityIndex, selectedCountyIndex));
}
/**
* 隐藏省级行政区,只显示地市级和区县级。
* 设置为true的话地址数据中只需要某个省份的即可
* 参见示例中的“city2.json”
*
* @param hideProvince the hide province
*/
public void setHideProvince(boolean hideProvince) {
this.hideProvince = hideProvince;
}
/**
* 隐藏县级行政区,只显示省级和市级。
* 设置为true的话hideProvince将强制为false
* 数据源依然使用“city.json” 仅在逻辑上隐藏县级选择框。
*
* @param hideCounty the hide county
*/
public void setHideCounty(boolean hideCounty) {
this.hideCounty = hideCounty;
}
/**
* Sets on address pick listener.
*
* @param listener the listener
*/
public void setOnAddressPickListener(OnAddressPickListener listener) {
this.onAddressPickListener = listener;
}
@Override
@NonNull
protected View makeCenterView() {
if (hideCounty) {
hideProvince = false;
}
if (provinceList.size() == 0) {
throw new IllegalArgumentException("please initial options at first, can't be empty");
}
LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setGravity(Gravity.CENTER);
final WheelView provinceView = new WheelView(activity);
provinceView.setLayoutParams(new LinearLayout.LayoutParams(screenWidthPixels / 3, WRAP_CONTENT));
provinceView.setTextSize(textSize);
provinceView.setTextColor(textColorNormal, textColorFocus);
provinceView.setLineVisible(lineVisible);
provinceView.setLineColor(lineColor);
provinceView.setOffset(offset);
layout.addView(provinceView);
if (hideProvince) {
provinceView.setVisibility(View.GONE);
}
final WheelView cityView = new WheelView(activity);
cityView.setLayoutParams(new LinearLayout.LayoutParams(screenWidthPixels / 3, WRAP_CONTENT));
cityView.setTextSize(textSize);
cityView.setTextColor(textColorNormal, textColorFocus);
cityView.setLineVisible(lineVisible);
cityView.setLineColor(lineColor);
cityView.setOffset(offset);
layout.addView(cityView);
final WheelView countyView = new WheelView(activity);
countyView.setLayoutParams(new LinearLayout.LayoutParams(screenWidthPixels / 3, WRAP_CONTENT));
countyView.setTextSize(textSize);
countyView.setTextColor(textColorNormal, textColorFocus);
countyView.setLineVisible(lineVisible);
countyView.setLineColor(lineColor);
countyView.setOffset(offset);
layout.addView(countyView);
if (hideCounty) {
countyView.setVisibility(View.GONE);
}
provinceView.setItems(provinceList, selectedProvinceIndex);
provinceView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedProvince = item;
selectedProvinceIndex = selectedIndex;
selectedCountyIndex = 0;
//根据省份获取地市
cityView.setItems(cityList.get(selectedProvinceIndex), isUserScroll ? 0 : selectedCityIndex);
//根据地市获取区县
countyView.setItems(countyList.get(selectedProvinceIndex).get(0), isUserScroll ? 0 : selectedCountyIndex);
}
});
cityView.setItems(cityList.get(selectedProvinceIndex), selectedCityIndex);
cityView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedCity = item;
selectedCityIndex = selectedIndex;
//根据地市获取区县
countyView.setItems(countyList.get(selectedProvinceIndex).get(selectedCityIndex), isUserScroll ? 0 : selectedCountyIndex);
}
});
countyView.setItems(countyList.get(selectedProvinceIndex).get(selectedCityIndex), selectedCountyIndex);
countyView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedCounty = item;
selectedCountyIndex = selectedIndex;
}
});
return layout;
}
@Override
public void onSubmit() {
if (onAddressPickListener != null) {
if (hideCounty) {
onAddressPickListener.onAddressPicked(selectedProvince, selectedCity, null);
} else {
onAddressPickListener.onAddressPicked(selectedProvince, selectedCity, selectedCounty);
}
}
}
/**
* The interface On address pick listener.
*/
public interface OnAddressPickListener {
/**
* On address picked.
*
* @param province the province
* @param city the city
* @param county the county if {@hideCounty} is truethis is null
*/
void onAddressPicked(String province, String city, String county);
}
/**
* The type Area.
*/
public abstract static class Area {
/**
* The Area id.
*/
String areaId;
/**
* The Area name.
*/
String areaName;
/**
* Gets area id.
*
* @return the area id
*/
public String getAreaId() {
return areaId;
}
/**
* Sets area id.
*
* @param areaId the area id
*/
public void setAreaId(String areaId) {
this.areaId = areaId;
}
/**
* Gets area name.
*
* @return the area name
*/
public String getAreaName() {
return areaName;
}
/**
* Sets area name.
*
* @param areaName the area name
*/
public void setAreaName(String areaName) {
this.areaName = areaName;
}
@Override
public String toString() {
return "areaId=" + areaId + ",areaName=" + areaName;
}
}
/**
* The type Province.
*/
public static class Province extends Area {
/**
* The Cities.
*/
ArrayList<City> cities = new ArrayList<City>();
/**
* Gets cities.
*
* @return the cities
*/
public ArrayList<City> getCities() {
return cities;
}
/**
* Sets cities.
*
* @param cities the cities
*/
public void setCities(ArrayList<City> cities) {
this.cities = cities;
}
}
/**
* The type City.
*/
public static class City extends Area {
private ArrayList<County> counties = new ArrayList<County>();
/**
* Gets counties.
*
* @return the counties
*/
public ArrayList<County> getCounties() {
return counties;
}
/**
* Sets counties.
*
* @param counties the counties
*/
public void setCounties(ArrayList<County> counties) {
this.counties = counties;
}
}
/**
* The type County.
*/
public static class County extends Area {
}
}

View File

@@ -0,0 +1,35 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
/**
* 生肖选择器
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /12/15
*/
public class ChineseZodiacPicker extends OptionPicker {
/**
* Instantiates a new Chinese zodiac picker.
*
* @param activity the activity
*/
public ChineseZodiacPicker(Activity activity) {
super(activity, new String[]{
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
});
}
}

View File

@@ -0,0 +1,36 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
/**
* 星座选择器
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /12/15
*/
public class ConstellationPicker extends OptionPicker {
/**
* Instantiates a new Constellation picker.
*
* @param activity the activity
*/
public ConstellationPicker(Activity activity) {
super(activity, new String[]{
"水瓶",
"双鱼",
"白羊",
"金牛",
"双子",
"巨蟹",
"狮子",
"处女",
"天秤",
"天蝎",
"射手",
"摩羯",
});
setLabel("");
}
}

View File

@@ -0,0 +1,414 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import cn.qqtheme.framework.util.DateUtils;
import cn.qqtheme.framework.widget.WheelView;
/**
* 日期选择器
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /12/14
*/
public class DatePicker extends WheelPicker {
/**
* 年月日
*/
public static final int YEAR_MONTH_DAY = 0;
/**
* 年月
*/
public static final int YEAR_MONTH = 1;
/**
* 月日
*/
public static final int MONTH_DAY = 2;
private ArrayList<String> years = new ArrayList<String>();
private ArrayList<String> months = new ArrayList<String>();
private ArrayList<String> days = new ArrayList<String>();
private OnDatePickListener onDatePickListener;
private String yearLabel = "", monthLabel = "", dayLabel = "";
private int selectedYearIndex = 0, selectedMonthIndex = 0, selectedDayIndex = 0;
private int mode = YEAR_MONTH_DAY;
/**
* 安卓开发应避免使用枚举类enum因为相比于静态常量enum会花费两倍以上的内存。
*
* @link http ://developer.android.com/training/articles/memory.html#Overhead
*/
@IntDef(flag = false, value = {YEAR_MONTH_DAY, YEAR_MONTH, MONTH_DAY})
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {
}
/**
* Instantiates a new Date picker.
*
* @param activity the activity
*/
public DatePicker(Activity activity) {
this(activity, YEAR_MONTH_DAY);
}
/**
* Instantiates a new Date picker.
*
* @param activity the activity
* @param mode the mode
* @see #YEAR_MONTH_DAY #YEAR_MONTH_DAY#YEAR_MONTH_DAY
* @see #YEAR_MONTH #YEAR_MONTH#YEAR_MONTH
* @see #MONTH_DAY #MONTH_DAY#MONTH_DAY
*/
public DatePicker(Activity activity, @Mode int mode) {
super(activity);
this.mode = mode;
for (int i = 2000; i <= 2050; i++) {
years.add(String.valueOf(i));
}
for (int i = 1; i <= 12; i++) {
months.add(DateUtils.fillZero(i));
}
for (int i = 1; i <= 31; i++) {
days.add(DateUtils.fillZero(i));
}
}
/**
* Sets label.
*
* @param yearLabel the year label
* @param monthLabel the month label
* @param dayLabel the day label
*/
public void setLabel(String yearLabel, String monthLabel, String dayLabel) {
this.yearLabel = yearLabel;
this.monthLabel = monthLabel;
this.dayLabel = dayLabel;
}
/**
* Sets range.
*
* @param startYear the start year
* @param endYear the end year
*/
public void setRange(int startYear, int endYear) {
years.clear();
for (int i = startYear; i <= endYear; i++) {
years.add(String.valueOf(i));
}
}
private int findItemIndex(ArrayList<String> items, int item) {
//折半查找有序元素的索引
int index = Collections.binarySearch(items, item, new Comparator<Object>() {
@Override
public int compare(Object lhs, Object rhs) {
String lhsStr = lhs.toString();
String rhsStr = rhs.toString();
lhsStr = lhsStr.startsWith("0") ? lhsStr.substring(1) : lhsStr;
rhsStr = rhsStr.startsWith("0") ? rhsStr.substring(1) : rhsStr;
return Integer.parseInt(lhsStr) - Integer.parseInt(rhsStr);
}
});
if (index < 0) {
index = 0;
}
return index;
}
/**
* Sets selected item.
*
* @param year the year
* @param month the month
* @param day the day
*/
public void setSelectedItem(int year, int month, int day) {
selectedYearIndex = findItemIndex(years, year);
selectedMonthIndex = findItemIndex(months, month);
selectedDayIndex = findItemIndex(days, day);
}
/**
* Sets selected item.
*
* @param yearOrMonth the year or month
* @param monthOrDay the month or day
*/
public void setSelectedItem(int yearOrMonth, int monthOrDay) {
if (mode == MONTH_DAY) {
selectedMonthIndex = findItemIndex(months, yearOrMonth);
selectedDayIndex = findItemIndex(days, monthOrDay);
} else {
selectedYearIndex = findItemIndex(years, yearOrMonth);
selectedMonthIndex = findItemIndex(months, monthOrDay);
}
}
/**
* Sets on date pick listener.
*
* @param listener the listener
*/
public void setOnDatePickListener(OnDatePickListener listener) {
this.onDatePickListener = listener;
}
@Override
@NonNull
protected View makeCenterView() {
LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setGravity(Gravity.CENTER);
WheelView yearView = new WheelView(activity);
yearView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
yearView.setTextSize(textSize);
yearView.setTextColor(textColorNormal, textColorFocus);
yearView.setLineVisible(lineVisible);
yearView.setLineColor(lineColor);
yearView.setOffset(offset);
layout.addView(yearView);
TextView yearTextView = new TextView(activity);
yearTextView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
yearTextView.setTextSize(textSize);
yearTextView.setTextColor(textColorFocus);
if (!TextUtils.isEmpty(yearLabel)) {
yearTextView.setText(yearLabel);
}
layout.addView(yearTextView);
WheelView monthView = new WheelView(activity);
monthView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
monthView.setTextSize(textSize);
monthView.setTextColor(textColorNormal, textColorFocus);
monthView.setLineVisible(lineVisible);
monthView.setLineColor(lineColor);
monthView.setOffset(offset);
layout.addView(monthView);
TextView monthTextView = new TextView(activity);
monthTextView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
monthTextView.setTextSize(textSize);
monthTextView.setTextColor(textColorFocus);
if (!TextUtils.isEmpty(monthLabel)) {
monthTextView.setText(monthLabel);
}
layout.addView(monthTextView);
final WheelView dayView = new WheelView(activity);
dayView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
dayView.setTextSize(textSize);
dayView.setTextColor(textColorNormal, textColorFocus);
dayView.setLineVisible(lineVisible);
dayView.setLineColor(lineColor);
dayView.setOffset(offset);
layout.addView(dayView);
TextView dayTextView = new TextView(activity);
dayTextView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
dayTextView.setTextSize(textSize);
dayTextView.setTextColor(textColorFocus);
if (!TextUtils.isEmpty(dayLabel)) {
dayTextView.setText(dayLabel);
}
layout.addView(dayTextView);
if (mode == YEAR_MONTH) {
dayView.setVisibility(View.GONE);
dayTextView.setVisibility(View.GONE);
} else if (mode == MONTH_DAY) {
yearView.setVisibility(View.GONE);
yearTextView.setVisibility(View.GONE);
}
if (mode != MONTH_DAY) {
if (!TextUtils.isEmpty(yearLabel)) {
yearTextView.setText(yearLabel);
}
if (selectedYearIndex == 0) {
yearView.setItems(years);
} else {
yearView.setItems(years, selectedYearIndex);
}
yearView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedYearIndex = selectedIndex;
//需要根据年份及月份动态计算天数
days.clear();
int maxDays = DateUtils.calculateDaysInMonth(stringToYearMonthDay(item), stringToYearMonthDay(months.get(selectedMonthIndex)));
for (int i = 1; i <= maxDays; i++) {
days.add(DateUtils.fillZero(i));
}
if (selectedDayIndex >= maxDays) {
//年或月变动时,保持之前选择的日不动:如果之前选择的日是之前年月的最大日,则日自动为该年月的最大日
selectedDayIndex = days.size() - 1;
}
dayView.setItems(days, selectedDayIndex);
}
});
}
if (!TextUtils.isEmpty(monthLabel)) {
monthTextView.setText(monthLabel);
}
if (selectedMonthIndex == 0) {
monthView.setItems(months);
} else {
monthView.setItems(months, selectedMonthIndex);
}
monthView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedMonthIndex = selectedIndex;
if (mode != YEAR_MONTH) {
//年月日或年月模式下,需要根据年份及月份动态计算天数
days.clear();
int maxDays = DateUtils.calculateDaysInMonth(stringToYearMonthDay(years.get(selectedYearIndex)), stringToYearMonthDay(item));
for (int i = 1; i <= maxDays; i++) {
days.add(DateUtils.fillZero(i));
}
if (selectedDayIndex >= maxDays) {
//年或月变动时,保持之前选择的日不动:如果之前选择的日是之前年月的最大日,则日自动为该年月的最大日
selectedDayIndex = days.size() - 1;
}
dayView.setItems(days, selectedDayIndex);
}
}
});
if (mode != YEAR_MONTH) {
if (!TextUtils.isEmpty(dayLabel)) {
dayTextView.setText(dayLabel);
}
if (selectedDayIndex == 0) {
dayView.setItems(days);
} else {
dayView.setItems(days, selectedDayIndex);
}
dayView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedDayIndex = selectedIndex;
}
});
}
return layout;
}
private int stringToYearMonthDay(String text) {
if (text.startsWith("0")) {
//截取掉前缀0以便转换为整数
text = text.substring(1);
}
return Integer.parseInt(text);
}
@Override
protected void onSubmit() {
if (onDatePickListener != null) {
String year = getSelectedYear();
String month = getSelectedMonth();
String day = getSelectedDay();
switch (mode) {
case YEAR_MONTH:
((OnYearMonthPickListener) onDatePickListener).onDatePicked(year, month);
break;
case MONTH_DAY:
((OnMonthDayPickListener) onDatePickListener).onDatePicked(month, day);
break;
default:
((OnYearMonthDayPickListener) onDatePickListener).onDatePicked(year, month, day);
break;
}
}
}
/**
* Gets selected year.
*
* @return the selected year
*/
public String getSelectedYear() {
return years.get(selectedYearIndex);
}
/**
* Gets selected month.
*
* @return the selected month
*/
public String getSelectedMonth() {
return months.get(selectedMonthIndex);
}
/**
* Gets selected day.
*
* @return the selected day
*/
public String getSelectedDay() {
return days.get(selectedDayIndex);
}
/**
* The interface On date pick listener.
*/
protected interface OnDatePickListener {
}
/**
* The interface On year month day pick listener.
*/
public interface OnYearMonthDayPickListener extends OnDatePickListener {
/**
* On date picked.
*
* @param year the year
* @param month the month
* @param day the day
*/
void onDatePicked(String year, String month, String day);
}
/**
* The interface On year month pick listener.
*/
public interface OnYearMonthPickListener extends OnDatePickListener {
/**
* On date picked.
*
* @param year the year
* @param month the month
*/
void onDatePicked(String year, String month);
}
/**
* The interface On month day pick listener.
*/
public interface OnMonthDayPickListener extends OnDatePickListener {
/**
* On date picked.
*
* @param month the month
* @param day the day
*/
void onDatePicked(String month, String day);
}
}

View File

@@ -0,0 +1,370 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import cn.qqtheme.framework.util.LogUtils;
import cn.qqtheme.framework.widget.WheelView;
/**
* 地址选择器(包括省级、地级、县级)。
* 地址数据见示例项目的“city.json”来源于国家统计局官网http://www.stats.gov.cn/tjsj/tjbz/xzqhdm
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /12/15
*/
public class MyTimePicker extends WheelPicker {
private ArrayList<String> provinceList = new ArrayList<String>();
private ArrayList<String> cityList = new ArrayList<String>();
private ArrayList<String> countyList = new ArrayList<String>();
private OnAddressPickListener onAddressPickListener;
private String selectedProvince = "", selectedCity = "", selectedCounty = "";
private int selectedProvinceIndex = 0, selectedCityIndex = 0, selectedCountyIndex = 0;
private boolean hideProvince = false;
private boolean hideCounty = false;
//
// /**
// * Instantiates a new Address picker.
// *
// * @param activity the activity
// * @param data the data
// */
// public MyTimePicker(Activity activity, ArrayList<String> provinceList,ArrayList<String> cityList ,ArrayList<String> countyList) {
// super(activity);
//
//// int provinceSize = data.size();
//// //添加省
//// for (int x = 0; x < provinceSize; x++) {
//// Province pro = data.get(x);
//// provinceList.add(pro.getAreaName());
//// ArrayList<City> cities = pro.getCities();
//// ArrayList<String> xCities = new ArrayList<String>();
//// ArrayList<ArrayList<String>> xCounties = new ArrayList<ArrayList<String>>();
//// int citySize = cities.size();
//// //添加地市
//// for (int y = 0; y < citySize; y++) {
//// City cit = cities.get(y);
//// xCities.add(cit.getAreaName());
//// ArrayList<County> counties = cit.getCounties();
//// ArrayList<String> yCounties = new ArrayList<String>();
//// int countySize = counties.size();
//// //添加区县
//// if (countySize == 0) {
//// yCounties.add(cit.getAreaName());
//// } else {
//// for (int z = 0; z < countySize; z++) {
//// yCounties.add(counties.get(z).getAreaName());
//// }
//// }
//// xCounties.add(yCounties);
//// }
//// cityList.add(xCities);
//// countyList.add(xCounties);
//// }
// }
public MyTimePicker(Activity activity, ArrayList<String> provinceList, ArrayList<String> cityList, ArrayList<String> countyList) {
super(activity);
this.provinceList = provinceList;
this.cityList = cityList;
this.countyList = countyList;
}
/**
* Sets selected item.
*
* @param selectedProvinceIndex the province
* @param selectedCityIndex the city
* @param selectedCountyIndex the county
*/
public void setSelectedItem(int selectedProvinceIndex, int selectedCityIndex,int selectedCountyIndex) {
this.selectedProvinceIndex=selectedProvinceIndex;
this.selectedCityIndex=selectedCityIndex;
this.selectedCountyIndex=selectedCountyIndex;
// LogUtils.debug(String.format("init select index: %s-%s-%s", selectedProvinceIndex, selectedCityIndex, selectedCountyIndex));
}
/**
* 隐藏省级行政区,只显示地市级和区县级。
* 设置为true的话地址数据中只需要某个省份的即可
* 参见示例中的“city2.json”
*
* @param hideProvince the hide province
*/
public void setHideProvince(boolean hideProvince) {
this.hideProvince = hideProvince;
}
/**
* 隐藏县级行政区,只显示省级和市级。
* 设置为true的话hideProvince将强制为false
* 数据源依然使用“city.json” 仅在逻辑上隐藏县级选择框。
*
* @param hideCounty the hide county
*/
public void setHideCounty(boolean hideCounty) {
this.hideCounty = hideCounty;
}
/**
* Sets on address pick listener.
*
* @param listener the listener
*/
public void setOnAddressPickListener(OnAddressPickListener listener) {
this.onAddressPickListener = listener;
}
@Override
@NonNull
protected View makeCenterView() {
if (hideCounty) {
hideProvince = false;
}
if (provinceList.size() == 0) {
throw new IllegalArgumentException("please initial options at first, can't be empty");
}
LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setGravity(Gravity.CENTER);
final WheelView provinceView = new WheelView(activity);
provinceView.setLayoutParams(new LinearLayout.LayoutParams(screenWidthPixels / 3, WRAP_CONTENT));
provinceView.setTextSize(textSize);
provinceView.setTextColor(textColorNormal, textColorFocus);
provinceView.setLineVisible(lineVisible);
provinceView.setLineColor(lineColor);
provinceView.setOffset(offset);
layout.addView(provinceView);
if (hideProvince) {
provinceView.setVisibility(View.GONE);
}
final WheelView cityView = new WheelView(activity);
cityView.setLayoutParams(new LinearLayout.LayoutParams(screenWidthPixels / 3, WRAP_CONTENT));
cityView.setTextSize(textSize);
cityView.setTextColor(textColorNormal, textColorFocus);
cityView.setLineVisible(lineVisible);
cityView.setLineColor(lineColor);
cityView.setOffset(offset);
layout.addView(cityView);
final WheelView countyView = new WheelView(activity);
countyView.setLayoutParams(new LinearLayout.LayoutParams(screenWidthPixels / 3, WRAP_CONTENT));
countyView.setTextSize(textSize);
countyView.setTextColor(textColorNormal, textColorFocus);
countyView.setLineVisible(lineVisible);
countyView.setLineColor(lineColor);
countyView.setOffset(offset);
layout.addView(countyView);
if (hideCounty) {
countyView.setVisibility(View.GONE);
}
provinceView.setItems(provinceList, selectedProvinceIndex);
provinceView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedProvince = item;
selectedProvinceIndex = selectedIndex;
if(0==selectedIndex){
cityView.setItems(cityList.subList(getSelectHours(),cityList.size()),0);
countyView.setItems(countyList.subList(getSelectMinutes(),countyList.size()),0);
}else{
cityView.setItems(cityList,selectedCityIndex);
countyView.setItems(countyList, selectedCountyIndex);
}
//根据省份获取地市
// cityView.setItems(cityList, isUserScroll ? 0 : selectedCityIndex);
// //根据地市获取区县
// countyView.setItems(countyList, isUserScroll ? 0 : selectedCountyIndex);
}
});
// cityView.setItems(cityList,selectedCityIndex);
cityView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedCity = item;
selectedCityIndex = selectedIndex;
//根据地市获取区县
// countyView.setItems(countyList, isUserScroll ? 0 : selectedCountyIndex);
}
});
// countyView.setItems(countyList, selectedCountyIndex);
countyView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedCounty = item;
selectedCountyIndex = selectedIndex;
System.out.println("selectedCountyIndex="+selectedCountyIndex);
}
});
return layout;
}
private int getSelectHours() {
SimpleDateFormat sdf = new SimpleDateFormat("HH时");
Calendar c = Calendar.getInstance();
Date monday = c.getTime();
String preMonday = sdf.format(monday);
return cityList.indexOf(preMonday);
}
private int getSelectMinutes() {
SimpleDateFormat sdf = new SimpleDateFormat("mm分");
Calendar c = Calendar.getInstance();
Date monday = c.getTime();
String preMonday = sdf.format(monday);
return countyList.indexOf(preMonday);
}
@Override
public void onSubmit() {
if (onAddressPickListener != null) {
if (hideCounty) {
onAddressPickListener.onAddressPicked(selectedProvince, selectedCity, null);
} else {
onAddressPickListener.onAddressPicked(selectedProvince, selectedCity, selectedCounty);
}
}
}
/**
* The interface On address pick listener.
*/
public interface OnAddressPickListener {
/**
* On address picked.
*
* @param province the province
* @param city the city
* @param county the county if {@hideCounty} is truethis is null
*/
void onAddressPicked(String province, String city, String county);
}
/**
* The type Area.
*/
public abstract static class Area {
/**
* The Area id.
*/
String areaId;
/**
* The Area name.
*/
String areaName;
/**
* Gets area id.
*
* @return the area id
*/
public String getAreaId() {
return areaId;
}
/**
* Sets area id.
*
* @param areaId the area id
*/
public void setAreaId(String areaId) {
this.areaId = areaId;
}
/**
* Gets area name.
*
* @return the area name
*/
public String getAreaName() {
return areaName;
}
/**
* Sets area name.
*
* @param areaName the area name
*/
public void setAreaName(String areaName) {
this.areaName = areaName;
}
@Override
public String toString() {
return "areaId=" + areaId + ",areaName=" + areaName;
}
}
/**
* The type Province.
*/
public static class Province extends Area {
/**
* The Cities.
*/
ArrayList<City> cities = new ArrayList<City>();
/**
* Gets cities.
*
* @return the cities
*/
public ArrayList<City> getCities() {
return cities;
}
/**
* Sets cities.
*
* @param cities the cities
*/
public void setCities(ArrayList<City> cities) {
this.cities = cities;
}
}
/**
* The type City.
*/
public static class City extends Area {
private ArrayList<County> counties = new ArrayList<County>();
/**
* Gets counties.
*
* @return the counties
*/
public ArrayList<County> getCounties() {
return counties;
}
/**
* Sets counties.
*
* @param counties the counties
*/
public void setCounties(ArrayList<County> counties) {
this.counties = counties;
}
}
/**
* The type County.
*/
public static class County extends Area {
}
}

View File

@@ -0,0 +1,55 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
/**
* 数字选择器
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /10/24
*/
public class NumberPicker extends OptionPicker {
/**
* Instantiates a new Number picker.
*
* @param activity the activity
*/
public NumberPicker(Activity activity) {
super(activity, new String[]{});
}
/**
* Sets range.
*
* @param startNumber the start number
* @param endNumber the end number
*/
public void setRange(int startNumber, int endNumber) {
setRange(startNumber, endNumber, 1);
}
/**
* Sets range.
*
* @param startNumber the start number
* @param endNumber the end number
* @param step the step
*/
public void setRange(int startNumber, int endNumber, int step) {
for (int i = startNumber; i <= endNumber; i = i + step) {
options.add(String.valueOf(i));
}
}
/**
* Sets selected item.
*
* @param number the number
*/
public void setSelectedItem(int number) {
setSelectedItem(String.valueOf(number));
}
}

View File

@@ -0,0 +1,152 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
import cn.qqtheme.framework.widget.WheelView;
/**
* 单项选择器
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /9/29
*/
public class OptionPicker extends WheelPicker {
/**
* The Options.
*/
protected ArrayList<String> options = new ArrayList<String>();
private OnOptionPickListener onOptionPickListener;
private String selectedOption = "";
private String label = "";
/**
* Instantiates a new Option picker.
*
* @param activity the activity
* @param options the options
*/
public OptionPicker(Activity activity, String[] options) {
super(activity);
this.options.addAll(Arrays.asList(options));
}
/**
* Sets label.
*
* @param label the label
*/
public void setLabel(String label) {
this.label = label;
}
/**
* Sets selected index.
*
* @param index the index
*/
public void setSelectedIndex(int index) {
for (int i = 0; i < options.size(); i++) {
if (index == i) {
selectedOption = options.get(index);
break;
}
}
}
/**
* Sets selected item.
*
* @param option the option
*/
public void setSelectedItem(String option) {
selectedOption = option;
}
/**
* Sets on option pick listener.
*
* @param listener the listener
*/
public void setOnOptionPickListener(OnOptionPickListener listener) {
this.onOptionPickListener = listener;
}
@Override
@NonNull
protected View makeCenterView() {
if (options.size() == 0) {
throw new IllegalArgumentException("please initial options at first, can't be empty");
}
LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setGravity(Gravity.CENTER);
WheelView optionView = new WheelView(activity);
optionView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
optionView.setTextSize(textSize);
optionView.setTextColor(textColorNormal, textColorFocus);
optionView.setLineVisible(lineVisible);
optionView.setLineColor(lineColor);
optionView.setOffset(offset);
layout.addView(optionView);
TextView labelView = new TextView(activity);
labelView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
labelView.setTextColor(textColorFocus);
labelView.setTextSize(textSize);
layout.addView(labelView);
if (!TextUtils.isEmpty(label)) {
labelView.setText(label);
}
if (TextUtils.isEmpty(selectedOption)) {
optionView.setItems(options);
} else {
optionView.setItems(options, selectedOption);
}
optionView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedOption = item;
}
});
return layout;
}
@Override
public void onSubmit() {
if (onOptionPickListener != null) {
onOptionPickListener.onOptionPicked(selectedOption);
}
}
/**
* Gets selected option.
*
* @return the selected option
*/
public String getSelectedOption() {
return selectedOption;
}
/**
* The interface On option pick listener.
*/
public interface OnOptionPickListener {
/**
* On option picked.
*
* @param option the option
*/
void onOptionPicked(String option);
}
}

View File

@@ -0,0 +1,33 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
/**
* 性别
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /12/15
*/
public class SexPicker extends OptionPicker {
/**
* Instantiates a new Sex picker.
*
* @param activity the activity
*/
public SexPicker(Activity activity) {
super(activity, new String[]{
"",
"",
"保密"
});
}
/**
* 仅仅提供男和女来选择
*/
public void onlyMaleAndFemale() {
options.remove(options.size() - 1);
}
}

View File

@@ -0,0 +1,213 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Calendar;
import cn.qqtheme.framework.util.DateUtils;
import cn.qqtheme.framework.widget.WheelView;
/**
* 时间选择器
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /12/14
*/
public class TimePicker extends WheelPicker {
/**
* 24小时
*/
public static final int HOUR_OF_DAY = 0;
/**
* 12小时
*/
public static final int HOUR = 1;
private OnTimePickListener onTimePickListener;
private int mode;
private String hourLabel = "", minuteLabel = "";
private String selectedHour = "", selectedMinute = "";
/**
* 安卓开发应避免使用枚举类enum因为相比于静态常量enum会花费两倍以上的内存。
*
* @link http ://developer.android.com/training/articles/memory.html#Overhead
*/
@IntDef(flag = false, value = {HOUR_OF_DAY, HOUR})
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {
}
/**
* Instantiates a new Time picker.
*
* @param activity the activity
*/
public TimePicker(Activity activity) {
this(activity, HOUR_OF_DAY);
}
/**
* Instantiates a new Time picker.
*
* @param activity the activity
* @param mode the mode
* @see #HOUR_OF_DAY #HOUR_OF_DAY#HOUR_OF_DAY
* @see #HOUR #HOUR#HOUR
*/
public TimePicker(Activity activity, @Mode int mode) {
super(activity);
this.mode = mode;
selectedHour = DateUtils.fillZero(Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
selectedMinute = DateUtils.fillZero(Calendar.getInstance().get(Calendar.MINUTE));
}
/**
* Sets label.
*
* @param hourLabel the hour label
* @param minuteLabel the minute label
*/
public void setLabel(String hourLabel, String minuteLabel) {
this.hourLabel = hourLabel;
this.minuteLabel = minuteLabel;
}
/**
* Sets selected item.
*
* @param hour the hour
* @param minute the minute
*/
public void setSelectedItem(int hour, int minute) {
selectedHour = String.valueOf(hour);
selectedMinute = String.valueOf(minute);
}
/**
* Sets on time pick listener.
*
* @param listener the listener
*/
public void setOnTimePickListener(OnTimePickListener listener) {
this.onTimePickListener = listener;
}
@Override
@NonNull
protected View makeCenterView() {
LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setGravity(Gravity.CENTER);
WheelView hourView = new WheelView(activity);
hourView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
hourView.setTextSize(textSize);
hourView.setTextColor(textColorNormal, textColorFocus);
hourView.setLineVisible(lineVisible);
hourView.setLineColor(lineColor);
layout.addView(hourView);
TextView hourTextView = new TextView(activity);
hourTextView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
hourTextView.setTextSize(textSize);
hourTextView.setTextColor(textColorFocus);
if (!TextUtils.isEmpty(hourLabel)) {
hourTextView.setText(hourLabel);
}
layout.addView(hourTextView);
WheelView minuteView = new WheelView(activity);
minuteView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
minuteView.setTextSize(textSize);
minuteView.setTextColor(textColorNormal, textColorFocus);
minuteView.setLineVisible(lineVisible);
minuteView.setLineColor(lineColor);
minuteView.setOffset(offset);
layout.addView(minuteView);
TextView minuteTextView = new TextView(activity);
minuteTextView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
minuteTextView.setTextSize(textSize);
minuteTextView.setTextColor(textColorFocus);
if (!TextUtils.isEmpty(minuteLabel)) {
minuteTextView.setText(minuteLabel);
}
layout.addView(minuteTextView);
ArrayList<String> hours = new ArrayList<String>();
if (mode == HOUR) {
for (int i = 1; i <= 12; i++) {
hours.add(DateUtils.fillZero(i));
}
} else {
for (int i = 0; i < 24; i++) {
hours.add(DateUtils.fillZero(i));
}
}
hourView.setItems(hours, selectedHour);
ArrayList<String> minutes = new ArrayList<String>();
for (int i = 0; i < 60; i++) {
minutes.add(DateUtils.fillZero(i));
}
minuteView.setItems(minutes, selectedMinute);
hourView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedHour = item;
}
});
minuteView.setOnWheelViewListener(new WheelView.OnWheelViewListener() {
@Override
public void onSelected(boolean isUserScroll, int selectedIndex, String item) {
selectedMinute = item;
}
});
return layout;
}
@Override
public void onSubmit() {
if (onTimePickListener != null) {
onTimePickListener.onTimePicked(selectedHour, selectedMinute);
}
}
/**
* Gets selected hour.
*
* @return the selected hour
*/
public String getSelectedHour() {
return selectedHour;
}
/**
* Gets selected minute.
*
* @return the selected minute
*/
public String getSelectedMinute() {
return selectedMinute;
}
/**
* The interface On time pick listener.
*/
public interface OnTimePickListener {
/**
* On time picked.
*
* @param hour the hour
* @param minute the minute
*/
void onTimePicked(String hour, String minute);
}
}

View File

@@ -0,0 +1,108 @@
package cn.qqtheme.framework.picker;
import android.app.Activity;
import android.support.annotation.ColorInt;
import android.support.annotation.IntRange;
import android.view.View;
import cn.qqtheme.framework.popup.ConfirmPopup;
import cn.qqtheme.framework.widget.WheelView;
/**
* 滑轮选择器
*
* @author 李玉江[QQ :1032694760]
* @version 2015 /12/22
*/
public abstract class WheelPicker extends ConfirmPopup<View> {
/**
* The Text size.
*/
protected int textSize = WheelView.TEXT_SIZE;
/**
* The Text color normal.
*/
protected int textColorNormal = WheelView.TEXT_COLOR_NORMAL;
/**
* The Text color focus.
*/
protected int textColorFocus = WheelView.TEXT_COLOR_FOCUS;
/**
* The Line color.
*/
protected int lineColor = WheelView.LINE_COLOR;
/**
* The Line visible.
*/
protected boolean lineVisible = true;
/**
* The Offset.
*/
protected int offset = WheelView.OFF_SET;
/**
* Instantiates a new Wheel picker.
*
* @param activity the activity
*/
public WheelPicker(Activity activity) {
super(activity);
}
/**
* Sets text size.
*
* @param textSize the text size
*/
public void setTextSize(int textSize) {
this.textSize = textSize;
}
/**
* Sets text color.
*
* @param textColorFocus the text color focus
* @param textColorNormal the text color normal
*/
public void setTextColor(@ColorInt int textColorFocus, @ColorInt int textColorNormal) {
this.textColorFocus = textColorFocus;
this.textColorNormal = textColorNormal;
}
/**
* Sets text color.
*
* @param textColor the text color
*/
public void setTextColor(@ColorInt int textColor) {
this.textColorFocus = textColor;
}
/**
* Sets line visible.
*
* @param lineVisible the line visible
*/
public void setLineVisible(boolean lineVisible) {
this.lineVisible = lineVisible;
}
/**
* Sets line color.
*
* @param lineColor the line color
*/
public void setLineColor(@ColorInt int lineColor) {
this.lineColor = lineColor;
}
/**
* Sets offset.
*
* @param offset the offset
*/
public void setOffset(@IntRange(from = 1, to = 4) int offset) {
this.offset = offset;
}
}

View File

@@ -0,0 +1,617 @@
package cn.qqtheme.framework.widget;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import cn.qqtheme.framework.util.LogUtils;
/**
* 基于原版作了一下修改:
* 去掉回弹阴影
* 修正以便支持联动效果
* 可设置颜色
* 设置文字大小
* 分隔线是否可见
* 初始设置选中选项
*
* @author 李玉江[QQ :1023694760]
* @version 2015 -12-17
* @link https ://github.com/wangjiegulu/WheelView
*/
public class WheelView extends ScrollView {
/**
* The constant TEXT_SIZE.
*/
public static final int TEXT_SIZE = 20;
/**
* The constant TEXT_COLOR_FOCUS.
*/
public static final int TEXT_COLOR_FOCUS = 0XFF0288CE;
/**
* The constant TEXT_COLOR_NORMAL.
*/
public static final int TEXT_COLOR_NORMAL = 0XFFBBBBBB;
/**
* The constant LINE_COLOR.
*/
public static final int LINE_COLOR = 0XFF83CDE6;
/**
* The constant OFF_SET.
*/
public static final int OFF_SET = 1;
private static final int DELAY = 50;
private Context context;
private LinearLayout views;
private List<String> items = new ArrayList<String>();
private int offset = OFF_SET; // 偏移量(需要在最前面和最后面补全)
private int displayItemCount; // 每页显示的数量
private int selectedIndex = OFF_SET;
private int initialY;
private Runnable scrollerTask = new ScrollerTask();
private int itemHeight = 0;
private int[] selectedAreaBorder;//获取选中区域的边界
private OnWheelViewListener onWheelViewListener;
private Paint paint;
private int viewWidth;
private int textSize = TEXT_SIZE;
private int textColorNormal = TEXT_COLOR_NORMAL;
private int textColorFocus = TEXT_COLOR_FOCUS;
private int lineColor = LINE_COLOR;
private boolean lineVisible = true;
private boolean isUserScroll = false;//是否用户手动滚动
private float previousY = 0;//记录按下时的Y坐标
/**
* Instantiates a new Wheel view.
*
* @param context the context
*/
public WheelView(Context context) {
super(context);
init(context);
}
/**
* Instantiates a new Wheel view.
*
* @param context the context
* @param attrs the attrs
*/
public WheelView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* Instantiates a new Wheel view.
*
* @param context the context
* @param attrs the attrs
* @param defStyle the def style
*/
public WheelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
this.context = context;
// 2015/12/15 去掉ScrollView的阴影
setFadingEdgeLength(0);
if (Build.VERSION.SDK_INT >= 9) {
setOverScrollMode(OVER_SCROLL_NEVER);
}
setVerticalScrollBarEnabled(false);
views = new LinearLayout(context);
views.setOrientation(LinearLayout.VERTICAL);
addView(views);
}
private void startScrollerTask() {
initialY = getScrollY();
postDelayed(scrollerTask, DELAY);
}
private void initData() {
displayItemCount = offset * 2 + 1;
// 2015/12/15 添加此句才可以支持联动效果
views.removeAllViews();
for (String item : items) {
views.addView(createView(item));
}
// 2016/1/15 焦点文字颜色高亮位置逆推“int position = y / itemHeight + offset”
refreshItemView(itemHeight * (selectedIndex - offset));
}
private TextView createView(String item) {
TextView tv = new TextView(context);
tv.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
tv.setSingleLine(true);
tv.setEllipsize(TextUtils.TruncateAt.END);
tv.setText(item);
tv.setTextSize(textSize);
tv.setGravity(Gravity.CENTER);
int padding = dip2px(15);
tv.setPadding(padding, padding, padding, padding);
if (0 == itemHeight) {
itemHeight = getViewMeasuredHeight(tv);
LogUtils.debug(this, "itemHeight: " + itemHeight);
views.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, itemHeight * displayItemCount));
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) this.getLayoutParams();
setLayoutParams(new LinearLayout.LayoutParams(lp.width, itemHeight * displayItemCount));
}
return tv;
}
private void refreshItemView(int y) {
int position = y / itemHeight + offset;
int remainder = y % itemHeight;
int divided = y / itemHeight;
if (remainder == 0) {
position = divided + offset;
} else {
if (remainder > itemHeight / 2) {
position = divided + offset + 1;
}
}
int childSize = views.getChildCount();
for (int i = 0; i < childSize; i++) {
TextView itemView = (TextView) views.getChildAt(i);
if (null == itemView) {
return;
}
// 2015/12/15 可设置颜色
if (position == i) {
itemView.setTextColor(textColorFocus);
} else {
itemView.setTextColor(textColorNormal);
}
}
}
private int[] obtainSelectedAreaBorder() {
if (null == selectedAreaBorder) {
selectedAreaBorder = new int[2];
selectedAreaBorder[0] = itemHeight * offset;
selectedAreaBorder[1] = itemHeight * (offset + 1);
}
return selectedAreaBorder;
}
/**
* 选中回调
*/
private void onSelectedCallBack() {
if (null != onWheelViewListener) {
// 2015/12/25 真实的index应该忽略偏移量
onWheelViewListener.onSelected(isUserScroll, selectedIndex - offset, items.get(selectedIndex));
}
}
private int dip2px(float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
private int getViewMeasuredHeight(View view) {
int width = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
view.measure(width, expandSpec);
return view.getMeasuredHeight();
}
@Override
public void setBackground(Drawable background) {
setBackgroundDrawable(background);
}
@SuppressWarnings("deprecation")
@Override
public void setBackgroundDrawable(Drawable background) {
if (viewWidth == 0) {
viewWidth = ((Activity) context).getWindowManager().getDefaultDisplay().getWidth();
LogUtils.debug(this, "viewWidth: " + viewWidth);
}
// 2015/12/22 可设置分隔线是否可见
if (!lineVisible) {
return;
}
if (null == paint) {
paint = new Paint();
paint.setColor(lineColor);
paint.setStrokeWidth(dip2px(1f));
}
background = new Drawable() {
@Override
public void draw(Canvas canvas) {
int[] areaBorder = obtainSelectedAreaBorder();
canvas.drawLine(0, areaBorder[0], viewWidth , areaBorder[0], paint);
canvas.drawLine(0, areaBorder[1], viewWidth , areaBorder[1], paint);
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter cf) {
}
@Override
public int getOpacity() {
return 0;
}
};
super.setBackgroundDrawable(background);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
refreshItemView(t);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
LogUtils.debug(this, "w: " + w + ", h: " + h + ", oldw: " + oldw + ", oldh: " + oldh);
viewWidth = w;
setBackgroundDrawable(null);
}
@Override
public void fling(int velocityY) {
super.fling(velocityY / 3);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
previousY = ev.getY();
break;
case MotionEvent.ACTION_UP:
LogUtils.debug(this, String.format("items=%s, offset=%s", items.size(), offset));
LogUtils.debug(this, "selectedIndex=" + selectedIndex);
float delta = ev.getY() - previousY;
LogUtils.debug(this, "delta=" + delta);
if (selectedIndex == offset && delta > 0) {
//滑动到第一项时,若继续向下滑动,则自动跳到最后一项
setSelectedIndex(items.size() - offset * 2 - 1);
} else if (selectedIndex == items.size() - offset - 1 && delta < 0) {
//滑动到最后一项时,若继续向上滑动,则自动跳到第一项
setSelectedIndex(0);
} else {
isUserScroll = true;
startScrollerTask();
}
break;
}
return super.onTouchEvent(ev);
}
private void _setItems(List<String> list) {
items.clear();
items.addAll(list);
// 前面和后面补全
for (int i = 0; i < offset; i++) {
items.add(0, "");
items.add("");
}
initData();
}
/**
* Sets items.
*
* @param list the list
*/
public void setItems(List<String> list) {
_setItems(list);
// 2015/12/25 初始化时设置默认选中项
setSelectedIndex(0);
}
/**
* Sets items.
*
* @param list the list
* @param index the index
*/
public void setItems(List<String> list, int index) {
_setItems(list);
setSelectedIndex(index);
}
/**
* Sets items.
*
* @param list the list
* @param item the item
*/
public void setItems(List<String> list, String item) {
_setItems(list);
setSelectedItem(item);
}
/**
* Gets text size.
*
* @return the text size
*/
public int getTextSize() {
return textSize;
}
/**
* Sets text size.
*
* @param textSize the text size
*/
public void setTextSize(int textSize) {
this.textSize = textSize;
}
/**
* Gets text color.
*
* @return the text color
*/
public int getTextColor() {
return textColorFocus;
}
/**
* Sets text color.
*
* @param textColorNormal the text color normal
* @param textColorFocus the text color focus
*/
public void setTextColor(@ColorInt int textColorNormal, @ColorInt int textColorFocus) {
this.textColorNormal = textColorNormal;
this.textColorFocus = textColorFocus;
}
/**
* Sets text color.
*
* @param textColor the text color
*/
public void setTextColor(@ColorInt int textColor) {
this.textColorFocus = textColor;
}
/**
* Is line visible boolean.
*
* @return the boolean
*/
public boolean isLineVisible() {
return lineVisible;
}
/**
* Sets line visible.
*
* @param lineVisible the line visible
*/
public void setLineVisible(boolean lineVisible) {
this.lineVisible = lineVisible;
}
/**
* Gets line color.
*
* @return the line color
*/
public int getLineColor() {
return lineColor;
}
/**
* Sets line color.
*
* @param lineColor the line color
*/
public void setLineColor(@ColorInt int lineColor) {
this.lineColor = lineColor;
}
/**
* Gets offset.
*
* @return the offset
*/
public int getOffset() {
return offset;
}
/**
* Sets offset.
*
* @param offset the offset
*/
public void setOffset(int offset) {
if (offset < 1 || offset > 4) {
throw new IllegalArgumentException("Offset must between 1 and 4");
}
this.offset = offset;
}
/**
* 从0开始计数所有项包括偏移量
*
* @param index
*/
private void setSelectedIndex(final int index) {
isUserScroll = false;
this.post(new Runnable() {
@Override
public void run() {
//滚动到选中项的位置
smoothScrollTo(0, index * itemHeight);
//选中这一项的值
selectedIndex = index + offset;
onSelectedCallBack();
}
});
}
/**
* Sets selected item.
*
* @param item the item
*/
public void setSelectedItem(String item) {
for (int i = 0; i < items.size(); i++) {
if (items.get(i).equals(item)) {
//调用_setItems(List)时额外添加了offset个占位符到items里需要忽略占位符所占的位
setSelectedIndex(i - offset);
break;
}
}
}
/**
* Use {@link #getSelectedItem()} instead
*/
@Deprecated
public String getSeletedItem() {
return getSelectedItem();
}
/**
* Gets selected item.
*
* @return the selected item
*/
public String getSelectedItem() {
return items.get(selectedIndex);
}
/**
* Use {@link #getSelectedIndex()} instead
*/
@Deprecated
public int getSeletedIndex() {
return getSelectedIndex();
}
/**
* Gets selected index.
*
* @return the selected index
*/
public int getSelectedIndex() {
return selectedIndex - offset;
}
/**
* Sets on wheel view listener.
*
* @param onWheelViewListener the on wheel view listener
*/
public void setOnWheelViewListener(OnWheelViewListener onWheelViewListener) {
this.onWheelViewListener = onWheelViewListener;
}
/**
* The interface On wheel view listener.
*/
public interface OnWheelViewListener {
/**
* On selected.
*
* @param isUserScroll the is user scroll
* @param selectedIndex the selected index
* @param item the item
*/
void onSelected(boolean isUserScroll, int selectedIndex, String item);
}
private class ScrollerTask implements Runnable {
@Override
public void run() {
// 2015/12/17 java.lang.ArithmeticException: divide by zero
if (itemHeight == 0) {
LogUtils.debug(this, "itemHeight is zero");
return;
}
int newY = getScrollY();
if (initialY - newY == 0) { // stopped
final int remainder = initialY % itemHeight;
final int divided = initialY / itemHeight;
LogUtils.debug(this, "initialY: " + initialY + ", remainder: " + remainder + ", divided: " + divided);
if (remainder == 0) {
selectedIndex = divided + offset;
onSelectedCallBack();
} else {
if (remainder > itemHeight / 2) {
post(new Runnable() {
@Override
public void run() {
smoothScrollTo(0, initialY - remainder + itemHeight);
selectedIndex = divided + offset + 1;
onSelectedCallBack();
}
});
} else {
post(new Runnable() {
@Override
public void run() {
smoothScrollTo(0, initialY - remainder);
selectedIndex = divided + offset;
onSelectedCallBack();
}
});
}
}
} else {
startScrollerTask();
}
}
}
}

5
publish.cmd Normal file
View File

@@ -0,0 +1,5 @@
@rem C:\Users\Administrator\.m2\repository
gradlew publishToMavenLocal --info
@rem http://jcenter.bintray.com
@rem gradlew bintrayUpload --info

BIN
screenshots/address.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

BIN
screenshots/address.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
screenshots/color.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

BIN
screenshots/custom.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

BIN
screenshots/date.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

BIN
screenshots/dir.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

BIN
screenshots/file.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

BIN
screenshots/number.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

BIN
screenshots/option.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

BIN
screenshots/time.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

5
settings.gradle Normal file
View File

@@ -0,0 +1,5 @@
include ':app'
include ':library:Common'
include ':library:WheelPicker'
include ':library:FilePicker'
include ':library:ColorPicker'

View File

@@ -0,0 +1,23 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 18
buildToolsVersion "19.1.0"
defaultConfig {
applicationId "com.zf.iosdialog"
minSdkVersion 8
targetSdkVersion 15
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
dependencies {
compile 'com.android.support:support-v4:18.+'
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zf.iosdialog"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<application
android:allowBackup="true"
android:icon="@drawable/app_icon"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.zf.iosdialog.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
/*
* Android Wheel Control.
* https://code.google.com/p/android-wheel/
*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity;
/**
* Range for visible items.
*/
public class ItemsRange {
// First item number
private int first;
// Items count
private int count;
/**
* Default constructor. Creates an empty range
*/
public ItemsRange() {
this(0, 0);
}
/**
* Constructor
* @param first the number of first item
* @param count the count of items
*/
public ItemsRange(int first, int count) {
this.first = first;
this.count = count;
}
/**
* Gets number of first item
* @return the number of the first item
*/
public int getFirst() {
return first;
}
/**
* Gets number of last item
* @return the number of last item
*/
public int getLast() {
return getFirst() + getCount() - 1;
}
/**
* Get items count
* @return the count of items
*/
public int getCount() {
return count;
}
/**
* Tests whether item is contained by range
* @param index the item number
* @return true if item is contained
*/
public boolean contains(int index) {
return index >= getFirst() && index <= getLast();
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity;
/**
* Wheel changed listener interface.
* <p>The onChanged() method is called whenever current wheel positions is changed:
* <li> New Wheel position is set
* <li> Wheel view is scrolled
*/
public interface OnWheelChangedListener {
/**
* Callback method to be invoked when current item changed
* @param wheel the wheel view whose state has changed
* @param oldValue the old value of current item
* @param newValue the new value of current item
*/
void onChanged(WheelView wheel, int oldValue, int newValue);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity;
/**
* Wheel clicked listener interface.
* <p>The onItemClicked() method is called whenever a wheel item is clicked
* <li> New Wheel position is set
* <li> Wheel view is scrolled
*/
public interface OnWheelClickedListener {
/**
* Callback method to be invoked when current item clicked
* @param wheel the wheel view
* @param itemIndex the index of clicked item
*/
void onItemClicked(WheelView wheel, int itemIndex);
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2010 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity;
/**
* Wheel scrolled listener interface.
*/
public interface OnWheelScrollListener {
/**
* Callback method to be invoked when scrolling started.
* @param wheel the wheel view whose state has changed.
*/
void onScrollingStarted(WheelView wheel);
/**
* Callback method to be invoked when scrolling ended.
* @param wheel the wheel view whose state has changed.
*/
void onScrollingFinished(WheelView wheel);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2010 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity;
/**
* Wheel adapter interface
*
* @deprecated Use WheelViewAdapter
*/
public interface WheelAdapter {
/**
* Gets items count
* @return the count of wheel items
*/
public int getItemsCount();
/**
* Gets a wheel item by index.
*
* @param index the item index
* @return the wheel item text or null
*/
public String getItem(int index);
/**
* Gets maximum item length. It is used to determine the wheel width.
* If -1 is returned there will be used the default wheel width.
*
* @return the maximum item length or -1
*/
public int getMaximumLength();
}

View File

@@ -0,0 +1,153 @@
/*
* Android Wheel Control.
* https://code.google.com/p/android-wheel/
*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity;
import java.util.LinkedList;
import java.util.List;
import android.view.View;
import android.widget.LinearLayout;
/**
* Recycle stores wheel items to reuse.
*/
public class WheelRecycle {
// Cached items
private List<View> items;
// Cached empty items
private List<View> emptyItems;
// Wheel view
private WheelView wheel;
/**
* Constructor
* @param wheel the wheel view
*/
public WheelRecycle(WheelView wheel) {
this.wheel = wheel;
}
/**
* Recycles items from specified layout.
* There are saved only items not included to specified range.
* All the cached items are removed from original layout.
*
* @param layout the layout containing items to be cached
* @param firstItem the number of first item in layout
* @param range the range of current wheel items
* @return the new value of first item number
*/
public int recycleItems(LinearLayout layout, int firstItem, ItemsRange range) {
int index = firstItem;
for (int i = 0; i < layout.getChildCount();) {
if (!range.contains(index)) {
recycleView(layout.getChildAt(i), index);
layout.removeViewAt(i);
if (i == 0) { // first item
firstItem++;
}
} else {
i++; // go to next item
}
index++;
}
return firstItem;
}
/**
* Gets item view
* @return the cached view
*/
public View getItem() {
return getCachedView(items);
}
/**
* Gets empty item view
* @return the cached empty view
*/
public View getEmptyItem() {
return getCachedView(emptyItems);
}
/**
* Clears all views
*/
public void clearAll() {
if (items != null) {
items.clear();
}
if (emptyItems != null) {
emptyItems.clear();
}
}
/**
* Adds view to specified cache. Creates a cache list if it is null.
* @param view the view to be cached
* @param cache the cache list
* @return the cache list
*/
private List<View> addView(View view, List<View> cache) {
if (cache == null) {
cache = new LinkedList<View>();
}
cache.add(view);
return cache;
}
/**
* Adds view to cache. Determines view type (item view or empty one) by index.
* @param view the view to be cached
* @param index the index of view
*/
private void recycleView(View view, int index) {
int count = wheel.getViewAdapter().getItemsCount();
if ((index < 0 || index >= count) && !wheel.isCyclic()) {
// empty view
emptyItems = addView(view, emptyItems);
} else {
while (index < 0) {
index = count + index;
}
index %= count;
items = addView(view, items);
}
}
/**
* Gets view from specified cache.
* @param cache the cache
* @return the first view from cache.
*/
private View getCachedView(List<View> cache) {
if (cache != null && cache.size() > 0) {
View view = cache.get(0);
cache.remove(0);
return view;
}
return null;
}
}

View File

@@ -0,0 +1,252 @@
/*
* Android Wheel Control.
* https://code.google.com/p/android-wheel/
*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.animation.Interpolator;
import android.widget.Scroller;
/**
* Scroller class handles scrolling events and updates the
*/
public class WheelScroller {
/**
* Scrolling listener interface
*/
public interface ScrollingListener {
/**
* Scrolling callback called when scrolling is performed.
* @param distance the distance to scroll
*/
void onScroll(int distance);
/**
* Starting callback called when scrolling is started
*/
void onStarted();
/**
* Finishing callback called after justifying
*/
void onFinished();
/**
* Justifying callback called to justify a view when scrolling is ended
*/
void onJustify();
}
/** Scrolling duration */
private static final int SCROLLING_DURATION = 400;
/** Minimum delta for scrolling */
public static final int MIN_DELTA_FOR_SCROLLING = 1;
// Listener
private ScrollingListener listener;
// Context
private Context context;
// Scrolling
private GestureDetector gestureDetector;
private Scroller scroller;
private int lastScrollY;
private float lastTouchedY;
private boolean isScrollingPerformed;
/**
* Constructor
* @param context the current context
* @param listener the scrolling listener
*/
public WheelScroller(Context context, ScrollingListener listener) {
gestureDetector = new GestureDetector(context, gestureListener);
gestureDetector.setIsLongpressEnabled(false);
scroller = new Scroller(context);
this.listener = listener;
this.context = context;
}
/**
* Set the the specified scrolling interpolator
* @param interpolator the interpolator
*/
public void setInterpolator(Interpolator interpolator) {
scroller.forceFinished(true);
scroller = new Scroller(context, interpolator);
}
/**
* Scroll the wheel
* @param distance the scrolling distance
* @param time the scrolling duration
*/
public void scroll(int distance, int time) {
scroller.forceFinished(true);
lastScrollY = 0;
scroller.startScroll(0, 0, 0, distance, time != 0 ? time : SCROLLING_DURATION);
setNextMessage(MESSAGE_SCROLL);
startScrolling();
}
/**
* Stops scrolling
*/
public void stopScrolling() {
scroller.forceFinished(true);
}
/**
* Handles Touch event
* @param event the motion event
* @return
*/
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastTouchedY = event.getY();
scroller.forceFinished(true);
clearMessages();
break;
case MotionEvent.ACTION_MOVE:
// perform scrolling
int distanceY = (int)(event.getY() - lastTouchedY);
if (distanceY != 0) {
startScrolling();
listener.onScroll(distanceY);
lastTouchedY = event.getY();
}
break;
}
if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) {
justify();
}
return true;
}
// gesture listener
private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() {
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// Do scrolling in onTouchEvent() since onScroll() are not call immediately
// when user touch and move the wheel
return true;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
lastScrollY = 0;
final int maxY = 0x7FFFFFFF;
final int minY = -maxY;
scroller.fling(0, lastScrollY, 0, (int) -velocityY, 0, 0, minY, maxY);
setNextMessage(MESSAGE_SCROLL);
return true;
}
};
// Messages
private final int MESSAGE_SCROLL = 0;
private final int MESSAGE_JUSTIFY = 1;
/**
* Set next message to queue. Clears queue before.
*
* @param message the message to set
*/
private void setNextMessage(int message) {
clearMessages();
animationHandler.sendEmptyMessage(message);
}
/**
* Clears messages from queue
*/
private void clearMessages() {
animationHandler.removeMessages(MESSAGE_SCROLL);
animationHandler.removeMessages(MESSAGE_JUSTIFY);
}
// animation handler
private Handler animationHandler = new Handler() {
public void handleMessage(Message msg) {
scroller.computeScrollOffset();
int currY = scroller.getCurrY();
int delta = lastScrollY - currY;
lastScrollY = currY;
if (delta != 0) {
listener.onScroll(delta);
}
// scrolling is not finished when it comes to final Y
// so, finish it manually
if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) {
currY = scroller.getFinalY();
scroller.forceFinished(true);
}
if (!scroller.isFinished()) {
animationHandler.sendEmptyMessage(msg.what);
} else if (msg.what == MESSAGE_SCROLL) {
justify();
} else {
finishScrolling();
}
}
};
/**
* Justifies wheel
*/
private void justify() {
listener.onJustify();
setNextMessage(MESSAGE_JUSTIFY);
}
/**
* Starts scrolling
*/
private void startScrolling() {
if (!isScrollingPerformed) {
isScrollingPerformed = true;
listener.onStarted();
}
}
/**
* Finishes scrolling
*/
void finishScrolling() {
if (isScrollingPerformed) {
listener.onFinished();
isScrollingPerformed = false;
}
}
}

View File

@@ -0,0 +1,955 @@
/*
* Android Wheel Control.
* https://code.google.com/p/android-wheel/
*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity;
import java.util.LinkedList;
import java.util.List;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable.Orientation;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import com.gghl.view.wheelcity.adapters.WheelViewAdapter;
import com.zf.iosdialog.R;
/**
* Numeric wheel view.
*
* @author Yuri Kanivets
*/
public class WheelView extends View {
/** Top and bottom shadows colors */
private static final int[] SHADOWS_COLORS = new int[] { 0xFFFFFFFF,
0x00FFFFFF, 0x00FFFFFF };
/** Top and bottom items offset (to hide that) */
private static final int ITEM_OFFSET_PERCENT = 10;
/** Left and right padding value */
private static final int PADDING = 10;
/** Default count of visible items */
private static final int DEF_VISIBLE_ITEMS = 5;
// Wheel Values
private int currentItem = 0;
// Count of visible items
private int visibleItems = DEF_VISIBLE_ITEMS;
// Item height
private int itemHeight = 0;
// Center Line
private Drawable centerDrawable;
// Shadows drawables
private GradientDrawable topShadow;
private GradientDrawable bottomShadow;
// Scrolling
private WheelScroller scroller;
private boolean isScrollingPerformed;
private int scrollingOffset;
// Cyclic
boolean isCyclic = false;
// Items layout
private LinearLayout itemsLayout;
// The number of first item in layout
private int firstItem;
// View adapter
private WheelViewAdapter viewAdapter;
// Recycle
private WheelRecycle recycle = new WheelRecycle(this);
// Listeners
private List<OnWheelChangedListener> changingListeners = new LinkedList<OnWheelChangedListener>();
private List<OnWheelScrollListener> scrollingListeners = new LinkedList<OnWheelScrollListener>();
private List<OnWheelClickedListener> clickingListeners = new LinkedList<OnWheelClickedListener>();
/**
* Constructor
*/
public WheelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initData(context);
}
/**
* Constructor
*/
public WheelView(Context context, AttributeSet attrs) {
super(context, attrs);
initData(context);
}
/**
* Constructor
*/
public WheelView(Context context) {
super(context);
initData(context);
}
/**
* Initializes class data
*
* @param context
* the context
*/
private void initData(Context context) {
scroller = new WheelScroller(getContext(), scrollingListener);
}
// Scrolling listener
WheelScroller.ScrollingListener scrollingListener = new WheelScroller.ScrollingListener() {
public void onStarted() {
isScrollingPerformed = true;
notifyScrollingListenersAboutStart();
}
public void onScroll(int distance) {
doScroll(distance);
int height = getHeight();
if (scrollingOffset > height) {
scrollingOffset = height;
scroller.stopScrolling();
} else if (scrollingOffset < -height) {
scrollingOffset = -height;
scroller.stopScrolling();
}
}
public void onFinished() {
if (isScrollingPerformed) {
notifyScrollingListenersAboutEnd();
isScrollingPerformed = false;
}
scrollingOffset = 0;
invalidate();
}
public void onJustify() {
if (Math.abs(scrollingOffset) > WheelScroller.MIN_DELTA_FOR_SCROLLING) {
scroller.scroll(scrollingOffset, 0);
}
}
};
/**
* Set the the specified scrolling interpolator
*
* @param interpolator
* the interpolator
*/
public void setInterpolator(Interpolator interpolator) {
scroller.setInterpolator(interpolator);
}
/**
* Gets count of visible items
*
* @return the count of visible items
*/
public int getVisibleItems() {
return visibleItems;
}
/**
* Sets the desired count of visible items. Actual amount of visible items
* depends on wheel layout parameters. To apply changes and rebuild view
* call measure().
*
* @param count
* the desired count for visible items
*/
public void setVisibleItems(int count) {
visibleItems = count;
}
/**
* Gets view adapter
*
* @return the view adapter
*/
public WheelViewAdapter getViewAdapter() {
return viewAdapter;
}
// Adapter listener
private DataSetObserver dataObserver = new DataSetObserver() {
@Override
public void onChanged() {
invalidateWheel(false);
}
@Override
public void onInvalidated() {
invalidateWheel(true);
}
};
/**
* Sets view adapter. Usually new adapters contain different views, so it
* needs to rebuild view by calling measure().
*
* @param viewAdapter
* the view adapter
*/
public void setViewAdapter(WheelViewAdapter viewAdapter) {
if (this.viewAdapter != null) {
this.viewAdapter.unregisterDataSetObserver(dataObserver);
}
this.viewAdapter = viewAdapter;
if (this.viewAdapter != null) {
this.viewAdapter.registerDataSetObserver(dataObserver);
}
invalidateWheel(true);
}
/**
* Adds wheel changing listener
*
* @param listener
* the listener
*/
public void addChangingListener(OnWheelChangedListener listener) {
changingListeners.add(listener);
}
/**
* Removes wheel changing listener
*
* @param listener
* the listener
*/
public void removeChangingListener(OnWheelChangedListener listener) {
changingListeners.remove(listener);
}
/**
* Notifies changing listeners
*
* @param oldValue
* the old wheel value
* @param newValue
* the new wheel value
*/
protected void notifyChangingListeners(int oldValue, int newValue) {
for (OnWheelChangedListener listener : changingListeners) {
listener.onChanged(this, oldValue, newValue);
}
}
/**
* Adds wheel scrolling listener
*
* @param listener
* the listener
*/
public void addScrollingListener(OnWheelScrollListener listener) {
scrollingListeners.add(listener);
}
/**
* Removes wheel scrolling listener
*
* @param listener
* the listener
*/
public void removeScrollingListener(OnWheelScrollListener listener) {
scrollingListeners.remove(listener);
}
/**
* Notifies listeners about starting scrolling
*/
protected void notifyScrollingListenersAboutStart() {
for (OnWheelScrollListener listener : scrollingListeners) {
listener.onScrollingStarted(this);
}
}
/**
* Notifies listeners about ending scrolling
*/
protected void notifyScrollingListenersAboutEnd() {
for (OnWheelScrollListener listener : scrollingListeners) {
listener.onScrollingFinished(this);
}
}
/**
* Adds wheel clicking listener
*
* @param listener
* the listener
*/
public void addClickingListener(OnWheelClickedListener listener) {
clickingListeners.add(listener);
}
/**
* Removes wheel clicking listener
*
* @param listener
* the listener
*/
public void removeClickingListener(OnWheelClickedListener listener) {
clickingListeners.remove(listener);
}
/**
* Notifies listeners about clicking
*/
protected void notifyClickListenersAboutClick(int item) {
for (OnWheelClickedListener listener : clickingListeners) {
listener.onItemClicked(this, item);
}
}
/**
* Gets current value
*
* @return the current value
*/
public int getCurrentItem() {
return currentItem;
}
/**
* Sets the current item. Does nothing when index is wrong.
*
* @param index
* the item index
* @param animated
* the animation flag
*/
public void setCurrentItem(int index, boolean animated) {
if (viewAdapter == null || viewAdapter.getItemsCount() == 0) {
return; // throw?
}
int itemCount = viewAdapter.getItemsCount();
if (index < 0 || index >= itemCount) {
if (isCyclic) {
while (index < 0) {
index += itemCount;
}
index %= itemCount;
} else {
return; // throw?
}
}
if (index != currentItem) {
if (animated) {
int itemsToScroll = index - currentItem;
if (isCyclic) {
int scroll = itemCount + Math.min(index, currentItem)
- Math.max(index, currentItem);
if (scroll < Math.abs(itemsToScroll)) {
itemsToScroll = itemsToScroll < 0 ? scroll : -scroll;
}
}
scroll(itemsToScroll, 0);
} else {
scrollingOffset = 0;
int old = currentItem;
currentItem = index;
notifyChangingListeners(old, currentItem);
invalidate();
}
}
}
/**
* Sets the current item w/o animation. Does nothing when index is wrong.
*
* @param index
* the item index
*/
public void setCurrentItem(int index) {
setCurrentItem(index, false);
}
/**
* Tests if wheel is cyclic. That means before the 1st item there is shown
* the last one
*
* @return true if wheel is cyclic
*/
public boolean isCyclic() {
return isCyclic;
}
/**
* Set wheel cyclic flag
*
* @param isCyclic
* the flag to set
*/
public void setCyclic(boolean isCyclic) {
this.isCyclic = isCyclic;
invalidateWheel(false);
}
/**
* Invalidates wheel
*
* @param clearCaches
* if true then cached views will be clear
*/
public void invalidateWheel(boolean clearCaches) {
if (clearCaches) {
recycle.clearAll();
if (itemsLayout != null) {
itemsLayout.removeAllViews();
}
scrollingOffset = 0;
} else if (itemsLayout != null) {
// cache all items
recycle.recycleItems(itemsLayout, firstItem, new ItemsRange());
}
invalidate();
}
/**
* Initializes resources
*/
private void initResourcesIfNecessary() {
if (centerDrawable == null) {
centerDrawable = getContext().getResources().getDrawable(
R.drawable.wheel_val);
}
if (topShadow == null) {
topShadow = new GradientDrawable(Orientation.TOP_BOTTOM,
SHADOWS_COLORS);
}
if (bottomShadow == null) {
bottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP,
SHADOWS_COLORS);
}
setBackgroundResource(R.drawable.wheel_bg);
}
/**
* Calculates desired height for layout
*
* @param layout
* the source layout
* @return the desired layout height
*/
private int getDesiredHeight(LinearLayout layout) {
if (layout != null && layout.getChildAt(0) != null) {
itemHeight = layout.getChildAt(0).getMeasuredHeight();
}
int desired = itemHeight * visibleItems - itemHeight
* ITEM_OFFSET_PERCENT / 50;
return Math.max(desired, getSuggestedMinimumHeight());
}
/**
* Returns height of wheel item
*
* @return the item height
*/
private int getItemHeight() {
if (itemHeight != 0) {
return itemHeight;
}
if (itemsLayout != null && itemsLayout.getChildAt(0) != null) {
itemHeight = itemsLayout.getChildAt(0).getHeight();
return itemHeight;
}
return getHeight() / visibleItems;
}
/**
* Calculates control width and creates text layouts
*
* @param widthSize
* the input layout width
* @param mode
* the layout mode
* @return the calculated control width
*/
private int calculateLayoutWidth(int widthSize, int mode) {
initResourcesIfNecessary();
// TODO: make it static
itemsLayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
itemsLayout
.measure(MeasureSpec.makeMeasureSpec(widthSize,
MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(
0, MeasureSpec.UNSPECIFIED));
int width = itemsLayout.getMeasuredWidth();
if (mode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width += 2 * PADDING;
// Check against our minimum width
width = Math.max(width, getSuggestedMinimumWidth());
if (mode == MeasureSpec.AT_MOST && widthSize < width) {
width = widthSize;
}
}
itemsLayout.measure(MeasureSpec.makeMeasureSpec(width - 2 * PADDING,
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED));
return width;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
buildViewForMeasuring();
int width = calculateLayoutWidth(widthSize, widthMode);
int height;
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = getDesiredHeight(itemsLayout);
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(height, heightSize);
}
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
layout(r - l, b - t);
}
/**
* Sets layouts width and height
*
* @param width
* the layout width
* @param height
* the layout height
*/
private void layout(int width, int height) {
int itemsWidth = width - 2 * PADDING;
itemsLayout.layout(0, 0, itemsWidth, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (viewAdapter != null && viewAdapter.getItemsCount() > 0) {
updateView();
drawItems(canvas);
drawCenterRect(canvas);
}
drawShadows(canvas);
}
/**
* Draws shadows on top and bottom of control
*
* @param canvas
* the canvas for drawing
*/
private void drawShadows(Canvas canvas) {
int height = (int) (1.5 * getItemHeight());
topShadow.setBounds(0, 0, getWidth(), height);
topShadow.draw(canvas);
bottomShadow
.setBounds(0, getHeight() - height, getWidth(), getHeight());
bottomShadow.draw(canvas);
}
/**
* Draws items
*
* @param canvas
* the canvas for drawing
*/
private void drawItems(Canvas canvas) {
canvas.save();
int top = (currentItem - firstItem) * getItemHeight()
+ (getItemHeight() - getHeight()) / 2;
canvas.translate(PADDING, -top + scrollingOffset);
itemsLayout.draw(canvas);
canvas.restore();
}
/**
* Draws rect for current value
*
* @param canvas
* the canvas for drawing
*/
private void drawCenterRect(Canvas canvas) {
int center = getHeight() / 2;
int offset = (int) (getItemHeight() / 2 * 1.2);
centerDrawable.setBounds(0, center - offset, getWidth(), center
+ offset);
centerDrawable.draw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled() || getViewAdapter() == null) {
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
if (!isScrollingPerformed) {
int distance = (int) event.getY() - getHeight() / 2;
if (distance > 0) {
distance += getItemHeight() / 2;
} else {
distance -= getItemHeight() / 2;
}
int items = distance / getItemHeight();
if (items != 0 && isValidItemIndex(currentItem + items)) {
notifyClickListenersAboutClick(currentItem + items);
}
}
break;
}
return scroller.onTouchEvent(event);
}
/**
* Scrolls the wheel
*
* @param delta
* the scrolling value
*/
private void doScroll(int delta) {
scrollingOffset += delta;
int itemHeight = getItemHeight();
int count = scrollingOffset / itemHeight;
int pos = currentItem - count;
int itemCount = viewAdapter.getItemsCount();
int fixPos = scrollingOffset % itemHeight;
if (Math.abs(fixPos) <= itemHeight / 2) {
fixPos = 0;
}
if (isCyclic && itemCount > 0) {
if (fixPos > 0) {
pos--;
count++;
} else if (fixPos < 0) {
pos++;
count--;
}
// fix position by rotating
while (pos < 0) {
pos += itemCount;
}
pos %= itemCount;
} else {
//
if (pos < 0) {
count = currentItem;
pos = 0;
} else if (pos >= itemCount) {
count = currentItem - itemCount + 1;
pos = itemCount - 1;
} else if (pos > 0 && fixPos > 0) {
pos--;
count++;
} else if (pos < itemCount - 1 && fixPos < 0) {
pos++;
count--;
}
}
int offset = scrollingOffset;
if (pos != currentItem) {
setCurrentItem(pos, false);
} else {
invalidate();
}
// update offset
scrollingOffset = offset - count * itemHeight;
if (scrollingOffset > getHeight()) {
scrollingOffset = scrollingOffset % getHeight() + getHeight();
}
}
/**
* Scroll the wheel
*
* @param itemsToSkip
* items to scroll
* @param time
* scrolling duration
*/
public void scroll(int itemsToScroll, int time) {
int distance = itemsToScroll * getItemHeight() - scrollingOffset;
scroller.scroll(distance, time);
}
/**
* Calculates range for wheel items
*
* @return the items range
*/
private ItemsRange getItemsRange() {
if (getItemHeight() == 0) {
return null;
}
int first = currentItem;
int count = 1;
while (count * getItemHeight() < getHeight()) {
first--;
count += 2; // top + bottom items
}
if (scrollingOffset != 0) {
if (scrollingOffset > 0) {
first--;
}
count++;
// process empty items above the first or below the second
int emptyItems = scrollingOffset / getItemHeight();
first -= emptyItems;
count += Math.asin(emptyItems);
}
return new ItemsRange(first, count);
}
/**
* Rebuilds wheel items if necessary. Caches all unused items.
*
* @return true if items are rebuilt
*/
private boolean rebuildItems() {
boolean updated = false;
ItemsRange range = getItemsRange();
if (itemsLayout != null) {
int first = recycle.recycleItems(itemsLayout, firstItem, range);
updated = firstItem != first;
firstItem = first;
} else {
createItemsLayout();
updated = true;
}
if (!updated) {
updated = firstItem != range.getFirst()
|| itemsLayout.getChildCount() != range.getCount();
}
if (firstItem > range.getFirst() && firstItem <= range.getLast()) {
for (int i = firstItem - 1; i >= range.getFirst(); i--) {
if (!addViewItem(i, true)) {
break;
}
firstItem = i;
}
} else {
firstItem = range.getFirst();
}
int first = firstItem;
for (int i = itemsLayout.getChildCount(); i < range.getCount(); i++) {
if (!addViewItem(firstItem + i, false)
&& itemsLayout.getChildCount() == 0) {
first++;
}
}
firstItem = first;
return updated;
}
/**
* Updates view. Rebuilds items and label if necessary, recalculate items
* sizes.
*/
private void updateView() {
if (rebuildItems()) {
calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY);
layout(getWidth(), getHeight());
}
}
/**
* Creates item layouts if necessary
*/
private void createItemsLayout() {
if (itemsLayout == null) {
itemsLayout = new LinearLayout(getContext());
itemsLayout.setOrientation(LinearLayout.VERTICAL);
}
}
/**
* Builds view for measuring
*/
private void buildViewForMeasuring() {
// clear all items
if (itemsLayout != null) {
recycle.recycleItems(itemsLayout, firstItem, new ItemsRange());
} else {
createItemsLayout();
}
// add views
int addItems = visibleItems / 2;
for (int i = currentItem + addItems; i >= currentItem - addItems; i--) {
if (addViewItem(i, true)) {
firstItem = i;
}
}
}
/**
* Adds view for item to items layout
*
* @param index
* the item index
* @param first
* the flag indicates if view should be first
* @return true if corresponding item exists and is added
*/
private boolean addViewItem(int index, boolean first) {
View view = getItemView(index);
if (view != null) {
if (first) {
itemsLayout.addView(view, 0);
} else {
itemsLayout.addView(view);
}
return true;
}
return false;
}
/**
* Checks whether intem index is valid
*
* @param index
* the item index
* @return true if item index is not out of bounds or the wheel is cyclic
*/
private boolean isValidItemIndex(int index) {
return viewAdapter != null
&& viewAdapter.getItemsCount() > 0
&& (isCyclic || index >= 0
&& index < viewAdapter.getItemsCount());
}
/**
* Returns view for specified item
*
* @param index
* the item index
* @return item view or empty view if index is out of bounds
*/
private View getItemView(int index) {
if (viewAdapter == null || viewAdapter.getItemsCount() == 0) {
return null;
}
int count = viewAdapter.getItemsCount();
if (!isValidItemIndex(index)) {
return viewAdapter
.getEmptyItem(recycle.getEmptyItem(), itemsLayout);
} else {
while (index < 0) {
index = count + index;
}
}
index %= count;
return viewAdapter.getItem(index, recycle.getItem(), itemsLayout);
}
/**
* Stops scrolling
*/
public void stopScrolling() {
scroller.stopScrolling();
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity.adapters;
import java.util.LinkedList;
import java.util.List;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
/**
* Abstract Wheel adapter.
*/
public abstract class AbstractWheelAdapter implements WheelViewAdapter {
// Observers
private List<DataSetObserver> datasetObservers;
@Override
public View getEmptyItem(View convertView, ViewGroup parent) {
return null;
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
if (datasetObservers == null) {
datasetObservers = new LinkedList<DataSetObserver>();
}
datasetObservers.add(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
if (datasetObservers != null) {
datasetObservers.remove(observer);
}
}
/**
* Notifies observers about data changing
*/
protected void notifyDataChangedEvent() {
if (datasetObservers != null) {
for (DataSetObserver observer : datasetObservers) {
observer.onChanged();
}
}
}
/**
* Notifies observers about invalidating data
*/
protected void notifyDataInvalidatedEvent() {
if (datasetObservers != null) {
for (DataSetObserver observer : datasetObservers) {
observer.onInvalidated();
}
}
}
}

View File

@@ -0,0 +1,267 @@
/*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity.adapters;
import android.content.Context;
import android.graphics.Typeface;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
* Abstract wheel adapter provides common functionality for adapters.
*/
public abstract class AbstractWheelTextAdapter extends AbstractWheelAdapter {
/** Text view resource. Used as a default view for adapter. */
public static final int TEXT_VIEW_ITEM_RESOURCE = -1;
/** No resource constant. */
protected static final int NO_RESOURCE = 0;
/** Default text color */
public static final int DEFAULT_TEXT_COLOR = 0xFF101010;
/** Default text color */
public static final int LABEL_COLOR = 0xFF700070;
/** Default text size */
public static final int DEFAULT_TEXT_SIZE = 24;
// Text settings
private int textColor = DEFAULT_TEXT_COLOR;
private int textSize = DEFAULT_TEXT_SIZE;
// Current context
protected Context context;
// Layout inflater
protected LayoutInflater inflater;
// Items resources
protected int itemResourceId;
protected int itemTextResourceId;
// Empty items resources
protected int emptyItemResourceId;
/**
* Constructor
* @param context the current context
*/
protected AbstractWheelTextAdapter(Context context) {
this(context, TEXT_VIEW_ITEM_RESOURCE);
}
/**
* Constructor
* @param context the current context
* @param itemResource the resource ID for a layout file containing a TextView to use when instantiating items views
*/
protected AbstractWheelTextAdapter(Context context, int itemResource) {
this(context, itemResource, NO_RESOURCE);
}
/**
* Constructor
* @param context the current context
* @param itemResource the resource ID for a layout file containing a TextView to use when instantiating items views
* @param itemTextResource the resource ID for a text view in the item layout
*/
protected AbstractWheelTextAdapter(Context context, int itemResource, int itemTextResource) {
this.context = context;
itemResourceId = itemResource;
itemTextResourceId = itemTextResource;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/**
* Gets text color
* @return the text color
*/
public int getTextColor() {
return textColor;
}
/**
* Sets text color
* @param textColor the text color to set
*/
public void setTextColor(int textColor) {
this.textColor = textColor;
}
/**
* Gets text size
* @return the text size
*/
public int getTextSize() {
return textSize;
}
/**
* Sets text size
* @param textSize the text size to set
*/
public void setTextSize(int textSize) {
this.textSize = textSize;
}
/**
* Gets resource Id for items views
* @return the item resource Id
*/
public int getItemResource() {
return itemResourceId;
}
/**
* Sets resource Id for items views
* @param itemResourceId the resource Id to set
*/
public void setItemResource(int itemResourceId) {
this.itemResourceId = itemResourceId;
}
/**
* Gets resource Id for text view in item layout
* @return the item text resource Id
*/
public int getItemTextResource() {
return itemTextResourceId;
}
/**
* Sets resource Id for text view in item layout
* @param itemTextResourceId the item text resource Id to set
*/
public void setItemTextResource(int itemTextResourceId) {
this.itemTextResourceId = itemTextResourceId;
}
/**
* Gets resource Id for empty items views
* @return the empty item resource Id
*/
public int getEmptyItemResource() {
return emptyItemResourceId;
}
/**
* Sets resource Id for empty items views
* @param emptyItemResourceId the empty item resource Id to set
*/
public void setEmptyItemResource(int emptyItemResourceId) {
this.emptyItemResourceId = emptyItemResourceId;
}
/**
* Returns text for specified item
* @param index the item index
* @return the text of specified items
*/
protected abstract CharSequence getItemText(int index);
@Override
public View getItem(int index, View convertView, ViewGroup parent) {
if (index >= 0 && index < getItemsCount()) {
if (convertView == null) {
convertView = getView(itemResourceId, parent);
}
TextView textView = getTextView(convertView, itemTextResourceId);
if (textView != null) {
CharSequence text = getItemText(index);
if (text == null) {
text = "";
}
textView.setText(text);
if (itemResourceId == TEXT_VIEW_ITEM_RESOURCE) {
configureTextView(textView);
}
}
return convertView;
}
return null;
}
@Override
public View getEmptyItem(View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getView(emptyItemResourceId, parent);
}
if (emptyItemResourceId == TEXT_VIEW_ITEM_RESOURCE && convertView instanceof TextView) {
configureTextView((TextView)convertView);
}
return convertView;
}
/**
* Configures text view. Is called for the TEXT_VIEW_ITEM_RESOURCE views.
* @param view the text view to be configured
*/
protected void configureTextView(TextView view) {
view.setTextColor(textColor);
view.setGravity(Gravity.CENTER);
view.setTextSize(textSize);
view.setLines(1);
view.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD);
}
/**
* Loads a text view from view
* @param view the text view or layout containing it
* @param textResource the text resource Id in layout
* @return the loaded text view
*/
private TextView getTextView(View view, int textResource) {
TextView text = null;
try {
if (textResource == NO_RESOURCE && view instanceof TextView) {
text = (TextView) view;
} else if (textResource != NO_RESOURCE) {
text = (TextView) view.findViewById(textResource);
}
} catch (ClassCastException e) {
Log.e("AbstractWheelAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"AbstractWheelAdapter requires the resource ID to be a TextView", e);
}
return text;
}
/**
* Loads view from resources
* @param resource the resource Id
* @return the loaded view or null if resource is not set
*/
private View getView(int resource, ViewGroup parent) {
switch (resource) {
case NO_RESOURCE:
return null;
case TEXT_VIEW_ITEM_RESOURCE:
return new TextView(context);
default:
return inflater.inflate(resource, parent, false);
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity.adapters;
import com.gghl.view.wheelcity.WheelAdapter;
import android.content.Context;
/**
* Adapter class for old wheel adapter (deprecated WheelAdapter class).
*
* @deprecated Will be removed soon
*/
public class AdapterWheel extends AbstractWheelTextAdapter {
// Source adapter
private WheelAdapter adapter;
/**
* Constructor
* @param context the current context
* @param adapter the source adapter
*/
public AdapterWheel(Context context, WheelAdapter adapter) {
super(context);
this.adapter = adapter;
}
/**
* Gets original adapter
* @return the original adapter
*/
public WheelAdapter getAdapter() {
return adapter;
}
@Override
public int getItemsCount() {
return adapter.getItemsCount();
}
@Override
protected CharSequence getItemText(int index) {
return adapter.getItem(index);
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gghl.view.wheelcity.adapters;
import android.content.Context;
/**
* The simple Array wheel adapter
* @param <T> the element type
*/
public class ArrayWheelAdapter<T> extends AbstractWheelTextAdapter {
// items
private T items[];
/**
* Constructor
* @param context the current context
* @param items the items
*/
public ArrayWheelAdapter(Context context, T items[]) {
super(context);
//setEmptyItemResource(TEXT_VIEW_ITEM_RESOURCE);
this.items = items;
}
@Override
public CharSequence getItemText(int index) {
if (index >= 0 && index < items.length) {
T item = items[index];
if (item instanceof CharSequence) {
return (CharSequence) item;
}
return item.toString();
}
return null;
}
@Override
public int getItemsCount() {
return items.length;
}
}

Some files were not shown because too many files have changed in this diff Show More