自定义时间选择器DatePicker

    有时候我们需要用到时间选择器DatePicker,但是Android自带的DatePicker可能有时不能满足我们的需求,比如一些样式。所有我们自定义一个DatePicker就可以满足我们自己的需求。

    查看Android自带的DatePicker的源码可以发现,DatePicker就是由三个NumberPicker组成的,所以我们就可以通过自定义NumberPicker来完成DatePicker的自定义。

    下面是效果图:


NumberPicker的代码如下:


package com.example.wheeltest2;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

public class WheelView extends ScrollView{

	public WheelView(Context context) {
		super(context);
		init(context);
	}

	public WheelView(Context context, AttributeSet attr){
		super(context, attr);
		init(context);
	}
	
	public WheelView(Context context, AttributeSet attr, int defStyle){
		super(context, attr, defStyle);
		init(context);
	}
	
	public static interface OnScrollListener{
		public void onRefreshDayWheelView();
	}
	
	private List<String> items = null;
	private Context mContext = null;
	private LinearLayout viewsLayout = null;
	/**想要显示的的个数,默认是三个*/
	private int displayItemCount = 3;
	/**一个item的高度*/
	private int itemHeight = 0;
	private int initialY = 0;
	/**当前选中的位置*/
	public int currentIndex = 1;
	/**滑动监听器*/
	private OnScrollListener mOnScrollListener = null;
	
	/**处理松手后滑动到对应的位置上*/
	private Runnable scrollTask = new Runnable() {
		public void run() {
			int newY = getScrollY();
			if(initialY == newY){  //滑动停止
				int position = initialY / itemHeight + 1 ;
				int remain = initialY % itemHeight;
				if(remain == 0){
					currentIndex = position;
				} else {//下一行
					if(remain > itemHeight/2){
						currentIndex = position +1;
						scrollerToItem(currentIndex);
					} else {//停在当前行
						currentIndex = position ;
						scrollerToItem(currentIndex);
					}
				}
				
				if(mOnScrollListener != null){
					mOnScrollListener.onRefreshDayWheelView();
				}
			} else {	//滑动没有停止
				initialY = getScrollY();
				WheelView.this.postDelayed(scrollTask, 50);
			}
			Log.i("Garment0424", "currentIndex:" + currentIndex);
		}
	};
	
	/**
	 * 获得添加的所有数据
	 * @return
	 */
	public List<String> getItems(){
		return this.items;
	}
	
	/**
	 * 加载数据
	 */
	public void setItems(List<String> itemList){
		if(items == null){
			items = new ArrayList<String>();
		}
		items.clear();
		items.addAll(itemList);
		/**数据的第一项和最后一项添加空字符串,以便数据都能显示到中间选中的位置*/
		items.add(0, "");
		items.add("");
		initWheelViewDatas();
		refreshWheelView(0);
	}
	
	/**
	 * 初始化WheelView的布局
	 * 本次只是添加了一个LinearLayout布局
	 * @author garment
	 * @since 2016/04/24
	 * @param context
	 */
	private void init(Context context){
		this.mContext = context;
		viewsLayout = new LinearLayout(mContext);
		viewsLayout.setOrientation(LinearLayout.VERTICAL);
		this.addView(viewsLayout);
	}
	
	/**
	 * 把数据添加到布局当中
	 */
	private void initWheelViewDatas(){
		if(viewsLayout != null){
			viewsLayout.removeAllViews();
		}
		for(String item : items){
			viewsLayout.addView(createView(item));
		}
		//设置WheelView控件的宽和高
		LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) this.getLayoutParams();
		this.setLayoutParams(new LinearLayout.LayoutParams(300, itemHeight*displayItemCount));
	}
	
	private TextView createView(String text){
		TextView textView = new TextView(mContext);
		textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
		textView.setSingleLine();
		textView.setText(text);
		textView.setTextSize(40);
		textView.setGravity(Gravity.CENTER);
		textView.setPadding(20, 20, 20, 20);
		if(itemHeight == 0){
			itemHeight = getViewMeasuredHeight(textView);
		}
		return textView;
	}
	
	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		// TODO Auto-generated method stub
		super.onScrollChanged(l, t, oldl, oldt);
