Commit 3528d02b3d76cf90e8472faef48c087b832944c5
1 parent
5d685fa7
Exists in
yxb_dev
and in
2 other branches
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<ConsultContract.Consu | @@ -55,7 +68,7 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -64,7 +77,7 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -86,14 +99,14 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -104,7 +117,8 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -155,7 +169,7 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -181,6 +195,7 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -191,9 +206,9 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -207,6 +222,7 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -232,7 +248,7 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -283,7 +299,7 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ConsultContract.Consu | @@ -292,9 +308,9 @@ public class ConsultFragment extends BaseMVPCompatFragment<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<ReportContract.Rep | @@ -79,7 +79,6 @@ public class ChengZhangFragment extends BaseMVPCompatFragment<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 |
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 |
| @@ -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 |
| @@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
| 1 | +/build |
| @@ -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 |
| @@ -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 | +} |
| @@ -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
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
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 |
| @@ -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 |
| @@ -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> |
| @@ -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> |