android日历价格控件,Android 自定义价格日历控件
介绍
上个星期项目有一个日历价格的需求,类似一个商品在不同的日期价格可能会不同,由于时间给得特别紧所以打算找个合适的开源项目进行修改。参考了网上大多数是通过继承view直接draw一个monthView,然后通过listview来实现monthView的复用。但是继承view通过draw来实现月份日历比较麻烦,如果需要修改样式或者添加额外的信息会比较麻烦,所以为什么不用gridview来实现月份的显示呢?这样monthview的每个布局都是写在xml里的,别人参考你的改起来也方便,并且大多于自己的需求不符合,所以自己实现了一个价格日历,这里分享出来给大家参考。先贴一下效果图
实现思路
前面提到了用gridView来显示月份,要实现日历肯定有很多月份,所以我们用viewPager+gridView来实现,这里我们不仅要实现效果 还要以后遇到类似的功能只需要简单使用而不是重写,所以考虑自定义view中的组合类型。关于自定义view我之前有多篇文章讲到,概念性的东西就不提了,直接开始。
具体实现
组合控件其实就是将多个控件组合在一个控件里并且在该控件中实现一些交互和处理,是为了封装一些内部特性,方便直接使用。
步骤1 获取内部控件
。上面我们提到用viewPager+gridView来实现,当然还有一个显示月份的textview和两个button。那么第一步就是获取到这些控件。声明变量并在onFinishInflate() 方法中获取。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47public class CommonCalendarView extends FrameLayout implements View.OnClickListener {
private ViewPager mViewPager;
private TextView mMonthTv;
private Context mContext;
private android.widget.ImageButton mLeftMonthBtn;
private android.widget.ImageButton mRightMonthBtn;
public CommonCalendarView(Context context) {
this(context,null);
}
public CommonCalendarView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CommonCalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
View view = LayoutInflater.from(mContext).inflate(R.layout.activity_page_calendar_price,this,true);
this.mViewPager = (ViewPager) view.findViewById(R.id.viewPager);
this.mRightMonthBtn = (ImageButton) view.findViewById(R.id.right_month_btn);
this.mMonthTv = (TextView) view.findViewById(R.id.month_tv);
this.mLeftMonthBtn = (ImageButton) view.findViewById(R.id.left_month_btn);
this.mLeftMonthBtn.setOnClickListener(this);
this.mRightMonthBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.left_month_btn:
mViewPager.setCurrentItem(mViewPager.getCurrentItem()-1,true);
break;
case R.id.right_month_btn:
mViewPager.setCurrentItem(mViewPager.getCurrentItem()+1,true);
break;
}
}
}
代码大家肯定一看就知道,获取控件,为左右两个按钮设置点击实现。直接调用viewPager的setCurrentItem();
步骤2 为ViewPager设置适配器显示年月
有了基本的控件以后那么我们还需要用来显示数据,从使用者的角度来说我可能只想告诉你最大最小日期,或者最大年份就可以了。这里提供一个接口获取最大年份并且声明最大最小日期变量,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33...
private DatePickerController mController;
private CalendarAdapter adapter;
private Date maxDate;
private Date minDate;
public void setMaxDate(Date maxDate) {
this.maxDate = maxDate;
}
public void setMinDate(Date minDate) {
this.minDate = minDate;
}
public interface DatePickerController {
int getMaxYear();
void onDayOfMonthSelected(int year, int month, int day);//日期选择
void onDayOfMonthAndDataSelected(int year,int month,int day,List obj);//日期附加信息选择
//展示其它属性(用于扩展数据日期相等时设置显示效果)
void showOtherFields(Object obj, View view, int gridItemYear, int gridItemMonth, int gridItemDay);
//获取附加信息
Map getDataSource();
}
...
为外部提供控件初始化方法用来获取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62public void init(DatePickerController controller){
if (controller==null){
mController = new DatePickerController() {
@Override
public int getMaxYear() {
return DateUtils.getToYear()+1;
}
@Override
public void onDayOfMonthSelected(int year, int month, int day) {
Toast.makeText(mContext, String.format("%s-%s-%s", year,StringUtils.leftPad(String.valueOf(month),2,"0"),
StringUtils.leftPad(String.valueOf(day),2,"0")), Toast.LENGTH_SHORT).show();
}
@Override
public void onDayOfMonthAndDataSelected(int year, int month, int day, List obj) {
}
@Override
public void showOtherFields(Object obj, View view, int gridItemYear, int gridItemMonth, int gridItemDay) {
}
@Override
public Map getDataSource() {
return null;
}
};
}else{
mController = controller;
}
this.mYearMonthMap = mController.getDataSource();
adapter = new CalendarAdapter(mContext);
mViewPager.setPageTransformer(true,new DepthPageTransformer());
mViewPager.setAdapter(adapter);
if (minDate!=null){
mMonthTv.setText(String.format("%s年%s月",DateUtils.getYear(minDate), StringUtils.leftPad(String.valueOf(DateUtils.getMonth(minDate)),2,"0")));
}else{
mMonthTv.setText(String.format("%s年%s月",DateUtils.getToYear(), StringUtils.leftPad(String.valueOf(DateUtils.getToMonth()),2,"0")));
}
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
mMonthTv.setText(adapter.getPageTitle(position));
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
上面的代码如果用户没有提供给我们,我们默认数据源提供maxYear 为今年+1,默认点击事件实现Toast提示。
否则的话直接为controller赋值。
同时设置viewPager的adapter。
来看adapter的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97class CalendarAdapter extends PagerAdapter implements AdapterView.OnItemClickListener {
protected static final int MONTHS_IN_YEAR = 12;
private final Calendar calendar = Calendar.getInstance();
private Integer firstMonth = calendar.get(Calendar.MONTH);
private LayoutInflater inflater;
private Integer lastMonth = (calendar.get(Calendar.MONTH) - 1) % MONTHS_IN_YEAR;
private Integer startYear = calendar.get(Calendar.YEAR);
public CalendarAdapter(Context context) {
inflater = LayoutInflater.from(context);
mContext = context;
if (maxDate!=null){
lastMonth = DateUtils.getMonth(maxDate)-1;
}
if (minDate!=null){
startYear = DateUtils.getYear(minDate);
firstMonth = DateUtils.getMonth(minDate)-1;
}
}
@Override
public CharSequence getPageTitle(int position) {
int year = position / MONTHS_IN_YEAR + startYear + ((firstMonth + (position % MONTHS_IN_YEAR)) / MONTHS_IN_YEAR);
int month = (firstMonth + (position % MONTHS_IN_YEAR)) % MONTHS_IN_YEAR;
return String.format("%s年%s月",year, StringUtils.leftPad(String.valueOf(month+1),2,"0"));
}
@Override
public int getCount() {
int maxYear = mController.getMaxYear();
int minYear = calendar.get(Calendar.YEAR) ;
if (maxDate!=null){
maxYear = DateUtils.getYear(maxDate);
}
if (minDate!=null){
minYear = DateUtils.getYear(minDate);
}
int itemCount = (maxYear-minYear+1) * MONTHS_IN_YEAR;
if (firstMonth != -1)
itemCount -= firstMonth;
if (lastMonth != -1)
itemCount -= (MONTHS_IN_YEAR - lastMonth) - 1;
return itemCount;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
GridView mGridView = mViewMap.get(position);
if (mGridView ==null){
mGridView = (GridView) inflater.inflate(R.layout.item_page_month_day, container, false);
mViewMap.put(position,mGridView);
}
int year = position / MONTHS_IN_YEAR + startYear + ((firstMonth + (position % MONTHS_IN_YEAR)) / MONTHS_IN_YEAR);
int month = (firstMonth + (position % MONTHS_IN_YEAR)) % MONTHS_IN_YEAR;
DateBean dateBean = new DateBean(year, month + 1);
mGridView.setOnItemClickListener(this);
mGridView.setAdapter(new MyGridAdapter(dateBean));
container.addView(mGridView);
return mGridView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
MyGridAdapter gridAdapter = (MyGridAdapter) parent.getAdapter();
int day = (int) gridAdapter.getItem(position);
if (day == -1) {
return;
}
DateBean bean = gridAdapter.getDateBean();
List list = gridAdapter.getProductDatePriceList();
if (mController!=null){
if (list!=null&&!list.isEmpty()){
mController.onDayOfMonthAndDataSelected(bean.currentYear,bean.currentMonth,day+1,list);
}else{
mController.onDayOfMonthSelected(bean.currentYear,bean.currentMonth,day+1);
}
}
}
}
通过getCount方法获取总count,确定要显示的总数。
在instantiateItem方法中根据position获取当前年月,然后为gridView设置adapter。
gridView代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87class MyGridAdapter extends BaseAdapter {
private DateBean mDateBean;
private int days;
private int dayOfWeeks;
private List mProductDatePriceList;
public DateBean getDateBean() {
return mDateBean;
}
public MyGridAdapter(DateBean dateBean) {
this.mDateBean = dateBean;
if (mYearMonthMap!=null){
this.mProductDatePriceList = mYearMonthMap.get(String.format("%s-%s", dateBean.currentYear, StringUtils.leftPad(dateBean.currentMonth + "", 2, "0")));
}
GregorianCalendar c = new GregorianCalendar(dateBean.currentYear, dateBean.currentMonth - 1, 0);
days = DateUtils.getDaysOfMonth(dateBean.currentYear, dateBean.currentMonth); //返回当前月的总天数。
dayOfWeeks = c.get(Calendar.DAY_OF_WEEK);
if (dayOfWeeks == 7) {
dayOfWeeks = 0;
}
}
public List getProductDatePriceList() {
return mProductDatePriceList;
}
@Override
public int getCount() {
return days + dayOfWeeks;
}
@Override
public Object getItem(int i) {
if (i < dayOfWeeks) {
return -1;
} else {
return i - dayOfWeeks;
}
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
GridViewHolder viewHolder ;
if (view == null) {
view = LayoutInflater.from(mContext).inflate(R.layout.item_day, viewGroup, false);
viewHolder = new GridViewHolder();
viewHolder.mTextView = (TextView) view.findViewById(R.id.day_tv);
viewHolder.mPriceTv = (TextView) view.findViewById(R.id.price_tv);
viewHolder.mLineView = view.findViewById(R.id.line_view);
view.setTag(viewHolder);
} else {
viewHolder = (GridViewHolder) view.getTag();
}
int item = (int) getItem(i);
if (item == -1) {
viewHolder.mTextView.setText("");
viewHolder.mPriceTv.setText("");
} else {
viewHolder.mTextView.setText(String.valueOf(item + 1));
viewHolder.mPriceTv.setText("");
if (i%7==0||i%7==6){
viewHolder.mTextView.setActivated(true);
}else{
viewHolder.mTextView.setActivated(false);
}
if (mProductDatePriceList != null) {
viewHolder.mTextView.setEnabled(false);
view.setEnabled(false);
for (Object obj : mProductDatePriceList) {//用于展示价格等额外的属性
if (mController!=null){
mController.showOtherFields(obj,view,mDateBean.currentYear,mDateBean.currentMonth,item+1);
}
}
}
}
return view;
}
}
主要是根据当前年月 获取dayOfWeeks,本月第一天为星期几,然后判断需要空出几格。
使用
简单使用
1、xml中声明view
1
2
3
4
5
6
7
1
2
3
4
5
6
7
android:id="@+id/calendarView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
2、获取view并设置数据源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class SimpleCalendarActivity extends AppCompatActivity {
private CommonCalendarView calendarView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple_calendar);
this.calendarView = (CommonCalendarView) findViewById(R.id.calendarView);
this.calendarView.setMinDate(DateUtils.stringtoDate("1937-01-01","yyyy-MM-dd"));
this.calendarView.setMaxDate(DateUtils.stringtoDate("2100-01-22","yyyy-MM-dd"));
this.calendarView.init(null);
}
}
日历添加额外信息
1、xml中声明view
1
2
3
4
5
6
7
1
2
3
4
5
6
7
android:id="@+id/calendarView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
2、获取view并且设置数据源,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85public class MoreInfoCalendarActivity extends AppCompatActivity {
private CommonCalendarView calendarView;
private Map mYearMonthMap = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_more_info_calendar);
List mDatePriceList = new ArrayList<>();
for (int i = 1; i <= 12; i++) {//构造12个月每天的价格数据
for (int j = 1; j <= 28; j++) {
ProductDatePrice price = new ProductDatePrice();
price.setPriceDate(String.format("2017-%s-%s", StringUtils.leftPad(String.valueOf(i), 2, "0"), StringUtils.leftPad(String.valueOf(j), 2, "0")));
price.setPrice(RandomUtils.nextInt(1000));
mDatePriceList.add(price);
}
}
for (ProductDatePrice productDatePrice : mDatePriceList) {//把价格数据改为同一个月的list 在一个key value里,减少渲染界面时循环判断数量
productDatePrice.getPriceDate();
String yearMonth = TextUtils.substring(productDatePrice.getPriceDate(), 0, TextUtils.lastIndexOf(productDatePrice.getPriceDate(), '-'));
List list = mYearMonthMap.get(yearMonth);
if (list == null) {
list = new ArrayList();
list.add(productDatePrice);
mYearMonthMap.put(yearMonth, list);
} else {
list.add(productDatePrice);
}
}
this.calendarView = (CommonCalendarView) findViewById(R.id.calendarView);
this.calendarView.init(new CommonCalendarView.DatePickerController() {
@Override
public int getMaxYear() {
return 2018;
}
@Override
public void onDayOfMonthSelected(int year, int month, int day) {
Toast.makeText(MoreInfoCalendarActivity.this, String.format("%s-%s-%s", year,StringUtils.leftPad(String.valueOf(month),2,"0"),
StringUtils.leftPad(String.valueOf(day),2,"0")), Toast.LENGTH_SHORT).show();
}
@Override
public void onDayOfMonthAndDataSelected(int year, int month, int day, List obj) {
if (obj==null){
return;
}
String priceDate = String.format("%s-%s-%s", year,
StringUtils.leftPad(month + "", 2, "0"), StringUtils.leftPad(String.valueOf(day), 2, "0"));
for (int i = 0; i < obj.size(); i++) {
ProductDatePrice datePrice = (ProductDatePrice) obj.get(i);
if (datePrice==null){
continue;
}
if (TextUtils.equals(datePrice.getPriceDate(),priceDate)){
Toast.makeText(MoreInfoCalendarActivity.this, datePrice.toString(), Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void showOtherFields(Object obj, View view, int gridItemYear, int gridItemMonth, int gridItemDay) {
//当你设置了数据源之后,界面渲染会循环调用showOtherFields方法,在该方法中实现同一日期设置界面显示效果。
ProductDatePrice productDatePrice = (ProductDatePrice) obj;
if (TextUtils.equals(productDatePrice.getPriceDate(), String.format("%s-%s-%s", gridItemYear,
StringUtils.leftPad(gridItemMonth + "", 2, "0"), StringUtils.leftPad(String.valueOf(gridItemDay), 2, "0")))) {
CommonCalendarView.GridViewHolder viewHolder = (CommonCalendarView.GridViewHolder) view.getTag();
viewHolder.mPriceTv.setText(String.format("¥ %s", productDatePrice.getPrice()));
view.setEnabled(true);
viewHolder.mTextView.setEnabled(true);
}
}
@Override
public Map getDataSource() {
return mYearMonthMap;
}
});
}
}
结语
本偏的自定义价格日历控件还有很多不完善的地方,在这里分享出来只是抛砖引玉,希望对大家有所帮助,欢迎关注我的博客!
android日历价格控件,Android 自定义价格日历控件相关推荐
- Android歌词显示控件TextView自定义
1. 音乐播放,音乐播放,音乐播放放入服务中,那么App 退入后台音乐也可以播放 2. 歌词显示控件TextView自定义: 使用控件TextView, 为什么不用Listview,歌词不可以 ...
- 自定义工作流任务控件
读moss sdk中的自定义工作流任务控件. 自定义工作流任务控件:任务的创建,修改,删除,完成于一体,同时定义了这四个动作的历史纪录. 自定义时封装属性: 1. 封装任务属性 IsTaskComp ...
- android 商城常用控件,android 自定义商城app价格正序倒序控件
1.效果图如下: 2.自定义视图布局文件 price_up_down.xml android:layout_width="wrap_content" android:layout_ ...
- android自定义选年控件,Android精美日历控件CalendarView自定义使用完全解析
项目github地址 此框架采用组合的方式,各个模块互相独立,可自由采用各种提供的控件组合,完全自定义自己需要的UI,周视图和月视图可通过简单自定义任意自由绘制,不怕美工提需求!!!下面教程将介绍如何 ...
- android 自定义view控件,Android 自定义View——自定义View控件
Android给我们提供了大量的View控件,但这还是远远满足不了我们的要求,有时候开发所需要的控件形式是在Android提供的控件中是不存在,这就需要我们自己去定义一个.那么如何自定义控件? 学习自 ...
- android高仿钉钉和小米的日历控件
原文地址:http://www.jianshu.com/p/622fdded4dc9 简介 这是一个高仿钉钉和小米的日历控件,支持快速滑动,界面缓存.想要定制化UI,使用起来非常简单,就像使用List ...
- android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...
转载:http://blog.csdn.net/xiabing082/article/details/48781489 1. 大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...
- Android View体系(十)自定义组合控件
相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...
- android自定义xml弹窗,Android自定义弹窗提醒控件使用详解
Android中原生的Dialog弹窗提醒控件样式单一,有时候并不能满足我们的项目需求,而且一个工程里面有时候会在多处都用到弹窗提醒的功能,代码会出现大量的冗余,工作之余,就自己实现了这么一个弹窗提醒 ...
最新文章
- 后浪们 : 难道要先结婚后恋爱?
- 基于 abp vNext 和 .NET Core 开发博客项目 - 自定义仓储之增删改查
- WebLogic11g-常用运维操作
- html代码里面换图片不显示不出来了,微擎 Ueditor 百度编辑器 替换图片不显示问题...
- jQuery的鼠标事件总结
- 教你摆脱低级程序猿 项目中cocopads的安装使用
- 【精品】Android游戏类源码大集合
- 树莓派 Ubuntu 18.04 启动2.4Ghz或5Ghz热点及部分5G信道启动失败解决方法
- 向量与直线,梯度与法向量,切向量
- php 截掉最后一个字符_php 截取并删除字符串最后一个字符的方法
- 知网文献nh、caj格式文件转成pdf
- Vue实现点击上传图片预览图片功能
- 如何关闭电脑自动更新
- web安全防火墙介绍
- 零知识证明:重要构造
- 从战略管理角度看ERP系统信息缺失
- 汇编工具安装一:MASM32的安装!
- 色彩校正中的 gamma 值是什么
- 分享六个经典的 SpringBoot 开源项目
- 深入浅出WPF知识点汇总一