Commit 3528d02b3d76cf90e8472faef48c087b832944c5

Authored by wwx
1 parent 5d685fa7

no message

Showing 30 changed files with 1634 additions and 64 deletions   Show diff stats
app/build.gradle
@@ -97,4 +97,6 @@ dependencies { @@ -97,4 +97,6 @@ dependencies {
97 compile 'me.leolin:ShortcutBadger:1.1.19@aar' 97 compile 'me.leolin:ShortcutBadger:1.1.19@aar'
98 annotationProcessor 'com.google.dagger:dagger-compiler:2.12' 98 annotationProcessor 'com.google.dagger:dagger-compiler:2.12'
99 compile files('libs/processor.jar') 99 compile files('libs/processor.jar')
  100 +
  101 + compile 'com.contrarywind:Android-PickerView:4.1.3'
100 } 102 }
101 \ No newline at end of file 103 \ No newline at end of file
app/src/main/java/com/shunzhi/parent/contract/apply/ApplySigninContract.java 0 → 100644
@@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
  1 +package com.shunzhi.parent.contract.apply;
  2 +
  3 +import android.widget.LinearLayout;
  4 +
  5 +import com.google.gson.JsonObject;
  6 +import com.share.mvpsdk.base.BasePresenter;
  7 +import com.share.mvpsdk.base.IBaseFragment;
  8 +import com.share.mvpsdk.base.IBaseModel;
  9 +import com.shunzhi.parent.bean.ToolBean;
  10 +
  11 +import java.util.List;
  12 +
  13 +import io.reactivex.Observable;
  14 +
  15 +/**
  16 + * Created by ToaHanDong on 2018/3/14.
  17 + */
  18 +
  19 +public interface ApplySigninContract {
  20 +
  21 + abstract class ApplySigninPresenter extends BasePresenter<IApplySigninModel,IApplySigninView>{
  22 + public abstract void getTools(LinearLayout linearLayout,String areaName);
  23 + }
  24 +
  25 +
  26 + interface IApplySigninModel extends IBaseModel{
  27 + Observable<JsonObject> getTools(String areaName);
  28 + }
  29 +
  30 +
  31 + interface IApplySigninView extends IBaseFragment{
  32 +
  33 + void showTools(List<ToolBean> toolBeanList);
  34 +
  35 + }
  36 +
  37 +}
app/src/main/java/com/shunzhi/parent/model/apply/ApplySigninModel.java 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +package com.shunzhi.parent.model.apply;
  2 +
  3 +import com.google.gson.JsonObject;
  4 +import com.share.mvpsdk.base.BaseModel;
  5 +import com.shunzhi.parent.contract.apply.ApplySigninContract;
  6 +import com.shunzhi.parent.contract.ceping.CepingContract;
  7 +
  8 +import io.reactivex.Observable;
  9 +
  10 +/**
  11 + * Created by Administrator on 2018/4/17 0017.
  12 + */
  13 +
  14 +public class ApplySigninModel extends BaseModel implements ApplySigninContract.IApplySigninModel{
  15 + @Override
  16 + public Observable<JsonObject> getTools(String areaName) {
  17 + return null;
  18 + }
  19 +
  20 + public static ApplySigninContract.IApplySigninModel newInstance() {
  21 + return new ApplySigninModel();
  22 + }
  23 +}
app/src/main/java/com/shunzhi/parent/presenter/apply/ApplySigninPresenter.java 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +package com.shunzhi.parent.presenter.apply;
  2 +
  3 +import android.widget.LinearLayout;
  4 +
  5 +import com.shunzhi.parent.contract.apply.ApplySigninContract;
  6 +import com.shunzhi.parent.model.apply.ApplySigninModel;
  7 +
  8 +/**
  9 + * Created by Administrator on 2018/4/17 0017.
  10 + */
  11 +
  12 +public class ApplySigninPresenter extends ApplySigninContract.ApplySigninPresenter{
  13 + @Override
  14 + public ApplySigninContract.IApplySigninModel getModel() {
  15 + return ApplySigninModel.newInstance();
  16 + }
  17 +
  18 + @Override
  19 + public void onStart() {
  20 +
  21 + }
  22 +
  23 + @Override
  24 + public void getTools(LinearLayout linearLayout, String areaName) {
  25 +
  26 + }
  27 +}
app/src/main/java/com/shunzhi/parent/ui/activity/apply/ApplySigninActivity.java
@@ -10,12 +10,19 @@ import android.view.View; @@ -10,12 +10,19 @@ import android.view.View;
10 import android.widget.FrameLayout; 10 import android.widget.FrameLayout;
11 import android.widget.ImageView; 11 import android.widget.ImageView;
12 import android.widget.TextView; 12 import android.widget.TextView;
  13 +import android.widget.Toast;
13 14
  15 +import com.bigkoo.pickerview.builder.TimePickerBuilder;
  16 +import com.bigkoo.pickerview.listener.CustomListener;
  17 +import com.bigkoo.pickerview.listener.OnTimeSelectListener;
14 import com.share.mvpsdk.base.BasePresenter; 18 import com.share.mvpsdk.base.BasePresenter;
15 import com.share.mvpsdk.base.activity.BaseMVPCompatActivity; 19 import com.share.mvpsdk.base.activity.BaseMVPCompatActivity;
16 import com.shunzhi.parent.R; 20 import com.shunzhi.parent.R;
17 import com.shunzhi.parent.ui.fragment.apply.ApplySigninFragment; 21 import com.shunzhi.parent.ui.fragment.apply.ApplySigninFragment;
18 22
  23 +import java.util.Calendar;
  24 +import java.util.Date;
  25 +
