1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Android 自定义价格日历控件

Android 自定义价格日历控件

时间:2019-05-04 03:12:58

相关推荐

Android 自定义价格日历控件

介绍

上个星期项目有一个日历价格的需求,类似一个商品在不同的日期价格可能会不同,由于时间给得特别紧所以打算找个合适的开源项目进行修改。参考了网上大多数是通过继承view直接draw一个monthView,然后通过listview来实现monthView的复用。但是继承view通过draw来实现月份日历比较麻烦,如果需要修改样式或者添加额外的信息会比较麻烦,所以为什么不用gridview来实现月份的显示呢?这样monthview的每个布局都是写在xml里的,别人参考你的改起来也方便,并且大多于自己的需求不符合,所以自己实现了一个价格日历,这里分享出来给大家参考。先贴一下效果图

实现思路

前面提到了用gridView来显示月份,要实现日历肯定有很多月份,所以我们用viewPager+gridView来实现,这里我们不仅要实现效果 还要以后遇到类似的功能只需要简单使用而不是重写,所以考虑自定义view中的组合类型。关于自定义view我之前有多篇文章讲到,概念性的东西就不提了,直接开始。

具体实现

组合控件其实就是将多个控件组合在一个控件里并且在该控件中实现一些交互和处理,是为了封装一些内部特性,方便直接使用。

步骤1 获取内部控件

。上面我们提到用viewPager+gridView来实现,当然还有一个显示月份的textview和两个button。那么第一步就是获取到这些控件。声明变量并在onFinishInflate() 方法中获取。

代码如下:

public 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;}@Overrideprotected 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);}@Overridepublic 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设置适配器显示年月

有了基本的控件以后那么我们还需要用来显示数据,从使用者的角度来说我可能只想告诉你最大最小日期,或者最大年份就可以了。这里提供一个接口获取最大年份并且声明最大最小日期变量,代码如下:

...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, intgridItemMonth, int gridItemDay);//获取附加信息Map<String,List> getDataSource();}...

为外部提供控件初始化方法用来获取数据

public void init(DatePickerController controller){if (controller==null){mController = new DatePickerController() {@Overridepublic int getMaxYear() {return DateUtils.getToYear()+1;}@Overridepublic 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();}@Overridepublic void onDayOfMonthAndDataSelected(int year, int month, int day, List obj) {}@Overridepublic void showOtherFields(Object obj, View view, int gridItemYear, int gridItemMonth, int gridItemDay) {}@Overridepublic Map<String, List> 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() {@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Overridepublic void onPageSelected(int position) {mMonthTv.setText(adapter.getPageTitle(position));}@Overridepublic void onPageScrollStateChanged(int state) {}});}

上面的代码如果用户没有提供给我们,我们默认数据源提供maxYear 为今年+1,默认点击事件实现Toast提示。

否则的话直接为controller赋值。

同时设置viewPager的adapter。

来看adapter的代码

class 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;}}@Overridepublic 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"));}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView((View) object);}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic 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<ProductDatePrice> 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代码如下:

class 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;}@Overridepublic int getCount() {return days + dayOfWeeks;}@Overridepublic Object getItem(int i) {if (i < dayOfWeeks) {return -1;} else {return i - dayOfWeeks;}}@Overridepublic long getItemId(int i) {return 0;}@Overridepublic 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

<com.monCalendarViewandroid:id="@+id/calendarView"android:layout_width="match_parent"android:layout_height="wrap_content"></com.monCalendarView>

2、获取view并设置数据源

public class SimpleCalendarActivity extends AppCompatActivity {private CommonCalendarView calendarView;@Overrideprotected 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

<com.monCalendarViewandroid:id="@+id/calendarView"android:layout_width="match_parent"android:layout_height="wrap_content"></com.monCalendarView>

2、获取view并且设置数据源,

public class MoreInfoCalendarActivity extends AppCompatActivity {private CommonCalendarView calendarView;private Map<String,List> mYearMonthMap = new HashMap<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_more_info_calendar);List<ProductDatePrice> 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("-%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() {@Overridepublic int getMaxYear() {return ;}@Overridepublic 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();}@Overridepublic 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();}}}@Overridepublic 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);}}@Overridepublic Map<String, List> getDataSource() {return mYearMonthMap;}});}}

结语

本偏的自定义价格日历控件还有很多不完善的地方,在这里分享出来只是抛砖引玉,希望对大家有所帮助,欢迎关注我的博客!

源码下载地址(github):

gitHub CalendarView

源码下载地址(csdn):

gitHub android自定义价格日历控件

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。