有时候就是这样,研究一个问题,一开始想到了一个觉得可行的方案,然后去尝试;尝试了很久、很多次,已经要放弃了,关掉电脑心里
想这个需求没办法实现;在去上厕所的路上突然想到了一个点子,第二天一试,尼玛,搞了这么久的东西,十几二十分钟就解决了......
这次我遇到的是这样一个问题,由于系统的CalendarView不太美观,所以要自己实现一个日历的布局;所以想到了使用GridView,然后
用ViewPager做左右滑动切换日期的效果;
但是ViewPager是确定页数来滑动的,而且一般启动的时候只能像左滑;而日历是两边都能够滑动的,并且页数不确定;为了实现这个效果,我的思路是给ViewPager指定Adapter的时候,在adapter的getCount方法中返回一个很大的值,大到用户很定不会吃多了去滑那么多次;我指定的是1000;然后设置当前的页面数为500,这要就能够左右滑很多次了,相当于无限滑了;
开始做的时候不太了解ViewPager的工作原理,就想在ViewPager的page里定义一个静态的Calendar,和一个静态的
mCurrentPageNumber
来分别代表当前展示的日期和当前所示的ViewPager的页数;然后滑动的时候,根据传进来的pageNum和当前的mCurrentPageNumber的大小来判断创建的日历是下个月,还是上个月;
于是我按照这个思路开始写代码,完成之后发现老是有问题;总是月数不对,或者滑动的时候就错位了;后来我慢慢理解了ViewPager的工作原理,发现ViewPager是在初始化的时候按照当前页分别创建当前页的左边的页面和当前页右边的页面;按照我的上述思路的顺序是:
500,499,501;也就是初始化结束的时候
mCurrentPageNumber的值为501;问题就出在这里,刚开始的时候初始化500,比如500代表8月,那么499的时候就是七月,这没有问题,可是当七月的界面创建完成之后,当前的Calendar就是七月,而
mCurrentPageNumber是499,在创建501的时候七月加1就是八月,也就是本来501代表9月,可是显示的是8月;滑动之后还出现了各种不可预知的结果......然后我就开始无数次的微调,判断如果是初始化就怎么样,然后就怎么样;改变当前页的值等等;一直一直弄了很久之后我都要昏了可还是没有搞定;最后决定算了,解决不了......
后来上了个厕所回来突然想到了另一个方法,如果这个保存当前状态然后相对的来增加减少月份不行,那使用500为基准,使用传入的页数的与500的绝对差来作为创建月数的标准行不行呢?然后就将这个方法记录了一下,今天来试验的时候发现,尼玛二十分钟就搞定了....而且代码比昨天写的还少了太多太多,根本就不需要那么多的静态变量来存储当前状态;
有时候编程序真的需要灵感和运气还有对使用控件的了解情况,之前保存当前状态的方法在很多地方都用到,所以刚开始我就想到的是这个方法;而且当我了解了ViewPager的工作原理的时候还想用这种方法来尝试,其实它跟ViewPager的工作原理是不和的,肯定会出问题;这个时候我就应该考虑其他的方法,要是这样的话问题早解决了.....
好了,来看看我最后是怎么实现这个需求的;
程序的大致结构是,最外层是ViewPager,ViewPager的每一个页面是一个Fragment,然后再Fragment里根据ViewPager的Adapter传入的页面数创建不同月份的Fragment;
首先创建一个能够显示日期的GridView的Adapter:
package tk.sweetvvck.calender.adapter; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import tk.sweetvvck.calender.R; import android.app.Activity; import android.content.res.Resources; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.TextView; public class CalendarGridViewAdapter extends BaseAdapter { private Calendar calStartDate = Calendar.getInstance();// 当前显示的日历 private Calendar calToday = Calendar.getInstance(); // 今日 private int iMonthViewCurrentMonth = 0; // 当前视图月 // 根据改变的日期更新日历 // 填充日历控件用 private void UpdateStartDateForMonth() { calStartDate.set(Calendar.DATE, 1); // 设置成当月第一天 iMonthViewCurrentMonth = calStartDate.get(Calendar.MONTH);// 得到当前日历显示的月 // 星期一是2 星期天是1 填充剩余天数 int iDay = 0; int iFirstDayOfWeek = Calendar.MONDAY; int iStartDay = iFirstDayOfWeek; if (iStartDay == Calendar.MONDAY) { iDay = calStartDate.get(Calendar.DAY_OF_WEEK) - Calendar.MONDAY; if (iDay < 0) iDay = 6; } if (iStartDay == Calendar.SUNDAY) { iDay = calStartDate.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY; if (iDay < 0) iDay = 6; } calStartDate.add(Calendar.DAY_OF_WEEK, -iDay); calStartDate.add(Calendar.DAY_OF_MONTH, -1);// 周日第一位 } ArrayList<java.util.Date> titles; private ArrayList<java.util.Date> getDates() { UpdateStartDateForMonth(); ArrayList<java.util.Date> alArrayList = new ArrayList<java.util.Date>(); for (int i = 1; i <= 42; i++) { alArrayList.add(calStartDate.getTime()); calStartDate.add(Calendar.DAY_OF_MONTH, 1); } return alArrayList; } private Activity activity; Resources resources; // construct public CalendarGridViewAdapter(Activity a,Calendar cal) { calStartDate=cal; activity = a; resources=activity.getResources(); titles = getDates(); } public CalendarGridViewAdapter(Activity a) { activity = a; resources=activity.getResources(); } @Override public int getCount() { return titles.size(); } @Override public Object getItem(int position) { return titles.get(position); } @Override public long getItemId(int position) { return position; } @SuppressWarnings("deprecation") @Override public View getView(int position, View convertView, ViewGroup parent) { LinearLayout iv = new LinearLayout(activity); iv.setGravity(Gravity.CENTER); iv.setOrientation(LinearLayout.VERTICAL); iv.setBackgroundColor(resources.getColor(R.color.white)); Date myDate = (Date) getItem(position); Calendar calCalendar = Calendar.getInstance(); calCalendar.setTime(myDate); final int iMonth = calCalendar.get(Calendar.MONTH); final int iDay = calCalendar.get(Calendar.DAY_OF_WEEK); // 判断周六周日 iv.setBackgroundColor(resources.getColor(R.color.white)); if (iDay == 7) { // 周六 iv.setBackgroundColor(resources.getColor(R.color.text_6)); } else if (iDay == 1) { // 周日 iv.setBackgroundColor(resources.getColor(R.color.text_7)); } else { } // 判断周六周日结束 TextView txtToDay = new TextView(activity); txtToDay.setGravity(Gravity.CENTER_HORIZONTAL); txtToDay.setTextSize(9); if (equalsDate(calToday.getTime(), myDate)) { // 当前日期 iv.setBackgroundColor(resources.getColor(R.color.selection)); txtToDay.setText("TODAY!"); } // 设置背景颜色结束 // 日期开始 TextView txtDay = new TextView(activity);// 日期 txtDay.setGravity(Gravity.CENTER_HORIZONTAL); // 判断是否是当前月 if (iMonth == iMonthViewCurrentMonth) { txtToDay.setTextColor(resources.getColor(R.color.ToDayText)); txtDay.setTextColor(resources.getColor(R.color.Text)); } else { txtDay.setTextColor(resources.getColor(R.color.noMonth)); txtToDay.setTextColor(resources.getColor(R.color.noMonth)); } int day = myDate.getDate(); // 日期 txtDay.setText(String.valueOf(day)); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); iv.addView(txtDay, lp); LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); iv.addView(txtToDay, lp1); return iv; } @SuppressWarnings("deprecation") private Boolean equalsDate(Date date1, Date date2) { if (date1.getYear() == date2.getYear() && date1.getMonth() == date2.getMonth() && date1.getDate() == date2.getDate()) { return true; } else { return false; } } }
然后编写根据页数不同来创建不同日历的Fragment:
/* * Copyright 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package tk.sweetvvck.calender.activity; import java.util.Calendar; import tk.sweetvvck.calender.R; import tk.sweetvvck.calender.adapter.CalendarGridViewAdapter; import tk.sweetvvck.calender.utils.Utils; import android.app.Activity; import android.app.Fragment; import android.content.res.Resources; import android.graphics.Color; import android.os.Bundle; import android.view.Display; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.TextView; public class CalendarFragment extends Fragment { public static final String ARG_PAGE = "page"; private int mPageNumber; private Calendar mCalendar; private CalendarGridViewAdapter calendarGridViewAdapter; public static Fragment create(int pageNumber) { CalendarFragment fragment = new CalendarFragment(); Bundle args = new Bundle(); args.putInt(ARG_PAGE, pageNumber); fragment.setArguments(args); return fragment; } public CalendarFragment() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPageNumber = getArguments().getInt(ARG_PAGE); mCalendar = Utils.getSelectCalendar(mPageNumber); calendarGridViewAdapter = new CalendarGridViewAdapter(getActivity(), mCalendar); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout containing a title and body text. ViewGroup rootView = (ViewGroup) inflater.inflate( R.layout.calendar_view, container, false); GridView titleGridView = (GridView) rootView .findViewById(R.id.gridview); TitleGridAdapter titleAdapter = new TitleGridAdapter(getActivity()); initGridView(titleGridView, titleAdapter); GridView calendarView = (GridView) rootView .findViewById(R.id.calendarView); initGridView(calendarView, calendarGridViewAdapter); calendarView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { for (int i = 0; i < parent.getCount(); i++) { if ((i % 7) == 6) { parent.getChildAt(i).setBackgroundColor( getActivity().getResources().getColor( R.color.text_6)); } else if ((i % 7) == 0) { parent.getChildAt(i).setBackgroundColor( getActivity().getResources().getColor( R.color.text_7)); } else { parent.getChildAt(i).setBackgroundColor( Color.TRANSPARENT); } } view.setBackgroundColor(getActivity().getResources().getColor( R.color.selection)); } }); return rootView; } private void initGridView(GridView gridView, BaseAdapter adapter) { gridView = setGirdView(gridView); gridView.setAdapter(adapter);// 设置菜单Adapter } @SuppressWarnings("deprecation") private GridView setGirdView(GridView gridView) { gridView.setNumColumns(7);// 设置每行列数 gridView.setGravity(Gravity.CENTER_VERTICAL);// 位置居中 gridView.setVerticalSpacing(1);// 垂直间隔 gridView.setHorizontalSpacing(1);// 水平间隔 gridView.setBackgroundColor(getResources().getColor( R.color.calendar_background)); WindowManager windowManager = getActivity().getWindowManager(); Display display = windowManager.getDefaultDisplay(); int i = display.getWidth() / 7; int j = display.getWidth() - (i * 7); int x = j / 2; gridView.setPadding(x, 0, 0, 0);// 居中 return gridView; } public class TitleGridAdapter extends BaseAdapter { int[] titles = new int[] { R.string.Sun, R.string.Mon, R.string.Tue, R.string.Wed, R.string.Thu, R.string.Fri, R.string.Sat }; private Activity activity; // construct public TitleGridAdapter(Activity a) { activity = a; } @Override public int getCount() { return titles.length; } @Override public Object getItem(int position) { return titles[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { LinearLayout iv = new LinearLayout(activity); TextView txtDay = new TextView(activity); txtDay.setFocusable(false); txtDay.setBackgroundColor(Color.TRANSPARENT); iv.setOrientation(LinearLayout.VERTICAL); txtDay.setGravity(Gravity.CENTER); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); int i = (Integer) getItem(position); txtDay.setTextColor(Color.GRAY); Resources res = getResources(); if (i == R.string.Sat) { // 周六 txtDay.setBackgroundColor(res.getColor(R.color.title_text_6)); } else if (i == R.string.Sun) { // 周日 txtDay.setBackgroundColor(res.getColor(R.color.title_text_7)); } else { } txtDay.setText((Integer) getItem(position)); iv.addView(txtDay, lp); return iv; } } }
然后在来完成Activity里的ViewPager的创建以及ViewPager的Adapter:
package tk.sweetvvck.calender.activity; import java.util.Calendar; import tk.sweetvvck.calender.R; import tk.sweetvvck.calender.utils.Utils; import android.app.Fragment; import android.app.FragmentManager; import android.os.Bundle; import android.support.v13.app.FragmentStatePagerAdapter; import android.support.v4.app.FragmentActivity; import android.support.v4.view.ViewPager; import android.widget.TextView; /** * 日历 * @author 程科 */ public class MainActivity extends FragmentActivity { private ViewPager viewPager; private TextView tvMonth; private String month; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = (ViewPager) this.findViewById(R.id.viewpager); final ScreenSlidePagerAdapter screenSlidePagerAdapter = new ScreenSlidePagerAdapter( getFragmentManager()); viewPager.setAdapter(screenSlidePagerAdapter); viewPager.setCurrentItem(500); tvMonth = (TextView) this.findViewById(R.id.tv_month); month = Calendar.getInstance().get(Calendar.YEAR) + "-" + Utils.LeftPad_Tow_Zero(Calendar.getInstance().get( Calendar.MONTH) + 1); tvMonth.setText(month); viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { Calendar calendar = Utils.getSelectCalendar(position); month = calendar.get(Calendar.YEAR) + "-" + Utils.LeftPad_Tow_Zero(calendar.get(Calendar.MONTH) + 1); tvMonth.setText(month); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageScrollStateChanged(int state) { } }); } private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { public ScreenSlidePagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return CalendarFragment.create(position); } @Override public int getCount() { return 1000; } } }