19 /** 26 /**
20 * Created by wwx on 2018/4/10 0010. 27 * Created by wwx on 2018/4/10 0010.
21 * 28 *
@@ -69,4 +76,5 @@ public class ApplySigninActivity extends BaseMVPCompatActivity implements View.O @@ -69,4 +76,5 @@ public class ApplySigninActivity extends BaseMVPCompatActivity implements View.O
69 public BasePresenter initPresenter() { 76 public BasePresenter initPresenter() {
70 return null; 77 return null;
71 } 78 }
  79 +
72 } 80 }
app/src/main/java/com/shunzhi/parent/ui/fragment/ConsultFragment.java
1 package com.shunzhi.parent.ui.fragment; 1 package com.shunzhi.parent.ui.fragment;
2 2
  3 +import android.annotation.TargetApi;
  4 +import android.app.DatePickerDialog;
3 import android.content.BroadcastReceiver; 5 import android.content.BroadcastReceiver;
4 import android.content.Context; 6 import android.content.Context;
5 import android.content.Intent; 7 import android.content.Intent;
6 import android.content.IntentFilter; 8 import android.content.IntentFilter;
  9 +import android.os.Build;
7 import android.os.Bundle; 10 import android.os.Bundle;
8 import android.support.annotation.NonNull; 11 import android.support.annotation.NonNull;
9 import android.support.annotation.Nullable; 12 import android.support.annotation.Nullable;
  13 +import android.support.annotation.RequiresApi;
10 import android.support.v4.widget.NestedScrollView; 14 import android.support.v4.widget.NestedScrollView;
11 import android.text.TextUtils; 15 import android.text.TextUtils;
  16 +import android.util.Log;
12 import android.view.View; 17 import android.view.View;
13 import android.widget.EditText; 18 import android.widget.EditText;
14 import android.widget.ImageView; 19 import android.widget.ImageView;
15 import android.widget.LinearLayout; 20 import android.widget.LinearLayout;
16 import android.widget.TextView; 21 import android.widget.TextView;
  22 +import android.widget.Toast;
17 23
  24 +import com.bigkoo.pickerview.builder.TimePickerBuilder;
  25 +import com.bigkoo.pickerview.listener.CustomListener;
  26 +import com.bigkoo.pickerview.listener.OnTimeSelectListener;
  27 +import com.bigkoo.pickerview.view.TimePickerView;
18 import com.bumptech.glide.Glide; 28 import com.bumptech.glide.Glide;
19 import com.jcodecraeer.xrecyclerview.XRecyclerView; 29 import com.jcodecraeer.xrecyclerview.XRecyclerView;
20 import com.share.mvpsdk.base.BasePresenter; 30 import com.share.mvpsdk.base.BasePresenter;
@@ -35,7 +45,10 @@ import com.shunzhi.parent.util.GlideUtils; @@ -35,7 +45,10 @@ import com.shunzhi.parent.util.GlideUtils;
35 import com.shunzhi.parent.views.TextAndImgShowView; 45 import com.shunzhi.parent.views.TextAndImgShowView;
36 import com.stx.xhb.xbanner.XBanner; 46 import com.stx.xhb.xbanner.XBanner;
37 47
  48 +import java.text.SimpleDateFormat;
38 import java.util.ArrayList; 49 import java.util.ArrayList;
  50 +import java.util.Calendar;
  51 +import java.util.Date;
39 import java.util.List; 52 import java.util.List;
40 53
41 import cn.jzvd.JZVideoPlayerStandard; 54 import cn.jzvd.JZVideoPlayerStandard;
@@ -55,7 +68,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -55,7 +68,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
55 MyConsultAdapter contextAdapter; 68 MyConsultAdapter contextAdapter;
56 69
57 List<String> imgesUrl = new ArrayList<>(); 70 List<String> imgesUrl = new ArrayList<>();
58 - List<String> imgWebUrl=new ArrayList<>();//跳转的连接 71 + List<String> imgWebUrl = new ArrayList<>();//跳转的连接
59 List<String> describeList = new ArrayList<>(); 72 List<String> describeList = new ArrayList<>();
60 List<GuangGaoBean> guanggaoList = new ArrayList<>(); 73 List<GuangGaoBean> guanggaoList = new ArrayList<>();
61 List<ChannelContextBean> contextList = new ArrayList<>(); 74 List<ChannelContextBean> contextList = new ArrayList<>();
@@ -64,7 +77,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -64,7 +77,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
64 77
65 TextView tvLocalAddress; 78 TextView tvLocalAddress;
66 79
67 - LinearLayout layout_control,layout_consult; 80 + LinearLayout layout_control, layout_consult;
68 81
69 CityPicker cityPicker = null; 82 CityPicker cityPicker = null;
70 83
@@ -86,14 +99,14 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -86,14 +99,14 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
86 recycler_context = view.findViewById(R.id.recycler_content); 99 recycler_context = view.findViewById(R.id.recycler_content);
87 initRecycler(); 100 initRecycler();
88 101
89 - nesteScrollView=view.findViewById(R.id.nesteScrollView); 102 + nesteScrollView = view.findViewById(R.id.nesteScrollView);
90 ivSearch = view.findViewById(R.id.ivSearch); 103 ivSearch = view.findViewById(R.id.ivSearch);
91 xBanner = view.findViewById(R.id.xBanner); 104 xBanner = view.findViewById(R.id.xBanner);
92 - layout_consult=view.findViewById(R.id.layout_consult); 105 + layout_consult = view.findViewById(R.id.layout_consult);
93 videoplayer = view.findViewById(R.id.videoplayer); 106 videoplayer = view.findViewById(R.id.videoplayer);
94 tvLocalAddress = view.findViewById(R.id.tvLocalAddress); 107 tvLocalAddress = view.findViewById(R.id.tvLocalAddress);
95 layout_control = view.findViewById(R.id.layout_control); 108 layout_control = view.findViewById(R.id.layout_control);
96 - et_search=view.findViewById(R.id.et_search); 109 + et_search = view.findViewById(R.id.et_search);
97 tvLocalAddress.setText(AppContext.getInstance().district); 110 tvLocalAddress.setText(AppContext.getInstance().district);
98 videoplayer.batteryLevel.setVisibility(View.GONE); 111 videoplayer.batteryLevel.setVisibility(View.GONE);
99 videoplayer.replayTextView.setVisibility(View.GONE); 112 videoplayer.replayTextView.setVisibility(View.GONE);
@@ -104,7 +117,8 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -104,7 +117,8 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
104 initBroadCast(); 117 initBroadCast();
105 118
106 initListeners(); 119 initListeners();
107 - layout_consult.measure(0,0); 120 +
  121 + layout_consult.measure(0, 0);
108 nesteScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { 122 nesteScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
109 @Override 123 @Override
110 public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { 124 public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
@@ -155,7 +169,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -155,7 +169,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
155 }); 169 });
156 } 170 }
157 171
158 - private XBanner.XBannerAdapter xBannerAdapter=new XBanner.XBannerAdapter() { 172 + private XBanner.XBannerAdapter xBannerAdapter = new XBanner.XBannerAdapter() {
159 @Override 173 @Override
160 public void loadBanner(XBanner banner, Object model, View view, int position) { 174 public void loadBanner(XBanner banner, Object model, View view, int position) {
161 Glide.with(getContext()).load(imgesUrl.get(position)).into((ImageView) view); 175 Glide.with(getContext()).load(imgesUrl.get(position)).into((ImageView) view);
@@ -181,6 +195,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -181,6 +195,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
181 xBanner.stopAutoPlay(); 195 xBanner.stopAutoPlay();
182 } 196 }
183 197
  198 + @RequiresApi(api = Build.VERSION_CODES.N)
184 @Override 199 @Override
185 public void onClick(View view) { 200 public void onClick(View view) {
186 switch (view.getId()) { 201 switch (view.getId()) {
@@ -191,9 +206,9 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -191,9 +206,9 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
191 else cityPicker.show(); 206 else cityPicker.show();
192 break; 207 break;
193 case R.id.ivSearch://搜索按钮 208 case R.id.ivSearch://搜索按钮
194 - if (!TextUtils.isEmpty(et_search.getText().toString())){ 209 + if (!TextUtils.isEmpty(et_search.getText().toString())) {
195 contextList.clear(); 210 contextList.clear();
196 - mPresenter.getInformationTopic(et_search.getText().toString(),AppContext.getInstance().district,"0","1",1); 211 + mPresenter.getInformationTopic(et_search.getText().toString(), AppContext.getInstance().district, "0", "1", 1);
197 } 212 }
198 break; 213 break;
199 } 214 }
@@ -207,6 +222,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -207,6 +222,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
207 222
208 } 223 }
209 224
  225 +
210 private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { 226 private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
211 @Override 227 @Override
212 public void onReceive(Context context, Intent intent) { 228 public void onReceive(Context context, Intent intent) {
@@ -232,7 +248,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -232,7 +248,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
232 public void showBanners(List<GuangGaoBean> guangGaoBeanList) { 248 public void showBanners(List<GuangGaoBean> guangGaoBeanList) {
233 describeList.clear(); 249 describeList.clear();
234 imgesUrl.clear(); 250 imgesUrl.clear();
235 - guanggaoList=guangGaoBeanList; 251 + guanggaoList = guangGaoBeanList;
236 for (int i = 0; i < guangGaoBeanList.size(); i++) { 252 for (int i = 0; i < guangGaoBeanList.size(); i++) {
237 imgesUrl.add(AppConfig.BASE_URL_FILE + guangGaoBeanList.get(i).fileSrc); 253 imgesUrl.add(AppConfig.BASE_URL_FILE + guangGaoBeanList.get(i).fileSrc);
238 describeList.add(guangGaoBeanList.get(i).describe); 254 describeList.add(guangGaoBeanList.get(i).describe);
@@ -283,7 +299,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -283,7 +299,7 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
283 299
284 @Override 300 @Override
285 public void getCity(String name) { 301 public void getCity(String name) {
286 - first=true; 302 + first = true;
287 tvLocalAddress.setText(name.split(" ")[2]); 303 tvLocalAddress.setText(name.split(" ")[2]);
288 mPresenter.getBanners("2", name.split(" ")[2]); 304 mPresenter.getBanners("2", name.split(" ")[2]);
289 mPresenter.getContextChannel(name.split(" ")[2], 0, 1, 1); 305 mPresenter.getContextChannel(name.split(" ")[2], 0, 1, 1);
@@ -292,9 +308,9 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu @@ -292,9 +308,9 @@ public class ConsultFragment extends BaseMVPCompatFragment&lt;ConsultContract.Consu
292 } 308 }
293 309
294 public void refresh() { 310 public void refresh() {
295 - first=true; 311 + first = true;
296 tvLocalAddress.setText(AppContext.getInstance().district); 312 tvLocalAddress.setText(AppContext.getInstance().district);
297 - mPresenter.getContextChannel(AppContext.getInstance().district,0,1,pageIndex); 313 + mPresenter.getContextChannel(AppContext.getInstance().district, 0, 1, pageIndex);
298 mPresenter.getBanners("2", AppContext.getInstance().district); 314 mPresenter.getBanners("2", AppContext.getInstance().district);
299 } 315 }
300 } 316 }
app/src/main/java/com/shunzhi/parent/ui/fragment/apply/ApplyReplaceCardFragment.java
@@ -54,10 +54,6 @@ public class ApplyReplaceCardFragment extends BaseMVPCompatFragment implements V @@ -54,10 +54,6 @@ public class ApplyReplaceCardFragment extends BaseMVPCompatFragment implements V
54 if (TextUtils.isEmpty(cardnum)){ 54 if (TextUtils.isEmpty(cardnum)){
55 Toast.makeText(getActivity(),"卡号不能为空,请重新输入",Toast.LENGTH_SHORT).show(); 55 Toast.makeText(getActivity(),"卡号不能为空,请重新输入",Toast.LENGTH_SHORT).show();
56 } 56 }
57 -// if (et_cardnum.getText().toString().trim().equals("")||  
58 -// et_cardnum.getText().toString().trim().equals(null)){  
59 -// Toast.makeText(getActivity(),"卡号不能为空,请重新输入",Toast.LENGTH_SHORT).show();  
60 -// }  
61 else { 57 else {
62 replaceCardDialog.setTitle("补卡提示"); 58 replaceCardDialog.setTitle("补卡提示");
63 replaceCardDialog.setText("您已补卡成功!"); 59 replaceCardDialog.setText("您已补卡成功!");
app/src/main/java/com/shunzhi/parent/ui/fragment/apply/ApplySigninFragment.java
1 package com.shunzhi.parent.ui.fragment.apply; 1 package com.shunzhi.parent.ui.fragment.apply;
2 2
  3 +import android.app.DatePickerDialog;
  4 +import android.os.Build;
3 import android.os.Bundle; 5 import android.os.Bundle;
4 -import android.support.annotation.NonNull;  
5 import android.support.annotation.Nullable; 6 import android.support.annotation.Nullable;
  7 +import android.support.annotation.RequiresApi;
6 import android.support.v7.widget.LinearLayoutManager; 8 import android.support.v7.widget.LinearLayoutManager;
7 import android.support.v7.widget.RecyclerView; 9 import android.support.v7.widget.RecyclerView;
  10 +import android.util.Log;
8 import android.view.View; 11 import android.view.View;
  12 +import android.widget.CheckBox;
  13 +import android.widget.CompoundButton;
  14 +import android.widget.DatePicker;
  15 +import android.widget.ImageView;
9 import android.widget.LinearLayout; 16 import android.widget.LinearLayout;
10 import android.widget.TextView; 17 import android.widget.TextView;
  18 +import android.widget.Toast;
11 19
  20 +import com.bigkoo.pickerview.builder.TimePickerBuilder;
  21 +import com.bigkoo.pickerview.listener.CustomListener;
  22 +import com.bigkoo.pickerview.listener.OnTimeSelectListener;
  23 +import com.bigkoo.pickerview.view.TimePickerView;
12 import com.share.mvpsdk.base.BasePresenter; 24 import com.share.mvpsdk.base.BasePresenter;
13 import com.share.mvpsdk.base.fragment.BaseMVPCompatFragment; 25 import com.share.mvpsdk.base.fragment.BaseMVPCompatFragment;
  26 +import com.share.mvpsdk.utils.ToastUtils;
  27 +import com.shunzhi.parent.AppContext;
14 import com.shunzhi.parent.R; 28 import com.shunzhi.parent.R;
15 import com.shunzhi.parent.adapter.AttendanceAdapter; 29 import com.shunzhi.parent.adapter.AttendanceAdapter;
16 import com.shunzhi.parent.bean.apply.AttendanceBean; 30 import com.shunzhi.parent.bean.apply.AttendanceBean;
17 -import com.shunzhi.parent.bean.report.DeyuDetialBean; 31 +import com.shunzhi.parent.contract.apply.ApplySigninContract;
  32 +import com.shunzhi.parent.presenter.apply.ApplySigninPresenter;
18 33
  34 +import java.sql.Time;
  35 +import java.text.SimpleDateFormat;
19 import java.util.ArrayList; 36 import java.util.ArrayList;
  37 +import java.util.Calendar;
  38 +import java.util.Date;
20 import java.util.List; 39 import java.util.List;
21 40
22 /** 41 /**
23 * Created by Administrator on 2018/4/10 0010. 42 * Created by Administrator on 2018/4/10 0010.
24 */ 43 */
25 44
26 -public class ApplySigninFragment extends BaseMVPCompatFragment { 45 +public class ApplySigninFragment extends BaseMVPCompatFragment<ApplySigninContract.ApplySigninPresenter, ApplySigninContract.IApplySigninModel> implements View.OnClickListener{
27 private RecyclerView recycle_attendance; 46 private RecyclerView recycle_attendance;
28 private AttendanceAdapter attendanceAdapter; 47 private AttendanceAdapter attendanceAdapter;
29 List<AttendanceBean> list=new ArrayList<>(); 48 List<AttendanceBean> list=new ArrayList<>();
30 private TextView tv_tips,tv_kaoqin_num,tv_kaoqin_user,tv_kaoqin_date; 49 private TextView tv_tips,tv_kaoqin_num,tv_kaoqin_user,tv_kaoqin_date;
31 private LinearLayout layout_tv; 50 private LinearLayout layout_tv;
  51 + private ImageView iv_calendar;
  52 + private TimePickerView pvCustomLunar;
32 53
33 54
34 public BasePresenter initPresenter() { 55 public BasePresenter initPresenter() {
35 - return null; 56 + return new ApplySigninPresenter();
36 } 57 }
37 58
38 @Override 59 @Override
@@ -47,6 +68,10 @@ public class ApplySigninFragment extends BaseMVPCompatFragment { @@ -47,6 +68,10 @@ public class ApplySigninFragment extends BaseMVPCompatFragment {
47 tv_kaoqin_user = view.findViewById(R.id.tv_kaoqin_user); 68 tv_kaoqin_user = view.findViewById(R.id.tv_kaoqin_user);
48 tv_kaoqin_date = view.findViewById(R.id.tv_kaoqin_date); 69 tv_kaoqin_date = view.findViewById(R.id.tv_kaoqin_date);
49 tv_kaoqin_num = view.findViewById(R.id.tv_kaoqin_num); 70 tv_kaoqin_num = view.findViewById(R.id.tv_kaoqin_num);
  71 + iv_calendar = view.findViewById(R.id.iv_calendar);
  72 +
  73 + iv_calendar .setOnClickListener(this);
  74 +
50 recycle_attendance = view.findViewById(R.id.recycle_attendance); 75 recycle_attendance = view.findViewById(R.id.recycle_attendance);
51 recycle_attendance.setLayoutManager(new LinearLayoutManager(getActivity())); 76 recycle_attendance.setLayoutManager(new LinearLayoutManager(getActivity()));
52 attendanceAdapter = new AttendanceAdapter(getActivity()); 77 attendanceAdapter = new AttendanceAdapter(getActivity());
@@ -62,5 +87,63 @@ public class ApplySigninFragment extends BaseMVPCompatFragment { @@ -62,5 +87,63 @@ public class ApplySigninFragment extends BaseMVPCompatFragment {
62 attendanceAdapter.addAll(list); 87 attendanceAdapter.addAll(list);
63 recycle_attendance.setAdapter(attendanceAdapter); 88 recycle_attendance.setAdapter(attendanceAdapter);
64 } 89 }
  90 + initLunarPicker();
  91 + }
  92 +
  93 + @RequiresApi(api = Build.VERSION_CODES.N)
  94 + @Override
  95 + public void onClick(View v) {
  96 + switch (v.getId()){
  97 + case R.id.iv_calendar:
  98 + pvCustomLunar.show();
  99 + break;
  100 + }
65 } 101 }
  102 +
  103 + private void initLunarPicker() {
  104 + Calendar selectedDate = Calendar.getInstance();//系统当前时间
  105 + Calendar startDate = Calendar.getInstance();
  106 + startDate.set(1900, 1, 1);
  107 + Calendar endDate = Calendar.getInstance();
  108 + endDate.set(2099, 12, 31);
  109 + //AppContext.getInstance().startLocation();
  110 + //时间选择器
  111 + pvCustomLunar = new TimePickerBuilder(getActivity(), new OnTimeSelectListener() {
  112 + @Override
  113 + public void onTimeSelect(Date date, View v) {
  114 + ToastUtils.showToast(getTime(date));
  115 + tv_kaoqin_date.setText(getTime(date));
  116 + }
  117 + })
  118 + .setDate(selectedDate)
  119 + .setRangDate(startDate, endDate)
  120 + .setLayoutRes(R.layout.pickerview_custom_lunar, new CustomListener() {
  121 + @Override
  122 + public void customLayout(View v) {
  123 + final TextView tvSubmit = (TextView) v.findViewById(R.id.tv_finish);
  124 + ImageView ivCancel = (ImageView) v.findViewById(R.id.iv_cancel);
  125 + tvSubmit.setOnClickListener(new View.OnClickListener() {
  126 + @Override
  127 + public void onClick(View v) {
  128 + pvCustomLunar.returnData();
  129 + pvCustomLunar.dismiss();
  130 + }
  131 + });
  132 + ivCancel.setOnClickListener(new View.OnClickListener() {
  133 + @Override
  134 + public void onClick(View v) {
  135 + pvCustomLunar.dismiss();
  136 + }
  137 + });
  138 + }
  139 + }).build();
  140 + }
  141 +
  142 +
  143 + private String getTime(Date date) {//可根据需要自行截取数据显示
  144 + Log.d("getTime()", "choice date millis: " + date.getTime());
  145 + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
  146 + return format.format(date);
  147 + }
  148 +
66 } 149 }
app/src/main/java/com/shunzhi/parent/ui/fragment/report/ChengZhangFragment.java
@@ -79,7 +79,6 @@ public class ChengZhangFragment extends BaseMVPCompatFragment&lt;ReportContract.Rep @@ -79,7 +79,6 @@ public class ChengZhangFragment extends BaseMVPCompatFragment&lt;ReportContract.Rep
79 layout_report.setVisibility(View.GONE); 79 layout_report.setVisibility(View.GONE);
80 80
81 } else { 81 } else {
82 -  
83 layout_chengzhang.setVisibility(View.GONE); 82 layout_chengzhang.setVisibility(View.GONE);
84 layout_report.setVisibility(View.VISIBLE); 83 layout_report.setVisibility(View.VISIBLE);
85 84
app/src/main/res/drawable-xhdpi/to_down.png 0 → 100644

549 Bytes

app/src/main/res/layout/fragment_apply_signin.xml
@@ -36,6 +36,7 @@ @@ -36,6 +36,7 @@
36 android:layout_width="match_parent" 36 android:layout_width="match_parent"
37 android:layout_height="0dp" 37 android:layout_height="0dp"
38 android:layout_weight="1" 38 android:layout_weight="1"
  39 + android:gravity="center"
39 android:orientation="horizontal"> 40 android:orientation="horizontal">
40 41
41 <TextView 42 <TextView
@@ -57,8 +58,6 @@ @@ -57,8 +58,6 @@
57 android:text="张三" 58 android:text="张三"
58 android:textColor="@color/hintTextColor" 59 android:textColor="@color/hintTextColor"
59 android:textSize="@dimen/textSize16" /> 60 android:textSize="@dimen/textSize16" />
60 -  
61 -  
62 </LinearLayout> 61 </LinearLayout>
63 62
64 63
@@ -66,8 +65,8 @@ @@ -66,8 +65,8 @@
66 android:layout_width="match_parent" 65 android:layout_width="match_parent"
67 android:layout_height="0dp" 66 android:layout_height="0dp"
68 android:layout_weight="1" 67 android:layout_weight="1"
  68 + android:gravity="center"
69 android:orientation="horizontal"> 69 android:orientation="horizontal">
70 -  
71 <TextView 70 <TextView
72 android:id="@+id/tv_date" 71 android:id="@+id/tv_date"
73 android:layout_width="0dp" 72 android:layout_width="0dp"
@@ -77,25 +76,37 @@ @@ -77,25 +76,37 @@
77 android:text="考勤日期:" 76 android:text="考勤日期:"
78 android:textColor="@color/hintTextColor" 77 android:textColor="@color/hintTextColor"
79 android:textSize="@dimen/textSize16" /> 78 android:textSize="@dimen/textSize16" />
80 -  
81 - <TextView  
82 - android:id="@+id/tv_kaoqin_date" 79 + <LinearLayout
83 android:layout_width="0dp" 80 android:layout_width="0dp"
84 android:layout_height="wrap_content" 81 android:layout_height="wrap_content"
85 android:layout_weight="3" 82 android:layout_weight="3"
86 android:gravity="center" 83 android:gravity="center"
87 - android:text="2018-04-10"  
88 - android:textColor="@color/hintTextColor"  
89 - android:textSize="@dimen/textSize16" />  
90 - 84 + android:orientation="horizontal">
  85 + <TextView
  86 + android:id="@+id/tv_kaoqin_date"
  87 + android:layout_width="0dp"
  88 + android:layout_height="wrap_content"
  89 + android:layout_weight="2"
  90 + android:gravity="right"
  91 + android:text="2018-04-10"
  92 + android:textColor="@color/hintTextColor"
  93 + android:textSize="@dimen/textSize16" />
  94 + <ImageView
  95 + android:id="@+id/iv_calendar"
  96 + android:layout_width="0dp"
  97 + android:layout_height="wrap_content"
  98 + android:layout_weight="1"
  99 + android:gravity="left"
  100 + android:src="@drawable/kaoqin" />
  101 + </LinearLayout>
91 </LinearLayout> 102 </LinearLayout>
92 103
93 <LinearLayout 104 <LinearLayout
94 android:layout_width="match_parent" 105 android:layout_width="match_parent"
95 android:layout_height="0dp" 106 android:layout_height="0dp"
96 android:layout_weight="1" 107 android:layout_weight="1"
  108 + android:gravity="center"
97 android:orientation="horizontal"> 109 android:orientation="horizontal">
98 -  
99 <TextView 110 <TextView
100 android:id="@+id/tv_number" 111 android:id="@+id/tv_number"
101 android:layout_width="0dp" 112 android:layout_width="0dp"
@@ -105,7 +116,6 @@ @@ -105,7 +116,6 @@
105 android:text="考勤次数:" 116 android:text="考勤次数:"
106 android:textColor="@color/hintTextColor" 117 android:textColor="@color/hintTextColor"
107 android:textSize="@dimen/textSize16" /> 118 android:textSize="@dimen/textSize16" />
108 -  
109 <TextView 119 <TextView
110 android:id="@+id/tv_kaoqin_num" 120 android:id="@+id/tv_kaoqin_num"
111 android:layout_width="0dp" 121 android:layout_width="0dp"
app/src/main/res/layout/item_apply_signin.xml
@@ -2,65 +2,105 @@ @@ -2,65 +2,105 @@
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent" 3 android:layout_width="match_parent"
4 android:layout_height="wrap_content" 4 android:layout_height="wrap_content"
  5 + android:layout_marginTop="@dimen/size_dp_10"
5 android:background="@color/white" 6 android:background="@color/white"
6 - android:divider="@color/divider_gray"  
7 - android:layout_marginTop="@dimen/size_dp_10"> 7 + android:divider="@color/divider_gray">
8 8
9 <LinearLayout 9 <LinearLayout
10 android:id="@+id/item_view" 10 android:id="@+id/item_view"
11 - android:layout_margin="10dp"  
12 android:layout_width="match_parent" 11 android:layout_width="match_parent"
13 android:layout_height="wrap_content" 12 android:layout_height="wrap_content"
14 - android:weightSum="10" 13 + android:layout_margin="10dp"
15 android:orientation="horizontal"> 14 android:orientation="horizontal">
16 15
17 <LinearLayout 16 <LinearLayout
18 android:layout_width="5dp" 17 android:layout_width="5dp"
19 android:layout_height="wrap_content" 18 android:layout_height="wrap_content"
20 - android:orientation="vertical"  
21 - android:layout_weight="1"> 19 + android:layout_marginLeft="@dimen/dp_5"
  20 + android:layout_weight="1"
  21 + android:orientation="vertical">
22 22
23 <ImageView 23 <ImageView
24 android:layout_width="10dp" 24 android:layout_width="10dp"
25 android:layout_height="10dp" 25 android:layout_height="10dp"
26 android:background="@drawable/guanlianchild" /> 26 android:background="@drawable/guanlianchild" />
  27 +
27 <TextView 28 <TextView
28 android:layout_width="2dp" 29 android:layout_width="2dp"
29 - android:layout_height="180dp" 30 + android:layout_height="144dp"
30 android:layout_marginLeft="4dp" 31 android:layout_marginLeft="4dp"
31 android:layout_weight="1" 32 android:layout_weight="1"
32 - android:background="@color/line_color"/> 33 + android:background="@color/line_color" />
33 34
34 </LinearLayout> 35 </LinearLayout>
  36 +
35 <LinearLayout 37 <LinearLayout
36 android:layout_width="wrap_content" 38 android:layout_width="wrap_content"
37 android:layout_height="wrap_content" 39 android:layout_height="wrap_content"
38 - android:orientation="vertical"  
39 - android:layout_weight="9">  
40 - <TextView  
41 - android:id="@+id/tv_call"  
42 - android:layout_width="wrap_content"  
43 - android:layout_height="wrap_content"  
44 - android:layout_marginLeft="0dp"  
45 - android:padding="5dp"  
46 - android:text="尊敬的张三家长,"  
47 - android:textColor="@color/hintTextColor"  
48 - android:textSize="@dimen/size_dp_18"/> 40 + android:layout_weight="9"
  41 + android:orientation="vertical">
  42 +
  43 + <LinearLayout
  44 + android:layout_width="match_parent"
  45 + android:layout_height="0dp"
  46 + android:layout_weight="1">
  47 +
  48 + <TextView
  49 + android:layout_width="wrap_content"
  50 + android:layout_height="wrap_content"
  51 + android:text="尊敬的 "
  52 + android:textColor="@color/hintTextColor"
  53 + android:textSize="@dimen/size_dp_18" />
  54 +
  55 + <TextView
  56 + android:id="@+id/tv_call"
  57 + android:layout_width="wrap_content"
  58 + android:layout_height="wrap_content"
  59 + android:text="张三"
  60 + android:textColor="@color/hintTextColor"
  61 + android:textSize="@dimen/size_dp_18" />
  62 +
  63 + <TextView
  64 + android:layout_width="wrap_content"
  65 + android:layout_height="wrap_content"
  66 + android:text=" 家长,"
  67 + android:textColor="@color/hintTextColor"
  68 + android:textSize="@dimen/size_dp_18" />
  69 + </LinearLayout>
  70 +
  71 + <LinearLayout
  72 + android:layout_width="match_parent"
  73 + android:layout_height="0dp"
  74 + android:layout_weight="1">
  75 +
  76 + <TextView
  77 + android:layout_width="wrap_content"
  78 + android:layout_height="wrap_content"
  79 + android:text="您的孩子已与 "
  80 + android:textColor="@color/hintTextColor"
  81 + android:textSize="@dimen/size_dp_16" />
  82 + <TextView
  83 + android:id="@+id/tv_attendance_date"
  84 + android:layout_width="wrap_content"
  85 + android:layout_height="wrap_content"
  86 + android:textColor="@color/hintTextColor"
  87 + android:textSize="@dimen/size_dp_16"
  88 + android:text="10:00:15"/>
  89 + <TextView
  90 + android:layout_width="wrap_content"
  91 + android:layout_height="wrap_content"
  92 + android:textSize="@dimen/size_dp_16"
  93 + android:textColor="@color/hintTextColor"
  94 + android:text=" 进校!"/>
  95 + </LinearLayout>
49 96
50 - <TextView  
51 - android:id="@+id/tv_attendance_date"  
52 - android:layout_width="wrap_content"  
53 - android:layout_height="wrap_content"  
54 - android:layout_marginLeft="0dp"  
55 - android:padding="5dp"  
56 - android:text="您的孩子已与8:00:15进校!"  
57 - android:textColor="@color/hintTextColor"  
58 - android:textSize="@dimen/size_dp_16" />  
59 <ImageView 97 <ImageView
60 android:id="@+id/iv_photo" 98 android:id="@+id/iv_photo"
  99 + android:layout_weight="2"
  100 + android:scaleType="fitCenter"
61 android:layout_width="wrap_content" 101 android:layout_width="wrap_content"
62 - android:layout_height="wrap_content"  
63 - android:background="@drawable/photo"/> 102 + android:layout_height="0dp"
  103 + android:background="@drawable/photo" />
64 </LinearLayout> 104 </LinearLayout>
65 </LinearLayout> 105 </LinearLayout>
66 106
app/src/main/res/layout/pickerview_custom_lunar.xml 0 → 100644
@@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:layout_width="wrap_content"
  4 + android:layout_height="wrap_content"
  5 + android:orientation="vertical">
  6 +
  7 + <RelativeLayout
  8 + android:layout_width="match_parent"
  9 + android:layout_height="50dp"
  10 + android:background="#EEEEEE">
  11 +
  12 + <View
  13 + android:layout_width="match_parent"
  14 + android:layout_height="0.5dp"
  15 + android:background="#aaa" />
  16 +
  17 + <ImageView
  18 + android:id="@+id/iv_cancel"
  19 + android:layout_width="35dp"
  20 + android:layout_height="35dp"
  21 + android:layout_centerVertical="true"
  22 + android:layout_marginLeft="17dp"
  23 + android:padding="8dp"
  24 + android:src="@drawable/to_down" />
  25 +
  26 + <!--<CheckBox-->
  27 + <!--android:layout_centerInParent="true"-->
  28 + <!--android:id="@+id/cb_lunar"-->
  29 + <!--android:layout_width="wrap_content"-->
  30 + <!--android:layout_height="wrap_content"-->
  31 + <!--android:layout_marginRight="17dp"-->
  32 + <!--android:text="农历"-->
  33 + <!--android:textColor="#24AD9D"-->
  34 + <!--android:textSize="18sp" />-->
  35 +
  36 + <TextView
  37 + android:id="@+id/tv_finish"
  38 + android:layout_width="wrap_content"
  39 + android:layout_height="wrap_content"
  40 + android:layout_alignParentRight="true"
  41 + android:layout_centerVertical="true"
  42 + android:layout_marginRight="17dp"
  43 + android:padding="8dp"
  44 + android:text="完成"
  45 + android:textColor="#24AD9D"
  46 + android:textSize="18sp" />
  47 +
  48 + <View
  49 + android:layout_width="match_parent"
  50 + android:layout_height="0.5dp"
  51 + android:background="#aaa" />
  52 + </RelativeLayout>
  53 +
  54 +
  55 + <!--此部分需要完整复制过去,删减或者更改ID会导致初始化找不到内容而报空-->
  56 + <LinearLayout
  57 + android:id="@+id/timepicker"
  58 + android:layout_width="fill_parent"
  59 + android:layout_height="wrap_content"
  60 + android:background="@android:color/white"
  61 + android:orientation="horizontal">
  62 +
  63 + <com.contrarywind.view.WheelView
  64 + android:id="@+id/year"
  65 + android:layout_width="fill_parent"
  66 + android:layout_height="wrap_content"
  67 + android:layout_weight="1" />
  68 +
  69 + <com.contrarywind.view.WheelView
  70 +
  71 + android:id="@+id/month"
  72 + android:layout_width="fill_parent"
  73 + android:layout_height="wrap_content"
  74 + android:layout_weight="1.1" />
  75 +
  76 + <com.contrarywind.view.WheelView
  77 + android:id="@+id/day"
  78 + android:layout_width="fill_parent"
  79 + android:layout_height="wrap_content"
  80 + android:layout_weight="1.1" />
  81 +
  82 + <com.contrarywind.view.WheelView
  83 + android:id="@+id/hour"
  84 + android:layout_width="fill_parent"
  85 + android:layout_height="wrap_content"
  86 + android:layout_weight="1.1" />
  87 +
  88 + <com.contrarywind.view.WheelView
  89 + android:id="@+id/min"
  90 + android:layout_width="fill_parent"
  91 + android:layout_height="wrap_content"
  92 + android:layout_weight="1.1" />
  93 +
  94 + <com.contrarywind.view.WheelView
  95 + android:id="@+id/second"
  96 + android:layout_width="fill_parent"
  97 + android:layout_height="wrap_content"
  98 + android:layout_weight="1.1" />
  99 + </LinearLayout>
  100 +
  101 +
  102 +</LinearLayout>
0 \ No newline at end of file 103 \ No newline at end of file
wheelview/.gitignore 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +/build
wheelview/build.gradle 0 → 100644
@@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
  1 +apply plugin: 'com.android.library'
  2 +apply plugin: 'com.github.dcendents.android-maven'
  3 +apply plugin: 'com.novoda.bintray-release'//添加插件
  4 +
  5 +
  6 +
  7 +android {
  8 + compileSdkVersion 26
  9 + buildToolsVersion "26.0.2"
  10 +
  11 + defaultConfig {
  12 + minSdkVersion 14
  13 + targetSdkVersion 26
  14 + versionCode 28
  15 + versionName "4.0.5"
  16 + }
  17 + buildTypes {
  18 + release {
  19 + minifyEnabled false
  20 + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  21 + }
  22 + }
  23 + lintOptions {
  24 + abortOnError false
  25 + }
  26 +}
  27 +
  28 +allprojects {
  29 + tasks.withType(Javadoc) {//兼容中文字符
  30 + options{
  31 + encoding "UTF-8"
  32 + charSet 'UTF-8'
  33 + links "http://docs.oracle.com/javase/7/docs/api"
  34 + }
  35 + }
  36 +}
  37 +publish {
  38 + userOrg = 'contrarywind'//bintray.com 用户名/组织名 user/org name
  39 + groupId = 'com.contrarywind'//JCenter上显示的路径 path
  40 + artifactId = 'wheelview'//项目名称 project name
  41 + publishVersion = '4.0.5'//版本号 version code
  42 + desc = 'this is a wheelview for android'//项目描述 description
  43 + website = 'https://github.com/Bigkoo/Android-PickerView' //项目网址链接 link
  44 +}
  45 +
  46 +dependencies {
  47 + compile fileTree(include: ['*.jar'], dir: 'libs')
  48 +
  49 +}
0 \ No newline at end of file 50 \ No newline at end of file
wheelview/proguard-rules.pro 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +# Add project specific ProGuard rules here.
  2 +# By default, the flags in this file are appended to flags specified
  3 +# in C:\Users\song\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
  4 +# You can edit the include path and order by changing the proguardFiles
  5 +# directive in build.gradle.
  6 +#
  7 +# For more details, see
  8 +# http://developer.android.com/guide/developing/tools/proguard.html
  9 +
  10 +# Add any project specific keep options here:
  11 +
  12 +# If your project uses WebView with JS, uncomment the following
  13 +# and specify the fully qualified class name to the JavaScript interface
  14 +# class:
  15 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  16 +# public *;
  17 +#}
  18 +
  19 +# Uncomment this to preserve the line number information for
  20 +# debugging stack traces.
  21 +#-keepattributes SourceFile,LineNumberTable
  22 +
  23 +# If you keep the line number information, uncomment this to
  24 +# hide the original source file name.
  25 +#-renamesourcefileattribute SourceFile
wheelview/src/androidTest/java/test/wheelview/ExampleInstrumentedTest.java 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +package test.wheelview;
  2 +
  3 +import android.content.Context;
  4 +import android.support.test.InstrumentationRegistry;
  5 +import android.support.test.runner.AndroidJUnit4;
  6 +
  7 +import org.junit.Test;
  8 +import org.junit.runner.RunWith;
  9 +
  10 +import static org.junit.Assert.*;
  11 +
  12 +/**
  13 + * Instrumentation test, which will execute on an Android device.
  14 + *
  15 + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
  16 + */
  17 +@RunWith(AndroidJUnit4.class)
  18 +public class ExampleInstrumentedTest {
  19 + @Test
  20 + public void useAppContext() throws Exception {
  21 + // Context of the app under test.
  22 + Context appContext = InstrumentationRegistry.getTargetContext();
  23 +
  24 + assertEquals("test.wheelview.test", appContext.getPackageName());
  25 + }
  26 +}
wheelview/src/main/AndroidManifest.xml 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2 +
  3 + package="com.contrarywind.view">
  4 +
  5 + <application android:allowBackup="true" android:label="@string/app_name"
  6 + android:supportsRtl="true">
  7 +
  8 + </application>
  9 +
  10 +</manifest>
wheelview/src/main/java/com/contrarywind/adapter/WheelAdapter.java 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +package com.contrarywind.adapter;
  2 +
  3 +
  4 +public interface WheelAdapter<T> {
  5 + /**
  6 + * Gets items count
  7 + * @return the count of wheel items
  8 + */
  9 + int getItemsCount();
  10 +
  11 + /**
  12 + * Gets a wheel item by index.
  13 + * @param index the item index
  14 + * @return the wheel item text or null
  15 + */
  16 + T getItem(int index);
  17 +
  18 + /**
  19 + * Gets maximum item length. It is used to determine the wheel width.
  20 + * If -1 is returned there will be used the default wheel width.
  21 + * @param o the item object
  22 + * @return the maximum item length or -1
  23 + */
  24 + int indexOf(T o);
  25 +}
wheelview/src/main/java/com/contrarywind/interfaces/IPickerViewData.java 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +package com.contrarywind.interfaces;
  2 +
  3 +/**
  4 + * Created by Sai on 2016/7/13.
  5 + */
  6 +public interface IPickerViewData {
  7 + String getPickerViewText();
  8 +}
wheelview/src/main/java/com/contrarywind/listener/LoopViewGestureListener.java 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +package com.contrarywind.listener;
  2 +
  3 +import android.view.MotionEvent;
  4 +
  5 +import com.contrarywind.view.WheelView;
  6 +
  7 +
  8 +/**
  9 + * 手势监听
  10 + */
  11 +public final class LoopViewGestureListener extends android.view.GestureDetector.SimpleOnGestureListener {
  12 +
  13 + private final WheelView wheelView;
  14 +
  15 +
  16 + public LoopViewGestureListener(WheelView wheelView) {
  17 + this.wheelView = wheelView;
  18 + }
  19 +
  20 + @Override
  21 + public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
  22 + wheelView.scrollBy(velocityY);
  23 + return true;
  24 + }
  25 +}
