先上效果图
下面再上 https://github.com/JustinRoom/WheelViewDemo 中需要用到的几个类
1. IWheelViewSetting.java
import android.support.annotation.ColorInt; /** * Wheel view without selected mask. * * <br>Email:1006368252@qq.com * <br>QQ:1006368252 * <br><a href="https://github.com/JustinRoom/WheelViewDemo" target="_blank">https://github.com/JustinRoom/WheelViewDemo</a> * * @author jiangshicheng */ public interface IWheelViewSetting { WheelItemBean getSelectWheelItem(); void setTextSize(float textSize); void setTextColor(@ColorInt int textColor); void setShowCount(int showCount); void setTotalOffsetX(int totalOffsetX); void setItemVerticalSpace(int itemVerticalSpace); void setItems(WheelItemBean[] items, int initIndex); int getSelectedIndex(); void setSelectedIndex(int targetIndexPosition); void setSelectedIndex(int targetIndexPosition, boolean withAnimation); void setOnSelectedListener(WheelView.OnSelectedListener onSelectedListener); boolean isScrolling(); }
2. WheelItemView.java
import android.content.Context; import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.ViewGroup; import android.widget.FrameLayout; /** * Wheel view without selected mask. * * <br>Email:1006368252@qq.com * <br>QQ:1006368252 * <br><a href="https://github.com/JustinRoom/WheelViewDemo" target="_blank">https://github.com/JustinRoom/WheelViewDemo</a> * * @author jiangshicheng */ public class WheelItemView extends FrameLayout implements IWheelViewSetting { private WheelView wheelView; private WheelMaskView wheelMaskView; public WheelItemView(@NonNull Context context) { super(context); initAttr(context, null, 0); } public WheelItemView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); initAttr(context, attrs, 0); } public WheelItemView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttr(context, attrs, defStyleAttr); } private void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { wheelView = new WheelView(context); wheelView.initAttr(context, attrs, defStyleAttr); wheelMaskView = new WheelMaskView(context); wheelMaskView.initAttr(context, attrs, defStyleAttr); addView(wheelView, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); addView(wheelMaskView, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ViewGroup.LayoutParams params = wheelMaskView.getLayoutParams(); params.height = wheelView.getMeasuredHeight(); wheelMaskView.setLayoutParams(params); wheelMaskView.updateMask(wheelView.getShowCount(), wheelView.getItemHeight()); } @Override public WheelItemBean getSelectWheelItem(){ try{ return wheelView.getSelectWheelItem(); }catch (Exception e){} return null; } public WheelItemBean getIndexWheelItem(int index){ try{ return wheelView.getItemIndex(index); }catch (Exception e){} return null; } @Override public void setTextSize(float textSize) { wheelView.setTextSize(textSize); } @Override public void setTextColor(@ColorInt int textColor) { wheelView.setTextColor(textColor); } @Override public void setShowCount(int showCount) { wheelView.setShowCount(showCount); } @Override public void setTotalOffsetX(int totalOffsetX) { wheelView.setTotalOffsetX(totalOffsetX); } @Override public void setItemVerticalSpace(int itemVerticalSpace) { wheelView.setItemVerticalSpace(itemVerticalSpace); } @Override public void setItems(WheelItemBean[] items, int initIndex) { wheelView.setItems(items,initIndex); } @Override public int getSelectedIndex() { return wheelView.getSelectedIndex(); } @Override public void setSelectedIndex(int targetIndexPosition) { setSelectedIndex(targetIndexPosition, true); } @Override public void setSelectedIndex(int targetIndexPosition, boolean withAnimation) { wheelView.setSelectedIndex(targetIndexPosition, withAnimation); } @Override public void setOnSelectedListener(WheelView.OnSelectedListener onSelectedListener) { wheelView.setOnSelectedListener(onSelectedListener); } public void setMaskLineColor(@ColorInt int color) { wheelMaskView.setLineColor(color); } @Override public boolean isScrolling() { return wheelView.isScrolling(); } public WheelView getWheelView() { return wheelView; } public WheelMaskView getWheelMaskView() { return wheelMaskView; } }
3. WheelMaskView.java
import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.support.annotation.ColorInt; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; /** * Wheel view without selected mask. * * <br>Email:1006368252@qq.com * <br>QQ:1006368252 * <br><a href="https://github.com/JustinRoom/WheelViewDemo" target="_blank">https://github.com/JustinRoom/WheelViewDemo</a> * * @author jiangshicheng */ public class WheelMaskView extends View { private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private int top = 0; private int bottom = 0; // private int lineColor = 0x8F0000FF; private int lineColor = Color.GREEN ; public WheelMaskView(Context context) { super(context); initAttr(context, null, 0); } public WheelMaskView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initAttr(context, attrs, 0); } public WheelMaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttr(context, attrs, defStyleAttr); } public void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(1); } public void updateMask(int heightCount, int itemHeight) { if (heightCount > 0) { int centerIndex = heightCount / 2; top = centerIndex * itemHeight; bottom = top + itemHeight; } else { top = 0; bottom = 0; } invalidate(); } public void setLineColor(@ColorInt int lineColor) { this.lineColor = lineColor; invalidate(); } @Override protected void onDraw(Canvas canvas) { if (top > 0 && bottom > 0) { paint.setColor(lineColor); canvas.drawLine(0, top, getWidth(), top, paint); canvas.drawLine(0, bottom, getWidth(), bottom, paint); } } }
4. WheelView.java
import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Camera; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.support.annotation.ColorInt; import android.support.annotation.Nullable; import android.text.TextPaint; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.animation.LinearInterpolator; import android.widget.OverScroller; /** * Wheel view without selected mask. * * <br>Email:1006368252@qq.com * <br>QQ:1006368252 * <br><a href="https://github.com/JustinRoom/WheelViewDemo" target="_blank">https://github.com/JustinRoom/WheelViewDemo</a> * * @author jiangshicheng */ public class WheelView extends View implements IWheelViewSetting { private static final float DEFAULT_ROTATION_X = 45.0f; private static final int DEFAULT_VELOCITY_UNITS = 600; private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private Camera camera = new Camera(); private Matrix matrix = new Matrix(); private float textBaseLine = 0; private WheelItemBean[] items = null; private int textColor = 0xFF333333; /** * The size of showing text. * Default value is 14dp. */ private float textSize = 0.0f; /** * The offset pixel from x coordination. * <br>text align {@code right} with a positive value * <br>text align {@code center} with 0 value * <br>text align {@code left} with a negative value */ private int totalOffsetX = 0; /** * the average pixel length of show text. */ private float averageShowTextLength = 0; /** * The most showing item count. * use it to measure view‘s height */ private int showCount = 5; /** * The most draw item count. */ private int drawCount = showCount + 2; private Rect[] defaultRectArray = null; private Rect[] drawRectArray = null; private int offsetY = 0; private int totalMoveY = 0;// private float wheelRotationX = DEFAULT_ROTATION_X ; private int velocityUnits = DEFAULT_VELOCITY_UNITS; /** * the space width of two items */ private int itemVerticalSpace = 32; /** * the height of every item */ private int itemHeight = 0; private float lastX = 0.0f; private float lastY = 0.0f; private int[] calculateResult = new int[2];//for saving the calculate result. private int selectedIndex = 0;//the selected index position private OnSelectedListener onSelectedListener = null; private ValueAnimator animator = null; private boolean isScrolling = false; private boolean isAnimatorCanceledForwardly = false;//whether cancel auto scroll animation forwardly private static final long CLICK_EVENT_INTERNAL_TIME = 1000; private RectF rectF = new RectF(); private long touchDownTimeStamp = 0; //about fling action private int mMinimumVelocity; private int mMaximumVelocity; private int scaledTouchSlop; private VelocityTracker mVelocityTracker = null; private OverScroller mOverScroller; private int flingDirection = 0;//-1向上、1向下 public WheelView(Context context) { super(context); initAttr(context, null, 0); } public WheelView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initAttr(context, attrs, 0); } public WheelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttr(context, attrs, defStyleAttr); } public void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { mOverScroller = new OverScroller(context); final ViewConfiguration viewConfiguration = ViewConfiguration.get(context); mMinimumVelocity = viewConfiguration.getScaledMinimumFlingVelocity(); mMaximumVelocity = viewConfiguration.getScaledMaximumFlingVelocity(); scaledTouchSlop = viewConfiguration.getScaledTouchSlop(); float defaultTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, getResources().getDisplayMetrics()); textSize = defaultTextSize; if (velocityUnits < 0) { velocityUnits = Math.abs(velocityUnits); } initConfig(); if (isInEditMode()) { WheelItemBean[] items = new WheelItemBean[50]; for (int i = 0; i < items.length; i++) { items[i] = new WheelItemBean(); items[i].setLabel("选项" + (i < 10 ? "0" + i : String.valueOf(i))); } setItems(items,0); } } private void initConfig() { textPaint.setColor(textColor); textPaint.setTextSize(textSize); Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); String testText = "选项"; Rect rect = new Rect(); textPaint.getTextBounds(testText, 0, testText.length(), rect); itemHeight = rect.height() + itemVerticalSpace; textBaseLine = -itemHeight / 2.0f + (itemHeight - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top; if (showCount < 5) { showCount = 5; } if (showCount % 2 == 0) { showCount++; } drawCount = showCount + 2; defaultRectArray = new Rect[drawCount]; drawRectArray = new Rect[drawCount]; for (int i = 0; i < drawCount; i++) { defaultRectArray[i] = new Rect(); drawRectArray[i] = new Rect(); } } @Override public void setTextSize(float textSize) { this.textSize = textSize; initConfig(); requestLayout(); } @Override public void setTextColor(@ColorInt int textColor) { this.textColor = textColor; textPaint.setColor(textColor); invalidate(); } @Override public WheelItemBean getSelectWheelItem(){ try{ return items[selectedIndex]; }catch (Exception e){} return null; } public WheelItemBean[] getItems(){ return items; } public WheelItemBean getItemIndex(int index){ try{ if(index>=items.length||index<0) return null; return items[index]; }catch (Exception e){} return null; } @Override public void setShowCount(int showCount) { this.showCount = showCount; initConfig(); requestLayout(); } @Override public void setTotalOffsetX(int totalOffsetX) { this.totalOffsetX = totalOffsetX; invalidate(); } @Override public void setItemVerticalSpace(int itemVerticalSpace) { this.itemVerticalSpace = itemVerticalSpace; initConfig(); requestLayout(); } /** * Set the fling velocity units. * The default value is {@link #DEFAULT_VELOCITY_UNITS}. * @param velocityUnits the velocity units */ public void setVelocityUnits(int velocityUnits) { this.velocityUnits = Math.abs(velocityUnits); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int top = 0 - itemHeight; for (int i = 0; i < drawCount; i++) { defaultRectArray[i].set(0, top, 0, top + itemHeight); top += itemHeight; } heightMeasureSpec = MeasureSpec.makeMeasureSpec(itemHeight * showCount, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override public boolean onTouchEvent(MotionEvent event) { if (isEmpty()) return super.onTouchEvent(event); initVelocityTrackerIfNotExists(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //add event into velocity tracker. mVelocityTracker.clear(); //stop fling and reset fling direction flingDirection = 0; mOverScroller.forceFinished(true); if (animator != null && animator.isRunning()) { isAnimatorCanceledForwardly = true; animator.cancel(); } lastX = event.getX(); lastY = event.getY(); //Make it as a click event when touch down, //and record touch down time stamp. touchDownTimeStamp = System.currentTimeMillis(); break; case MotionEvent.ACTION_MOVE: //add event into velocity tracker and compute velocity. mVelocityTracker.addMovement(event); float currentX = event.getX(); float currentY = event.getY(); int distance = (int) (currentY - lastY); int direction = 0; if (distance == 0) break; //if moved, cancel click event touchDownTimeStamp = 0; direction = distance / Math.abs(distance); //initialize touch area rectF.set(0, 0, getWidth(), getHeight()); if (rectF.contains(currentX, currentY)) { //inside touch area, execute move event. lastX = currentX; lastY = currentY; updateByTotalMoveY(totalMoveY + distance, direction); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (System.currentTimeMillis() - touchDownTimeStamp <= CLICK_EVENT_INTERNAL_TIME) { //it‘s a click event, do it executeClickEvent(event.getX(), event.getY()); break; } //calculate current velocity final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(velocityUnits, mMaximumVelocity); float currentVelocity = velocityTracker.getYVelocity(); recycleVelocityTracker(); final int tempFlingDirection = currentVelocity == 0 ? 0 : (currentVelocity < 0 ? -1 : 1); if (Math.abs(currentVelocity) >= mMinimumVelocity) { //it‘s a fling event. flingDirection = tempFlingDirection; mOverScroller.fling( 0, totalMoveY, 0, (int) currentVelocity, 0, 0, -(getItemCount() + showCount / 2) * itemHeight, (showCount / 2) * itemHeight, 0, 0 ); invalidate(); } else { calculateSelectedIndex(totalMoveY, tempFlingDirection); selectedIndex = calculateResult[0]; offsetY = calculateResult[1]; //execute rebound animation executeAnimation( totalMoveY, 0 - selectedIndex * itemHeight ); } break; } return true; } public void setOnSelectedListener(OnSelectedListener onSelectedListener) { this.onSelectedListener = onSelectedListener; } public void setItems(WheelItemBean[] items, int initIndex) { this.items = items; this.selectedIndex = initIndex ; totalMoveY = 0 - itemHeight * initIndex; offsetY = 0; if (!isEmpty()) { averageShowTextLength = calAverageShowTextLength(); invalidate(); } } public int getItemHeight() { return itemHeight; } public int getShowCount() { return showCount; } /** * Get the current selected index position. * * @return the current selected index position */ public int getSelectedIndex() { return selectedIndex; } /** * Scroll to fixed index position with animation. * * @param targetIndexPosition the target index position */ public void setSelectedIndex(int targetIndexPosition) { setSelectedIndex(targetIndexPosition, true); } /** * Set the 3D rotation. * * @param wheelRotationX the rotate wheel base x axis */ public void setWheelRotationX(float wheelRotationX) { if (this.wheelRotationX != wheelRotationX) { this.wheelRotationX = wheelRotationX; invalidate(); } } /** * Scroll to fixed index position. * * @param targetIndexPosition the target index position * @param withAnimation true, scroll with animation */ public void setSelectedIndex(int targetIndexPosition, boolean withAnimation) { if (targetIndexPosition < 0 || targetIndexPosition >= getItemCount()) throw new IndexOutOfBoundsException("Out of array bounds."); if (withAnimation) { executeAnimation(totalMoveY, 0 - itemHeight * targetIndexPosition); } else { totalMoveY = 0 - itemHeight * targetIndexPosition; selectedIndex = targetIndexPosition; offsetY = 0; invalidate(); if (onSelectedListener != null) onSelectedListener.onSelected(getContext(), selectedIndex); } } @Override public boolean isScrolling() { return isScrolling; } /** * Calculate average pixel length of show text. * * @return the average pixel length of show text */ private float calAverageShowTextLength() { float totalLength = 0; String showText = null; for (WheelItemBean wheel : items) { showText = wheel.getLabel(); if (showText == null || showText.length() == 0) continue; totalLength += textPaint.measureText(showText); } return totalLength / getItemCount(); } /** * Execute click event. * * @return true, valid click event, else invalid. */ private void executeClickEvent(float upX, float upY) { boolean isValidTempSelectedIndex = false; int tempSelectedIndex = selectedIndex - drawCount / 2; for (int i = 0; i < drawCount; i++) { rectF.set(drawRectArray[i]); if (rectF.contains(upX, upY)) { isValidTempSelectedIndex = true; break; } tempSelectedIndex++; } if (isValidTempSelectedIndex && tempSelectedIndex >= 0 && tempSelectedIndex < getItemCount()) { //Move to target selected index setSelectedIndex(tempSelectedIndex); } } private int getItemCount() { return items == null ? 0 : items.length; } private WheelItemBean getItemAt(int position) { if (isEmpty() || position < 0 || position >= getItemCount()) return null; return items[position]; } private boolean isEmpty() { return getItemCount() == 0; } /** * Execute animation. */ private void executeAnimation(int... values) { //if it‘s invalid animation, call back immediately. if (invalidAnimation(values)) { if (onSelectedListener != null) onSelectedListener.onSelected(getContext(), selectedIndex); return; } int duration = 0; for (int i = 0; i < values.length; i++) { if (i > 0) { duration += Math.abs(values[i] - values[i - 1]); } } if (duration == 0) { if (onSelectedListener != null) onSelectedListener.onSelected(getContext(), selectedIndex); return; } createAnimatorIfNecessary(); if (animator.isRunning()) { isAnimatorCanceledForwardly = true; animator.cancel(); } animator.setIntValues(values); animator.setDuration(calSuitableDuration(duration)); animator.start(); } private boolean invalidAnimation(int... values) { if (values == null || values.length < 2) return true; int firstValue = values[0]; for (int value : values) { if (firstValue != value) return false; } return true; } private int calSuitableDuration(int duration) { int result = duration; while (result > 1200) { result = result / 2; } return result; } /** * Create auto-scroll animation. */ private void createAnimatorIfNecessary() { if (animator == null) { animator = new ValueAnimator(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int tempTotalMoveY = (int) animation.getAnimatedValue(); updateByTotalMoveY(tempTotalMoveY, 0); } }); animator.setInterpolator(new LinearInterpolator()); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { isScrolling = true; } @Override public void onAnimationEnd(Animator animation) { isScrolling = false; //Cancel animation forwardly. if (isAnimatorCanceledForwardly) { isAnimatorCanceledForwardly = false; return; } if (onSelectedListener != null) onSelectedListener.onSelected(getContext(), selectedIndex); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } } public int getTotalMoveY() { return totalMoveY; } private void updateByTotalMoveY(final int totalMoveY, int direction) { calculateSelectedIndex(totalMoveY, direction); this.totalMoveY = totalMoveY; this.selectedIndex = calculateResult[0]; this.offsetY = calculateResult[1]; invalidate(); } private void calculateSelectedIndex(int totalMoveY, int direction) { int selectedIndex = totalMoveY / (0 - itemHeight); int rest = totalMoveY % (0 - itemHeight); if (direction > 0 && rest != 0) { selectedIndex ++; rest = itemHeight - Math.abs(rest); } //move up if (direction < 0 && Math.abs(rest) >= itemHeight / 4) { selectedIndex++; } //move down if (direction > 0 && Math.abs(rest) >= itemHeight / 4) { selectedIndex --; } selectedIndex = Math.max(selectedIndex, 0); selectedIndex = Math.min(selectedIndex, getItemCount() - 1); int offsetY = (0 - selectedIndex * itemHeight) - totalMoveY; calculateResult[0] = selectedIndex; calculateResult[1] = offsetY; } @Override public void computeScroll() { if (mOverScroller.computeScrollOffset()) { totalMoveY = mOverScroller.getCurrY(); updateByTotalMoveY(totalMoveY, 0); invalidate(); return; } if (flingDirection != 0) { final int flingDirectionCopy = flingDirection; flingDirection = 0; calculateSelectedIndex(totalMoveY, flingDirectionCopy); selectedIndex = calculateResult[0]; offsetY = calculateResult[1]; //execute rebound animation executeAnimation( totalMoveY, 0 - selectedIndex * itemHeight ); } } @Override protected void onDraw(Canvas canvas) { if (isEmpty()) return; int tempStartSelectedIndex = selectedIndex - drawCount / 2; for (int i = 0; i < drawCount; i++) { Rect rect = drawRectArray[i]; rect.set(defaultRectArray[i]); //record touch area for click event rect.left = 0; rect.right = getWidth(); if (tempStartSelectedIndex >= 0 && tempStartSelectedIndex < getItemCount()) { drawItem(canvas, rect, getItemAt(tempStartSelectedIndex), -offsetY, textPaint); } tempStartSelectedIndex++; } computeScroll(); } private void drawItem(Canvas canvas, Rect rect, WheelItemBean item, int offsetY, TextPaint textPaint) { String text = item == null ? "" : item.getLabel(); if (text == null || text.trim().length() == 0) return; rect.offset(0, offsetY); textPaint.setAlpha(calAlpha(rect)); final int offsetX = totalOffsetX == 0 ? 0 : calOffsetX(totalOffsetX, rect); final float w = textPaint.measureText(text); float startX = 0; if (totalOffsetX > 0) { //show text align right final float rightAlignPosition = (getWidth() + averageShowTextLength) / 2.0f; startX = rightAlignPosition - w + offsetX; } else if (totalOffsetX < 0) { //show text align left final float leftAlignPosition = (getWidth() - averageShowTextLength) / 2.0f; startX = leftAlignPosition + offsetX; } else { //show text align center_horizontal startX = (getWidth() - w) / 2.0f + offsetX; } float centerX = getWidth() / 2.0f; float centerY = rect.exactCenterY(); float baseLine = centerY + textBaseLine; matrix.reset(); camera.save(); camera.rotateX(calRotationX(rect, wheelRotationX)); camera.getMatrix(matrix); camera.restore(); matrix.preTranslate(-centerX, -centerY); matrix.postTranslate(centerX, centerY); if (totalOffsetX > 0) { float skewX = 0 - calSkewX(rect); centerX = (startX + w) / 2.0f; matrix.setSkew(skewX, 0, centerX, centerY); } else if (totalOffsetX < 0) { float skewX = calSkewX(rect); centerX = (startX + w) / 2.0f; matrix.setSkew(skewX, 0, centerX, centerY); } canvas.save(); canvas.concat(matrix); canvas.drawText(text, startX, baseLine, textPaint); canvas.restore(); } private int calAlpha(Rect rect) { int centerY = getHeight() / 2; int distance = Math.abs(centerY - rect.centerY()); int totalDistance = itemHeight * (showCount / 2); float alpha = 0.6f * distance / totalDistance; return (int) ((1 - alpha) * 0xFF); } private float calRotationX(Rect rect, float baseRotationX) { int centerY = getHeight() / 2; int distance = centerY - rect.centerY(); int totalDistance = itemHeight * (showCount / 2); return baseRotationX * distance * 1.0f / totalDistance; } private float calSkewX(Rect rect) { int centerY = getHeight() / 2; int distance = centerY - rect.centerY(); int totalDistance = itemHeight * (showCount / 2); return 0.3f * distance / totalDistance; } private int calOffsetX(int totalOffsetX, Rect rect) { int centerY = getHeight() / 2; int distance = Math.abs(centerY - rect.centerY()); int totalDistance = itemHeight * (showCount / 2); return totalOffsetX * distance / totalDistance; } private void initVelocityTrackerIfNotExists() { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } } private void recycleVelocityTracker() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } public interface OnSelectedListener { void onSelected(Context context, int selectedIndex); } }
对于滚轮最重要的就是这个 WheelView.java
5. WheelItemBean.java 此类和原来的有更改
public class WheelItemBean { private String id ; private String label; private WheelItemBean[] wheelItems ; /////////////////////////////////////////// public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public WheelItemBean[] getWheelItems() { return wheelItems; } public void setWheelItems(WheelItemBean[] wheelItems) { this.wheelItems = wheelItems; } public String getId() { return id; } public void setId(String id) { this.id = id; } }
6. ColumnWheelDialog.java 入口类
import android.app.Dialog; import android.content.Context; import android.graphics.Color; import android.os.Bundle; import android.support.annotation.NonNull; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TableRow; import android.widget.TextView; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class ColumnWheelDialog extends Dialog { private String tvTitle; private String tvCancel; private OnClickCallBack cancelCallBack = null; private String tvOK; private OnClickCallBack okCallBack = null; ////////////////////////// private int evel = 0;//层级 private Map<Integer,WheelItemView> wheelItemViewMap = new HashMap<>(); private String initLabelVal[] = null;//初始化值 private boolean isViewInitialized = false; private float textSize; private int itemVerticalSpace; private WheelItemBean[] fWheelItems = null; public ColumnWheelDialog(@NonNull Context context) { this(context, R.style.WheelDialog); } private ColumnWheelDialog(@NonNull Context context, int themeResId) { super(context, themeResId); } public ColumnWheelDialog setData(int evel, WheelItemBean[] wheelItems){ this.evel = evel; this.fWheelItems = wheelItems; return this; } public ColumnWheelDialog setInitLabel(String ... strings){ initLabelVal = strings;//设置初始化 return this; } public ColumnWheelDialog setTitle(String title) { tvTitle = title ; return this; } public ColumnWheelDialog setTextSize(float textSize) { this.textSize = textSize; return this; } public ColumnWheelDialog setCancelButton(String cancel, OnClickCallBack cancelCallBack) { tvCancel = cancel ; this.cancelCallBack = cancelCallBack; return this; } public ColumnWheelDialog setOKButton(String ok, OnClickCallBack okCallBack) { tvOK = ok ; this.okCallBack = okCallBack; return this; } //show 的时候才调用 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); if (getWindow() != null) { getWindow().setGravity(Gravity.BOTTOM); getWindow().setBackgroundDrawable(null); getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT); } initView(); } public List<WheelItemBean> getSelectWheelItem(){ List list = new ArrayList(); for(int i=0;i<wheelItemViewMap.size();i++){ WheelItemView wheelItemView = wheelItemViewMap.get(i); WheelItemBean wheelItem = wheelItemView.getSelectWheelItem(); list.add(wheelItem);//设置数据 } return list; } private void addText(ViewGroup viewGroup,String text,int gravity,int color,View.OnClickListener onClickListener){ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); layoutParams.gravity = gravity ; TextView textView = new TextView(viewGroup.getContext()); textView.setText(text); textView.setTextColor(color); textView.setTextSize(18); textView.setPadding(12,8,12,8); if(onClickListener!=null){ textView.setOnClickListener(onClickListener);//添加事件 } viewGroup.addView(textView,layoutParams); } /** * 获取初始化数据 * @param theEvel * @return */ private int getInitIndex(int theEvel, WheelItemBean[] wheelItems){ try{ String initLabelStr = initLabelVal[theEvel];//初始化 for(int i=0;i<wheelItems.length;i++){ if(initLabelStr.equals(wheelItems[i].getLabel())){ return i ;//返回Index } } }catch (Exception e){} return 0 ;//返回0 } private void initView(){ isViewInitialized = true; LinearLayout rootView = new LinearLayout(getContext());//设置 rootView.setBackgroundColor(Color.WHITE); rootView.setOrientation(LinearLayout.VERTICAL); /////////////////////////////////////////////////// FrameLayout frameLayout = new FrameLayout(getContext());//设置 frameLayout.setBackgroundColor(Color.parseColor("#F5F5F5"));//设置灰色 rootView.addView(frameLayout, TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT); /////////////////////////////// addText(frameLayout, this.tvCancel, Gravity.LEFT, Color.RED, new View.OnClickListener() { @Override public void onClick(View v) { if (cancelCallBack == null) { dismiss(); return; } cancelCallBack.callBack(v,getSelectWheelItem()); dismiss(); } }); addText(frameLayout, this.tvOK, Gravity.RIGHT, Color.parseColor("#7CCD7C"), new View.OnClickListener() { @Override public void onClick(View v) { if (okCallBack == null) { dismiss(); return; } if (isScrolling()) { //正在滚动 return; } okCallBack.callBack(v,getSelectWheelItem()) ; dismiss(); } }); addText(frameLayout,this.tvTitle,Gravity.CENTER,Color.BLACK,null); ////////////////////////////////// LinearLayout contentLine = new LinearLayout(getContext()); rootView.addView(contentLine,LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT); setContentView(rootView); ////////////////////////////////////////////////////////////////////////// for(int i=0;i<evel;i++) { WheelItemView wheelItemView0 = new WheelItemView(contentLine.getContext()); if(i==0){ wheelItemView0.setItems(fWheelItems,getInitIndex(i,fWheelItems));//设置数据 }else{ WheelItemBean wheelItem = wheelItemViewMap.get(i-1).getSelectWheelItem(); if(wheelItem!=null){ wheelItemView0.setItems(wheelItem.getWheelItems(),getInitIndex(i,wheelItem.getWheelItems())); } } contentLine.addView(wheelItemView0, new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1)); if (textSize > 0) { wheelItemView0.setTextSize(textSize); } if (itemVerticalSpace > 0) { wheelItemView0.setItemVerticalSpace(itemVerticalSpace); } int nextId = i+1; wheelItemView0.setOnSelectedListener(new WheelView.OnSelectedListener() { @Override public void onSelected(Context context, int selectedIndex) { //选择 if(fWheelItems!=null){ setDatas(nextId,fWheelItems[selectedIndex].getWheelItems()); } } }); wheelItemViewMap.put(i,wheelItemView0);//添加 } } @Override public void show() { super.show(); if (getWindow() != null) { getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } } public void setItemVerticalSpace(int itemVerticalSpace) { this.itemVerticalSpace = itemVerticalSpace; } public void setDatas(int index, WheelItemBean[] wheelItems){ if(index<wheelItemViewMap.size()){ updateShowPicker(wheelItemViewMap.get(index),wheelItems); // updateOffsetX(4); } } private boolean isScrolling() { boolean reBool = isScrolling(wheelItemViewMap.get(0));//End false for(int i=1;i<wheelItemViewMap.size();i++){ reBool = reBool || isScrolling(wheelItemViewMap.get(i)); } return reBool; } private void updateShowPicker(WheelItemView wheelItemView, WheelItemBean[] items) { boolean hide = (items == null || items.length == 0); wheelItemView.setVisibility(hide ? View.GONE : View.VISIBLE); if(!hide) { wheelItemView.setItems(items, 0); } } private boolean isScrolling(WheelItemView view) { return view.isShown() && view.isScrolling(); } public interface OnClickCallBack { void callBack(View v,List<WheelItemBean> selectList); } }
里面用到了style 。 this(context, R.style.WheelDialog);
在styles.xml 中添加如下内容
<style name="WheelDialog" parent="android:style/Theme.Dialog"> <item name="android:windowFrame">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsFloating">true</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowBackground">@null</item> </style>
调用如下
WheelItemBean wheelItems[] = null; wheelItems = new WheelItemBean[6]; for(int i=0;i<6;i++){ wheelItems[i] = new WheelItemBean(); wheelItems[i].setLabel("第一层"+i); WheelItemBean secondWheels[] = new WheelItemBean[6]; for(int j=0;j<6;j++){ secondWheels[j] = new WheelItemBean(); secondWheels[j].setLabel("第二层:"+i+","+j); /////////////////////// WheelItemBean thWheels[] = new WheelItemBean[6]; for(int k=0;k<thWheels.length;k++){ thWheels[k] = new WheelItemBean(); thWheels[k].setLabel("第三层:"+i+","+j+","+k); } secondWheels[j].setWheelItems(thWheels); } wheelItems[i].setWheelItems(secondWheels); } ColumnWheelDialog dialog = new ColumnWheelDialog(UserInfoActivity.this); dialog.setData(3,wheelItems) //evel 表示几层,wheelItems 级层关系数据 .setTitle("选择") .setInitLabel("第一层3","第二层:3,3") //初始化数据 .setCancelButton("取消", null) .setOKButton("确定",new com.szjpsj.collegeex.view.wheel.ColumnWheelDialog.OnClickCallBack(){ @Override public void callBack(View v, List<WheelItemBean> selectList) { try{ for(int i=0;i<selectList.size();i++){ System.out.println("第"+(i+1)+"层选择内容:"+selectList.get(i).getLabel());//如果需要id 可以设置id,此处选择id } }catch (Exception e){} } }) .show();