//		Log.i("Garment0424", "onScrollChange t:" + t);
		refreshWheelView(t);
	}
	
	/**
	 * 根据滑动的位置刷新WheelView文字的颜色
	 * @param t
	 */
	private void refreshWheelView(int t){
		int position = t / itemHeight + 1;
		int remain = t % itemHeight;
		if(remain == 0){
			position = t / itemHeight +1;
		} else {
			if(remain > itemHeight/2){
				position = position  + 1;
			} 
		}
		
		int childSize = viewsLayout.getChildCount();
		for(int i = 0; i < childSize; i ++){
			TextView tv = (TextView) viewsLayout.getChildAt(i);
			if (tv == null) {
				return;
			}
			if(i == position){
				tv.setTextColor(Color.WHITE);
			} else {
				tv.setTextColor(Color.BLACK);
			}
		}
//		if(mOnScrollListener != null){
//			mOnScrollListener.onRefreshDayWheelView();
//		}
	}
	
	public void refreshWheelViewByPosition(int position){
		int childSize = viewsLayout.getChildCount();
		for(int i = 0; i < childSize; i ++){
			TextView tv = (TextView) viewsLayout.getChildAt(i);
			if (tv == null) {
				return;
			}
			if(i == position){
				tv.setTextColor(Color.WHITE);
			} else {
				tv.setTextColor(Color.BLACK);
			}
		}
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		if(ev.getAction() == MotionEvent.ACTION_UP){
			startScrollTask();
		}
		return super.onTouchEvent(ev);
	}
	
	private void startScrollTask(){
		initialY = getScrollY();
		this.postDelayed(scrollTask, 50);
	}
	
	private int getViewMeasuredHeight(View view) {
        int width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        int expandSpec = View.MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, View.MeasureSpec.AT_MOST);
        view.measure(width, expandSpec);
        return view.getMeasuredHeight();
    }
	/**控件的宽度*/
	private int viewWidth = 0;
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		// TODO Auto-generated method stub
		viewWidth = w;
		setBackgroundDrawable(null);
		super.onSizeChanged(w, h, oldw, oldh);
	}
	
	private Paint paint = null;
	
	@Override
	@Deprecated
	public void setBackgroundDrawable(Drawable background) {
		// TODO Auto-generated method stub
		if(paint == null){
			paint  = new Paint();
		}
		background = new Drawable() {
			
			@Override
			public void setColorFilter(ColorFilter cf) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void setAlpha(int alpha) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public int getOpacity() {
				// TODO Auto-generated method stub
				return 0;
			}
			
			@Override
			public void draw(Canvas canvas) {
				// TODO Auto-generated method stub
				canvas.drawLine(0, itemHeight, viewWidth, itemHeight, paint);
				canvas.drawLine(0, 2*itemHeight, viewWidth, 2*itemHeight, paint);
			}
		};
		
		super.setBackgroundDrawable(background);
	}
	
	/**
	 * 滑动到指定的位置
	 * @param pos
	 */
	private void scrollerToItem(int pos){
		WheelView.this.smoothScrollTo(0, (pos-1) * itemHeight);
	}
	
	/**
	 * 指定到某个位置
	 * @param pos
	 */
	public void setCurrentItem(int pos){
		currentIndex = pos;
		Log.i("Garment0424"," setCurrentItem current:" + currentIndex);
		scrollerToItem(currentIndex);
		refreshWheelViewByPosition(currentIndex);
	}
	
	/**
	 * 获取当前选中位置的对象
	 * @return
	 */
	public String getCurrentItem(){
		return items.get(currentIndex);
	}
	
	public void registerOnScrollListener(OnScrollListener onScrollListener){
		mOnScrollListener = onScrollListener;
	}
	
	public int getCurrentIndex(){
		return this.currentIndex;
	}
	
}

DatePicker的代码如下:

package com.example.wheeltest2;


import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import com.example.wheeltest2.WheelView.OnScrollListener;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;

public class HLDatePicker extends LinearLayout implements OnScrollListener{

	private WheelView yearWheelView = null;
	private WheelView monthWheelView = null;
	private WheelView dayWheelView = null;
	private Context mContext = null;
	private List<String> yearWheelViewDataList = null;
	private List<String> monthWheelViewDataList = null;
	private List<String> dayWheelViewDataList = null;
	private LinearLayout.LayoutParams layoutParams = null;
	public HLDatePicker(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		init(context);
	}
	
	public HLDatePicker(Context context, AttributeSet attrs){
		super(context, attrs);
		init(context);
	}
	
	@SuppressLint("NewApi")
	public HLDatePicker(Context context, AttributeSet attrs, int defStyle){
		super(context, attrs, defStyle);
		init(context);
	}
	
	private void init(Context context){
		Log.i("Garment0424", "HLDatePicker init");
		mContext = context;
		yearWheelView = new WheelView(mContext);
		monthWheelView = new WheelView(mContext);
		dayWheelView = new WheelView(mContext);
		
		yearWheelView.setVerticalScrollBarEnabled(false);
		monthWheelView.setVerticalScrollBarEnabled(false);
		dayWheelView.setVerticalScrollBarEnabled(false);
		
		yearWheelView.setScrollbarFadingEnabled(false);
		monthWheelView.setScrollbarFadingEnabled(false);
		dayWheelView.setScrollbarFadingEnabled(false);
		
		this.setOrientation(HORIZONTAL);
		layoutParams = new LayoutParams(100,LayoutParams.WRAP_CONTENT);
		yearWheelView.setLayoutParams(layoutParams);
		monthWheelView.setLayoutParams(layoutParams);
		dayWheelView.setLayoutParams(layoutParams);
		
		this.addView(yearWheelView);
		this.addView(monthWheelView);
		this.addView(dayWheelView);
		initYearWheelViewData();
		initMonthWheelData();
		initDayWheelData(1900, 1);
		
		yearWheelView.registerOnScrollListener(this);
		monthWheelView.registerOnScrollListener(this);
	}
	