wheelview/src/main/java/com/contrarywind/listener/OnItemSelectedListener.java 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +package com.contrarywind.listener;
  2 +
  3 +
  4 +public interface OnItemSelectedListener {
  5 + void onItemSelected(int index);
  6 +}
wheelview/src/main/java/com/contrarywind/timer/InertiaTimerTask.java 0 → 100644
@@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
  1 +package com.contrarywind.timer;
  2 +
  3 +import com.contrarywind.view.WheelView;
  4 +
  5 +import java.util.TimerTask;
  6 +
  7 +/**
  8 + * 滚动惯性的实现
  9 + *
  10 + * @author 小嵩
  11 + * date: 2017-12-23 23:20:44
  12 + */
  13 +public final class InertiaTimerTask extends TimerTask {
  14 +
  15 + private float mCurrentVelocityY; //当前滑动速度
  16 + private final float mFirstVelocityY;//手指离开屏幕时的初始速度
  17 + private final WheelView mWheelView;
  18 +
  19 + /**
  20 + * @param wheelView 滚轮对象
  21 + * @param velocityY Y轴滑行速度
  22 + */
  23 + public InertiaTimerTask(WheelView wheelView, float velocityY) {
  24 + super();
  25 + this.mWheelView = wheelView;
  26 + this.mFirstVelocityY = velocityY;
  27 + mCurrentVelocityY = Integer.MAX_VALUE;
  28 + }
  29 +
  30 + @Override
  31 + public final void run() {
  32 +
  33 + //防止闪动,对速度做一个限制。
  34 + if (mCurrentVelocityY == Integer.MAX_VALUE) {
  35 + if (Math.abs(mFirstVelocityY) > 2000F) {
  36 + mCurrentVelocityY = mFirstVelocityY > 0 ? 2000F : -2000F;
  37 + } else {
  38 + mCurrentVelocityY = mFirstVelocityY;
  39 + }
  40 + }
  41 +
  42 + //发送handler消息 处理平顺停止滚动逻辑
  43 + if (Math.abs(mCurrentVelocityY) >= 0.0F && Math.abs(mCurrentVelocityY) <= 20F) {
  44 + mWheelView.cancelFuture();
  45 + mWheelView.getHandler().sendEmptyMessage(MessageHandler.WHAT_SMOOTH_SCROLL);
  46 + return;
  47 + }
  48 +
  49 + int dy = (int) (mCurrentVelocityY / 100F);
  50 + mWheelView.setTotalScrollY(mWheelView.getTotalScrollY() - dy);
  51 + if (!mWheelView.isLoop()) {
  52 + float itemHeight = mWheelView.getItemHeight();
  53 + float top = (-mWheelView.getInitPosition()) * itemHeight;
  54 + float bottom = (mWheelView.getItemsCount() - 1 - mWheelView.getInitPosition()) * itemHeight;
  55 + if (mWheelView.getTotalScrollY() - itemHeight * 0.25 < top) {
  56 + top = mWheelView.getTotalScrollY() + dy;
  57 + } else if (mWheelView.getTotalScrollY() + itemHeight * 0.25 > bottom) {
  58 + bottom = mWheelView.getTotalScrollY() + dy;
  59 + }
  60 +
  61 + if (mWheelView.getTotalScrollY() <= top) {
  62 + mCurrentVelocityY = 40F;
  63 + mWheelView.setTotalScrollY((int) top);
  64 + } else if (mWheelView.getTotalScrollY() >= bottom) {
  65 + mWheelView.setTotalScrollY((int) bottom);
  66 + mCurrentVelocityY = -40F;
  67 + }
  68 + }
  69 +
  70 + if (mCurrentVelocityY < 0.0F) {
  71 + mCurrentVelocityY = mCurrentVelocityY + 20F;
  72 + } else {
  73 + mCurrentVelocityY = mCurrentVelocityY - 20F;
  74 + }
  75 +
  76 + //刷新UI
  77 + mWheelView.getHandler().sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW);
  78 + }
  79 +}
