因为项目需要,所以写好后总结一下,借鉴这篇文章AndroidIOS风格底部选择器(支持时间,日期,自定义)这里面写的很详细,我再他的基础上改了点代码,改成自己所需要的。
1.创建LoopView和LoopScrollView类(这2个基本没动,)
LoopView代码如下:
package com.xxx;
//隐藏包名
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import com.xxx.R;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* 自定义时间选择器view
*/
public class LoopView extends View {
private static final String TAG = LoopView.class.getSimpleName();
public static final int MSG_INVALIDATE = 1000;
public static final int MSG_SCROLL_LOOP = 2000;
public static final int MSG_SELECTED_ITEM = 3000;
private ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> mScheduledFuture;
private int mTotalScrollY;
private LoopScrollListener mLoopListener;
private GestureDetector mGestureDetector;
private int mSelectedItem;
private GestureDetector.SimpleOnGestureListener mOnGestureListener;
private Context mContext;
private Paint mTopBottomTextPaint; //paint that draw top and bottom text
private Paint mCenterTextPaint; // paint that draw center text
private Paint mCenterLinePaint; // paint that draw line besides center text
private ArrayList mDataList;
private int mTextSize;
private int mMaxTextWidth;
private int mMaxTextHeight;
private int mTopBottomTextColor;
private int mCenterTextColor;
private int mCenterLineColor;
private float lineSpacingMultiplier;
private boolean mCanLoop;
private int mTopLineY;
private int mBottomLineY;
private int mCurrentIndex;
private int mInitPosition;
private int mPaddingLeftRight;
private int mPaddingTopBottom;
private float mItemHeight;
private int mDrawItemsCount;
private int mCircularDiameter;
private int mWidgetHeight;
private int mCircularRadius;
private int mWidgetWidth;
public Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == MSG_INVALIDATE)
invalidate();
if (msg.what == MSG_SCROLL_LOOP)
startSmoothScrollTo();
else if (msg.what == MSG_SELECTED_ITEM)
itemSelected();
return false;
}
});
public LoopView(Context context) {
this(context, null);
}
public LoopView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LoopView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context,attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public LoopView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context,attrs);
}
private void initView(Context context,AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LoopView);
if (array != null) {
mTopBottomTextColor = array.getColor(R.styleable.LoopView_topBottomTextColor, 0xffafafaf);
mCenterTextColor = array.getColor(R.styleable.LoopView_centerTextColor, 0xff313131);
mCenterLineColor = array.getColor(R.styleable.LoopView_lineColor, 0xffc5c5c5);
mCanLoop = array.getBoolean(R.styleable.LoopView_canLoop, true);
mInitPosition = array.getInt(R.styleable.LoopView_initPosition, -1);
mTextSize = array.getDimensionPixelSize(R.styleable.LoopView_textSize, sp2px(context, 16));
mDrawItemsCount = array.getInt(R.styleable.LoopView_drawItemCount, 7);
array.recycle();
}
lineSpacingMultiplier = 2.0F;
this.mContext = context;
mOnGestureListener = new LoopViewGestureListener();
mTopBottomTextPaint = new Paint();
mCenterTextPaint = new Paint();
mCenterLinePaint = new Paint();
if (Build.VERSION.SDK_INT >= 11) {
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
mGestureDetector = new GestureDetector(context, mOnGestureListener);
mGestureDetector.setIsLongpressEnabled(false);
}
private void initData() {
if (mDataList == null) {
throw new IllegalArgumentException("data list must not be null!");
}
mTopBottomTextPaint.setColor(mTopBottomTextColor);
mTopBottomTextPaint.setAntiAlias(true);
mTopBottomTextPaint.setTypeface(Typeface.MONOSPACE);
mTopBottomTextPaint.setTextSize(mTextSize);
mCenterTextPaint.setColor(mCenterTextColor);
mCenterTextPaint.setAntiAlias(true);
mCenterTextPaint.setTextScaleX(1.05F);
mCenterTextPaint.setTypeface(Typeface.MONOSPACE);
mCenterTextPaint.setTextSize(mTextSize);
mCenterLinePaint.setColor(mCenterLineColor);
mCenterLinePaint.setAntiAlias(true);
mCenterLinePaint.setTypeface(Typeface.MONOSPACE);
mCenterLinePaint.setTextSize(mTextSize);
measureTextWidthHeight();
//计算半圆周 -- mMaxTextHeight * lineSpacingMultiplier 表示每个item的高度 mDrawItemsCount = 7
//实际显示5个,留两个是在圆周的上下面
//lineSpacingMultiplier是指text上下的距离的值和maxTextHeight一样的意思 所以 = 2
//mDrawItemsCount - 1 代表圆周的上下两面各被剪切了一半 相当于高度少了一个 mMaxTextHeight
int mHalfCircumference = (int) (mMaxTextHeight * lineSpacingMultiplier * (mDrawItemsCount - 1));
//the diameter of circular 2πr = cir, 2r = height
mCircularDiameter = (int) ((mHalfCircumference * 2) / Math.PI);
//the radius of circular
mCircularRadius = (int) (mHalfCircumference / Math.PI);
// FIXME: 7/8/16 通过控件的高度来计算圆弧的周长
if (mInitPosition == -1) {
if (mCanLoop) {
mInitPosition = (mDataList.size() + 1) / 2;
} else {
mInitPosition = 0;
}
}
mCurrentIndex = mInitPosition;
invalidate();
}
private void measureTextWidthHeight() {
Rect rect = new Rect();
for (int i = 0; i < mDataList.size(); i++) {
String s1 = (String) mDataList.get(i);
mCenterTextPaint.getTextBounds(s1, 0, s1.length(), rect);
int textWidth = rect.width();
if (textWidth > mMaxTextWidth) {
mMaxTextWidth = textWidth;
}
int textHeight = rect.height();
if (textHeight > mMaxTextHeight) {
mMaxTextHeight = textHeight;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidgetWidth = getMeasuredWidth();
mWidgetHeight = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
Log.i(TAG, "onMeasure -> heightMode:" + heightMode);
mItemHeight = lineSpacingMultiplier * mMaxTextHeight;
//auto calculate the text's left/right value when draw
mPaddingLeftRight = (mWidgetWidth - mMaxTextWidth) / 2;
mPaddingTopBottom = (mWidgetHeight - mCircularDiameter) / 2;
//topLineY = diameter/2 - itemHeight(mItemHeight)/2 + mPaddingTopBottom
mTopLineY = (int) ((mCircularDiameter - mItemHeight) / 2.0F) + mPaddingTopBottom;
mBottomLineY = (int) ((mCircularDiameter + mItemHeight) / 2.0F) + mPaddingTopBottom;
}
@Override
protected void onDraw(Canvas canvas) {
if (mDataList == null) {
super.onDraw(canvas);
return;
}
super.onDraw(canvas);
//the length of single item is mItemHeight
int mChangingItem = (int) (mTotalScrollY / (mItemHeight));
mCurrentIndex = mInitPosition + mChangingItem % mDataList.size();
if (!mCanLoop) { // can loop
if (mCurrentIndex < 0) {
mCurrentIndex = 0;
}
if (mCurrentIndex > mDataList.size() - 1) {
mCurrentIndex = mDataList.size() - 1;
}
} else { //can not loop
if (mCurrentIndex < 0) {
mCurrentIndex = mDataList.size() + mCurrentIndex;
}
if (mCurrentIndex > mDataList.size() - 1) {
mCurrentIndex = mCurrentIndex - mDataList.size();
}
}
int count = 0;
String itemCount[] = new String[mDrawItemsCount];
//reconfirm each item's value from dataList according to currentIndex,
while (count < mDrawItemsCount) {
int templateItem = mCurrentIndex - (mDrawItemsCount / 2 - count);
if (mCanLoop) {
if (templateItem < 0) {
templateItem = templateItem + mDataList.size();
}
if (templateItem > mDataList.size() - 1) {
templateItem = templateItem - mDataList.size();
}
itemCount[count] = (String) mDataList.get(templateItem);
} else if (templateItem < 0) {
itemCount[count] = "";
} else if (templateItem > mDataList.size() - 1) {
itemCount[count] = "";
} else {
itemCount[count] = (String) mDataList.get(templateItem);
}
count++;
}
//draw top and bottom line
canvas.drawLine(0.0F, mTopLineY, mWidgetWidth, mTopLineY, mCenterLinePaint);
canvas.drawLine(0.0F, mBottomLineY, mWidgetWidth, mBottomLineY, mCenterLinePaint);
count = 0;
int changingLeftY = (int) (mTotalScrollY % (mItemHeight));
while (count < mDrawItemsCount) {
canvas.save();
// L= å * r -> å = rad
float itemHeight = mMaxTextHeight * lineSpacingMultiplier;
//get radian L = (itemHeight * count - changingLeftY),r = mCircularRadius
double radian = (itemHeight * count - changingLeftY) / mCircularRadius;
// a = rad * 180 / π
//get angle
float angle = (float) (radian * 180 / Math.PI);
//when angle >= 180 || angle <= 0 don't draw
if (angle >= 180F || angle <= 0F) {
canvas.restore();
} else {
// translateY = r - r*cos(å) -
//(Math.sin(radian) * mMaxTextHeight) / 2 this is text offset
int translateY = (int) (mCircularRadius - Math.cos(radian) * mCircularRadius - (Math.sin(radian) * mMaxTextHeight) / 2) + mPaddingTopBottom;
canvas.translate(0.0F, translateY);
//scale offset = Math.sin(radian) -> 0 - 1
canvas.scale(1.0F, (float) Math.sin(radian));
if (translateY <= mTopLineY) {
//draw text y between 0 -> mTopLineY,include incomplete text
canvas.save();
canvas.clipRect(0, 0, mWidgetWidth, mTopLineY - translateY);
canvas.drawText(itemCount[count], mPaddingLeftRight, mMaxTextHeight, mTopBottomTextPaint);
canvas.restore();
canvas.save();
canvas.clipRect(0, mTopLineY - translateY, mWidgetWidth, (int) (itemHeight));
canvas.drawText(itemCount[count], mPaddingLeftRight, mMaxTextHeight, mCenterTextPaint);
canvas.restore();
} else if (mMaxTextHeight + translateY >= mBottomLineY) {
//draw text y between mTopLineY -> mBottomLineY ,include incomplete text
canvas.save();
canvas.clipRect(0, 0, mWidgetWidth, mBottomLineY - translateY);
canvas.drawText(itemCount[count], mPaddingLeftRight, mMaxTextHeight, mCenterTextPaint);
canvas.restore();
canvas.save();
canvas.clipRect(0, mBottomLineY - translateY, mWidgetWidth, (int) (itemHeight));
canvas.drawText(itemCount[count], mPaddingLeftRight, mMaxTextHeight, mTopBottomTextPaint);
canvas.restore();
} else if (translateY >= mTopLineY && mMaxTextHeight + translateY <= mBottomLineY) {
//draw center complete text
canvas.clipRect(0, 0, mWidgetWidth, (int) (itemHeight));
canvas.drawText(itemCount[count], mPaddingLeftRight, mMaxTextHeight, mCenterTextPaint);
//center one indicate selected item
mSelectedItem = mDataList.indexOf(itemCount[count]);
}
canvas.restore();
}
count++;
}
}
@Override
public boolean onTouchEvent(MotionEvent motionevent) {
switch (motionevent.getAction()) {
case MotionEvent.ACTION_UP:
default:
if (!mGestureDetector.onTouchEvent(motionevent)) {
startSmoothScrollTo();
}
}
return true;
}
public final void setCanLoop(boolean canLoop) {
mCanLoop = canLoop;
invalidate();
}
/**
* set text size
*
* @param size size indicate sp,not px
*/
public final void setTextSize(float size) {
if (size > 0) {
mTextSize = sp2px(mContext, size);
}
}
public void setInitPosition(int initPosition) {
this.mInitPosition = initPosition;
invalidate();
}
public void setLoopListener(LoopScrollListener LoopListener) {
mLoopListener = LoopListener;
}
/**
* All public method must be called before this method
* @param list data list
*/
public final void setDataList(List<String> list) {
this.mDataList = (ArrayList) list;
initData();
}
public int getSelectedItem() {
return mSelectedItem;
}
private void itemSelected() {
if (mLoopListener != null) {
postDelayed(new SelectedRunnable(), 200L);
}
}
private void cancelSchedule() {
if (mScheduledFuture != null && !mScheduledFuture.isCancelled()) {
mScheduledFuture.cancel(true);
mScheduledFuture = null;
}
}
private void startSmoothScrollTo() {
int offset = (int) (mTotalScrollY % (mItemHeight));
cancelSchedule();
mScheduledFuture = mExecutor.scheduleWithFixedDelay(new HalfHeightRunnable(offset), 0, 10, TimeUnit.MILLISECONDS);
}
private void startSmoothScrollTo(float velocityY) {
cancelSchedule();
int velocityFling = 20;
mScheduledFuture = mExecutor.scheduleWithFixedDelay(new FlingRunnable(velocityY), 0, velocityFling, TimeUnit.MILLISECONDS);
}
class LoopViewGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public final boolean onDown(MotionEvent motionevent) {
cancelSchedule();
Log.i(TAG, "LoopViewGestureListener->onDown");
return true;
}
@Override
public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
startSmoothScrollTo(velocityY);
Log.i(TAG, "LoopViewGestureListener->onFling");
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.i(TAG, "LoopViewGestureListener->onScroll");
mTotalScrollY = (int) ((float) mTotalScrollY + distanceY);
if (!mCanLoop) {
int initPositionCircleLength = (int) (mInitPosition * (mItemHeight));
int initPositionStartY = -1 * initPositionCircleLength;
if (mTotalScrollY < initPositionStartY) {
mTotalScrollY = initPositionStartY;
}
int circleLength = (int) ((float) (mDataList.size() - 1 - mInitPosition) * (mItemHeight));
if (mTotalScrollY >= circleLength) {
mTotalScrollY = circleLength;
}
}
invalidate();
return true;
}
}
public int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
class SelectedRunnable implements Runnable {
@Override
public final void run() {
LoopScrollListener listener = LoopView.this.mLoopListener;
int selectedItem = getSelectedItem();
mDataList.get(selectedItem);
listener.onItemSelect(selectedItem);
}
}
/**
* Use in ACTION_UP
*/
class HalfHeightRunnable implements Runnable {
int realTotalOffset;
int realOffset;
int offset;
public HalfHeightRunnable(int offset) {
this.offset = offset;
realTotalOffset = Integer.MAX_VALUE;
realOffset = 0;
}
@Override
public void run() {
//first in
if (realTotalOffset == Integer.MAX_VALUE) {
if ((float) offset > mItemHeight / 2.0F) {
//move to next item
realTotalOffset = (int) (mItemHeight - (float) offset);
} else {
//move to pre item
realTotalOffset = -offset;
}
}
realOffset = (int) ((float) realTotalOffset * 0.1F);
if (realOffset == 0) {
if (realTotalOffset < 0) {
realOffset = -1;
} else {
realOffset = 1;
}
}
if (Math.abs(realTotalOffset) <= 0) {
cancelSchedule();
mHandler.sendEmptyMessage(MSG_SELECTED_ITEM);
return;
} else {
mTotalScrollY = mTotalScrollY + realOffset;
mHandler.sendEmptyMessage(MSG_INVALIDATE);
realTotalOffset = realTotalOffset - realOffset;
return;
}
}
}
/**
* Use in {@link LoopViewGestureListener#onFling(MotionEvent, MotionEvent, float, float)}
*/
class FlingRunnable implements Runnable {
float velocity;
final float velocityY;
FlingRunnable(float velocityY) {
this.velocityY = velocityY;
velocity = Integer.MAX_VALUE;
}
@Override
public void run() {
if (velocity == Integer.MAX_VALUE) {
if (Math.abs(velocityY) > 2000F) {
if (velocityY > 0.0F) {
velocity = 2000F;
} else {
velocity = -2000F;
}
} else {
velocity = velocityY;
}
}
Log.i(TAG, "velocity->" + velocity);
if (Math.abs(velocity) >= 0.0F && Math.abs(velocity) <= 20F) {
cancelSchedule();
mHandler.sendEmptyMessage(MSG_SCROLL_LOOP);
return;
}
int i = (int) ((velocity * 10F) / 1000F);
mTotalScrollY = mTotalScrollY - i;
if (!mCanLoop) {
float itemHeight = lineSpacingMultiplier * mMaxTextHeight;
if (mTotalScrollY <= (int) ((float) (-mInitPosition) * itemHeight)) {
velocity = 40F;
mTotalScrollY = (int) ((float) (-mInitPosition) * itemHeight);
} else if (mTotalScrollY >= (int) ((float) (mDataList.size() - 1 - mInitPosition) * itemHeight)) {
mTotalScrollY = (int) ((float) (mDataList.size() - 1 - mInitPosition) * itemHeight);
velocity = -40F;
}
}
if (velocity < 0.0F) {
velocity = velocity + 20F;
} else {
velocity = velocity - 20F;
}
mHandler.sendEmptyMessage(MSG_INVALIDATE);
}
}
}
LoopScrollListener类代码
// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: braces fieldsfirst space lnc
package com.hsz88.merchant.timeselect;
public interface LoopScrollListener {
void onItemSelect(int item);
}
2.创建TimePickerPopWin类
代码如下:
package com.xxx; import android.app.Activity; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.support.annotation.IdRes; import android.text.TextUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.widget.Button; import android.widget.PopupWindow; import android.widget.RadioGroup; import android.widget.Toast; import com.xxx; import java.util.ArrayList; import java.util.Calendar; import java.util.List; /** * 自定义时间选择器对象 */ public class TimePickerPopWin extends PopupWindow implements View.OnClickListener { private RadioGroup rg_time_select; //取消按钮 //private Button cancelBtn; //确认按钮 //private Button confirmBtn; //确定开门和关门时间 private Button btn_confirm_time; //小时咯loopview private LoopView hourLoopView; //分钟loopView private LoopView minuteLoopView; //显示上午和下午 // private LoopView meridianLoopView; //整个弹窗布局 private View pickerContainerV; //整个xml文件转换的view对象 private View contentView; //选中的时间,小时 private int hourPos = 0; //选中的时间,分钟 private int minutePos = 0; //am或者pm //private int meridianPos = 0; private Context mContext; //取消按钮显示的内容 private String textCancel; //确定按钮显示的内容 private String textConfirm; //取消按钮显示的颜色 private int colorCancel; //确定按钮显示的颜色 private int colorConfirm; //取消和确定按钮显示字体大小 private int btnTextsize; //选择器上时间字体大小 private int viewTextSize; //小时集合 List<String> hourList = new ArrayList(); //分钟集合 List<String> minList = new ArrayList(); //上午和下午集合 //List<String> meridianList = new ArrayList(); public static class Builder { private Context context; private OnTimePickListener listener; //调用的值这个方法 public Builder(Context context, OnTimePickListener listener) { this.context = context; this.listener = listener; } //Optional Parameters private String textCancel = "Cancel"; private String textConfirm = "Confirm"; private int colorCancel = Color.parseColor("#999999"); private int colorConfirm = Color.parseColor("#303F9F"); private int btnTextSize = 16;//text btnTextsize of cancel and confirm button //选择器上时间字体大小 private int viewTextSize = 25; public Builder textCancel(String textCancel){ this.textCancel = textCancel; return this; } public Builder textConfirm(String textConfirm){ this.textConfirm = textConfirm; return this; } public Builder colorCancel(int colorCancel){ this.colorCancel = colorCancel; return this; } public Builder colorConfirm(int colorConfirm){ this.colorConfirm = colorConfirm; return this; } public Builder btnTextSize(int textSize){ this.btnTextSize = textSize; return this; } public Builder viewTextSize(int textSize){ this.viewTextSize = textSize; return this; } public TimePickerPopWin build(){ return new TimePickerPopWin(this); } } //构造方法 public TimePickerPopWin(Builder builder){ this.textCancel = builder.textCancel; this.textConfirm = builder.textConfirm; this.mContext = builder.context; this.mListener = builder.listener; this.colorCancel = builder.colorCancel; this.colorConfirm = builder.colorConfirm; this.btnTextsize = builder.btnTextSize; this.viewTextSize = builder.viewTextSize; initView(); } private OnTimePickListener mListener; //初始化数据, private void initView(){ contentView= LayoutInflater.from(mContext).inflate(R.layout.layout_time_picker,null); //radioground rg_time_select= (RadioGroup) contentView.findViewById(R.id.rg_time_select); // cancelBtn=(Button)contentView.findViewById(R.id.btn_cancel); // cancelBtn.setTextColor(colorCancel); // cancelBtn.setTextSize(btnTextsize); // // confirmBtn=(Button)contentView.findViewById(R.id.btn_confirm); // confirmBtn.setTextColor(colorConfirm); // confirmBtn.setTextSize(btnTextsize); //确认按钮 btn_confirm_time= (Button) contentView.findViewById(R.id.btn_confirm_time); //显示小时 hourLoopView = (LoopView) contentView.findViewById(R.id.picker_hour); //显示分钟 minuteLoopView = (LoopView) contentView.findViewById(R.id.picker_minute); //显示上午和下午 //meridianLoopView = (LoopView) contentView.findViewById(R.id.picker_meridian); pickerContainerV = contentView.findViewById(R.id.container_picker); hourLoopView.setLoopListener(new LoopScrollListener() { @Override public void onItemSelect(int item) { hourPos=item; } }); minuteLoopView.setLoopListener(new LoopScrollListener() { @Override public void onItemSelect(int item) { minutePos=item; } }); // meridianLoopView.setLoopListener(new LoopScrollListener() { // @Override // public void onItemSelect(int item) { // meridianPos=item; // } // }); initPickerViews(); // init hour and minute loop view // cancelBtn.setOnClickListener(this); // confirmBtn.setOnClickListener(this); //点击按钮切换执行此方法 rg_time_select.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) { switch (checkedId){ case R.id.rbtn_open_door: //开门时间按钮 Toast.makeText(mContext,"开门时间",Toast.LENGTH_SHORT).show(); //让确定按钮隐藏 btn_confirm_time.setVisibility(View.GONE); break; case R.id.rbtn_clos_door: //关门时间,按钮 // Toast.makeText(mContext,"关门时间",Toast.LENGTH_SHORT).show(); //获取关门时间 StringBuffer sb = new StringBuffer(); //小时 sb.append(String.valueOf(hourList.get(hourPos))); sb.append(":"); sb.append(String.valueOf(minList.get(minutePos))); //获取开门时间 mListener.onTimePickCompleted(hourPos+1,minutePos,sb.toString()); //让确定按钮显示 btn_confirm_time.setVisibility(View.VISIBLE); break; } } }); //确认开关门时间 btn_confirm_time.setOnClickListener(this); contentView.setOnClickListener(this); if(!TextUtils.isEmpty(textConfirm)){ //confirmBtn.setText(textConfirm); } if(!TextUtils.isEmpty(textCancel)){ //cancelBtn.setText(textCancel); } setTouchable(true); setFocusable(true); setBackgroundDrawable(new BitmapDrawable()); setAnimationStyle(R.style.FadeInPopWin); setContentView(contentView); setWidth(ViewGroup.LayoutParams.MATCH_PARENT); setHeight(ViewGroup.LayoutParams.MATCH_PARENT); } private void initPickerViews(){ hourPos = Calendar.getInstance().get(Calendar.HOUR)-1; minutePos= Calendar.getInstance().get(Calendar.MINUTE); //meridianPos=Calendar.getInstance().get(Calendar.AM_PM); for (int i = 0; i <=23; i++) { hourList.add(format2LenStr(i)); } for (int j = 0; j <60; j++) { minList.add(format2LenStr(j)); } hourLoopView.setDataList(hourList); hourLoopView.setInitPosition(hourPos); minuteLoopView.setDataList( minList); minuteLoopView.setInitPosition(minutePos); } @Override public void onClick(View v) { if(v == btn_confirm_time){ //确认按钮点击事件 //获取关门时间 StringBuffer sb = new StringBuffer(); //小时 sb.append(String.valueOf(hourList.get(hourPos))); sb.append(":"); sb.append(String.valueOf(minList.get(minutePos))); //sb.append(amPm); //mListener.onTimePickCompleted(hourPos+1,minutePos,sb.toString()); mListener.onTimeClose(hourPos+1,minutePos,sb.toString()); //隐藏选择器 dismissPopWin(); } } /** * Show time picker popWindow * * @param activity */ public void showPopWin(Activity activity) { if (null != activity) { TranslateAnimation trans = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0); showAtLocation(activity.getWindow().getDecorView(), Gravity.BOTTOM, 0, 0); trans.setDuration(400); trans.setInterpolator(new AccelerateDecelerateInterpolator()); pickerContainerV.startAnimation(trans); } } /** * 时间选择器动画 * Dismiss time picker popWindow */ public void dismissPopWin() { TranslateAnimation trans = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1); trans.setDuration(400); trans.setInterpolator(new AccelerateInterpolator()); trans.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { dismiss(); } }); pickerContainerV.startAnimation(trans); } /** * Transform int to String with prefix "0" if less than 10 * @param num * @return */ public static String format2LenStr(int num) { return (num < 10) ? "0" + num : String.valueOf(num); } public interface OnTimePickListener { /** * Listener when date been selected 封装数据 * //获取开门时间 * 不需要an和pm * @param time */ void onTimePickCompleted(int hour, int minute, String time); //获取关门时间 void onTimeClose(int hour, int minute, String closeTime); //void onTimePickCompleted(int hour, int minute, String AM_PM, String time); } }
3.创建2个布局文件(layout_time_picker.xml)
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#33000000"> <!--自定义选择器的样式--> <LinearLayout android:id="@+id/container_picker" android:layout_width="222dp" android:layout_height="267dp" android:background="@drawable/shape_time_select" android:layout_centerInParent="true" android:orientation="vertical"> <RadioGroup android:id="@+id/rg_time_select" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:background="@drawable/shape_time_rg"> <!--默认为选中状态--> <RadioButton android:id="@+id/rbtn_open_door" android:layout_width="0dp" android:layout_height="30dp" android:layout_weight="1" android:button="@null" android:text="开门时间" android:gravity="center" android:checked="true" android:textColor="@color/time_select_color" android:background="@drawable/select_time_select" android:textSize="16sp"/> <RadioButton android:id="@+id/rbtn_clos_door" android:layout_width="0dp" android:layout_height="30dp" android:layout_weight="1" android:gravity="center" android:button="@null" android:text="关门时间" android:textColor="@color/time_select_color" android:background="@drawable/select_time_select" android:textSize="16sp"/> </RadioGroup> <RelativeLayout android:layout_width="match_parent" android:layout_height="180dp" android:background="#ffffff" android:padding="3dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!--显示小时--> <com.hsz88.merchant.timeselect.LoopView android:id="@+id/picker_hour" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" app:textSize="25sp" app:canLoop="false"/> <!--分钟--> <com.hsz88.merchant.timeselect.LoopView android:id="@+id/picker_minute" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="1dp" android:layout_weight="1" app:textSize="25sp" app:canLoop="false"/> </LinearLayout> </RelativeLayout> <!--确定开,关门时间按钮--> <Button android:id="@+id/btn_confirm_time" android:layout_width="match_parent" android:layout_marginTop="10dp" android:layout_height="0dp" android:visibility="gone" android:layout_weight="1" android:text="确定" android:textColor="#ffffff" android:background="@drawable/shape_time_btn"/> </LinearLayout> </RelativeLayout>布局效果( 设置在哪个位置,弹出后就显示在什么位置)
4.创建入场动画和出场动画(pop_win_content_fade_in.xml和pop_win_content_fade_out.xml)
入场:
<?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="150" android:fromAlpha="0" android:toAlpha="1" />
出场:
<?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="150" android:fromAlpha="1" android:toAlpha="0" />
5.创建自定义属性attrs.xml,颜色colors.xml,字体dimen.xml和style.xml
attrs:
<!--自定义时间选择器--> <declare-styleable name="LoopView"> <attr name="lineColor" format="color"/> <attr name="topBottomTextColor" format="color"/> <attr name="centerTextColor" format="color"/> <attr name="textSize" format="dimension"/> <attr name="canLoop" format="boolean"/> <attr name="initPosition" format="integer"/> <attr name="drawItemCount" format="integer"/> </declare-styleable>colors.xml
<resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> <color name="colorAccent">#FF4081</color>dimen.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <!--字体大小--> <dimen name="btn_text_size">16sp</dimen> <dimen name="view_text_size">10sp</dimen> </resources>
style.xml
<!--时间选择器--> <style name="FadeInPopWin"> <item name="android:backgroundDimEnabled">true</item> <item name="@android:windowEnterAnimation">@anim/pop_win_content_fade_in</item> <item name="@android:windowExitAnimation">@anim/pop_win_content_fade_out</item> </style>
6.点击后弹出选择器
//设置营业时间 public void setBusineseTime(){ //打开时间选择器 TimePickerPopWin timePickerPopWin=new TimePickerPopWin.Builder(mContext, new TimePickerPopWin.OnTimePickListener() { //获取开门时间 @Override public void onTimePickCompleted(int hour, int minute , String time) { // Toast.makeText(mContext, "开门时间"+time, Toast.LENGTH_SHORT).show(); open_door_time=time; } //获取关门时间 @Override public void onTimeClose(int hour, int minute, String closeTime) { //设置该时间给view显示 tv_business_time.setText(open_door_time+"-"+closeTime); } }).textConfirm("确认") .textCancel("取消") .btnTextSize(16) //取消和确认按钮文本字体大小 .viewTextSize(25) .colorCancel(Color.parseColor("#999999")) .colorConfirm(Color.parseColor("#009900")) .build(); timePickerPopWin.showPopWin(BasicInformationActivity.this); }
效果大概就是这个样子
这是项目要求的效果,另外我自己改了一个时间选择器,在github上,下面是地址