	private void initYearWheelViewData(){
		if(yearWheelViewDataList == null){
			yearWheelViewDataList = new ArrayList<>();
		}
		yearWheelViewDataList.clear();
		for(int i = 1900; i <= 2010; i ++ ){
			yearWheelViewDataList.add(String.valueOf(i));
		}
		yearWheelView.setItems(yearWheelViewDataList);
	}
	
	private void initMonthWheelData(){
		if(monthWheelViewDataList == null){
			monthWheelViewDataList = new ArrayList<>();
		}
		monthWheelViewDataList.clear();
		for(int i = 1; i <= 12; i ++){
			monthWheelViewDataList.add(String.valueOf(i));
		}
		monthWheelView.setItems(monthWheelViewDataList);
	}
	
	private void initDayWheelData(int year, int month){
		int dayCount = getDaysByYearMonth(year, month);
		Log.i("Garment0424", "initDayWheelData dacount:" + dayCount);
		if(dayWheelViewDataList == null){
			dayWheelViewDataList = new ArrayList<>();
		}
		dayWheelViewDataList.clear();
		for (int i = 1; i < dayCount; i++) {
			dayWheelViewDataList.add(String.valueOf(i));
		}
		dayWheelView.setItems(dayWheelViewDataList);
		Log.i("Garment0424", "initDayWheelData dayWheelView.currentIndex:" + dayWheelView.getCurrentIndex());
		if(dayWheelView.getCurrentIndex() > dayCount){
			dayWheelView.setCurrentItem(dayCount);
		} else {
			dayWheelView.setCurrentItem(dayWheelView.getCurrentIndex());
		}
	}
	
	/**
	 * 得到对应的年份和月份的天数
	 * @param year
	 * @param month
	 * @return
	 */
	public int getDaysByYearMonth(int year, int month) {  
        
        Calendar a = Calendar.getInstance();  
        a.set(Calendar.YEAR, year);  
        a.set(Calendar.MONTH, month - 1);  
        a.set(Calendar.DATE, 1);  
        a.roll(Calendar.DATE, -1);  
        int maxDate = a.get(Calendar.DATE);  
        return maxDate;  
    }

	@Override
	public void onRefreshDayWheelView() {
		// TODO Auto-generated method stub
		int year = Integer.parseInt(yearWheelView.getCurrentItem());
		int month = Integer.parseInt(monthWheelView.getCurrentItem());
		Log.i("Garment0424", "onRefreshDayWheelView year:" + year + ";month:" + month);
		initDayWheelData(year, month);
		
	}  

}

上面的DatePicker,能实现随着年份,月份的改变,日期的改变。其他功能还可以自己慢慢扩展。

### 回答1: WPF是一种强大的UI框架,它提供了许多控件,其中包括时间选择器控件。但是,由于在特定的应用程序中,需要自定义时间选择器以满足一些个性化需求。在这种情况下,可以使用WPF自定义控件来创建自己的时间选择器控件。 首先,在WPF中创建时间选择器控件,需要使用Calendar控件和TimePicker控件。Calendar控件用于显示日期,而TimePicker控件用于选择时间时间选择器控件的主体是StackPanel控件。在StackPanel控件中添加了两个控件Calendar和TimePicker,以实现时间选择器的基本功能。 然后,需要在时间选择器控件中定义一些附加属性,例如:选定日期、选定时间等等,以实现一些高级功能。 最后,为时间选择器控件添加样式,并实现一些触发器和动画效果,以使其外观和功能与应用程序的主题相匹配。 实现WPF自定义时间选择器可能需要一些时间和经验,但对于需要一个不寻常的时间选择器的应用程序来说,是值得的。 这样的时间选择器是用户友好的,具有很好的设计和功能,并且以C#编写,可以很容易地与WPF应用程序集成。 ### 回答2: WPF自定义时间选择器是一种功能强大、灵活性高的工具,它可以根据需求自行设计不同的选择器,可以实现小时、分钟、秒数的选择等多种功能。 首先,我们需要使用WPF自带的DatePicker控件和TimePicker控件来实现时间选择器。接下来,我们可以自定义控件的样式和模板,使其更符合我们的设计需求。 在自定义控件的样式时,我们需要设置控件的各个属性,比如控件的边框、背景、字体等。同时,我们可以通过设置样式来调整控件的布局和显示效果。 在时间选择器的实现中,需要涉及到一些比较复杂的计算,比如计算时间的差值、时间的格式转换等。我们可以使用C#中的DateTime类和TimeSpan类来实现这些功能。 最后对于自定义时间选择器的控件事件,需要自定义一些控件事件,使其更加符合我们的设计需求。比如增加或减少系统时钟里的时间。 总而言之,实现WPF自定义时间选择器需要对WPF控件、样式、模板、计算和控件事件等各个方面有深入的了解。只有掌握了这些知识,才能够设计出优秀的时间选择器,满足用户的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值