wheelview/src/main/java/com/contrarywind/timer/MessageHandler.java 0 → 100644
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
  1 +package com.contrarywind.timer;
  2 +
  3 +import android.os.Handler;
  4 +import android.os.Message;
  5 +
  6 +import com.contrarywind.view.WheelView;
  7 +
  8 +/**
  9 + * Handler 消息类
  10 + *
  11 + * @author 小嵩
  12 + * date: 2017-12-23 23:20:44
  13 + */
  14 +public final class MessageHandler extends Handler {
  15 + public static final int WHAT_INVALIDATE_LOOP_VIEW = 1000;
  16 + public static final int WHAT_SMOOTH_SCROLL = 2000;
  17 + public static final int WHAT_ITEM_SELECTED = 3000;
  18 +
  19 + private final WheelView wheelView;
  20 +
  21 + public MessageHandler(WheelView wheelView) {
  22 + this.wheelView = wheelView;
  23 + }
  24 +
  25 + @Override
  26 + public final void handleMessage(Message msg) {
  27 + switch (msg.what) {
  28 + case WHAT_INVALIDATE_LOOP_VIEW:
  29 + wheelView.invalidate();
  30 + break;
  31 +
  32 + case WHAT_SMOOTH_SCROLL:
  33 + wheelView.smoothScroll(WheelView.ACTION.FLING);
  34 + break;
  35 +
  36 + case WHAT_ITEM_SELECTED:
  37 + wheelView.onItemSelected();
  38 + break;
  39 + }
  40 + }
  41 +
  42 +}
