Android基于wheelView的自定义日期选择器
- 格式:doc
- 大小:84.00 KB
- 文档页数:19
[Android]竖直滑动选择器WheelView的实现以下内容为原创,转载请注明:公司项⽬中有这么⼀个需求,所以需要⾃⼰实现下。
效果类似android4.0以上原⽣的DatePicker这种。
先贴上效果图:讲下思路吧,因为这是⼀个滚动选择器,所以⾸先想到的是可以基于ListView或者ScrollView⾃定义。
刚开始我⽤的是继承ListView来实现,把滑动选择的每个item作为listview中的⼀个item,然后根据FirstVisiblePosition、LastVisiblePosition 来判断某个item是否在选中的区域,但是后来发现偶尔有时候调⽤getFirstVisiblePosition和getLastVisiblePosition的结果跟我看到的界⾯显⽰的item并不⼀致(我是在Adapter中调⽤的这两个⽅法),后来分析可能是因为我调⽤了scrollTo后没有刷新adapter的原因吧。
再后来,就打算使⽤ScrollView来实现了,其中的每个item都是⼀个View(我这⾥使⽤了TextView)。
因为这个选中框是在中间的位置,所以刚启动时第⼀个item应该需要在中间选中框显⽰,所以前⾯应该使⽤空⽩补全。
最后⼀个也是如此,后⾯也需要空⽩补全。
在滑动过程中需要实现这种场景:滑动结束后,必须且只有⼀个item完整显⽰在选择框中。
所以我们必须要监听滚动停⽌的事件,然后在滚动停⽌后判断是不是满⾜前⾯的场景,如果没有,则需要代码中实现滚动到正确位置。
但是,蛋疼的是,sdk中竟然没有提供原⽣的监听滚动停⽌的api,然后在⽹上找了很久,得到的结果是只能另辟蹊径,使⽤了⼀个⽹友的做法通过主动检测是否停⽌滚动的⽅法去实现(总觉得⽅法有点坑,额好吧但是暂时先⽤了这个⽅法)。
思路就讲到这⾥了。
使⽤⽅式:/*** Author: wangjie* Email: tiantian.china.2@* Date: 7/1/14.*/@AILayout(yout.main)public class MainActivity extends AIActivity {public static final String TAG = MainActivity.class.getSimpleName();private static final String[] PLANETS = new String[]{"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Uranus", "Neptune", "Pluto"};@AIView(R.id.main_wv)private WheelView wva;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);wva.setOffset(1);wva.setItems(Arrays.asList(PLANETS));wva.setOnWheelViewListener(new WheelView.OnWheelViewListener() {@Overridepublic void onSelected(int selectedIndex, String item) {Logger.d(TAG, "selectedIndex: " + selectedIndex + ", item: " + item);}});}@AIClick({R.id.main_show_dialog_btn})public void onClickCallbackSample(View view) {switch (view.getId()) {case R.id.main_show_dialog_btn:View outerView = LayoutInflater.from(context).inflate(yout.wheel_view, null);WheelView wv = (WheelView) outerView.findViewById(R.id.wheel_view_wv);wv.setOffset(2);wv.setItems(Arrays.asList(PLANETS));wv.setSeletion(3);wv.setOnWheelViewListener(new WheelView.OnWheelViewListener() {@Overridepublic void onSelected(int selectedIndex, String item) {Logger.d(TAG, "[Dialog]selectedIndex: " + selectedIndex + ", item: " + item);}});new AlertDialog.Builder(context).setTitle("WheelView in Dialog").setView(outerView).setPositiveButton("OK", null).show();break;}}}注意:这个Demo只是⼀个临时解决⽅案,⽐较适合选项较少的时候使⽤,⽐如选择性别、星期等。
AndroidPickerView滚动选择器的使⽤⽅法⼿机⾥设置闹钟需要选择时间,那个选择时间的控件就是滚动选择器,前⼏天⽤⼿机刷了MIUI,发现⾃带的那个时间选择器效果挺好看的,于是就⾃⼰仿写了⼀个,权当练⼿。
先来看效果:效果还⾏吧?实现思路就是⾃定义⼀个PickerView,单独滚动的是⼀个PickerView,显然上图中有分和秒的选择所以在布局⾥⽤了两个PickerView。
由于这⾥不涉及到text的点击事件,所以只需要继承View就⾏了,直接把text⽤canvas画上去。
PickerView的实现的主要难点:难点1:字体随距离的渐变。
可以看到,text随离中⼼位置的距离变化⽽变化,这⾥变化的是透明度alpha和字体⼤⼩TexSize,这两个值我都设置了Max和Min值,通过其与中⼼点的距离计算scale。
我⽤的是变化曲线是抛物线scale=1-ax^2(x<=Height/4),scale = 0(x>Height/4),a=(4/Height)^2。
x就是距离View中⼼的偏移量。
⽤图⽚表⽰如下:难点2:text的居中。
绘制text的时候不仅要使其在x⽅向上居中,还要在y⽅向上居中,在x⽅向上⽐较简单,设置Paint的Align为Align.CENTER就⾏了,但是y⽅向上很蛋疼,需要计算text的baseline。
难点3:循环滚动。
为了解决循环滚动的问题我把存放text的List从中间往上下摊开,通过不断地moveHeadToTail和moveTailToHead使选中的text始终是list的中间position的值。
以上就是⼏个难点,了解了之后可以来看PickerView的代码了:package com.jingchen.timerpicker;import java.util.ArrayList;import java.util.List;import java.util.Timer;import java.util.TimerTask;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Paint.Align;import android.graphics.Paint.FontMetricsInt;import android.graphics.Paint.Style;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;/*** 滚动选择器** @author chenjing**/public class PickerView extends View{public static final String TAG = "PickerView";/*** text之间间距和minTextSize之⽐*/public static final float MARGIN_ALPHA = 2.8f;/*** ⾃动回滚到中间的速度*/public static final float SPEED = 2;private List<String> mDataList;/*** 选中的位置,这个位置是mDataList的中⼼位置,⼀直不变private int mCurrentSelected;private Paint mPaint;private float mMaxTextSize = 80;private float mMinTextSize = 40;private float mMaxTextAlpha = 255;private float mMinTextAlpha = 120;private int mColorText = 0x333333;private int mViewHeight;private int mViewWidth;private float mLastDownY;/*** 滑动的距离*/private float mMoveLen = 0;private boolean isInit = false;private onSelectListener mSelectListener;private Timer timer;private MyTimerTask mTask;Handler updateHandler = new Handler(){@Overridepublic void handleMessage(Message msg){if (Math.abs(mMoveLen) < SPEED){mMoveLen = 0;if (mTask != null){mTask.cancel();mTask = null;performSelect();}} else// 这⾥mMoveLen / Math.abs(mMoveLen)是为了保有mMoveLen的正负号,以实现上滚或下滚 mMoveLen = mMoveLen - mMoveLen / Math.abs(mMoveLen) * SPEED;invalidate();}};public PickerView(Context context){super(context);init();}public PickerView(Context context, AttributeSet attrs){super(context, attrs);init();}public void setOnSelectListener(onSelectListener listener){mSelectListener = listener;}private void performSelect(){if (mSelectListener != null)mSelectListener.onSelect(mDataList.get(mCurrentSelected));}public void setData(List<String> datas){mDataList = datas;mCurrentSelected = datas.size() / 2;invalidate();public void setSelected(int selected){mCurrentSelected = selected;}private void moveHeadToTail(){String head = mDataList.get(0);mDataList.remove(0);mDataList.add(head);}private void moveTailToHead(){String tail = mDataList.get(mDataList.size() - 1);mDataList.remove(mDataList.size() - 1);mDataList.add(0, tail);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){super.onMeasure(widthMeasureSpec, heightMeasureSpec);mViewHeight = getMeasuredHeight();mViewWidth = getMeasuredWidth();// 按照View的⾼度计算字体⼤⼩mMaxTextSize = mViewHeight / 4.0f;mMinTextSize = mMaxTextSize / 2f;isInit = true;invalidate();}private void init(){timer = new Timer();mDataList = new ArrayList<String>();mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Style.FILL);mPaint.setTextAlign(Align.CENTER);mPaint.setColor(mColorText);}@Overrideprotected void onDraw(Canvas canvas){super.onDraw(canvas);// 根据index绘制viewif (isInit)drawData(canvas);}private void drawData(Canvas canvas){// 先绘制选中的text再往上往下绘制其余的textfloat scale = parabola(mViewHeight / 4.0f, mMoveLen);float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize;mPaint.setTextSize(size);mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha)); // text居中绘制,注意baseline的计算才能达到居中,y值是text中⼼坐标float x = (float) (mViewWidth / 2.0);float y = (float) (mViewHeight / 2.0 + mMoveLen);FontMetricsInt fmi = mPaint.getFontMetricsInt();float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0));canvas.drawText(mDataList.get(mCurrentSelected), x, baseline, mPaint);// 绘制上⽅datafor (int i = 1; (mCurrentSelected - i) >= 0; i++){drawOtherText(canvas, i, -1);}// 绘制下⽅datafor (int i = 1; (mCurrentSelected + i) < mDataList.size(); i++){drawOtherText(canvas, i, 1);}/*** @param canvas* @param position* 距离mCurrentSelected的差值* @param type* 1表⽰向下绘制,-1表⽰向上绘制*/private void drawOtherText(Canvas canvas, int position, int type){float d = (float) (MARGIN_ALPHA * mMinTextSize * position + type* mMoveLen);float scale = parabola(mViewHeight / 4.0f, d);float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize;mPaint.setTextSize(size);mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha)); float y = (float) (mViewHeight / 2.0 + type * d);FontMetricsInt fmi = mPaint.getFontMetricsInt();float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0));canvas.drawText(mDataList.get(mCurrentSelected + type * position),(float) (mViewWidth / 2.0), baseline, mPaint);}/*** 抛物线** @param zero* 零点坐标* @param x* 偏移量* @return scale*/private float parabola(float zero, float x){float f = (float) (1 - Math.pow(x / zero, 2));return f < 0 ? 0 : f;}@Overridepublic boolean onTouchEvent(MotionEvent event){switch (event.getActionMasked()){case MotionEvent.ACTION_DOWN:doDown(event);break;case MotionEvent.ACTION_MOVE:doMove(event);break;case MotionEvent.ACTION_UP:doUp(event);break;}return true;}private void doDown(MotionEvent event){if (mTask != null){mTask.cancel();mTask = null;}mLastDownY = event.getY();}private void doMove(MotionEvent event){mMoveLen += (event.getY() - mLastDownY);if (mMoveLen > MARGIN_ALPHA * mMinTextSize / 2){// 往下滑超过离开距离moveTailToHead();mMoveLen = mMoveLen - MARGIN_ALPHA * mMinTextSize;} else if (mMoveLen < -MARGIN_ALPHA * mMinTextSize / 2){// 往上滑超过离开距离moveHeadToTail();mMoveLen = mMoveLen + MARGIN_ALPHA * mMinTextSize;}mLastDownY = event.getY();invalidate();}private void doUp(MotionEvent event){// 抬起⼿后mCurrentSelected的位置由当前位置move到中间选中位置if (Math.abs(mMoveLen) < 0.0001){mMoveLen = 0;return;}if (mTask != null){mTask.cancel();mTask = null;}mTask = new MyTimerTask(updateHandler);timer.schedule(mTask, 0, 10);}class MyTimerTask extends TimerTask{Handler handler;public MyTimerTask(Handler handler){this.handler = handler;}@Overridepublic void run(){handler.sendMessage(handler.obtainMessage());}}public interface onSelectListener{void onSelect(String text);}}代码⾥的注释都写的很清楚了。
Android⽇期时间选择器实现以及⾃定义⼤⼩本⽂主要讲两个内容:1.如何将DatePicker和TimePicker放在⼀个dialog⾥⾯;2.改变他们的宽度;问题1:其实现思路就是⾃定义⼀个Dialog,然后往⾥⾯同时放⼊DatePicker和TimePicker,直接贴代码:date_time_picker.xml:<LinearLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal" ><!-- <DatePickerandroid:id="@+id/new_act_date_picker"android:layout_width="0dip"android:layout_height="wrap_content"android:layout_weight="0.6"android:calendarViewShown="false" /><TimePickerandroid:id="@+id/new_act_time_picker"android:layout_width="0dip"android:layout_height="wrap_content"android:layout_weight="0.3"/> --><DatePickerandroid:id="@+id/new_act_date_picker"android:layout_width="wrap_content"android:layout_height="wrap_content"android:calendarViewShown="false" /><TimePickerandroid:id="@+id/new_act_time_picker"android:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout>然后在需要显⽰⽇期时间选择器的地⽅(⼀般是⼀个onClickListener中)实例化dialog:DemoActivity.javaarriveAtBtn.setOnClickListener(new View.OnClickListener(){@SuppressLint("NewApi")@Overridepublic void onClick(View v) {View view = View.inflate(getApplicationContext(), yout.date_time_picker, null);final DatePicker datePicker = (DatePicker)view.findViewById(R.id.new_act_date_picker);final TimePicker timePicker = (TimePicker)view.findViewById(R.id.new_act_time_picker);// Init DatePickerint year;int month;int day;if (StringUtils.isEmpty(arriveDateBtn.getText().toString())) {// Use the current date as the default date in the pickerfinal Calendar c = Calendar.getInstance();year = c.get(Calendar.YEAR);month = c.get(Calendar.MONTH);day = c.get(Calendar.DAY_OF_MONTH);} else {year = NewActActivity.arrive_year;month = NewActActivity.arrive_month;day = NewActActivity.arrive_day;}datePicker.init(year, month, day, null);// Init TimePickerint hour;int minute;if (StringUtils.isEmpty(arriveTimeBtn.getText().toString())) {// Use the current time as the default values for the pickerfinal Calendar c = Calendar.getInstance();hour = c.get(Calendar.HOUR_OF_DAY);minute = c.get(Calendar.MINUTE);} else {hour = NewActActivity.arrive_hour;minute = NewActActivity.arrive_min;}timePicker.setIs24HourView(true);timePicker.setCurrentHour(hour);timePicker.setCurrentMinute(minute);// Build DateTimeDialogAlertDialog.Builder builder = new AlertDialog.Builder(NewActActivity.this);builder.setView(view);builder.setTitle(R.string.new_act_date_time_picker_title);builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {arrive_year = datePicker.getYear();arrive_month = datePicker.getMonth();arrive_day = datePicker.getDayOfMonth();String dateStr = DateUtil.formatDate(arrive_year, arrive_month, arrive_day);arriveDateBtn.setText(dateStr);arrive_hour = timePicker.getCurrentHour();arrive_min = timePicker.getCurrentMinute();String timeStr = DateUtil.formatTime(arrive_hour, arrive_min);arriveTimeBtn.setText(timeStr);}});builder.show();}});这样就可以实现⽇期时间选择器了,这⾥就有点layout上的⼩问题,你是需要datepicker和timepicker⽔平排列还是竖直排列,竖直排列是没问题的:下⾯给出两个数值排列的效果图:(1)DatePicker控件中设置android:calendarViewShown="false" 时的效果图:(2)(1)DatePicker控件中设置android:spinnersShown="false" 时的效果图:当然,如果你android:calendarViewShown和android:spinnersShown都不设置为false的话,会同时显⽰⽇历和滚动条样式,我想⼀般不会有⼈想要这样的视图吧。
package com.threeeyes.util;import java.util.Calendar;import com.threeeyes.activity.R;import android.R.color;import android.app.Activity;import android.content.Context;import android.graphics.Color;import android.os.Bundle;import android.util.Log;import android.view.Gravity;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ImageButton;import android.widget.LinearLayout;import android.widget.TableLayout;import android.widget.TableRow;import android.widget.TextView;import android.widget.Toast;import youtParams;public class DateTime extends LinearLayout {TableLayout table;TextView showDateTxt;TextView btnpre;TextView btnnext;TextView nowDate;int year;int month; // 0,1,..,,11int srcday; // 初始日int srcyear; // 初始年int srcmonth; // 初始月String[] weeks = { "周日", "周一", "周二", "周三", "周四", "周五", "周六" };View preSelectedView = null; // 前一个选中的日期Context ct;DateCallBack callBack;int weekTitleColor = 0xFFFF0000;int dayColor = 0xFFFF0000;int titleColor = Color.BLACK;int selectedColor = Color.TRANSPARENT;boolean init = false; // 初始化标志int colWidth = 30; // 单元格宽度int rowHeight = 0; // 单元格高度int textSize = 12;LinearLayout dayLayOut;public DateTime(Context context, final int year1, final int month1, int day1) { super(context);ct = context;this.srcmonth=month1;this.srcyear=year1;setOrientation(LinearLayout.VERTICAL);setLayoutParams(new LayoutParams(youtParams.WRAP_CONTENT,youtParams.WRAP_CONTENT));setBackgroundColor(Color.GRAY);LinearLayout titleLayOut = new LinearLayout(ct);titleLayOut.setOrientation(LinearLayout.HORIZONTAL);titleLayOut.setGravity(Gravity.CENTER_HORIZONTAL);addView(titleLayOut);table = new TableLayout(ct);addView(table);showDateTxt = new TextView(ct);youtParams la = new youtParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);la.rightMargin = 20;la.leftMargin = 20;showDateTxt.setLayoutParams(la);showDateTxt.setTextSize(textSize);this.year = year1;if (month1 < 1 || month1 > 12)this.month = 0;elsethis.month = month1 - 1;if (day1 < 1 || day1 > 31)this.srcday = 1;elsethis.srcday = day1;showDateTxt.setText(String.valueOf(year1) + "年" + String.valueOf(month1)); showDateTxt.setTextColor(titleColor);btnpre = new TextView(ct);btnpre.setTextColor(titleColor);btnpre.setText("上月");btnpre.setTextSize(textSize);btnnext = new TextView(ct);btnnext.setTextColor(titleColor);btnnext.setText("下月");btnnext.setTextSize(textSize);nowDate = new TextView(ct);nowDate.setTextColor(titleColor);nowDate.setText("今天");nowDate.setTextSize(textSize);nowDate.setGravity(Gravity.CENTER_HORIZONTAL);dayLayOut = new LinearLayout(ct);dayLayOut.setOrientation(LinearLayout.VERTICAL);dayLayOut.setGravity(Gravity.CENTER_VERTICAL);dayLayOut.addView(showDateTxt);dayLayOut.addView(nowDate);titleLayOut.addView(btnpre);titleLayOut.addView(dayLayOut);titleLayOut.addView(btnnext);nowDate.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubyear = srcyear;month = srcmonth-1;showDateTxt.setText(String.valueOf(year) + "年"+ String.valueOf(month + 1) + "月");loadDate(1,1+5);}});btnnext.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (preSelectedView != null)preSelectedView.setBackgroundColor(color.transparent);nextMonth();showDateTxt.setText(String.valueOf(year) + "年"+ String.valueOf(month + 1) + "月");loadDate(1, 1 + 5);}});btnpre.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubpreMonth();showDateTxt.setText(String.valueOf(year) + "年"+ String.valueOf(month + 1) + "月");loadDate(1, 1 + 5);}});}public void setPre_Next_BackGround(int preRes, int nextPre) {btnpre.setBackgroundResource(preRes);btnnext.setBackgroundResource(nextPre);btnpre.setText("");btnnext.setText("");}public void setNowDateBackGround(int res) {dayLayOut.setBackgroundResource(res);}/*** 初始化日期** @param titleCoclor* 标题颜色* @param weekTitleColor* 星期颜色* @param dayColor* 日期颜色*/public void initDate(int titleCoclor, int weekTitleColor, int dayColor, int selectedColor) {if (!init) {this.weekTitleColor = weekTitleColor;this.dayColor = dayColor;this.titleColor = titleCoclor;showDateTxt.setTextColor(titleCoclor);btnpre.setTextColor(titleCoclor);btnnext.setTextColor(titleCoclor);this.selectedColor = selectedColor;generateDate();}}/*** 初始化日期颜色默认*/public void initDate() {if (!init) {generateDate();}}/*** 以"周日","周一","周二","周三","周四","周五","周六"为顺序** @param weekdays*/public void setWeekTitle(String[] weekdays) {if (weekdays != null && weekdays.length > 0 && weekdays.length == 7) this.weeks = weekdays;}public int maxDay() {Calendar time = Calendar.getInstance();Log.v("", time.toString());time.clear();time.set(Calendar.YEAR, year);time.set(Calendar.MONTH, month);int day = time.getActualMaximum(Calendar.DAY_OF_MONTH);// 本月份的天数return day;}public int nowWeekDay() {Calendar time = Calendar.getInstance();Log.v("", time.toString());time.clear();time.set(Calendar.YEAR, year);time.set(Calendar.MONTH, month);time.set(Calendar.DATE, 1);int weekday = time.get(Calendar.DAY_OF_WEEK);if (weekday == 7)return 0;elsereturn weekday - 1;}public void generateDate() {youtParams params = new youtParams(youtParams.WRAP_CONTENT,youtParams.WRAP_CONTENT);TableRow row2 = new TableRow(ct);row2.setPadding(0, 2, 0, 0);row2.setGravity(Gravity.CENTER_HORIZONTAL);for (int i = 0; i < 7; i++) {TextView col1 = new TextView(ct);col1.setMinWidth(colWidth);col1.setMaxWidth(colWidth);if (rowHeight > 0)col1.setMinHeight(rowHeight);col1.setTextColor(weekTitleColor);col1.setText(weeks[i]);col1.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);col1.setTextSize(textSize);row2.addView(col1); // 添加列}table.addView(row2);int weekday = nowWeekDay();int maxday = maxDay();Log.v("date----", weekday + "-----------" + maxday);int count = 0;for (int i = 0; i < 5; i++) { // 添加6行TableRow row = new TableRow(ct);row.setPadding(0, 2, 0, 2);row.setGravity(Gravity.CENTER_HORIZONTAL);row.setLayoutParams(params);// row.setBackgroundColor(0xFF00FF00);for (int j = 0; j < 7; j++) { // 添加1列TextView col = new TextView(ct);col.setTextColor(dayColor);col.setBackgroundColor(color.transparent);col.setTextSize(textSize);if (rowHeight > 0)col.setMinHeight(rowHeight);if (i == 0) {if (weekday <= j) {count++;col.setText(String.valueOf(count));}} else {if (count < maxday) {count++;col.setText(String.valueOf(count));}}col.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (((TextView) v).getText().toString().length() > 0) {if (preSelectedView != null) {preSelectedView.setBackgroundColor(color.transparent);}preSelectedView = v;v.setBackgroundColor(selectedColor);callBack.execute(v, year + "", (month + 1) + "",((TextView) v).getText().toString());}}});if (srcday == count) {col.setBackgroundColor(selectedColor);this.preSelectedView = col;}col.setGravity(Gravity.CENTER_HORIZONTAL| Gravity.CENTER_VERTICAL);row.addView(col); // 添加列}table.addView(row);// 添加行}}public void nextMonth() {if (month == 11) {year++;month = 0;} else {month++;}}public void preMonth() {if (month == 0) {year--;month = 11;} else {month--;}}public void loadDate(int startIndex, int endIndex) {int weekday = nowWeekDay();int maxday = maxDay();Log.v("date----", weekday + "-----------" + maxday);int count = 0;for (int i = startIndex; i < endIndex; i++) {TableRow row = (TableRow) table.getChildAt(i);for (int j = 0; j < 7; j++) {TextView col = (TextView) row.getChildAt(j);if (i == startIndex) {if (weekday <= j) {count++;col.setText(String.valueOf(count));if (srcday == count) {col.setBackgroundColor(selectedColor);this.preSelectedView = col;}} else {col.setText("");}} else {if (count < maxday) {count++;col.setText(String.valueOf(count));} else {col.setText("");}}if (srcday == count) {col.setBackgroundColor(selectedColor);if (this.preSelectedView != col)this.preSelectedView.setBackgroundColor(color.transparent);this.preSelectedView = col;}}}}/*** 回调函数** @author Acer**/public interface DateCallBack {public void execute(View v, String year, String month, String day);}public void setCallBack(DateCallBack callBack) {this.callBack = callBack;}/*** 设置单元格的宽度,高度** @param colWidth* 单元格宽度* @param rowHeight* 单元格高度* @param textSize1* 文字大小*/public void setWidthHeightTextSize(int colWidth, int rowHeight,int textSize1) {if (colWidth > 0)this.colWidth = colWidth;this.rowHeight = rowHeight;this.textSize = textSize1;btnpre.setTextSize(textSize1);btnnext.setTextSize(textSize1);showDateTxt.setTextSize(textSize1);}}。
Android⾃定义控件之⽇期选择控件使⽤详解Android⽇期选择控件效果如下:调⽤的代码:@OnClick(R.id.btn0)public void btn0() {final AlertDialog dialog = new AlertDialog.Builder(context).create();dialog.show();Window window = dialog.getWindow();window.setContentView(yout.dialog_change_date);window.setBackgroundDrawable(new ColorDrawable(0x00000000)); // 处理5.0以上对话框的⽩边问题window.setGravity(Gravity.BOTTOM);final DatePickerView datePickerView = (DatePickerView) window.findViewById(R.id.datePickerView);//打开页⾯时需要选中的⽇期 TODOdatePickerView.setDate(2015, 5, 11);// datePickerView.setDate(birthdayArray[0], birthdayArray[1], birthdayArray[2]);final int[] birthdayArray = new int[3];datePickerView.addOnSelectedChangingListener(new DatePickerView.OnSelectedChangedListener() {@Overridepublic void OnSelectedChanged(int[] oldValue, int[] newValue) {birthdayArray[0] = newValue[0];birthdayArray[1] = newValue[1];birthdayArray[2] = newValue[2];}});window.findViewById(Cancel).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {dialog.dismiss();}});window.findViewById(OK).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {dialog.dismiss();btn0.setText(birthdayArray[0] + "年" + birthdayArray[1] + "⽉" + birthdayArray[2] + "⽇"); }});}1.WheelView 源码(有修改)2.xml布局⽂件<?xml version="1.0" encoding="utf-8"?><!--widget_date_picker.xml--><!--注意修改页⾯⾃定义控件的包名--><LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><de.bvb.rxdemo.widget.DateSelectWidget.wheelview.WheelViewandroid:id="@+id/wheelViewYear"android:layout_width="0dp"android:layout_height="match_parent"android:layout_gravity="center"android:layout_weight="1"/><de.bvb.rxdemo.widget.DateSelectWidget.wheelview.WheelViewandroid:id="@+id/wheelViewMonth"android:layout_width="0dp"android:layout_height="match_parent"android:layout_gravity="center"android:layout_weight="1"/><de.bvb.rxdemo.widget.DateSelectWidget.wheelview.WheelViewandroid:id="@+id/wheelViewDay"android:layout_width="0dp"android:layout_height="match_parent"android:layout_gravity="center"android:layout_weight="1"/></LinearLayout><?xml version="1.0" encoding="utf-8"?><!--dialog_change_date.xml--><!--注意修改页⾯⾃定义控件的包名--><LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/transparent"android:gravity="bottom"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="@android:color/white"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="48dp"android:orientation="horizontal"><TextViewandroid:id="@+id/tvCancel"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:background="#F9F9F9"android:gravity="center"android:text="取消"android:textColor="#43AAFC"/><Viewandroid:layout_width="1px"android:layout_height="match_parent"android:background="#D7D7D7"/><TextViewandroid:id="@+id/tvOK"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:background="#F9F9F9"android:gravity="center"android:text="确定"android:textColor="#43AAFC"/></LinearLayout><Viewandroid:layout_width="match_parent"android:layout_height="1px"android:background="#D7D7D7"/><de.bvb.rxdemo.widget.DateSelectWidget.DatePickerViewandroid:id="@+id/datePickerView"android:layout_width="match_parent"android:layout_height="wrap_content"/></LinearLayout></LinearLayout>3.java⽂件package de.bvb.rxdemo.widget.DateSelectWidget;import android.content.Context;import android.util.AttributeSet;import youtInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;import java.util.ArrayList;import de.bvb.rxdemo.R;import de.bvb.rxdemo.widget.DateSelectWidget.wheelview.OnWheelChangedListener;import de.bvb.rxdemo.widget.DateSelectWidget.wheelview.OnWheelScrollListener;import de.bvb.rxdemo.widget.DateSelectWidget.wheelview.WheelView;import de.bvb.rxdemo.widget.DateSelectWidget.wheelview.adapter.AbstractWheelTextAdapter1; public class DatePickerView extends LinearLayout {private static final int YEAR_MIN = 1950;private static final int YEAR_MAX = 2020;private int year = YEAR_MIN;private int month = 1;private int day = 1;private ArrayList<Integer> yearList = new ArrayList<>(YEAR_MAX - YEAR_MIN + 1);private ArrayList<Integer> monthList = new ArrayList<>(12);private ArrayList<Integer> dayList = new ArrayList<>(31);private DateTextAdapter yearAdapter;private DateTextAdapter monthAdapter;private DateTextAdapter dayAdapter;private Context context;private WheelView wheelViewYear;private WheelView wheelViewMonth;private WheelView wheelViewDay;public DatePickerView(Context context) {this(context, null);}public DatePickerView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public DatePickerView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);this.context = context;init();}private void init() {for (int i = YEAR_MIN; i < YEAR_MAX + 1; i++) {yearList.add(i);}for (int i = 1; i < 13; i++) {monthList.add(i);}for (int i = 1; i < 32; i++) {dayList.add(i);}LayoutInflater.from(context).inflate(yout.widget_date_picker, this);// View.inflate(context, yout.widget_date_picker, this);wheelViewYear = (WheelView) findViewById(R.id.wheelViewYear);wheelViewMonth = (WheelView) findViewById(R.id.wheelViewMonth);wheelViewDay = (WheelView) findViewById(R.id.wheelViewDay);wheelViewYear.setCyclic(true);// 可循环滚动wheelViewMonth.setCyclic(true);// 可循环滚动wheelViewDay.setCyclic(true);// 可循环滚动yearAdapter = new DateTextAdapter(context);monthAdapter = new DateTextAdapter(context);dayAdapter = new DateTextAdapter(context);yearAdapter.setList(yearList);monthAdapter.setList(monthList);dayAdapter.setList(dayList);wheelViewYear.setViewAdapter(yearAdapter);wheelViewMonth.setViewAdapter(monthAdapter);wheelViewDay.setViewAdapter(dayAdapter);wheelViewYear.addChangingListener(new OnWheelChangedListener() {@Overridepublic void onChanged(WheelView wheel, int oldValue, int newValue) {year = YEAR_MIN + newValue;int days = calcDay(year, month); // days=28dayList = getDayList(days);dayAdapter.setList(dayList);if (day > days) {dayAdapter.currentIndex = days - 1;wheelViewDay.setCurrentItem(dayAdapter.currentIndex);} else {dayAdapter.currentIndex = day - 1; // day = 30}if (onSelectedChangedListener != null) {onSelectedChangedListener.OnSelectedChanged(parseInt2Array(YEAR_MIN + oldValue, month, day), getSelectDate()); }}});wheelViewMonth.addChangingListener(new OnWheelChangedListener() {@Overridepublic void onChanged(WheelView wheel, int oldValue, int newValue) {month = 1 + newValue;int days = calcDay(year, month); // days=28dayList = getDayList(days);dayAdapter.setList(dayList);if (day > days) {dayAdapter.currentIndex = days - 1;wheelViewDay.setCurrentItem(dayAdapter.currentIndex);} else {dayAdapter.currentIndex = day - 1; // day = 30}if (onSelectedChangedListener != null) {onSelectedChangedListener.OnSelectedChanged(parseInt2Array(year, 1 + oldValue, day), getSelectDate()); }}});wheelViewDay.addChangingListener(new OnWheelChangedListener() {@Overridepublic void onChanged(WheelView wheel, int oldValue, int newValue) {day = 1 + newValue;if (onSelectedChangedListener != null) {onSelectedChangedListener.OnSelectedChanged(parseInt2Array(year, month, oldValue + 1), getSelectDate()); }}});wheelViewYear.addScrollingListener(onWheelScrollListener);wheelViewMonth.addScrollingListener(onWheelScrollListener);wheelViewDay.addScrollingListener(onWheelScrollListener);}OnWheelScrollListener onWheelScrollListener = new OnWheelScrollListener() {@Overridepublic void onScrollingStarted(WheelView wheel) {}@Overridepublic void onScrollingFinished(WheelView wheel) {setTextViewStyle();}};private void setTextViewStyle() {setTextViewSize(year + "", yearAdapter);setTextViewSize(month + "", monthAdapter);setTextViewSize(day + "", dayAdapter);}private void setTextViewSize(String currentItemText, AbstractWheelTextAdapter1 adapter) {ArrayList<View> arrayList = adapter.getTextViews();int size = arrayList.size();String currentText;TextView textView;boolean current;for (int i = 0; i < size; i++) {textView = (TextView) arrayList.get(i);currentText = textView.getText().toString();current = currentItemText.equals(currentText);textView.setTextColor(current ? adapter.selected_text_color : adapter.un_selected_text_color);textView.setTextSize(current ? adapter.selected_text_size : adapter.un_selected_text_size);}}/*** 设置控件的初始值** @param year* @param month* @param day*/public void setDate(int year, int month, int day) {//验证合法性if (year > YEAR_MAX || year < YEAR_MIN) {year = YEAR_MIN;// throw new RuntimeException("年份必须在[" + YEAR_MIN + "," + YEAR_MAX + "]之间");}if (month > 12 || month < 1) {month = 1;// throw new RuntimeException("⽉份份必须在[" + 1 + "," + 12 + "]之间");}final int days = calcDay(year, month);if (day > days || day < 1) {day = 1;// throw new RuntimeException("⽇期份必须在[" + 1 + "," + days + "]之间");}this.year = year;this.month = month;this.day = day;yearAdapter.currentIndex = DatePickerView.this.year - YEAR_MIN;monthAdapter.currentIndex = DatePickerView.this.month - 1;dayAdapter.currentIndex = DatePickerView.this.day - 1;wheelViewYear.setCurrentItem(yearAdapter.currentIndex);wheelViewMonth.setCurrentItem(monthAdapter.currentIndex);wheelViewDay.setCurrentItem(dayAdapter.currentIndex);}public void addOnSelectedChangingListener(OnSelectedChangedListener onSelectedChangedListener) { this.onSelectedChangedListener = onSelectedChangedListener;}private OnSelectedChangedListener onSelectedChangedListener;public interface OnSelectedChangedListener {void OnSelectedChanged(int[] oldValue, int[] newValue);}private int[] parseInt2Array(int year, int month, int day) {return new int[]{year, month, day};}private int[] getSelectDate() {return new int[]{year, month, day};}private ArrayList<Integer> getDayList(int days) {if (days <= 0) {return null;}ArrayList<Integer> list = new ArrayList(days);for (int i = 1; i < days + 1; i++) {list.add(i);}return list;}private int calcDay(int year, int month) {int days = 0;switch (month) {case 1:case 3:case 5:case 7:case 8:case 10:case 12:days = 31;break;case 4:case 6:case 9:case 11:days = 30;break;case 2:days = (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) ? 29 : 28;break;}return days;}private class DateTextAdapter extends AbstractWheelTextAdapter1 {ArrayList<Integer> list;public DateTextAdapter(Context context) {super(context, yout.simple_list_item_1);// super(context, yout.item_birth_year);// setItemTextResource(R.id.tempValue);// item_birth_year.xml 内容如下// <?xml version="1.0" encoding="utf-8"?>// <LinearLayout xmlns:android="/apk/res/android"// android:layout_width="match_parent"// android:layout_height="35dip"// android:gravity="center"// android:orientation="horizontal">// <TextView// android:id="@+id/tempValue"// android:layout_width="match_parent"// android:layout_height="match_parent"// android:gravity="center"// android:textColor="#ffff0000"/>// </LinearLayout>}public void setList(ArrayList<Integer> list) {this.list = list;notifyDataChangedEvent();}@Overrideprotected CharSequence getItemText(int index) {return list == null ? "" : String.valueOf(list.get(index));}@Overridepublic int getItemsCount() {return list == null ? 0 : list.size();}}}以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
android开发时间选择器TimePicker的使⽤前⾔ 我知道现在github上有很多功能强⼤的各⾊选择器,但是Android其实是⾃带⼀些时间选择器的。
在不需要太复杂的ui开发下可以选择使⽤这些系统⾃带的。
android系统⾃带时间控件:DatePicker ⽇期显⽰控件DatePickerDialog ⽇期对话框控件TimePicker 时间显⽰控件TimePickerDialog 时间对话框控件讲解TimePicker选择器的⼀些⾃定义设置⾸先是布局的创建:<TimePickerandroid:id="@+id/timepicker"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:background="#ffffff"/>设置TimePicker选择器风格TimePicker 本⾝⾃带两种风格,都可以在xml中设置,⼀种是android:timePickerMode="clock"另⼀种是:android:timePickerMode="spinner"TimePicker的显⽰规模⼤⼩设置android:scaleX="2"android:scaleY="2"效果:TimePicker的数据回调mTimepicker = (TimePicker) inflate.findViewById(R.id.timepicker);mTimepicker.setDescendantFocusability(TimePicker.FOCUS_BLOCK_DESCENDANTS); //设置点击事件不弹键盘mTimepicker.setIs24HourView(true); //设置时间显⽰为24⼩时mTimepicker.setHour(8); //设置当前⼩时mTimepicker.setMinute(10); //设置当前分(0-59)mTimepicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() { //获取当前选择的时间@Overridepublic void onTimeChanged(TimePicker view, int hourOfDay, int minute) {}});利⽤反射修改⼀些选择器的字体效果与背景颜⾊需要⽤到反射的⽅法了,⽐如更改分割线的样式,设置起始截⽌时间等:⾸先我们要通过反射获取TimePicker源码⾥hour和minute的id:Resources systemResources = Resources.getSystem();int hourNumberPickerId = systemResources.getIdentifier("hour", "id", "android");int minuteNumberPickerId = systemResources.getIdentifier("minute", "id", "android");然后⽤我们定义的TimePicker来获取这个id并转换成hour和minute对应的NumberPicker: NumberPicker hourNumberPicker = (NumberPicker) mTimepicker.findViewById(hourNumberPickerId); NumberPicker minuteNumberPicker = (NumberPicker) mTimepicker.findViewById(minuteNumberPickerId);通过获取到的hourNumberPicker和minuteNumberPicker我们可以先进⾏TimePicker的时间限制:hourNumberPicker.setMinValue(8); //设置最⼩hourhourNumberPicker.setMaxValue(12); //设置最⼤hourminuteNumberPicker .setMinValue(0); //设置最⼩minuteminuteNumberPicker .setMaxValue(30); //设置最⼤minute修改滚动条背景颜⾊hourNumberPicker.setBackgroundColor(getResources().getColor(R.color.colorBlue));// 修改背景颜⾊效果图:隐藏冒号⽤最暴⼒的⽅式隐藏滚动条的冒号,就是直接找ChildAtViewGroup view = (ViewGroup) mTimepicker.getChildAt(0);ViewGroup view2 = (ViewGroup) view.getChildAt(1);view2.getChildAt(1).setVisibility(View.GONE);禁⽌键盘输⼊mTimePicker.setDescendantFocusability(TimePicker.FOCUS_BLOCK_DESCENDANTS);TimePicker分割线的颜⾊和⾼度private void setNumberPickerDivider(NumberPicker numberPicker) {NumberPicker picker = numberPicker;Field[] pickerFields = NumberPicker.class.getDeclaredFields();for (Field pf : pickerFields) {if (pf.getName().equals("mSelectionDivider")) { //设置颜⾊pf.setAccessible(true);ColorDrawable colorDrawable = new ColorDrawable(ContextCompat.getColor(this, R.color.colortime)); //选择⾃⼰喜欢的颜⾊try {pf.set(numberPicker, colorDrawable);} catch (IllegalAccessException e) {e.printStackTrace();}}if (pf.getName().equals("mSelectionDividerHeight")) { //设置⾼度pf.setAccessible(true);try {int result = 3; //要设置的⾼度pf.set(picker, result);} catch (Exception e) {e.printStackTrace();}break;}picker.invalidate();}}。
Android自定义View(RollWeekView-炫酷的星期日期选择控件)一次展示一个星期的5天,中间放大的为当前选中的;如果点击了其中一个日期,比如星期五,那么整体向左滑动,并将星期五慢慢放大,星期三慢慢缩小,星期六和星期天就展示出来了;如果继续点击星期六,那么照样是整体向左滑动一个单位,星期日右边就需要塞进一个星期一。
就是一个无限循环的过程,中间选中的需要展示当天的日期。
1、分析最开始,我想到在LinearLayout中依次排开7个子控件(星期几),当点击之后,把所有的子控件通过动画移动相应的偏移量,但是,如果移动后就会出现空位的情况(周一的左边和周日的右边没有了),所以这种方式可是可以,但是要解决无限循环空位的情况。
于是就想到能不能多添加几个子控件作为空位替补?这个方法肯定能实现,但是究竟需要多少个替补呢?这些替补应该放在什么位置呢?当前展示的5个子控件中,如果点击最左边或者最右边,这样的偏移量是最大的,需要便宜两个子控件单位,所以左边和右边最多个需要2个替补就行了,算起来也不多,一共才9个(当前展示5个,左边隐藏替补2个,右边隐藏替补2个)。
在点击之前,先将替补位置设置好,当点击之后,所有子控件执行偏移动画(不可能出现空位,最多偏移2位),动画结束之后,再重新为隐藏的4个替补分配位置并绑定数据,绑定什么数据那就看当前中间显示的是星期几了。
分析图如下:但是新的问题又来了,动画执行完毕之后,怎么判断哪些子控件是当前正在显示的呢(中间五个)?由于正在显示的子控件的位置是可以确定的,因为要重写onMeasure(),在onMeasure()方法中可以得到单个子控件的宽度(item_width=getMeasureWidth()/5),动画结束之后,遍历这些子控件,如果x==0的那肯定就是当前正在展示的第一个子控件,x==item_width则是第二个….,剩余的4个就不用判断了,直接将两个移动到左边,两个移动到右边,然后重新为这四个替补设置数据。
Android自定义View(CustomCalendar-定制日历控件)如图,上部分是自定义日历的效果图,下面是系统自带CalendarView的效果,这两个控件的相关功能需求元素图上都有标注。
系统自带的日历控件能够左右滑动切换月份,效果还是挺酷的(这是我在自定义控件完毕之后才发现的),结果就后悔了,这么酷干嘛还要自定义啊?自定义当然不是为了装逼呐,请认真看需求,我们需要在日期下面显示任务完成情况,当日被切换之后需要标注为灰色圆圈背景,这些都是自带日历控件不可达到的,当然,我们也可以继承系统CalendarView然后试着修改;那为什么不选择开源的日历控件呢?如果我们不会自定义控件或者时间很紧,开源的当然是首选,谁叫我闲的慌,开源的控件也会有些问题,有的可能不够完善很多bug,比较完善的可能内容太多,如果要抽取有用的内容还是需要花一定时间,如果整个库工程都弄过来会造成大量的代码冗余。
另外一点很重要,任务情况需要从服务器上获取,当切换日期之后,日历控件下方要显示那天的任务详情(可能需要请求数据),这么多问题如果去修改开源库工程工作量不一定比自定义小。
综上,还是自定义更适合我,整个日历控件500多行代码就搞定(包括注释、接口、各种变量),后面如果项目需求有变动,改动起来也是so easy! 当然,日常开发中,自带控件能搞定的尽量就用系统自带的。
下面我们一起看看这个控件是怎样实现的。
1、分析怎样自定义这个日历控件呢?可能我们第一反应是GridView+组合控件的方式,GridView用来展示下面日期部分,这种方式实现起来相对比较容易,但是核心的内容(获取某月的天数、具体展示在什么位置)还是得自己做,这种方式代码量也不少,另外这样做也会加大系统性能开销,GridView中同时显示30来个item,item里面还嵌套子控件,之前我们讲控件填充、测量时讲过尽量减少布局的嵌套,这样会造成过多的遍历,一不小心又装逼了,现在的手机那么牛逼,这么点工作量跟我谈什么性能?第二种就是通过自定义View完全绘制出来,只需要一个类搞定。
Android⾃定义view实现⽇历打卡签到本⽂实例为⼤家分享了Android⾃定义view实现⽇历打卡签到的具体代码,供⼤家参考,具体内容如下1.说明⾃⼰写⼀个view实现每天签到的功能,设置背景图⽚2.效果图3.主界⾯package com.example.myapplication30;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import java.util.ArrayList;import java.util.Calendar;import java.util.List;public class MainActivity extends AppCompatActivity {//参考⽹址:https:///MacaoPark/article/details/102069775private TextView mTvDaySum;private TextView mTvMonth;private SignView mCvCalendar;private List<SignEntity> data;private Calendar calendar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);mTvDaySum = findViewById(R.id.punch_tv_day_sum);mTvMonth = findViewById(R.id.punch_tv_month);mCvCalendar = findViewById(R.id.punch_cv_calendar);}@Overrideprotected void onStart() {super.onStart();onReady();}@SuppressLint("SetTextI18n")private void onReady() {calendar = Calendar.getInstance();int year = calendar.get(Calendar.YEAR);int month = calendar.get(Calendar.MONTH);//int date = calendar.get(Calendar.DATE);int dayOfMonthToday = calendar.get(Calendar.DAY_OF_MONTH);List<SignDate> signDates = new ArrayList<>();signDates.add(new SignDate(2021, 5, 1, true));signDates.add(new SignDate(2021, 5, 2, true));signDates.add(new SignDate(2021, 5, 3, true));signDates.add(new SignDate(2021, 5, 4, true));signDates.add(new SignDate(2021, 5, 5, true));mTvDaySum.setText("本期连续登录\t"+signDates.size()+"\t天");mTvMonth.setText(year+"年"+getResources().getStringArray(R.array.month_array)[month]+"\t"+dayOfMonthToday+"⽇"); data = new ArrayList<>();for (int i = 1; i <= dayOfMonthToday; i++) {SignEntity signEntity = new SignEntity();if (i == dayOfMonthToday) {signEntity.setDayType(2);} else {signEntity.setDayType(1);}for (int j = 0; j < signDates.size(); j++) {if (signDates.get(j).getDay() == i) {signEntity.setDayType(0);break;} else if (dayOfMonthToday == i) {signEntity.setDayType(2);} else {signEntity.setDayType(1);}}data.add(signEntity);}SignAdapter signAdapter = new SignAdapter(data);mCvCalendar.setAdapter(signAdapter);}}4.适配器package com.example.myapplication30;import java.util.List;/*** SignAdapter* Created by E.M on 2016/4/21.*/public class SignAdapter extends CalendarAdapter {private List<SignEntity> data;public SignAdapter(List<SignEntity> data) {this.data = data;}@Overridepublic SignView.DayType getType(int dayOfMonth) {return SignView.DayType.valueOf(data.get(dayOfMonth - 1).getDayType());}}5.⾃定义viewpackage com.example.myapplication30;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Color;import poseShader;import android.graphics.LinearGradient;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PorterDuff;import android.graphics.RadialGradient;import android.graphics.Rect;import android.graphics.Shader;import android.graphics.SweepGradient;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import java.util.Calendar;/*** 签到⽇历控件* Created by E.M on 2016/4/20.*/public class SignView extends View {private static final String[] WEEK_MARK = {"⼀", "⼆", "三", "四", "五", "六", "⽇"}; private static final int MAX_COLUMN = 7;/*** 周内*/private static final int COLOR_MARKER_WEEKDAY = 0xFF999999;private static final int COLOR_MARKER_WEEKEND = 0xFF1B89CD;/*** 已签到背景⾊*///private static final int COLOR_BACKGROUND_HIGHLIGHT = 0xFFFF0000;/*** 未签到背景⾊*/private static final int COLOR_BACKGROUND_NORMAL = 0xFF9C9C9C;/*** 等待签到背景⾊*/private static final int COLOR_BACKGROUND_WAIT = 0xFFFE7471;/*** 已签到⽂字颜⾊*/private static final int COLOR_TEXT_HIGHLIGHT = 0xFFFFFFFF;/*** 未签到⽂字颜⾊*/private static final int COLOR_TEXT_NORMAL = 0xFF606060;// /**// * 不可⽤⽂字颜⾊// */// private static final int COLOR_TEXT_DISABLED = 0xFFD4D4D4;private static final int MARKER_TEXT_SIZE = 40;private static final int CELL_TEXT_SIZE = 40;private static final int VERTICAL_SPACE = 51;private static final int VERTICAL_MARGIN = 62;private static final int HORIZONTAL_MARGIN = 39;private static final int CELL_SIZE = 80;private static final int WAIT_LINE_SIZE = 14;private int dayOfMonthToday;private int markerTextY;private int verticalCellTop;private int sumDayOfMonth;private int daysOfFirstWeek;private int horizontalSpace;private int deltaTextCellY;private int deltaTextMarkerY;private int verticalSpace;private int verticalMargin;private int horizontalMargin;private int cellSize;private int waitLineSize;private Path waitPath;private Rect waitRect;private Paint paintWeekday;private Paint paintWeekend;private Paint paintTextNormal;private Paint paintTextHighlight;private Paint paintBackgroundWait;private Paint paintBackgroundNormal;private Paint paintBackgroundHighlight;private CalendarAdapter adapter;public SignView(Context context) {this(context, null);}public SignView(Context context, AttributeSet attrs) {this(context, attrs, -1);}public SignView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initResolution();initPaint();initData();}private void initResolution() {// resolutionUtil = ResolutionUtil.getInstance();// verticalSpace = resolutionUtil.formatVertical(VERTICAL_SPACE);// verticalMargin = resolutionUtil.formatVertical(VERTICAL_MARGIN);// horizontalMargin = resolutionUtil.formatHorizontal(HORIZONTAL_MARGIN); // cellSize = resolutionUtil.formatVertical(CELL_SIZE);// waitLineSize = resolutionUtil.formatVertical(WAIT_LINE_SIZE);verticalSpace = VERTICAL_SPACE;verticalMargin = VERTICAL_MARGIN;horizontalMargin = HORIZONTAL_MARGIN;cellSize = CELL_SIZE;waitLineSize = WAIT_LINE_SIZE;}private void initPaint() {// int markerTextSize = resolutionUtil.formatVertical(MARKER_TEXT_SIZE); // int cellTextSize = resolutionUtil.formatVertical(CELL_TEXT_SIZE);int markerTextSize = MARKER_TEXT_SIZE;int cellTextSize = CELL_TEXT_SIZE;paintWeekday = new Paint();paintWeekday.setAntiAlias(true);paintWeekday.setColor(COLOR_MARKER_WEEKDAY);paintWeekday.setTextSize(markerTextSize);paintWeekday.setTextAlign(Paint.Align.CENTER);paintWeekend = new Paint();paintWeekend.setAntiAlias(true);paintWeekend.setColor(COLOR_MARKER_WEEKEND);paintWeekend.setTextSize(markerTextSize);paintWeekend.setTextAlign(Paint.Align.CENTER);paintTextNormal = new Paint();paintTextNormal.setAntiAlias(true);paintTextNormal.setColor(COLOR_TEXT_NORMAL);paintTextNormal.setTextSize(cellTextSize);paintTextNormal.setTextAlign(Paint.Align.CENTER);paintTextHighlight = new Paint();paintTextHighlight.setAntiAlias(true);paintTextHighlight.setColor(COLOR_TEXT_HIGHLIGHT);paintTextHighlight.setTextSize(cellTextSize);paintTextHighlight.setTextAlign(Paint.Align.CENTER);paintBackgroundWait = new Paint();paintBackgroundWait.setAntiAlias(true);paintBackgroundWait.setColor(COLOR_BACKGROUND_WAIT);paintBackgroundWait.setStrokeWidth(2);paintBackgroundWait.setStyle(Paint.Style.STROKE);paintBackgroundNormal = new Paint();paintBackgroundNormal.setAntiAlias(true);paintBackgroundNormal.setColor(COLOR_BACKGROUND_NORMAL);paintBackgroundNormal.setStrokeWidth(2);paintBackgroundNormal.setStyle(Paint.Style.STROKE);paintBackgroundHighlight = new Paint();paintBackgroundHighlight.setAntiAlias(true);paintBackgroundHighlight.setStrokeWidth(2);paintBackgroundHighlight.setStyle(Paint.Style.FILL);//颜⾊//paintBackgroundHighlight.setColor(COLOR_BACKGROUND_HIGHLIGHT);//多种颜⾊数组//int[] colors = {Color.RED,Color.GREEN,Color.BLUE,Color.YELLOW,Color.MAGENTA};//float[] position = {0f, 0.2f, 0.4f, 0.6f, 1.0f};//Shader shader1 = new LinearGradient(100,850,600,850,colors,position,Shader.TileMode.CLAMP);//paintBackgroundHighlight.setShader(shader1);//设置背景图⽚/* Bitmap placeholder = BitmapFactory.decodeResource(getResources(), R.mipmap.small);Shader shader1 = new BitmapShader(placeholder, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); paintBackgroundHighlight.setShader(shader1);*/}private void initData() {Paint.FontMetricsInt fmiMarker = paintWeekday.getFontMetricsInt();deltaTextMarkerY = -(fmiMarker.bottom - fmiMarker.top) / 2 - fmiMarker.top;markerTextY = verticalMargin + cellSize / 2;Paint.FontMetricsInt fmiCell = paintTextNormal.getFontMetricsInt();deltaTextCellY = -(fmiCell.bottom - fmiCell.top) / 2 - fmiCell.top;verticalCellTop = verticalMargin + cellSize;Calendar calendarToday = Calendar.getInstance();dayOfMonthToday = calendarToday.get(Calendar.DAY_OF_MONTH);int dayOfWeek;sumDayOfMonth = calendarToday.getActualMaximum(Calendar.DAY_OF_MONTH);Calendar calendarFirstDay = Calendar.getInstance();calendarFirstDay.set(Calendar.DAY_OF_MONTH, 1);dayOfWeek = calendarFirstDay.get(Calendar.DAY_OF_WEEK);if (dayOfWeek == Calendar.SUNDAY) {dayOfWeek = 7;} else {dayOfWeek = dayOfWeek - 1;}daysOfFirstWeek = MAX_COLUMN - dayOfWeek + 1;}private void createWaitBackground(int topX, int topY) {waitPath = new Path();waitPath.moveTo(topX, topY + waitLineSize);waitPath.lineTo(topX, topY);waitPath.lineTo(topX + waitLineSize, topY);waitPath.moveTo(topX + cellSize - waitLineSize, topY + cellSize);waitPath.lineTo(topX + cellSize, topY + cellSize);waitPath.lineTo(topX + cellSize, topY + cellSize - waitLineSize);waitRect = new Rect(topX, topY, topX + cellSize, topY + cellSize);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);horizontalSpace = (w - MAX_COLUMN * cellSize - horizontalMargin * 2) / (MAX_COLUMN - 1);}@Overridepublic void draw(Canvas canvas) {super.draw(canvas);drawWeekMark(canvas);drawCellsBackground(canvas);drawCells(canvas);}private void drawWeekMark(Canvas canvas) {int y = markerTextY + deltaTextMarkerY;for (int i = 0; i < 7; i++) {int x = horizontalMargin + i * (horizontalSpace + cellSize)+ cellSize / 2;if (i < 5) {canvas.drawText(WEEK_MARK[i], x, y, paintWeekday);} else {canvas.drawText(WEEK_MARK[i], x, y, paintWeekend);}}}private void drawCellsBackground(Canvas canvas) {for (int i = 1; i <= dayOfMonthToday; i++) {drawCellBackground(canvas, i, getColumnIndex(i), getRowIndex(i));}}/*** 根据⾏列序号绘制⽇期背景** @param canvas 画布* @param dayOfMonth ⽇期* @param column 列序号* @param row ⾏序号*/private void drawCellBackground(Canvas canvas, int dayOfMonth, int column, int row) {int x = horizontalMargin + column * (horizontalSpace + cellSize)+ cellSize / 2;int y = verticalCellTop + verticalSpace * (row + 1) + cellSize * row + cellSize / 2;if (adapter != null) {DayType dayType = adapter.getType(dayOfMonth);switch (dayType) {case WAITING:if (waitPath == null) {createWaitBackground(x - cellSize / 2, y - cellSize / 2);}canvas.drawPath(waitPath, paintBackgroundWait);break;case SIGNED:// canvas.drawCircle(x, y, cellSize/2, paintBackgroundHighlight);// canvas.drawRect(x - 60, y - 60, x + 60, y + 60, paintBackgroundHighlight);// 正⽅形// Bitmap placeholder = BitmapFactory.decodeResource(getResources(), R.mipmap.purtest);// canvas.drawBitmap(placeholder,);wCircle(x, y, cellSize/2, paintBackgroundHighlight);canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.small3),x-40, y-40 , paintBackgroundHighlight); break;default:canvas.drawCircle(x, y, cellSize / 2, paintBackgroundNormal);break;}} else {canvas.drawCircle(x, y, cellSize / 2, paintBackgroundNormal);}}private void drawCells(Canvas canvas) {for (int i = 1; i <= sumDayOfMonth; i++) {drawCell(canvas, i, getColumnIndex(i), getRowIndex(i));}}/*** 根据⾏列序号绘制⽇期** @param canvas 画布* @param dayOfMonth ⽇期* @param column 列序号* @param row ⾏序号*/private void drawCell(Canvas canvas, int dayOfMonth, int column, int row) {int x = horizontalMargin + column * (horizontalSpace + cellSize)+ cellSize / 2;int y = verticalCellTop + verticalSpace * (row + 1) + cellSize * row + cellSize / 2+ deltaTextCellY;if (adapter != null && dayOfMonth <= dayOfMonthToday) {DayType dayType = adapter.getType(dayOfMonth);Paint paint;switch (dayType) {case SIGNED:paint = paintTextHighlight;break;default:paint = paintTextNormal;break;}canvas.drawText(String.valueOf(dayOfMonth), x, y, paint);} else {canvas.drawText(String.valueOf(dayOfMonth), x, y, paintTextNormal);}}/*** 获取列序号** @param dayOfMonth ⽇期* @return 列序号*/private int getColumnIndex(int dayOfMonth) {Calendar calendar = Calendar.getInstance();calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);if (dayOfWeek == Calendar.SUNDAY) {dayOfWeek = 6;} else {dayOfWeek = dayOfWeek - 2;}return dayOfWeek;}/*** 获取⾏序号** @param dayOfMonth ⽇期* @return ⾏序号*/private int getRowIndex(int dayOfMonth) {float weight = (dayOfMonth - daysOfFirstWeek) / (MAX_COLUMN * 1f);double rowIndexDouble = Math.abs(Math.ceil(weight));return (int) rowIndexDouble;}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_UP) {float x = event.getX();float y = event.getY();if (waitPath != null) {if (adapter.getType(dayOfMonthToday).equals(DayType.WAITING)) {if (x >= waitRect.left && y >= waitRect.top && x <= waitRect.right && y <= waitRect.bottom) { if (onTodayClickListener != null) {onTodayClickListener.onTodayClick();}}}}}return true;}public void setAdapter(CalendarAdapter adapter) {this.adapter = adapter;this.invalidate();}public int getDayOfMonthToday() {return dayOfMonthToday;}public void notifyDataSetChanged() {invalidate();}private OnTodayClickListener onTodayClickListener;public void setOnTodayClickListener(OnTodayClickListener onTodayClickListener) {this.onTodayClickListener = onTodayClickListener;}public interface OnTodayClickListener {void onTodayClick();}public enum DayType {/*** 已签到状态,时间已过*/SIGNED(0),/*** 未签到状态,时间已过*/UNSIGNED(1),/*** 等待状态,即当⽇还未签到*/WAITING(2),/*** 不可达到状态,未到时间*/UNREACHABLE(3),/*** 不可⽤状态,⾮当前⽉份*/DISABLED(4);private int value;DayType(int value) {this.value = value;}public int getValue() {return value;}public static DayType valueOf(int value) {switch (value) {case 0:return SIGNED;case 1:return UNSIGNED;case 2:return WAITING;case 3:return UNREACHABLE;case 4:return DISABLED;default:return DISABLED;}}}}以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
Android基于wheelView的自定义日期选择器系统自带的DatePicker、TimePicker大家都知道,只有这种效果:百度了很多,试了NumberPicker等都不行,本来打算自己写。
网友推荐了一个开源组件WheelView,下下来试了试,发现他已经定义的很完善了,在他的基础上拓展很容易。
现将基于wheelView自定义日期选择器记录如下:一.首先要了解WheelView为我们提供了什么:除了我写的”DateObject”与”StringWheelAdapter”,其余都是WheelView提供的,1. WheelView.java :可滚动的组件,主要方法:setAdapter(new StringWheelAdapter(dateList, 7)); //设置AdaptersetVisibleItems(3); //设置显示几行数据setCyclic(true); //设置是否循环显示数据addChangingListener(onDaysChangedListener) //设置滑动监听器2. WheelAdapter.java : 滑动组件的适配器的接口,子类适配器用于装载数据public interface WheelAdapter {/*** Gets items count* @return the count of wheel items*/public int getItemsCount();/*** Gets a wheel item by index.** @param index the item index* @return the wheel item text or null*/public String getItem(int index);/*** Gets maximum item length. It is used to determine the wheel width.* If -1 is returned there will be used the default wheel width.** @return the maximum item length or -1*/public int getMaximumLength();}3. OnWheelChangedListener.java : 滑动监听器接口public interface OnWheelChangedListener {/*** Callback method to be invoked when current item changed* @param wheel the wheel view whose state has changed* @param oldValue the old value of current item* @param newValue the new value of current item*/void onChanged(WheelView wheel, int oldValue, int newValue);}4.OnWheelScrollListener.java :滚动监听器接口(暂时没用到)5.NumericWheelAdapter.java : 当滚动内容为纯数字时调用的适配器6.DateObject.java : 日期实体类,用于存储、获取选择的数据package kankan.wheel.widget;import java.util.Calendar;public class DateObject extends Object{private int year ;private int month;private int day;private int week;private int hour;private int minute;private String listItem;/*** 日期对象的4个参数构造器,用于设置日期* @param year* @param month* @param day* @author sxzhang*/public DateObject(int year2, int month2, int day2,int week2) {super();this.year = year2;int maxDayOfMonth = Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_MONTH);if(day2 > maxDayOfMonth){this.month = month2 + 1;this.day = day2 % maxDayOfMonth;}else{this.month = month2;this.day = day2;}this.week = week2 % 7 == 0 ? 7 : week2 % 7;if(day == Calendar.getInstance().get(Calendar.DAY_OF_MONTH)){this.listItem = String.format("%02d", this.month) +"月" + String.format("%02d", this.day) +"日"+ " 今天";}else{this.listItem = String.format("%02d", this.month) +"月" + String.format("%02d", this.day) +"日"+ getDayOfWeekCN(week);}}/*** 日期对象的2个参数构造器,用于设置时间* @param hour2* @param minute2* @param isHourType true:传入的是hour; false: 传入的是minute* @author sxzhang*/public DateObject(int hour2,int minute2,boolean isHourType) {super();if(isHourType == true && hour2 != -1){ //设置小时if(hour2 > 24){this.hour = hour2 % 24;}elsethis.hour = hour2;this.listItem = this.hour + "时";}else if(isHourType == false && minute2 != -1){ //设置分钟if(minute2 > 60)this.minute = minute2 % 60;elsethis.minute = minute2;this.listItem = this.minute + "分";}}public int getHour() {return hour;}public void setHour(int hour) {this.hour = hour;}public int getMinute() {return minute;}public void setMinute(int minute) { this.minute = minute;}public int getWeek() {return week;}public void setWeek(int week) { this.week = week;}public int getYear() {return year;}public void setYear(int year) {this.year = year;}public int getMonth() {return month;}public void setMonth(int month) { this.month = month;}public int getDay() {return day;}public void setDay(int day) {this.day = day;}public String getListItem() {return listItem;}public void setListItem(String listItem) {this.listItem = listItem;}/*** 根据day_of_week得到汉字星期* @return*/public static String getDayOfWeekCN(int day_of_week){String result = null;switch(day_of_week){case 1:result = "星期日";break;case 2:result = "星期一";break;case 3:result = "星期二";break;case 4:result = "星期三";break;case 5:result = "星期四";break;case 6:result = "星期五";break;case 7:result = "星期六";break;default:break;}return result;}}7.StringWheelAdapter.java :一会儿将定义的滚动内容为字符串的适配器,当内容为字符串时我们就可以随意拓展滑动部分的内容package kankan.wheel.widget;import java.util.ArrayList;/*** The simple String Array wheel adapter**/public class StringWheelAdapter implements WheelAdapter {/** The default items length */public static final int DEFAULT_LENGTH = -1;// itemsprivate ArrayList<DateObject> list;// lengthprivate int length;/*** Constructor* @param items the items* @param length the max items length*/public StringWheelAdapter(ArrayList<DateObject> list, int length) { this.list = list;this.length = length;}@Overridepublic String getItem(int index) {if (index >= 0 && index < list.size()) {return list.get(index).getListItem();}return null;}@Overridepublic int getItemsCount() {return list.size();}@Overridepublic int getMaximumLength() {return length;}}二.了解以后就可以使用他定义我们需要的了。