binbinyang-----自定义VIEW.刻度尺.左右滑动选择对应数值
来源:互联网 发布:淘宝商品排行榜怎么看 编辑:程序博客网 时间:2024/05/05 16:24
很多APP.如今会用到横向刻度尺,或是纵向刻度尺....比如在个人资料填入.或是减肥APP里面...随处可见
今天我带来一个
先发,我录制的动态GIF效果图
放代码
主Activity
public class MainActivity extends AppCompatActivity implements HorizontalPicker.OnItemSelected, HorizontalPicker.OnItemClicked { HorizontalPicker pick; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //数据类型(0 卡路里,100-2000, 1:步数 2000-20000 2;公里:1-20 //默认选中数 pick =(HorizontalPicker)findViewById(R.id.picker); pick.setDataType(2,"10"); pick.setOnItemClickedListener(this); pick.setOnItemSelectedListener(this); } @Override public void onItemClicked(int index) { Toast.makeText(this, index+"", 1).show(); } @Override public void onItemSelected(String item) { Toast.makeText(this, item, 1).show(); }}
xml布局
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF" android:orientation="vertical" > <com.example.yangbin.pickm.HorizontalPicker android:id="@+id/picker" android:layout_width="match_parent" android:layout_height="80dp" android:layout_marginTop="30dp" android:background="#FFFFFF" android:ellipsize="marquee" android:focusable="true" android:focusableInTouchMode="true" android:marqueeRepeatLimit="2" android:paddingBottom="16dp" android:paddingTop="16dp" android:textColor="#727272" android:textSize="22sp" app:dividerSize="0dp" app:sideItems="2" /> <com.example.yangbin.pickm.MyScaleView android:layout_width="wrap_content" android:layout_height="15dp" /></LinearLayout>
自定义
HorizontalPicker
/* * Copyright 2014 Blaž Šolar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.yangbin.pickm;import android.animation.ArgbEvaluator;import android.annotation.TargetApi;import android.content.Context;import android.content.res.ColorStateList;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.graphics.RectF;import android.os.Build;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.os.Parcel;import android.os.Parcelable;import android.support.v4.text.TextDirectionHeuristicCompat;import android.support.v4.text.TextDirectionHeuristicsCompat;import android.support.v4.view.ViewCompat;import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;import android.support.v4.widget.ExploreByTouchHelper;import android.text.BoringLayout;import android.text.Layout;import android.text.TextPaint;import android.text.TextUtils;import android.util.AttributeSet;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.accessibility.AccessibilityEvent;import android.view.animation.DecelerateInterpolator;import android.widget.EdgeEffect;import android.widget.OverScroller;import java.lang.ref.WeakReference;import java.util.ArrayList;import java.util.List;/** * Created by Blaž Šolar on 24/01/14. */public class HorizontalPicker extends View { public static final String TAG = "HorizontalTimePicker"; /** * The coefficient by which to adjust (divide) the max fling velocity. */ private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 4; /** * The the duration for adjusting the selector wheel. */ private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800; /** * Determines speed during touch scrolling. */ private VelocityTracker mVelocityTracker; /** * @see ViewConfiguration#getScaledMinimumFlingVelocity() */ private int mMinimumFlingVelocity; /** * @see ViewConfiguration#getScaledMaximumFlingVelocity() */ private int maximumFlingVelocity; private final int overscrollDistance; private int touchSlop; private CharSequence[] values; private BoringLayout[] layouts; private TextPaint textPaint; private BoringLayout.Metrics boringMetrics; private TextUtils.TruncateAt ellipsize; private int itemWidth; private RectF itemClipBounds; private RectF itemClipBoundsOffset; private float lastDownEventX; private OverScroller flingScrollerX; private OverScroller adjustScrollerX; private int previousScrollerX; private boolean scrollingX; private int pressedItem = -1; private ColorStateList textColor; private OnItemSelected onItemSelected; private OnItemClicked onItemClicked; private int selectedItem; private EdgeEffect leftEdgeEffect; private EdgeEffect rightEdgeEffect; private Marquee marquee; private int marqueeRepeatLimit = 3; private float dividerSize = 0; private int sideItems = 1; private TextDirectionHeuristicCompat textDir; private final PickerTouchHelper touchHelper; private Paint paint; private int mWidth; private int mHeight; private String[] arrays; private int mDataType = 0; public void setDataType(int type, String value) { mDataType = type; setValues(getData(mDataType)); setSelectedItem(value); invalidate(); } public int getDataType() { return mDataType; } private void init() { paint = new Paint(); paint.setColor(Color.GRAY); paint.setAntiAlias(true); paint.setStrokeWidth(2); } public HorizontalPicker(Context context) { this(context, null); } public HorizontalPicker(Context context, AttributeSet attrs) { this(context, attrs, R.attr.horizontalPickerStyle); } public HorizontalPicker(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); // create the selector wheel paint TextPaint paint = new TextPaint(); paint.setAntiAlias(true); textPaint = paint; TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.HorizontalPicker, defStyle, 0 ); int ellipsize = 3; // END default value int sideItems = this.sideItems; try { textColor = a.getColorStateList(R.styleable.HorizontalPicker_android_textColor); if (textColor == null) { textColor = ColorStateList.valueOf(0xFF000000); } ellipsize = a.getInt(R.styleable.HorizontalPicker_android_ellipsize, ellipsize); marqueeRepeatLimit = a.getInt(R.styleable.HorizontalPicker_android_marqueeRepeatLimit, marqueeRepeatLimit); dividerSize = a.getDimension(R.styleable.HorizontalPicker_dividerSize, dividerSize); sideItems = a.getInt(R.styleable.HorizontalPicker_sideItems, sideItems); float textSize = a.getDimension(R.styleable.HorizontalPicker_android_textSize, -1); if (textSize > -1) { setTextSize(textSize); } } finally { a.recycle(); } switch (ellipsize) { case 1: setEllipsize(TextUtils.TruncateAt.START); break; case 2: setEllipsize(TextUtils.TruncateAt.MIDDLE); break; case 3: setEllipsize(TextUtils.TruncateAt.END); break; case 4: setEllipsize(TextUtils.TruncateAt.MARQUEE); break; } Paint.FontMetricsInt fontMetricsInt = textPaint.getFontMetricsInt(); boringMetrics = new BoringLayout.Metrics(); boringMetrics.ascent = fontMetricsInt.ascent; boringMetrics.bottom = fontMetricsInt.bottom; boringMetrics.descent = fontMetricsInt.descent; boringMetrics.leading = fontMetricsInt.leading; boringMetrics.top = fontMetricsInt.top; boringMetrics.width = itemWidth; setWillNotDraw(false); flingScrollerX = new OverScroller(context); adjustScrollerX = new OverScroller(context, new DecelerateInterpolator(2.5f)); // initialize constants ViewConfiguration configuration = ViewConfiguration.get(context); touchSlop = configuration.getScaledTouchSlop(); mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); maximumFlingVelocity = configuration.getScaledMaximumFlingVelocity() / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT; overscrollDistance = configuration.getScaledOverscrollDistance(); previousScrollerX = Integer.MIN_VALUE; //********添加数据********** setValues(getData(getDataType())); setSideItems(sideItems); touchHelper = new PickerTouchHelper(this); ViewCompat.setAccessibilityDelegate(this, touchHelper); } /** * 自定义数据 * * @return */ private String[] getData(int type) { List<String> list = new ArrayList<>(); if (type == 1) { arrays = new String[19]; for (int i = 2000; i <= 20000; i += 1000) { list.add(i + ""); } for (int k = 0; k < 19; k++) { arrays[k] = list.get(k); } } else { if (type == 0) { arrays = new String[20]; for (int i = 100; i <= 2000; i += 100) { list.add(i + ""); } } else if (type == 2) { arrays = new String[20]; for (int i = 1; i <= 20; i += 1) { list.add(i + ""); } } for (int k = 0; k < 20; k++) { arrays[k] = list.get(k); } } return arrays; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int heightMode = MeasureSpec.getMode(heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int height; if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); int heightText = (int) (Math.abs(fontMetrics.ascent) + Math.abs(fontMetrics.descent)); heightText += getPaddingTop() + getPaddingBottom(); if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(heightSize, heightText); } else { height = heightText; } } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mWidth = getWidth(); mHeight = getHeight(); int saveCount = canvas.getSaveCount(); canvas.save(); int selectedItem = this.selectedItem; float itemWithPadding = itemWidth + dividerSize; // translate horizontal to center canvas.translate(itemWithPadding * sideItems, 0); if (values != null) { for (int i = 0; i < values.length; i++) { // set text color for item textPaint.setColor(getTextColor(i)); // get text layout BoringLayout layout = layouts[i]; int saveCountHeight = canvas.getSaveCount(); canvas.save(); float x = 0; float lineWidth = layout.getLineWidth(0); if (lineWidth > itemWidth) { if (isRtl(values[i])) { x += (lineWidth - itemWidth) / 2; } else { x -= (lineWidth - itemWidth) / 2; } } if (marquee != null && i == selectedItem) { x += marquee.getScroll(); } // translate vertically to center canvas.translate(-x, (canvas.getHeight() - layout.getHeight()) / 2); RectF clipBounds; if (x == 0) { clipBounds = itemClipBounds; } else { clipBounds = itemClipBoundsOffset; clipBounds.set(itemClipBounds); clipBounds.offset(x, 0); } canvas.clipRect(clipBounds); layout.draw(canvas); if (marquee != null && i == selectedItem && marquee.shouldDrawGhost()) { canvas.translate(marquee.getGhostOffset(), 0); layout.draw(canvas); } // restore vertical translation canvas.restoreToCount(saveCountHeight); // 画刻度线 paint.setColor(getTextColor(i)); canvas.drawLine(itemWidth / 2, mHeight - 20, itemWidth / 2, mHeight - 45, paint); // translate horizontal for 1 item canvas.translate(itemWithPadding, 0); } } // restore horizontal translation canvas.restoreToCount(saveCount); drawEdgeEffect(canvas, leftEdgeEffect, 270); drawEdgeEffect(canvas, rightEdgeEffect, 90); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @Override public void onRtlPropertiesChanged(int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); textDir = getTextDirectionHeuristic(); } /** * TODO cache values * * @param text * @return */ private boolean isRtl(CharSequence text) { if (textDir == null) { textDir = getTextDirectionHeuristic(); } return textDir.isRtl(text, 0, text.length()); } private TextDirectionHeuristicCompat getTextDirectionHeuristic() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { return TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR; } else { // Always need to resolve layout direction first final boolean defaultIsRtl = (getLayoutDirection() == LAYOUT_DIRECTION_RTL); switch (getTextDirection()) { default: case TEXT_DIRECTION_FIRST_STRONG: return (defaultIsRtl ? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL : TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR); case TEXT_DIRECTION_ANY_RTL: return TextDirectionHeuristicsCompat.ANYRTL_LTR; case TEXT_DIRECTION_LTR: return TextDirectionHeuristicsCompat.LTR; case TEXT_DIRECTION_RTL: return TextDirectionHeuristicsCompat.RTL; case TEXT_DIRECTION_LOCALE: return TextDirectionHeuristicsCompat.LOCALE; } } } private void remakeLayout() { if (layouts != null && layouts.length > 0 && getWidth() > 0) { for (int i = 0; i < layouts.length; i++) { layouts[i].replaceOrMake(values[i], textPaint, itemWidth, Layout.Alignment.ALIGN_CENTER, 1f, 1f, boringMetrics, false, ellipsize, itemWidth); } } } private void drawEdgeEffect(Canvas canvas, EdgeEffect edgeEffect, int degrees) { if (canvas == null || edgeEffect == null || (degrees != 90 && degrees != 270)) { return; } if (!edgeEffect.isFinished()) { final int restoreCount = canvas.getSaveCount(); final int width = getWidth(); final int height = getHeight(); canvas.rotate(degrees); if (degrees == 270) { canvas.translate(-height, Math.max(0, getScrollX())); } else { // 90 canvas.translate(0, -(Math.max(getScrollRange(), getScaleX()) + width)); } edgeEffect.setSize(height, width); if (edgeEffect.draw(canvas)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { postInvalidateOnAnimation(); } else { postInvalidate(); } } canvas.restoreToCount(restoreCount); } } /** * Calculates text color for specified item based on its position and state. * * @param item Index of item to get text color for * @return Item text color */ private int getTextColor(int item) { int scrollX = getScrollX(); // set color of text int color = textColor.getDefaultColor(); int itemWithPadding = (int) (itemWidth + dividerSize); if (scrollX > itemWithPadding * item - itemWithPadding / 2 && scrollX < itemWithPadding * (item + 1) - itemWithPadding / 2) { int position = scrollX - itemWithPadding / 2; color = getColor(position, item); } else if (item == pressedItem) { color = textColor.getColorForState(new int[]{android.R.attr.state_pressed}, color); } return color; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); calculateItemSize(w, h); } @Override public boolean onTouchEvent(MotionEvent event) { if (!isEnabled()) { return false; } if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_MOVE: float currentMoveX = event.getX(); int deltaMoveX = (int) (lastDownEventX - currentMoveX); if (scrollingX || (Math.abs(deltaMoveX) > touchSlop) && values != null && values.length > 0) { if (!scrollingX) { deltaMoveX = 0; pressedItem = -1; scrollingX = true; getParent().requestDisallowInterceptTouchEvent(true); stopMarqueeIfNeeded(); } final int range = getScrollRange(); if (overScrollBy(deltaMoveX, 0, getScrollX(), 0, range, 0, overscrollDistance, 0, true)) { mVelocityTracker.clear(); } final float pulledToX = getScrollX() + deltaMoveX; if (pulledToX < 0) { leftEdgeEffect.onPull((float) deltaMoveX / getWidth()); if (!rightEdgeEffect.isFinished()) { rightEdgeEffect.onRelease(); } } else if (pulledToX > range) { rightEdgeEffect.onPull((float) deltaMoveX / getWidth()); if (!leftEdgeEffect.isFinished()) { leftEdgeEffect.onRelease(); } } lastDownEventX = currentMoveX; invalidate(); } break; case MotionEvent.ACTION_DOWN: if (!adjustScrollerX.isFinished()) { adjustScrollerX.forceFinished(true); } else if (!flingScrollerX.isFinished()) { flingScrollerX.forceFinished(true); } else { scrollingX = false; } lastDownEventX = event.getX(); if (!scrollingX) { pressedItem = getPositionFromTouch(event.getX()); } invalidate(); break; case MotionEvent.ACTION_UP: VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, maximumFlingVelocity); int initialVelocityX = (int) velocityTracker.getXVelocity(); if (scrollingX && Math.abs(initialVelocityX) > mMinimumFlingVelocity) { flingX(initialVelocityX); } else if (values != null) { float positionX = event.getX(); if (!scrollingX) { int itemPos = getPositionOnScreen(positionX); int relativePos = itemPos - sideItems; if (relativePos == 0) { selectItem(); } else { smoothScrollBy(relativePos); } } else if (scrollingX) { finishScrolling(); } } mVelocityTracker.recycle(); mVelocityTracker = null; if (leftEdgeEffect != null) { leftEdgeEffect.onRelease(); rightEdgeEffect.onRelease(); } case MotionEvent.ACTION_CANCEL: pressedItem = -1; invalidate(); if (leftEdgeEffect != null) { leftEdgeEffect.onRelease(); rightEdgeEffect.onRelease(); } break; } return true; } private void selectItem() { // post to the UI Thread to avoid potential interference with the OpenGL Thread if (onItemClicked != null) { post(new Runnable() { @Override public void run() { onItemClicked.onItemClicked(getSelectedItem()); } }); } adjustToNearestItemX(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (!isEnabled()) { return super.onKeyDown(keyCode, event); } switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: selectItem(); return true; case KeyEvent.KEYCODE_DPAD_LEFT: smoothScrollBy(-1); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: smoothScrollBy(1); return true; default: return super.onKeyDown(keyCode, event); } } @Override protected boolean dispatchHoverEvent(MotionEvent event) { if (touchHelper.dispatchHoverEvent(event)) { return true; } return super.dispatchHoverEvent(event); } @Override public void computeScroll() { computeScrollX(); } @Override public void getFocusedRect(Rect r) { super.getFocusedRect(r); // TODO this should only be current item } public void setOnItemSelectedListener(OnItemSelected onItemSelected) { this.onItemSelected = onItemSelected; } public void setOnItemClickedListener(OnItemClicked onItemClicked) { this.onItemClicked = onItemClicked; } public int getSelectedItem() { int x = getScrollX(); return getPositionFromCoordinates(x); } public void setSelectedItem(int index) { selectedItem = index; scrollToItem(index); } /** * 设置选中项 * * @param item */ public void setSelectedItem(String item) { selectedItem = getIndex(item); scrollToItem(selectedItem); } /** * 根据数据返回相应的下标 * * @param item * @return */ private int getIndex(String item) { for (int i = 0; i < arrays.length - 1; i++) { if (arrays[i].equals(item)) return i; } return 0; } public int getMarqueeRepeatLimit() { return marqueeRepeatLimit; } public void setMarqueeRepeatLimit(int marqueeRepeatLimit) { this.marqueeRepeatLimit = marqueeRepeatLimit; } /** * @return Number of items on each side of current item. */ public int getSideItems() { return sideItems; } public void setSideItems(int sideItems) { if (this.sideItems < 0) { throw new IllegalArgumentException("Number of items on each side must be grater or equal to 0."); } else if (this.sideItems != sideItems) { this.sideItems = sideItems; calculateItemSize(getWidth(), getHeight()); } } /** * @return */ public CharSequence[] getValues() { return values; } /** * Sets values to choose from * * @param values New values to choose from */ public void setValues(CharSequence[] values) { if (this.values != values) { this.values = values; if (this.values != null) { layouts = new BoringLayout[this.values.length]; for (int i = 0; i < layouts.length; i++) { layouts[i] = new BoringLayout(this.values[i], textPaint, itemWidth, Layout.Alignment.ALIGN_CENTER, 1f, 1f, boringMetrics, false, ellipsize, itemWidth); } } else { layouts = new BoringLayout[0]; } // start marque only if has already been measured if (getWidth() > 0) { startMarqueeIfNeeded(); } requestLayout(); invalidate(); } } @Override protected void onRestoreInstanceState(Parcelable state) { if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setSelectedItem(ss.mSelItem); } @Override protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.mSelItem = selectedItem; return savedState; } @Override public void setOverScrollMode(int overScrollMode) { if (overScrollMode != OVER_SCROLL_NEVER) { Context context = getContext(); leftEdgeEffect = new EdgeEffect(context); rightEdgeEffect = new EdgeEffect(context); } else { leftEdgeEffect = null; rightEdgeEffect = null; } super.setOverScrollMode(overScrollMode); } public TextUtils.TruncateAt getEllipsize() { return ellipsize; } public void setEllipsize(TextUtils.TruncateAt ellipsize) { if (this.ellipsize != ellipsize) { this.ellipsize = ellipsize; remakeLayout(); invalidate(); } } @Override protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { super.scrollTo(scrollX, scrollY); if (!flingScrollerX.isFinished() && clampedX) { flingScrollerX.springBack(scrollX, scrollY, 0, getScrollRange(), 0, 0); } } @Override protected void drawableStateChanged() { super.drawableStateChanged(); //TODO } private int getPositionFromTouch(float x) { return getPositionFromCoordinates((int) (getScrollX() - (itemWidth + dividerSize) * (sideItems + .5f) + x)); } private void computeScrollX() { OverScroller scroller = flingScrollerX; if (scroller.isFinished()) { scroller = adjustScrollerX; if (scroller.isFinished()) { return; } } if (scroller.computeScrollOffset()) { int currentScrollerX = scroller.getCurrX(); if (previousScrollerX == Integer.MIN_VALUE) { previousScrollerX = scroller.getStartX(); } int range = getScrollRange(); if (previousScrollerX >= 0 && currentScrollerX < 0) { leftEdgeEffect.onAbsorb((int) scroller.getCurrVelocity()); } else if (previousScrollerX <= range && currentScrollerX > range) { rightEdgeEffect.onAbsorb((int) scroller.getCurrVelocity()); } overScrollBy(currentScrollerX - previousScrollerX, 0, previousScrollerX, getScrollY(), getScrollRange(), 0, overscrollDistance, 0, false); previousScrollerX = currentScrollerX; if (scroller.isFinished()) { onScrollerFinishedX(scroller); } postInvalidate();// postInvalidateOnAnimation(); // TODO } } private void flingX(int velocityX) { previousScrollerX = Integer.MIN_VALUE; flingScrollerX.fling(getScrollX(), getScrollY(), -velocityX, 0, 0, (int) (itemWidth + dividerSize) * (values.length - 1), 0, 0, getWidth() / 2, 0); invalidate(); } private void adjustToNearestItemX() { int x = getScrollX(); int item = Math.round(x / (itemWidth + dividerSize * 1f)); if (item < 0) { item = 0; } else if (item > values.length) { item = values.length; } selectedItem = item; int itemX = (itemWidth + (int) dividerSize) * item; int deltaX = itemX - x; previousScrollerX = Integer.MIN_VALUE; adjustScrollerX.startScroll(x, 0, deltaX, 0, SELECTOR_ADJUSTMENT_DURATION_MILLIS); invalidate(); } private void calculateItemSize(int w, int h) { int items = sideItems * 2 + 1; int totalPadding = ((int) dividerSize * (items - 1)); itemWidth = (w - totalPadding) / items; itemClipBounds = new RectF(0, 0, itemWidth, h); itemClipBoundsOffset = new RectF(itemClipBounds); scrollToItem(selectedItem); remakeLayout(); startMarqueeIfNeeded(); } private void onScrollerFinishedX(OverScroller scroller) { if (scroller == flingScrollerX) { finishScrolling(); } } private void finishScrolling() { adjustToNearestItemX(); scrollingX = false; startMarqueeIfNeeded(); // post to the UI Thread to avoid potential interference with the OpenGL Thread if (onItemSelected != null) { post(new Runnable() { @Override public void run() {// onItemSelected.onItemSelected(getPositionFromCoordinates(getScrollX())); onItemSelected.onItemSelected(arrays[getPositionFromCoordinates(getScrollX())]); } }); } } private void startMarqueeIfNeeded() { stopMarqueeIfNeeded(); int item = getSelectedItem(); if (layouts != null && layouts.length > item) { Layout layout = layouts[item]; if (ellipsize == TextUtils.TruncateAt.MARQUEE && itemWidth < layout.getLineWidth(0)) { marquee = new Marquee(this, layout, isRtl(values[item])); marquee.start(marqueeRepeatLimit); } } } private void stopMarqueeIfNeeded() { if (marquee != null) { marquee.stop(); marquee = null; } } private int getPositionOnScreen(float x) { return (int) (x / (itemWidth + dividerSize)); } private void smoothScrollBy(int i) { int deltaMoveX = (itemWidth + (int) dividerSize) * i; deltaMoveX = getRelativeInBound(deltaMoveX); previousScrollerX = Integer.MIN_VALUE; flingScrollerX.startScroll(getScrollX(), 0, deltaMoveX, 0); stopMarqueeIfNeeded(); invalidate(); } /** * Calculates color for specific position on time picker * 修改选中颜色 * * @param scrollX * @return */ private int getColor(int scrollX, int position) { int itemWithPadding = (int) (itemWidth + dividerSize); float proportion = Math.abs(((1f * scrollX % itemWithPadding) / 2) / (itemWithPadding / 2f)); if (proportion > .5) { proportion = (proportion - .5f); } else { proportion = .5f - proportion; } proportion *= 2; int defaultColor; int selectedColor; if (pressedItem == position) { defaultColor = textColor.getColorForState(new int[]{android.R.attr.state_pressed}, textColor.getDefaultColor());// selectedColor = textColor.getColorForState(new int[]{android.R.attr.state_pressed, android.R.attr.state_selected}, defaultColor); selectedColor = Color.RED; } else { defaultColor = textColor.getDefaultColor();// selectedColor = textColor.getColorForState(new int[]{android.R.attr.state_selected}, defaultColor); selectedColor = Color.RED; } return (Integer) new ArgbEvaluator().evaluate(proportion, selectedColor, defaultColor); } /** * Sets text size for items * * @param size New item text size in px. */ private void setTextSize(float size) { if (size != textPaint.getTextSize()) { textPaint.setTextSize(size); requestLayout(); invalidate(); } } /** * Calculates item from x coordinate position. * * @param x Scroll position to calculate. * @return Selected item from scrolling position in {param x} */ private int getPositionFromCoordinates(int x) { return Math.round(x / (itemWidth + dividerSize)); } /** * Scrolls to specified item. * * @param index Index of an item to scroll to */ private void scrollToItem(int index) { scrollTo((itemWidth + (int) dividerSize) * index, 0); // invalidate() not needed because scrollTo() already invalidates the view } /** * Calculates relative horizontal scroll position to be within our scroll bounds. * * @param x Relative scroll position to calculate * @return Current scroll position + {param x} if is within our scroll bounds, otherwise it * will return min/max scroll position. */ private int getRelativeInBound(int x) { int scrollX = getScrollX(); return getInBoundsX(scrollX + x) - scrollX; } /** * Calculates x scroll position that is still in range of view scroller * * @param x Scroll position to calculate. * @return {param x} if is within bounds of over scroller, otherwise it will return min/max * value of scoll position. */ private int getInBoundsX(int x) { if (x < 0) { x = 0; } else if (x > ((itemWidth + (int) dividerSize) * (values.length - 1))) { x = ((itemWidth + (int) dividerSize) * (values.length - 1)); } return x; } private int getScrollRange() { int scrollRange = 0; if (values != null && values.length != 0) { scrollRange = Math.max(0, ((itemWidth + (int) dividerSize) * (values.length - 1))); } return scrollRange; } public interface OnItemSelected { // public void onItemSelected(int index); public void onItemSelected(String item); } public interface OnItemClicked { public void onItemClicked(int index); } private static final class Marquee extends Handler { // TODO: Add an option to configure this private static final float MARQUEE_DELTA_MAX = 0.07f; private static final int MARQUEE_DELAY = 1200; private static final int MARQUEE_RESTART_DELAY = 1200; private static final int MARQUEE_RESOLUTION = 1000 / 30; private static final int MARQUEE_PIXELS_PER_SECOND = 30; private static final byte MARQUEE_STOPPED = 0x0; private static final byte MARQUEE_STARTING = 0x1; private static final byte MARQUEE_RUNNING = 0x2; private static final int MESSAGE_START = 0x1; private static final int MESSAGE_TICK = 0x2; private static final int MESSAGE_RESTART = 0x3; private final WeakReference<HorizontalPicker> mView; private final WeakReference<Layout> mLayout; private byte mStatus = MARQUEE_STOPPED; private final float mScrollUnit; private float mMaxScroll; private float mMaxFadeScroll; private float mGhostStart; private float mGhostOffset; private float mFadeStop; private int mRepeatLimit; private float mScroll; private boolean mRtl; Marquee(HorizontalPicker v, Layout l, boolean rtl) { final float density = v.getContext().getResources().getDisplayMetrics().density; float scrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / MARQUEE_RESOLUTION; if (rtl) { mScrollUnit = -scrollUnit; } else { mScrollUnit = scrollUnit; } mView = new WeakReference<HorizontalPicker>(v); mLayout = new WeakReference<Layout>(l); mRtl = rtl; } @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_START: mStatus = MARQUEE_RUNNING; tick(); break; case MESSAGE_TICK: tick(); break; case MESSAGE_RESTART: if (mStatus == MARQUEE_RUNNING) { if (mRepeatLimit >= 0) { mRepeatLimit--; } start(mRepeatLimit); } break; } } void tick() { if (mStatus != MARQUEE_RUNNING) { return; } removeMessages(MESSAGE_TICK); final HorizontalPicker view = mView.get(); final Layout layout = mLayout.get(); if (view != null && layout != null && (view.isFocused() || view.isSelected())) { mScroll += mScrollUnit; if (Math.abs(mScroll) > mMaxScroll) { mScroll = mMaxScroll; if (mRtl) { mScroll *= -1; } sendEmptyMessageDelayed(MESSAGE_RESTART, MARQUEE_RESTART_DELAY); } else { sendEmptyMessageDelayed(MESSAGE_TICK, MARQUEE_RESOLUTION); } view.invalidate(); } } void stop() { mStatus = MARQUEE_STOPPED; removeMessages(MESSAGE_START); removeMessages(MESSAGE_RESTART); removeMessages(MESSAGE_TICK); resetScroll(); } private void resetScroll() { mScroll = 0.0f; final HorizontalPicker view = mView.get(); if (view != null) view.invalidate(); } void start(int repeatLimit) { if (repeatLimit == 0) { stop(); return; } mRepeatLimit = repeatLimit; final HorizontalPicker view = mView.get(); final Layout layout = mLayout.get(); if (view != null && layout != null) { mStatus = MARQUEE_STARTING; mScroll = 0.0f; final int textWidth = view.itemWidth; final float lineWidth = layout.getLineWidth(0); final float gap = textWidth / 3.0f; mGhostStart = lineWidth - textWidth + gap; mMaxScroll = mGhostStart + textWidth; mGhostOffset = lineWidth + gap; mFadeStop = lineWidth + textWidth / 6.0f; mMaxFadeScroll = mGhostStart + lineWidth + lineWidth; if (mRtl) { mGhostOffset *= -1; } view.invalidate(); sendEmptyMessageDelayed(MESSAGE_START, MARQUEE_DELAY); } } float getGhostOffset() { return mGhostOffset; } float getScroll() { return mScroll; } float getMaxFadeScroll() { return mMaxFadeScroll; } boolean shouldDrawLeftFade() { return mScroll <= mFadeStop; } boolean shouldDrawGhost() { return mStatus == MARQUEE_RUNNING && Math.abs(mScroll) > mGhostStart; } boolean isRunning() { return mStatus == MARQUEE_RUNNING; } boolean isStopped() { return mStatus == MARQUEE_STOPPED; } } public static class SavedState extends BaseSavedState { private int mSelItem; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); mSelItem = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(mSelItem); } @Override public String toString() { return "HorizontalPicker.SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " selItem=" + mSelItem + "}"; } @SuppressWarnings("hiding") public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } private static class PickerTouchHelper extends ExploreByTouchHelper { private HorizontalPicker mPicker; public PickerTouchHelper(HorizontalPicker picker) { super(picker); mPicker = picker; } @Override protected int getVirtualViewAt(float x, float y) { float itemWidth = mPicker.itemWidth + mPicker.dividerSize; float position = mPicker.getScrollX() + x - itemWidth * mPicker.sideItems; float item = position / itemWidth; if (item < 0 || item > mPicker.values.length) { return INVALID_ID; } return (int) item; } @Override protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { float itemWidth = mPicker.itemWidth + mPicker.dividerSize; float position = mPicker.getScrollX() - itemWidth * mPicker.sideItems; int first = (int) (position / itemWidth); int items = mPicker.sideItems * 2 + 1; if (position % itemWidth != 0) { // if start next item is starting to appear on screen items++; } if (first < 0) { items += first; first = 0; } else if (first + items > mPicker.values.length) { items = mPicker.values.length - first; } for (int i = 0; i < items; i++) { virtualViewIds.add(first + i); } } @Override protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { event.setContentDescription(mPicker.values[virtualViewId]); } @Override protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfoCompat node) { float itemWidth = mPicker.itemWidth + mPicker.dividerSize; float scrollOffset = mPicker.getScrollX() - itemWidth * mPicker.sideItems; int left = (int) (virtualViewId * itemWidth - scrollOffset); int right = left + mPicker.itemWidth; node.setContentDescription(mPicker.values[virtualViewId]); node.setBoundsInParent(new Rect(left, 0, right, mPicker.getHeight())); node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); } @Override protected boolean onPerformActionForVirtualView(int virtualViewId, int action, Bundle arguments) { return false; } }}自定义MyScaleView
package com.example.yangbin.pickm;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.View;/** * Created by Administrator on 2016/9/2 0002. */public class MyScaleView extends View { private Paint paint; private int mWidth; private int mHeight; public MyScaleView(Context context) { super(context); init(); } public MyScaleView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public MyScaleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private void init() { paint = new Paint(); paint.setColor(Color.parseColor("#7E7E7E")); paint.setAntiAlias(true); paint.setStrokeWidth(2); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mWidth = getWidth(); mHeight = getHeight(); // 画一条线 canvas.drawLine(0f, mHeight, mWidth, mHeight, paint); Path path = new Path(); path.moveTo(mWidth / 2, mHeight - 30);// 此点为多边形的起点 path.lineTo(mWidth / 2 - 20, mHeight); path.lineTo(mWidth / 2 + 20, mHeight); path.close(); // 使这些点构成封闭的多边形 canvas.drawPath(path, paint); } private int dp2px(int value) { float v = getContext().getResources().getDisplayMetrics().density; return (int) (v * value + 0.5f); }}
0 0
- binbinyang-----自定义VIEW.刻度尺.左右滑动选择对应数值
- Android 试题滑动刻度尺--自定义View
- 自定义view-滑动刻度尺计算金额
- 自定义View实例(三)----滑动刻度尺与流式布局
- Android自定义View之刻度尺滑动功能(一)
- 自定义view之刻度尺
- 自定义view--刻度尺rulerview
- (自定义View)左右滑动控件ScrollLayout
- 安卓自定义view之——可滑动时间轴(时间刻度尺)
- Android自定义view 滑动开关 支持左右滑动 适用于listview
- Android 自定义View 实现手势监听,左右滑动,上下滑动
- Android自定义view之(刻度尺view)
- 自定义view---滚动的刻度尺(一)
- 自定义view-----滚动的刻度尺(二)
- 自定义view---滚动的刻度尺(三)
- 自定义view---滚动的刻度尺(四)
- android自定义View之刻度尺MnScaleBar
- 自定义View----点击滑动选择字母列表
- 第7章 Jenkins – Management
- jQuery选择器总结
- CentOS 7下LAMP源码安装(1)防火墙设置
- 【设计模式】—— 中介者模式Mediator
- Android 系统的常用权限
- binbinyang-----自定义VIEW.刻度尺.左右滑动选择对应数值
- pull解析省市xml数据
- SSH在service更新视图VO后,视图会随机自动update到数据库的解决方法
- php DOM 乱码解决
- Eclipse中导入外部jar包——添加lib
- Android系统应用开发(九)屏蔽状态栏下拉
- 第一个linux 驱动
- CSharp Tips:怎样创建COM的实例
- 解决 Tomcat java.lang.OutOfMemoryError: PermGen space