wheelview/src/main/java/com/contrarywind/timer/SmoothScrollTimerTask.java 0 → 100644
@@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
  1 +package com.contrarywind.timer;
  2 +
  3 +import com.contrarywind.view.WheelView;
  4 +
  5 +import java.util.TimerTask;
  6 +
  7 +/**
  8 + * 平滑滚动的实现
  9 + *
  10 + * @author 小嵩
  11 + */
  12 +public final class SmoothScrollTimerTask extends TimerTask {
  13 +
  14 + private int realTotalOffset;
  15 + private int realOffset;
  16 + private int offset;
  17 + private final WheelView wheelView;
  18 +
  19 + public SmoothScrollTimerTask(WheelView wheelView, int offset) {
  20 + this.wheelView = wheelView;
  21 + this.offset = offset;
  22 + realTotalOffset = Integer.MAX_VALUE;
  23 + realOffset = 0;
  24 + }
  25 +
  26 + @Override
  27 + public final void run() {
  28 + if (realTotalOffset == Integer.MAX_VALUE) {
  29 + realTotalOffset = offset;
  30 + }
  31 + //把要滚动的范围细分成10小份,按10小份单位来重绘
  32 + realOffset = (int) ((float) realTotalOffset * 0.1F);
  33 +
  34 + if (realOffset == 0) {
  35 + if (realTotalOffset < 0) {
  36 + realOffset = -1;
  37 + } else {
  38 + realOffset = 1;
  39 + }
  40 + }
  41 +
  42 + if (Math.abs(realTotalOffset) <= 1) {
  43 + wheelView.cancelFuture();
  44 + wheelView.getHandler().sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED);
  45 + } else {
  46 + wheelView.setTotalScrollY(wheelView.getTotalScrollY() + realOffset);
  47 +
  48 + //这里如果不是循环模式,则点击空白位置需要回滚,不然就会出现选到-1 item的 情况
  49 + if (!wheelView.isLoop()) {
  50 + float itemHeight = wheelView.getItemHeight();
  51 + float top = (float) (-wheelView.getInitPosition()) * itemHeight;
  52 + float bottom = (float) (wheelView.getItemsCount() - 1 - wheelView.getInitPosition()) * itemHeight;
  53 + if (wheelView.getTotalScrollY() <= top || wheelView.getTotalScrollY() >= bottom) {
  54 + wheelView.setTotalScrollY(wheelView.getTotalScrollY() - realOffset);
  55 + wheelView.cancelFuture();
  56 + wheelView.getHandler().sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED);
  57 + return;
  58 + }
  59 + }
  60 + wheelView.getHandler().sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW);
  61 + realTotalOffset = realTotalOffset - realOffset;
  62 + }
  63 + }
  64 +}
