自定义时间选择器

因为项目需要,所以写好后总结一下,借鉴这篇文章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上,下面是地址

时间选择器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答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、付费专栏及课程。

余额充值