美文网首页自定义view菜菜AndroidAndroid知识
Android自定义滑轮城市选择器

Android自定义滑轮城市选择器

作者: 秦子帅 | 来源:发表于2017-06-27 12:26 被阅读545次

    效果图:

    city.gif

    源码没有放在github上,因为demo比较小,就放在CSDN上了

    源码下载地址

    github地址

    关于自定义的程序如下图:

    Q3LHD@U0[Z]E0`6ZF`0~$SC.png

    主要的代码我就不贴上了,只贴一下LoopView代码:

    package demo.spdbank.hcq.com.wheelview.wheel;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.Typeface;
    import android.os.Handler;
    import android.util.AttributeSet;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.View;
    
    import java.util.List;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.ScheduledFuture;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by qinzishuai on 2015/8/18.
     */
    public class LoopView extends View {
    
        private float scaleX = 1.05F;
    
        public enum ACTION {
            // 点击,滑翔(滑到尽头),拖拽事件
            CLICK, FLING, DAGGLE
        }
    
        Context context;
    
        Handler handler;
        private GestureDetector gestureDetector;
        OnItemSelectedListener onItemSelectedListener;
    
        // Timer mTimer;
        ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor();
        private ScheduledFuture<?> mFuture;
    
        Paint paintOuterText;
        Paint paintCenterText;
        Paint paintIndicator;
    
        List<String> items;
    
        int textSize;
        int maxTextWidth;
        int maxTextHeight;
    
        int colorGray;
        int colorBlack;
        int colorLightGray;
    
        // 条目间距倍数
        float lineSpacingMultiplier;
        boolean isLoop;
    
        // 第一条线Y坐标值
        int firstLineY;
        int secondLineY;
    
        int totalScrollY;
        int initPosition;
        private int selectedItem;
        int preCurrentIndex;
        int change;
    
        // 显示几个条目
        int itemsVisible;
    
        int measuredHeight;
        int measuredWidth;
        int paddingLeft = 0;
        int paddingRight = 0;
    
        // 半圆周长
        int halfCircumference;
        // 半径
        int radius;
    
        private int mOffset = 0;
        private float previousY;
        long startTime = 0;
    
        private Rect tempRect = new Rect();
    
        public LoopView(Context context) {
            super(context);
            initLoopView(context);
        }
    
        public LoopView(Context context, AttributeSet attributeset) {
            super(context, attributeset);
            initLoopView(context);
        }
    
        public LoopView(Context context, AttributeSet attributeset, int defStyleAttr) {
            super(context, attributeset, defStyleAttr);
            initLoopView(context);
        }
    
        private void initLoopView(Context context) {
            this.context = context;
            handler = new MessageHandler(this);
            gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this));
            gestureDetector.setIsLongpressEnabled(false);
    
            lineSpacingMultiplier = 2.0F;
            isLoop = true;
            itemsVisible = 9;
            textSize = 0;
            colorGray = 0xffafafaf;
            colorBlack = 0xff313131;
            colorLightGray = 0xffc5c5c5;
    
            totalScrollY = 0;
            initPosition = -1;
    
            initPaints();
    
            setTextSize(16F);
        }
    
        private void initPaints() {
            paintOuterText = new Paint();
            paintOuterText.setColor(colorGray);
            paintOuterText.setAntiAlias(true);
            paintOuterText.setTypeface(Typeface.MONOSPACE);
            paintOuterText.setTextSize(textSize);
    
            paintCenterText = new Paint();
            paintCenterText.setColor(colorBlack);
            paintCenterText.setAntiAlias(true);
            paintCenterText.setTextScaleX(scaleX);
            paintCenterText.setTypeface(Typeface.MONOSPACE);
            paintCenterText.setTextSize(textSize);
    
            paintIndicator = new Paint();
            paintIndicator.setColor(colorLightGray);
            paintIndicator.setAntiAlias(true);
    
            if (android.os.Build.VERSION.SDK_INT >= 11) {
                setLayerType(LAYER_TYPE_SOFTWARE, null);
            }
        }
    
        private void remeasure() {
            if (items == null) {
                return;
            }
    
            measureTextWidthHeight();
    
            halfCircumference = (int) (maxTextHeight * lineSpacingMultiplier * (itemsVisible - 1));
            measuredHeight = (int) ((halfCircumference * 2) / Math.PI);
            radius = (int) (halfCircumference / Math.PI);
            measuredWidth = maxTextWidth + paddingLeft + paddingRight;
            firstLineY = (int) ((measuredHeight - lineSpacingMultiplier * maxTextHeight) / 2.0F);
            secondLineY = (int) ((measuredHeight + lineSpacingMultiplier * maxTextHeight) / 2.0F);
            if (initPosition == -1) {
                if (isLoop) {
                    initPosition = (items.size() + 1) / 2;
                } else {
                    initPosition = 0;
                }
            }
    
            preCurrentIndex = initPosition;
        }
    
        private void measureTextWidthHeight() {
            for (int i = 0; i < items.size(); i++) {
                String s1 = items.get(i);
                paintCenterText.getTextBounds(s1, 0, s1.length(), tempRect);
                int textWidth = tempRect.width();
                if (textWidth > maxTextWidth) {
                    maxTextWidth = (int) (textWidth * scaleX);
                }
                paintCenterText.getTextBounds("\u661F\u671F", 0, 2, tempRect); // 星期
                int textHeight = tempRect.height();
                if (textHeight > maxTextHeight) {
                    maxTextHeight = textHeight;
                }
            }
    
        }
    
        void smoothScroll(ACTION action) {
            cancelFuture();
            if (action == ACTION.FLING || action == ACTION.DAGGLE) {
                float itemHeight = lineSpacingMultiplier * maxTextHeight;
                mOffset = (int) ((totalScrollY % itemHeight + itemHeight) % itemHeight);
                if ((float) mOffset > itemHeight / 2.0F) {
                    mOffset = (int) (itemHeight - (float) mOffset);
                } else {
                    mOffset = -mOffset;
                }
            }
            mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10, TimeUnit.MILLISECONDS);
        }
    
    //    void smoothScroll() {
    //        int offset = (int) (totalScrollY % (lineSpacingMultiplier * maxTextHeight));
    //        cancelFuture();
    //        mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, offset), 0, 10, TimeUnit.MILLISECONDS);
    //    }
    
        protected final void scrollBy(float velocityY) {
            cancelFuture();
            // 修改这个值可以改变滑行速度
            int velocityFling = 10;
            mFuture = mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, velocityFling, TimeUnit.MILLISECONDS);
        }
    
        public void cancelFuture() {
            if (mFuture != null && !mFuture.isCancelled()) {
                mFuture.cancel(true);
                mFuture = null;
            }
        }
    
        public final void setNotLoop() {
            isLoop = false;
        }
    
        public final void setTextSize(float size) {
            if (size > 0.0F) {
                textSize = (int) (context.getResources().getDisplayMetrics().density * size);
                paintOuterText.setTextSize(textSize);
                paintCenterText.setTextSize(textSize);
            }
        }
    
        public final void setInitPosition(int initPosition) {
            if (initPosition < 0) {
                this.initPosition = 0;
            } else {
                if (items != null && items.size() > initPosition) {
                    this.initPosition = initPosition;
                }
            }
        }
    
        public final void setListener(OnItemSelectedListener OnItemSelectedListener) {
            onItemSelectedListener = OnItemSelectedListener;
        }
    
        public final void setItems(List<String> items) {
            this.items = items;
            remeasure();
            invalidate();
        }
    
        @Override
        public int getPaddingLeft() {
            return paddingLeft;
        }
    
        @Override
        public int getPaddingRight() {
            return paddingRight;
        }
    
        // 设置左右内边距
        public void setViewPadding(int left, int top, int right, int bottom) {
            paddingLeft = left;
            paddingRight = right;
        }
    
        public final int getSelectedItem() {
            return selectedItem;
        }
    //
    //    protected final void scrollBy(float velocityY) {
    //        Timer timer = new Timer();
    //        mTimer = timer;
    //        timer.schedule(new InertiaTimerTask(this, velocityY, timer), 0L, 20L);
    //    }
    
        protected final void onItemSelected() {
            if (onItemSelectedListener != null) {
                postDelayed(new OnItemSelectedRunnable(this), 200L);
            }
        }
    
    
        /**
         * 设置中间文字的scaleX的值,如果为1.0,则没有错位效果,
         * link https://github.com/weidongjian/androidWheelView/issues/10
         *
         * @param scaleX
         */
        public void setScaleX(float scaleX) {
            this.scaleX = scaleX;
        }
    
    
        @Override
        protected void onDraw(Canvas canvas) {
            if (items == null) {
                return;
            }
    
            String as[] = new String[itemsVisible];
            change = (int) (totalScrollY / (lineSpacingMultiplier * maxTextHeight));
            preCurrentIndex = initPosition + change % items.size();
    
            if (!isLoop) {
                if (preCurrentIndex < 0) {
                    preCurrentIndex = 0;
                }
                if (preCurrentIndex > items.size() - 1) {
                    preCurrentIndex = items.size() - 1;
                }
            } else {
                if (preCurrentIndex < 0) {
                    preCurrentIndex = items.size() + preCurrentIndex;
                }
                if (preCurrentIndex > items.size() - 1) {
                    preCurrentIndex = preCurrentIndex - items.size();
                }
            }
    
            int j2 = (int) (totalScrollY % (lineSpacingMultiplier * maxTextHeight));
            // 设置as数组中每个元素的值
            int k1 = 0;
            while (k1 < itemsVisible) {
                int l1 = preCurrentIndex - (itemsVisible / 2 - k1);
                if (isLoop) {
                    while (l1 < 0) {
                        l1 = l1 + items.size();
                    }
                    while (l1 > items.size() - 1) {
                        l1 = l1 - items.size();
                    }
                    as[k1] = items.get(l1);
                } else if (l1 < 0) {
                    as[k1] = "";
                } else if (l1 > items.size() - 1) {
                    as[k1] = "";
                } else {
                    as[k1] = items.get(l1);
                }
                k1++;
            }
            canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator);
            canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator);
    
            int j1 = 0;
            while (j1 < itemsVisible) {
                canvas.save();
                // L(弧长)=α(弧度)* r(半径) (弧度制)
                // 求弧度--> (L * π ) / (π * r)   (弧长X派/半圆周长)
                float itemHeight = maxTextHeight * lineSpacingMultiplier;
                double radian = ((itemHeight * j1 - j2) * Math.PI) / halfCircumference;
                // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限
                float angle = (float) (90D - (radian / Math.PI) * 180D);
                if (angle >= 90F || angle <= -90F) {
                    canvas.restore();
                } else {
                    int translateY = (int) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D);
                    canvas.translate(0.0F, translateY);
                    canvas.scale(1.0F, (float) Math.sin(radian));
                    if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) {
                        // 条目经过第一条线
                        canvas.save();
                        canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY);
                        canvas.drawText(as[j1], getTextX(as[j1], paintOuterText, tempRect), maxTextHeight, paintOuterText);
                        canvas.restore();
                        canvas.save();
                        canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight));
                        canvas.drawText(as[j1], getTextX(as[j1], paintCenterText, tempRect), maxTextHeight, paintCenterText);
                        canvas.restore();
                    } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) {
                        // 条目经过第二条线
                        canvas.save();
                        canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY);
                        canvas.drawText(as[j1], getTextX(as[j1], paintCenterText, tempRect), maxTextHeight, paintCenterText);
                        canvas.restore();
                        canvas.save();
                        canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight));
                        canvas.drawText(as[j1], getTextX(as[j1], paintOuterText, tempRect), maxTextHeight, paintOuterText);
                        canvas.restore();
                    } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) {
                        // 中间条目
                        canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
                        canvas.drawText(as[j1], getTextX(as[j1], paintCenterText, tempRect), maxTextHeight, paintCenterText);
                        selectedItem = items.indexOf(as[j1]);
                    } else {
                        // 其他条目
                        canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
                        canvas.drawText(as[j1], getTextX(as[j1], paintOuterText, tempRect), maxTextHeight, paintOuterText);
                    }
                    canvas.restore();
                }
                j1++;
            }
        }
    
        // 绘制文字起始位置
        private int getTextX(String a, Paint paint, Rect rect) {
            paint.getTextBounds(a, 0, a.length(), rect);
            // 获取到的是实际文字宽度
            int textWidth = rect.width();
            // 转换成绘制文字宽度
            textWidth *= scaleX;
            return (measuredWidth - textWidth) / 2;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            remeasure();
            setMeasuredDimension(measuredWidth, measuredHeight);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            boolean eventConsumed = gestureDetector.onTouchEvent(event);
            float itemHeight = lineSpacingMultiplier * maxTextHeight;
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    startTime = System.currentTimeMillis();
                    cancelFuture();
                    previousY = event.getRawY();
                    break;
    
                case MotionEvent.ACTION_MOVE:
                    float dy = previousY - event.getRawY();
                    previousY = event.getRawY();
    
                    totalScrollY = (int) (totalScrollY + dy);
    
                    // 边界处理。
                    if (!isLoop) {
                        float top = -initPosition * itemHeight;
                        float bottom = (items.size() - 1 - initPosition) * itemHeight;
    
                        if (totalScrollY < top) {
                            totalScrollY = (int) top;
                        } else if (totalScrollY > bottom) {
                            totalScrollY = (int) bottom;
                        }
                    }
                    break;
    
                case MotionEvent.ACTION_UP:
                default:
                    if (!eventConsumed) {
                        float y = event.getY();
                        double l = Math.acos((radius - y) / radius) * radius;
                        int circlePosition = (int) ((l + itemHeight / 2) / itemHeight);
    
                        float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight;
                        mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset);
    
                        if ((System.currentTimeMillis() - startTime) > 120) {
                            // 处理拖拽事件
                            smoothScroll(ACTION.DAGGLE);
                        } else {
                            // 处理条目点击事件
                            smoothScroll(ACTION.CLICK);
                        }
                    }
                    break;
            }
    
            invalidate();
            return true;
        }
    }
    
    

    可以的话,欢迎大家关注我得公众号,公众号写的不好,我会努力提高质量的:


    qrcode_for_gh_e52f4684ce99_258.jpg

    相关文章

      网友评论

        本文标题:Android自定义滑轮城市选择器

        本文链接:https://www.haomeiwen.com/subject/krtecxtx.html