wheelview/src/main/java/com/contrarywind/view/WheelView.java 0 → 100644
@@ -0,0 +1,822 @@ @@ -0,0 +1,822 @@
  1 +package com.contrarywind.view;
  2 +
  3 +import android.annotation.SuppressLint;
  4 +import android.content.Context;
  5 +import android.content.res.TypedArray;
  6 +import android.graphics.Canvas;
  7 +import android.graphics.Paint;
  8 +import android.graphics.Rect;
  9 +import android.graphics.Typeface;
  10 +import android.os.Handler;
  11 +import android.text.TextUtils;
  12 +import android.util.AttributeSet;
  13 +import android.util.DisplayMetrics;
  14 +import android.util.Log;
  15 +import android.view.GestureDetector;
  16 +import android.view.Gravity;
  17 +import android.view.MotionEvent;
  18 +import android.view.View;
  19 +
  20 +import com.contrarywind.adapter.WheelAdapter;
  21 +import com.contrarywind.interfaces.IPickerViewData;
  22 +import com.contrarywind.listener.LoopViewGestureListener;
  23 +import com.contrarywind.listener.OnItemSelectedListener;
  24 +import com.contrarywind.timer.InertiaTimerTask;
  25 +import com.contrarywind.timer.MessageHandler;
  26 +import com.contrarywind.timer.SmoothScrollTimerTask;
  27 +
  28 +import java.util.Locale;
  29 +import java.util.concurrent.Executors;
  30 +import java.util.concurrent.ScheduledExecutorService;
  31 +import java.util.concurrent.ScheduledFuture;
  32 +import java.util.concurrent.TimeUnit;
  33 +
  34 +/**
  35 + * 3d滚轮控件
  36 + */
  37 +public class WheelView extends View {
  38 +
  39 + public enum ACTION { // 点击,滑翔(滑到尽头),拖拽事件
  40 + CLICK, FLING, DAGGLE
  41 + }
  42 +
  43 + public enum DividerType { // 分隔线类型
  44 + FILL, WRAP
  45 + }
  46 +
  47 + private DividerType dividerType;//分隔线类型
  48 +
  49 + private Context context;
  50 + private Handler handler;
  51 + private GestureDetector gestureDetector;
  52 + private OnItemSelectedListener onItemSelectedListener;
  53 +
  54 + private boolean isOptions = false;
  55 + private boolean isCenterLabel = true;
  56 +
  57 + // Timer mTimer;
  58 + private ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor();
  59 + private ScheduledFuture<?> mFuture;
  60 +
  61 + private Paint paintOuterText;
  62 + private Paint paintCenterText;
  63 + private Paint paintIndicator;
  64 +
  65 + private WheelAdapter adapter;
  66 +
  67 + private String label;//附加单位
  68 + private int textSize;//选项的文字大小
  69 + private int maxTextWidth;
  70 + private int maxTextHeight;
  71 + private int textXOffset;
  72 + private float itemHeight;//每行高度
  73 +
  74 +
  75 + private Typeface typeface = Typeface.MONOSPACE;//字体样式,默认是等宽字体
  76 + private int textColorOut;
  77 + private int textColorCenter;
  78 + private int dividerColor;
  79 +
  80 + // 条目间距倍数
  81 + private float lineSpacingMultiplier = 1.6F;
  82 + private boolean isLoop;
  83 +
  84 + // 第一条线Y坐标值
  85 + private float firstLineY;
  86 + //第二条线Y坐标
  87 + private float secondLineY;
  88 + //中间label绘制的Y坐标
  89 + private float centerY;
  90 +
  91 + //当前滚动总高度y值
  92 + private float totalScrollY;
  93 +
  94 + //初始化默认选中项
  95 + private int initPosition;
  96 +
  97 + //选中的Item是第几个
  98 + private int selectedItem;
  99 + private int preCurrentIndex;
  100 + //滚动偏移值,用于记录滚动了多少个item
  101 + private int change;
  102 +
  103 + // 绘制几个条目,实际上第一项和最后一项Y轴压缩成0%了,所以可见的数目实际为9
  104 + private int itemsVisible = 11;
  105 +
  106 + private int measuredHeight;// WheelView 控件高度
  107 + private int measuredWidth;// WheelView 控件宽度
  108 +
  109 + // 半径
  110 + private int radius;
  111 +
  112 + private int mOffset = 0;
  113 + private float previousY = 0;
  114 + private long startTime = 0;
  115 +
  116 + // 修改这个值可以改变滑行速度
  117 + private static final int VELOCITY_FLING = 5;
  118 + private int widthMeasureSpec;
  119 +
  120 + private int mGravity = Gravity.CENTER;
  121 + private int drawCenterContentStart = 0;//中间选中文字开始绘制位置
  122 + private int drawOutContentStart = 0;//非中间文字开始绘制位置
  123 + private static final float SCALE_CONTENT = 0.8F;//非中间文字则用此控制高度,压扁形成3d错觉
  124 + private float CENTER_CONTENT_OFFSET;//偏移量
  125 +
  126 + private final float DEFAULT_TEXT_TARGET_SKEWX = 0.5f;
  127 +
  128 + public WheelView(Context context) {
  129 + this(context, null);
  130 + }
  131 +
  132 + public WheelView(Context context, AttributeSet attrs) {
  133 + super(context, attrs);
  134 +
  135 + textSize = getResources().getDimensionPixelSize(R.dimen.pickerview_textsize);//默认大小
  136 +
  137 + DisplayMetrics dm = getResources().getDisplayMetrics();
  138 + float density = dm.density; // 屏幕密度比(0.75/1.0/1.5/2.0/3.0)
  139 +
  140 + if (density < 1) {//根据密度不同进行适配
  141 + CENTER_CONTENT_OFFSET = 2.4F;
  142 + } else if (1 <= density && density < 2) {
  143 + CENTER_CONTENT_OFFSET = 3.6F;
  144 + } else if (1 <= density && density < 2) {
  145 + CENTER_CONTENT_OFFSET = 4.5F;
  146 + } else if (2 <= density && density < 3) {
  147 + CENTER_CONTENT_OFFSET = 6.0F;
  148 + } else if (density >= 3) {
  149 + CENTER_CONTENT_OFFSET = density * 2.5F;
  150 + }
  151 +
  152 + if (attrs != null) {
  153 + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.pickerview, 0, 0);
  154 + mGravity = a.getInt(R.styleable.pickerview_wheelview_gravity, Gravity.CENTER);
  155 + textColorOut = a.getColor(R.styleable.pickerview_wheelview_textColorOut, 0xFFa8a8a8);
  156 + textColorCenter = a.getColor(R.styleable.pickerview_wheelview_textColorCenter, 0xFF2a2a2a);
  157 + dividerColor = a.getColor(R.styleable.pickerview_wheelview_dividerColor, 0xFFd5d5d5);
  158 + textSize = a.getDimensionPixelOffset(R.styleable.pickerview_wheelview_textSize, textSize);
  159 + lineSpacingMultiplier = a.getFloat(R.styleable.pickerview_wheelview_lineSpacingMultiplier, lineSpacingMultiplier);
  160 + a.recycle();//回收内存
  161 + }
  162 +
  163 + judgeLineSpace();
  164 + initLoopView(context);
  165 + }
  166 +
  167 + /**
  168 + * 判断间距是否在1.0-4.0之间
  169 + */
  170 + private void judgeLineSpace() {
  171 + if (lineSpacingMultiplier < 1.0f) {
  172 + lineSpacingMultiplier = 1.0f;
  173 + } else if (lineSpacingMultiplier > 4.0f) {
  174 + lineSpacingMultiplier = 4.0f;
  175 + }
  176 + }
  177 +
  178 + private void initLoopView(Context context) {
  179 + this.context = context;
  180 + handler = new MessageHandler(this);
  181 + gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this));
  182 + gestureDetector.setIsLongpressEnabled(false);
  183 + isLoop = true;
  184 +
  185 + totalScrollY = 0;
  186 + initPosition = -1;
  187 + initPaints();
  188 + }
  189 +
  190 + private void initPaints() {
  191 + paintOuterText = new Paint();
  192 + paintOuterText.setColor(textColorOut);
  193 + paintOuterText.setAntiAlias(true);
  194 + paintOuterText.setTypeface(typeface);
  195 + paintOuterText.setTextSize(textSize);
  196 +
  197 + paintCenterText = new Paint();
  198 + paintCenterText.setColor(textColorCenter);
  199 + paintCenterText.setAntiAlias(true);
  200 + paintCenterText.setTextScaleX(1.1F);
  201 + paintCenterText.setTypeface(typeface);
  202 + paintCenterText.setTextSize(textSize);
  203 +
  204 + paintIndicator = new Paint();
  205 + paintIndicator.setColor(dividerColor);
  206 + paintIndicator.setAntiAlias(true);
  207 +
  208 + setLayerType(LAYER_TYPE_SOFTWARE, null);
  209 + }
  210 +
  211 + private void remeasure() {//重新测量
  212 + if (adapter == null) {
  213 + return;
  214 + }
  215 +
  216 + measureTextWidthHeight();
  217 +
  218 + //半圆的周长 = item高度乘以item数目-1
  219 + int halfCircumference = (int) (itemHeight * (itemsVisible - 1));
  220 + //整个圆的周长除以PI得到直径,这个直径用作控件的总高度
  221 + measuredHeight = (int) ((halfCircumference * 2) / Math.PI);
  222 + //求出半径
  223 + radius = (int) (halfCircumference / Math.PI);
  224 + //控件宽度,这里支持weight
  225 + measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
  226 + //计算两条横线 和 选中项画笔的基线Y位置
  227 + firstLineY = (measuredHeight - itemHeight) / 2.0F;
  228 + secondLineY = (measuredHeight + itemHeight) / 2.0F;
  229 + centerY = secondLineY - (itemHeight - maxTextHeight) / 2.0f - CENTER_CONTENT_OFFSET;
  230 +
  231 + //初始化显示的item的position
  232 + if (initPosition == -1) {
  233 + if (isLoop) {
  234 + initPosition = (adapter.getItemsCount() + 1) / 2;
  235 + } else {
  236 + initPosition = 0;
  237 + }
  238 + }
  239 + preCurrentIndex = initPosition;
  240 + }
  241 +
  242 + /**
  243 + * 计算最大length的Text的宽高度
  244 + */
  245 + private void measureTextWidthHeight() {
  246 + Rect rect = new Rect();
  247 + for (int i = 0; i < adapter.getItemsCount(); i++) {
  248 + String s1 = getContentText(adapter.getItem(i));
  249 + paintCenterText.getTextBounds(s1, 0, s1.length(), rect);
  250 +
  251 + int textWidth = rect.width();
  252 +
  253 + if (textWidth > maxTextWidth) {
  254 + maxTextWidth = textWidth;
  255 + }
  256 + paintCenterText.getTextBounds("\u661F\u671F", 0, 2, rect); // 星期的字符编码(以它为标准高度)
  257 +
  258 + maxTextHeight = rect.height() + 2;
  259 +
  260 + }
  261 + itemHeight = lineSpacingMultiplier * maxTextHeight;
  262 + }
  263 +
  264 + public void smoothScroll(ACTION action) {//平滑滚动的实现
  265 + cancelFuture();
  266 + if (action == ACTION.FLING || action == ACTION.DAGGLE) {
  267 + mOffset = (int) ((totalScrollY % itemHeight + itemHeight) % itemHeight);
  268 + if ((float) mOffset > itemHeight / 2.0F) {//如果超过Item高度的一半,滚动到下一个Item去
  269 + mOffset = (int) (itemHeight - (float) mOffset);
  270 + } else {
  271 + mOffset = -mOffset;
  272 + }
  273 + }
  274 + //停止的时候,位置有偏移,不是全部都能正确停止到中间位置的,这里把文字位置挪回中间去
  275 + mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10, TimeUnit.MILLISECONDS);
  276 + }
  277 +
  278 + public final void scrollBy(float velocityY) {//滚动惯性的实现
  279 + cancelFuture();
  280 + mFuture = mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, VELOCITY_FLING, TimeUnit.MILLISECONDS);
  281 + }
  282 +
  283 + public void cancelFuture() {
  284 + if (mFuture != null && !mFuture.isCancelled()) {
  285 + mFuture.cancel(true);
  286 + mFuture = null;
  287 + }
  288 + }
  289 +
  290 + /**
  291 + * 设置是否循环滚动
  292 + *
  293 + * @param cyclic 是否循环
  294 + */
  295 + public final void setCyclic(boolean cyclic) {
  296 + isLoop = cyclic;
  297 + }
  298 +
  299 + public final void setTypeface(Typeface font) {
  300 + typeface = font;
  301 + paintOuterText.setTypeface(typeface);
  302 + paintCenterText.setTypeface(typeface);
  303 + }
  304 +
  305 + public final void setTextSize(float size) {
  306 + if (size > 0.0F) {
  307 + textSize = (int) (context.getResources().getDisplayMetrics().density * size);
  308 + paintOuterText.setTextSize(textSize);
  309 + paintCenterText.setTextSize(textSize);
  310 + }
  311 + }
  312 +
  313 + public final void setCurrentItem(int currentItem) {
  314 + //不添加这句,当这个wheelView不可见时,默认都是0,会导致获取到的时间错误
  315 + this.selectedItem = currentItem;
  316 + this.initPosition = currentItem;
  317 + totalScrollY = 0;//回归顶部,不然重设setCurrentItem的话位置会偏移的,就会显示出不对位置的数据
  318 + invalidate();
  319 + }
  320 +
  321 + public final void setOnItemSelectedListener(OnItemSelectedListener OnItemSelectedListener) {
  322 + this.onItemSelectedListener = OnItemSelectedListener;
  323 + }
  324 +
  325 + public final void setAdapter(WheelAdapter adapter) {
  326 + this.adapter = adapter;
  327 + remeasure();
  328 + invalidate();
  329 + }
  330 +
  331 + public final WheelAdapter getAdapter() {
  332 + return adapter;
  333 + }
  334 +
  335 + public final int getCurrentItem() {
  336 + return selectedItem;
  337 + }
  338 +
  339 + public final void onItemSelected() {
  340 + if (onItemSelectedListener != null) {
  341 + postDelayed(new Runnable() {
  342 + @Override
  343 + public void run() {
  344 + onItemSelectedListener.onItemSelected(getCurrentItem());
  345 + }
  346 + }, 200L);
  347 + }
  348 + }
  349 +
  350 + @Override
  351 + protected void onDraw(Canvas canvas) {
  352 + if (adapter == null) {
  353 + return;
  354 + }
  355 + //initPosition越界会造成preCurrentIndex的值不正确
  356 + initPosition = Math.min(Math.max(0, initPosition), adapter.getItemsCount() - 1);
  357 +
  358 + //可见的item数组
  359 + @SuppressLint("DrawAllocation")
  360 + Object visibles[] = new Object[itemsVisible];
  361 + //滚动的Y值高度除去每行Item的高度,得到滚动了多少个item,即change数
  362 + change = (int) (totalScrollY / itemHeight);
  363 + // Log.d("change", "" + change);
  364 +
  365 + try {
  366 + //滚动中实际的预选中的item(即经过了中间位置的item) = 滑动前的位置 + 滑动相对位置
  367 + preCurrentIndex = initPosition + change % adapter.getItemsCount();
  368 +
  369 + } catch (ArithmeticException e) {
  370 + Log.e("WheelView", "出错了!adapter.getItemsCount() == 0,联动数据不匹配");
  371 + }
  372 + if (!isLoop) {//不循环的情况
  373 + if (preCurrentIndex < 0) {
  374 + preCurrentIndex = 0;
  375 + }
  376 + if (preCurrentIndex > adapter.getItemsCount() - 1) {
  377 + preCurrentIndex = adapter.getItemsCount() - 1;
  378 + }
  379 + } else {//循环
  380 + if (preCurrentIndex < 0) {//举个例子:如果总数是5,preCurrentIndex = -1,那么preCurrentIndex按循环来说,其实是0的上面,也就是4的位置
  381 + preCurrentIndex = adapter.getItemsCount() + preCurrentIndex;
  382 + }
  383 + if (preCurrentIndex > adapter.getItemsCount() - 1) {//同理上面,自己脑补一下
  384 + preCurrentIndex = preCurrentIndex - adapter.getItemsCount();
  385 + }
  386 + }
  387 + //跟滚动流畅度有关,总滑动距离与每个item高度取余,即并不是一格格的滚动,每个item不一定滚到对应Rect里的,这个item对应格子的偏移值
  388 + float itemHeightOffset = (totalScrollY % itemHeight);
  389 +
  390 + // 设置数组中每个元素的值
  391 + int counter = 0;
  392 + while (counter < itemsVisible) {
  393 + int index = preCurrentIndex - (itemsVisible / 2 - counter);//索引值,即当前在控件中间的item看作数据源的中间,计算出相对源数据源的index值
  394 + //判断是否循环,如果是循环数据源也使用相对循环的position获取对应的item值,如果不是循环则超出数据源范围使用""空白字符串填充,在界面上形成空白无数据的item项
  395 + if (isLoop) {
  396 + index = getLoopMappingIndex(index);
  397 + visibles[counter] = adapter.getItem(index);
  398 + } else if (index < 0) {
  399 + visibles[counter] = "";
  400 + } else if (index > adapter.getItemsCount() - 1) {
  401 + visibles[counter] = "";
  402 + } else {
  403 + visibles[counter] = adapter.getItem(index);
  404 + }
  405 +
  406 + counter++;
  407 +
  408 + }
  409 +
  410 + //绘制中间两条横线
  411 + if (dividerType == DividerType.WRAP) {//横线长度仅包裹内容
  412 + float startX;
  413 + float endX;
  414 +
  415 + if (TextUtils.isEmpty(label)) {//隐藏Label的情况
  416 + startX = (measuredWidth - maxTextWidth) / 2 - 12;
  417 + } else {
  418 + startX = (measuredWidth - maxTextWidth) / 4 - 12;
  419 + }
  420 +
  421 + if (startX <= 0) {//如果超过了WheelView的边缘
  422 + startX = 10;
  423 + }
  424 + endX = measuredWidth - startX;
  425 + canvas.drawLine(startX, firstLineY, endX, firstLineY, paintIndicator);
  426 + canvas.drawLine(startX, secondLineY, endX, secondLineY, paintIndicator);
  427 + } else {
  428 + canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator);
  429 + canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator);
  430 + }
  431 +
  432 + //只显示选中项Label文字的模式,并且Label文字不为空,则进行绘制
  433 + if (!TextUtils.isEmpty(label) && isCenterLabel) {
  434 + //绘制文字,靠右并留出空隙
  435 + int drawRightContentStart = measuredWidth - getTextWidth(paintCenterText, label);
  436 + canvas.drawText(label, drawRightContentStart - CENTER_CONTENT_OFFSET, centerY, paintCenterText);
  437 + }
  438 +
  439 + counter = 0;
  440 + while (counter < itemsVisible) {
  441 + canvas.save();
  442 + // 弧长 L = itemHeight * counter - itemHeightOffset
  443 + // 求弧度 α = L / r (弧长/半径) [0,π]
  444 + double radian = ((itemHeight * counter - itemHeightOffset)) / radius;
  445 + // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限
  446 + // angle [-90°,90°]
  447 + float angle = (float) (90D - (radian / Math.PI) * 180D);//item第一项,从90度开始,逐渐递减到 -90度
  448 +
  449 + // 计算取值可能有细微偏差,保证负90°到90°以外的不绘制
  450 + if (angle >= 90F || angle <= -90F) {
  451 + canvas.restore();
  452 + } else {
  453 + // 根据当前角度计算出偏差系数,用以在绘制时控制文字的 水平移动 透明度 倾斜程度
  454 + float offsetCoefficient = (float) Math.pow(Math.abs(angle) / 90f, 2.2);
  455 + //获取内容文字
  456 + String contentText;
  457 +
  458 + //如果是label每项都显示的模式,并且item内容不为空、label 也不为空
  459 + if (!isCenterLabel && !TextUtils.isEmpty(label) && !TextUtils.isEmpty(getContentText(visibles[counter]))) {
  460 + contentText = getContentText(visibles[counter]) + label;
  461 + } else {
  462 + contentText = getContentText(visibles[counter]);
  463 + }
  464 +
  465 + reMeasureTextSize(contentText);
  466 + //计算开始绘制的位置
  467 + measuredCenterContentStart(contentText);
  468 + measuredOutContentStart(contentText);
  469 + float translateY = (float) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D);
  470 + //根据Math.sin(radian)来更改canvas坐标系原点,然后缩放画布,使得文字高度进行缩放,形成弧形3d视觉差
  471 + canvas.translate(0.0F, translateY);
  472 +// canvas.scale(1.0F, (float) Math.sin(radian));
  473 + if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) {
  474 + // 条目经过第一条线
  475 + canvas.save();
  476 + canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY);
  477 + canvas.scale(1.0F, (float) Math.sin(radian) * SCALE_CONTENT);
  478 + canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
  479 + canvas.restore();
  480 + canvas.save();
  481 + canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight));
  482 + canvas.scale(1.0F, (float) Math.sin(radian) * 1.0F);
  483 + canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTER_CONTENT_OFFSET, paintCenterText);
  484 + canvas.restore();
  485 + } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) {
  486 + // 条目经过第二条线
  487 + canvas.save();
  488 + canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY);
  489 + canvas.scale(1.0F, (float) Math.sin(radian) * 1.0F);
  490 + canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTER_CONTENT_OFFSET, paintCenterText);
  491 + canvas.restore();
  492 + canvas.save();
  493 + canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight));
  494 + canvas.scale(1.0F, (float) Math.sin(radian) * SCALE_CONTENT);
  495 + canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
  496 + canvas.restore();
  497 + } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) {
  498 + // 中间条目
  499 + canvas.clipRect(0, 0, measuredWidth, maxTextHeight);
  500 + //让文字居中
  501 + float Y = maxTextHeight - CENTER_CONTENT_OFFSET;//因为圆弧角换算的向下取值,导致角度稍微有点偏差,加上画笔的基线会偏上,因此需要偏移量修正一下
  502 + canvas.drawText(contentText, drawCenterContentStart, Y, paintCenterText);
  503 +
  504 + //设置选中项
  505 + selectedItem = preCurrentIndex - (itemsVisible / 2 - counter);
  506 +
  507 + } else {
  508 + // 其他条目
  509 + canvas.save();
  510 + canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
  511 + canvas.scale(1.0F, (float) Math.sin(radian) * SCALE_CONTENT);
  512 + // 控制文字倾斜角度
  513 + paintOuterText.setTextSkewX((textXOffset == 0 ? 0 : (textXOffset > 0 ? 1 : -1)) * (angle > 0 ? -1 : 1) * DEFAULT_TEXT_TARGET_SKEWX * offsetCoefficient);
  514 + // 控制透明度
  515 + paintOuterText.setAlpha((int) ((1 - offsetCoefficient) * 255));
  516 + // 控制文字水平偏移距离
  517 + canvas.drawText(contentText, drawOutContentStart + textXOffset * offsetCoefficient, maxTextHeight, paintOuterText);
  518 + canvas.restore();
  519 + }
  520 + canvas.restore();
  521 + paintCenterText.setTextSize(textSize);
  522 + }
  523 + counter++;
  524 + }
  525 + }
  526 +
  527 + /**
  528 + * reset the size of the text Let it can fully display
  529 + *
  530 + * @param contentText item text content.
  531 + */
  532 + private void reMeasureTextSize(String contentText) {
  533 + Rect rect = new Rect();
  534 + paintCenterText.getTextBounds(contentText, 0, contentText.length(), rect);
  535 + int width = rect.width();
  536 + int size = textSize;
  537 + while (width > measuredWidth) {
  538 + size--;
  539 + //设置2条横线中间的文字大小
  540 + paintCenterText.setTextSize(size);
  541 + paintCenterText.getTextBounds(contentText, 0, contentText.length(), rect);
  542 + width = rect.width();
  543 + }
  544 + //设置2条横线外面的文字大小
  545 + paintOuterText.setTextSize(size);
  546 + }
  547 +
  548 +
  549 + //递归计算出对应的index
  550 + private int getLoopMappingIndex(int index) {
  551 + if (index < 0) {
  552 + index = index + adapter.getItemsCount();
  553 + index = getLoopMappingIndex(index);
  554 + } else if (index > adapter.getItemsCount() - 1) {
  555 + index = index - adapter.getItemsCount();
  556 + index = getLoopMappingIndex(index);
  557 + }
  558 + return index;
  559 + }
  560 +
  561 + /**
  562 + * 获取所显示的数据源
  563 + *
  564 + * @param item data resource
  565 + * @return 对应显示的字符串
  566 + */
  567 + private String getContentText(Object item) {
  568 + if (item == null) {
  569 + return "";
  570 + } else if (item instanceof IPickerViewData) {
  571 + return ((IPickerViewData) item).getPickerViewText();
  572 + } else if (item instanceof Integer) {
  573 + //如果为整形则最少保留两位数.
  574 + return String.format(Locale.getDefault(), "%02d", (int) item);
  575 + }
  576 + return item.toString();
  577 + }
  578 +
  579 + private void measuredCenterContentStart(String content) {
  580 + Rect rect = new Rect();
  581 + paintCenterText.getTextBounds(content, 0, content.length(), rect);
  582 + switch (mGravity) {
  583 + case Gravity.CENTER://显示内容居中
  584 + if (isOptions || label == null || label.equals("") || !isCenterLabel) {
  585 + drawCenterContentStart = (int) ((measuredWidth - rect.width()) * 0.5);
  586 + } else {//只显示中间label时,时间选择器内容偏左一点,留出空间绘制单位标签
  587 + drawCenterContentStart = (int) ((measuredWidth - rect.width()) * 0.25);
  588 + }
  589 + break;
  590 + case Gravity.LEFT:
  591 + drawCenterContentStart = 0;
  592 + break;
  593 + case Gravity.RIGHT://添加偏移量
  594 + drawCenterContentStart = measuredWidth - rect.width() - (int) CENTER_CONTENT_OFFSET;
  595 + break;
  596 + }
  597 + }
  598 +
  599 + private void measuredOutContentStart(String content) {
  600 + Rect rect = new Rect();
  601 + paintOuterText.getTextBounds(content, 0, content.length(), rect);
  602 + switch (mGravity) {
  603 + case Gravity.CENTER:
  604 + if (isOptions || label == null || label.equals("") || !isCenterLabel) {
  605 + drawOutContentStart = (int) ((measuredWidth - rect.width()) * 0.5);
  606 + } else {//只显示中间label时,时间选择器内容偏左一点,留出空间绘制单位标签
  607 + drawOutContentStart = (int) ((measuredWidth - rect.width()) * 0.25);
  608 + }
  609 + break;
  610 + case Gravity.LEFT:
  611 + drawOutContentStart = 0;
  612 + break;
  613 + case Gravity.RIGHT:
  614 + drawOutContentStart = measuredWidth - rect.width() - (int) CENTER_CONTENT_OFFSET;
  615 + break;
  616 + }
  617 + }
  618 +
  619 + @Override
  620 + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  621 + this.widthMeasureSpec = widthMeasureSpec;
  622 + remeasure();
  623 + setMeasuredDimension(measuredWidth, measuredHeight);
  624 + }
  625 +
  626 + @Override
  627 + public boolean onTouchEvent(MotionEvent event) {
  628 + boolean eventConsumed = gestureDetector.onTouchEvent(event);
  629 + boolean isIgnore = false;//超过边界滑动时,不再绘制UI。
  630 +
  631 + float top = -initPosition * itemHeight;
  632 + float bottom = (adapter.getItemsCount() - 1 - initPosition) * itemHeight;
  633 + float ratio = 0.25f;
  634 +
  635 + switch (event.getAction()) {
  636 + //按下
  637 + case MotionEvent.ACTION_DOWN:
  638 + startTime = System.currentTimeMillis();
  639 + cancelFuture();
  640 + previousY = event.getRawY();
  641 + break;
  642 + //滑动中
  643 + case MotionEvent.ACTION_MOVE:
  644 +
  645 + float dy = previousY - event.getRawY();
  646 + previousY = event.getRawY();
  647 + totalScrollY = totalScrollY + dy;
  648 +
  649 + // 非循环模式下,边界处理。
  650 + if (!isLoop) {
  651 + if ((totalScrollY - itemHeight * ratio < top && dy < 0)
  652 + || (totalScrollY + itemHeight * ratio > bottom && dy > 0)) {
  653 + //快滑动到边界了,设置已滑动到边界的标志
  654 + totalScrollY -= dy;
  655 + isIgnore = true;
  656 + }/* else if (totalScrollY + itemHeight * ratio > bottom && dy > 0) {
  657 + totalScrollY -= dy;
  658 + isIgnore = true;
  659 + } */else {
  660 + isIgnore = false;
  661 + }
  662 + }
  663 + break;
  664 +
  665 + case MotionEvent.ACTION_UP:
  666 + default:
  667 +
  668 + if (!eventConsumed) {//未消费掉事件
  669 +
  670 + /**
  671 + *@describe <关于弧长的计算>
  672 + *
  673 + * 弧长公式: L = α*R
  674 + * 反余弦公式:arccos(cosα) = α
  675 + * 由于之前是有顺时针偏移90度,
  676 + * 所以实际弧度范围α2的值 :α2 = π/2-α (α=[0,π] α2 = [-π/2,π/2])
  677 + * 根据正弦余弦转换公式 cosα = sin(π/2-α)
  678 + * 代入,得: cosα = sin(π/2-α) = sinα2 = (R - y) / R
  679 + * 所以弧长 L = arccos(cosα)*R = arccos((R - y) / R)*R
  680 + */
  681 +
  682 + float y = event.getY();
  683 + double L = Math.acos((radius - y) / radius) * radius;
  684 + //item0 有一半是在不可见区域,所以需要加上 itemHeight / 2
  685 + int circlePosition = (int) ((L + itemHeight / 2) / itemHeight);
  686 + float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight;
  687 + //已滑动的弧长值
  688 + mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset);
  689 +
  690 + if ((System.currentTimeMillis() - startTime) > 120) {
  691 + // 处理拖拽事件
  692 + smoothScroll(ACTION.DAGGLE);
  693 + } else {
  694 + // 处理条目点击事件
  695 + smoothScroll(ACTION.CLICK);
  696 + }
  697 + }
  698 + break;
  699 + }
  700 + if (!isIgnore && event.getAction() != MotionEvent.ACTION_DOWN) {
  701 + invalidate();
  702 + }
  703 + return true;
  704 + }
  705 +
  706 + /**
  707 + * 获取Item个数
  708 + *
  709 + * @return item个数
  710 + */
  711 + public int getItemsCount() {
  712 + return adapter != null ? adapter.getItemsCount() : 0;
  713 + }
  714 +
  715 + /**
  716 + * 附加在右边的单位字符串
  717 + *
  718 + * @param label 单位
  719 + */
  720 + public void setLabel(String label) {
  721 + this.label = label;
  722 + }
  723 +
  724 + public void isCenterLabel(boolean isCenterLabel) {
  725 + this.isCenterLabel = isCenterLabel;
  726 + }
  727 +
  728 + public void setGravity(int gravity) {
  729 + this.mGravity = gravity;
  730 + }
  731 +
  732 + public int getTextWidth(Paint paint, String str) {//计算文字宽度
  733 + int iRet = 0;
  734 + if (str != null && str.length() > 0) {
  735 + int len = str.length();
  736 + float[] widths = new float[len];
  737 + paint.getTextWidths(str, widths);
  738 + for (int j = 0; j < len; j++) {
  739 + iRet += (int) Math.ceil(widths[j]);
  740 + }
  741 + }
  742 + return iRet;
  743 + }
  744 +
  745 + public void setIsOptions(boolean options) {
  746 + isOptions = options;
  747 + }
  748 +
  749 +
  750 + public void setTextColorOut(int textColorOut) {
  751 + if (textColorOut != 0) {
  752 + this.textColorOut = textColorOut;
  753 + paintOuterText.setColor(this.textColorOut);
  754 + }
  755 + }
  756 +
  757 + public void setTextColorCenter(int textColorCenter) {
  758 + if (textColorCenter != 0) {
  759 +
  760 + this.textColorCenter = textColorCenter;
  761 + paintCenterText.setColor(this.textColorCenter);
  762 + }
  763 + }
  764 +
  765 + public void setTextXOffset(int textXOffset) {
  766 + this.textXOffset = textXOffset;
  767 + if (textXOffset != 0) {
  768 + paintCenterText.setTextScaleX(1.0f);
  769 + }
  770 + }
  771 +
  772 + public void setDividerColor(int dividerColor) {
  773 + if (dividerColor != 0) {
  774 + this.dividerColor = dividerColor;
  775 + paintIndicator.setColor(this.dividerColor);
  776 + }
  777 + }
  778 +
  779 + public void setDividerType(DividerType dividerType) {
  780 + this.dividerType = dividerType;
  781 + }
  782 +
  783 + public void setLineSpacingMultiplier(float lineSpacingMultiplier) {
  784 + if (lineSpacingMultiplier != 0) {
  785 + this.lineSpacingMultiplier = lineSpacingMultiplier;
  786 + judgeLineSpace();
  787 + }
  788 + }
  789 +
  790 + public boolean isLoop() {
  791 + return isLoop;
  792 + }
  793 +
  794 + public float getTotalScrollY() {
  795 + return totalScrollY;
  796 + }
  797 +
  798 + public void setTotalScrollY(float totalScrollY) {
  799 + this.totalScrollY = totalScrollY;
  800 + }
  801 +
  802 + public float getItemHeight() {
  803 + return itemHeight;
  804 + }
  805 +
  806 + public void setItemHeight(float itemHeight) {
  807 + this.itemHeight = itemHeight;
  808 + }
  809 +
  810 + public int getInitPosition() {
  811 + return initPosition;
  812 + }
  813 +
  814 + public void setInitPosition(int initPosition) {
  815 + this.initPosition = initPosition;
  816 + }
  817 +
  818 + @Override
  819 + public Handler getHandler() {
  820 + return handler;
  821 + }
  822 +}
