binbinyang-----自定义VIEW.刻度尺.左右滑动选择对应数值

 很多APP.如今会用到横向刻度尺,或是纵向刻度尺....比如在个人资料填入.或是减肥APP里面...随处可见

今天我带来一个 

先发,我录制的动态GIF效果图

binbinyang-----自定义VIEW.刻度尺.左右滑动选择对应数值_第1张图片

放代码


主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布局



    

    



自定义 

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 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 mView;
        private final WeakReference 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(v);
            mLayout = new WeakReference(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 CREATOR
                = new Creator() {
            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 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);
    }
}


binbinyang-----自定义VIEW.刻度尺.左右滑动选择对应数值_第2张图片

你可能感兴趣的:(android自定义)