查看原文
其他

Android自定义滑轮城市选择器

2017-06-27 qzs 安卓干货铺

效果图:



简书地址:http://www.jianshu.com/p/b4c1a99dada8

源码下载地址:http://download.csdn.net/detail/qq_34908107/9881697


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


主要的代码我就不贴上了,只贴一下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;
   }
}



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存