0 \ No newline at end of file 823 \ No newline at end of file
wheelview/src/main/res/values/attrs.xml 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <declare-styleable name="pickerview">
  4 + <attr name="wheelview_gravity">
  5 + <enum name="center" value="17"/>
  6 + <enum name="left" value="3"/>
  7 + <enum name="right" value="5"/>
  8 + </attr>
  9 + <attr name="wheelview_textSize" format="dimension"/>
  10 + <attr name="wheelview_textColorOut" format="color"/>
  11 + <attr name="wheelview_textColorCenter" format="color"/>
  12 + <attr name="wheelview_dividerColor" format="color"/>
  13 + <attr name="wheelview_lineSpacingMultiplier" format="float"/>
  14 + </declare-styleable>
  15 +</resources>
0 \ No newline at end of file 16 \ No newline at end of file
wheelview/src/main/res/values/colors.xml 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <color name="pickerview_timebtn_nor">#057dff</color>
  4 + <color name="pickerview_timebtn_pre">#c2daf5</color>
  5 + <color name="pickerview_bg_topbar">#f5f5f5</color>
  6 +
  7 + <color name="pickerview_topbar_title">#000000</color>
  8 + <color name="pickerview_wheelview_textcolor_out">#a8a8a8</color>
  9 + <color name="pickerview_wheelview_textcolor_center">#2a2a2a</color>
  10 + <color name="pickerview_wheelview_textcolor_divider">#d5d5d5</color>
  11 + <color name="pickerview_bgColor_overlay">#60000000</color>
  12 + <color name="pickerview_bgColor_default">#FFFFFFFF</color>
  13 +
  14 +</resources>
wheelview/src/main/res/values/dimens.xml 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +<resources>
  2 + <!-- 顶部按钮栏高度 -->
  3 + <dimen name="pickerview_topbar_height">44dp</dimen>
  4 +
  5 + <!-- 顶部按钮padding -->
  6 + <dimen name="pickerview_topbar_padding">20dp</dimen>
  7 +
  8 + <!-- 顶部按钮文字大小 -->
  9 + <dimen name="pickerview_topbar_btn_textsize">17sp</dimen>
  10 + <dimen name="pickerview_topbar_title_textsize">18sp</dimen>
  11 + <!-- 选项文字大小 -->
  12 + <dimen name="pickerview_textsize">20sp</dimen>
  13 +</resources>
wheelview/src/main/res/values/strings.xml 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +<resources>
  2 + <string name="app_name">wheelview</string>
  3 +</resources>