刻度尺選擇控件

在一些項(xiàng)目中經(jīng)常遇到橫向滑行來選擇目標(biāo)值。
類似下面的效果圖

效果圖.png
package view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;

import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.OverScroller;

import com.ldoublem.selectView.R;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by lumingmin on 16/6/7.
 */
public class SelectView extends View {


    private Paint mPaint;//背景刻度畫筆
    private Paint mPaintRed;//中間刻度畫筆

    private Paint mPaintText;//文字畫筆
    private float mTextSize = 0;
    private float mPointX = 0f;
    private float mPointXoff = 0f;
    private int mPadding = dip2px(1);
    private OverScroller scroller;//控制滑動(dòng)
    private VelocityTracker velocityTracker;

    private float mUnit = 50;
    private int mMaxValue = 200;
    private int mMinValue = 150;
    private int mMiddleValue = (mMaxValue + mMinValue) / 2;
    private int mUnitLongLine = 5;
    private boolean isCanvasLine = true;

    private int bgColor = Color.rgb(228, 228, 228);
    private int textColor = Color.rgb(151, 151, 151);
    private int textSelectColor = Color.rgb(151, 151, 151);


    private int minvelocity;

    public SelectView(Context context) {
        this(context, null);
    }

    public SelectView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SelectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        scroller = new OverScroller(context);
        minvelocity = ViewConfiguration.get(getContext())
                .getScaledMinimumFlingVelocity();
        init(attrs);
    }


    private void init(AttributeSet attrs) {
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SelectView);
        if (typedArray != null) {
            isCanvasLine = typedArray.getBoolean(R.styleable.SelectView_isCanvasLine, true);
            mTextSize = typedArray.getDimension(R.styleable.SelectView_textSize, dip2px(12));
            bgColor = typedArray.getColor(R.styleable.SelectView_bgColor, Color.rgb(228, 228, 228));
            textColor = typedArray.getColor(R.styleable.SelectView_textColor, Color.rgb(151, 151, 151));
            mUnit = typedArray.getDimension(R.styleable.SelectView_unitSize, 50.f);
            mUnitLongLine = typedArray.getInteger(R.styleable.SelectView_unitLongLine, 5);
            textSelectColor = typedArray.getColor(R.styleable.SelectView_textSelectColor, Color.rgb(151, 151, 151));

            typedArray.recycle();
        }
        initPaint();
    }


    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(bgColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(dip2px(1));

        mPaintText = new Paint();
        mPaintText.setAntiAlias(true);
        mPaintText.setColor(textColor);
        mPaintText.setTextSize(mTextSize);
        mPaintText.setStyle(Paint.Style.FILL);


        mPaintRed = new Paint();
        mPaintRed.setAntiAlias(true);
        mPaintRed.setColor(Color.RED);
        mPaintRed.setStrokeWidth(dip2px(1) * 3 / 2);
        mPaintRed.setStyle(Paint.Style.STROKE);

    }


    @Override
    protected void onDraw(final Canvas canvas) {
        super.onDraw(canvas);
        canvasBg(canvas);
        canvasLineAndText(canvas);
        canvasRedLine(canvas);


    }


    private void canvasBg(Canvas canvas) {//畫圓角矩形
        RectF rectF = new RectF();
        rectF.top = mPadding;
        rectF.left = mPadding;
        rectF.bottom = getMeasuredHeight() - mPadding;
        rectF.right = getMeasuredWidth() - mPadding;
        canvas.drawRoundRect(rectF, dip2px(2), dip2px(2), mPaint);

    }

    private void canvasRedLine(Canvas canvas) {//中間紅色刻度
        Path pathRed = new Path();
        pathRed.moveTo(getMeasuredWidth() / 2, getMeasuredHeight() - mPadding);
        pathRed.lineTo(getMeasuredWidth() / 2, getMeasuredHeight() * 2 / 3);
        canvas.drawPath(pathRed, mPaintRed);
    }


    private void canvasLineAndText(Canvas canvas) {
        int current = (int) (Math.rint(mPointX / mUnit));
        for (int i = mMinValue; i <= mMaxValue; i++) {
            int space = mMiddleValue - i;
            float x = getMeasuredWidth() / 2 - space * mUnit + mPointX;
            if (x > mPadding && x < getMeasuredWidth() - mPadding) {//判斷x軸在視圖范圍內(nèi)

                float y = getMeasuredHeight() / 2;
                if (i % mUnitLongLine == 0) {//畫長刻度線 默認(rèn)每5格一個(gè)

                    mPaintText.setColor(textColor);
                    if (Math.abs(mMiddleValue - current - i) < (mUnitLongLine / 2 + 1)) {//計(jì)算絕對(duì)值在某一區(qū)間內(nèi)文字顯示高亮
                        mPaintText.setColor(textSelectColor);

                    }
                    String text = listValue.get(i - 1);


                    canvas.drawText(text,
                            x - getFontlength(mPaintText, text) / 2,
                            y,
                            mPaintText);
                    y = getMeasuredHeight() * 2 / 3;

                } else {
                    y = y + y * 2 / 3;
                }
                if (isCanvasLine) {//畫短刻度線
                    canvas.drawLine(x, getMeasuredHeight() - mPadding, x, y, mPaint);

                }


            }
        }
    }


    private boolean isActionUp = false;
    private float mLastX;
//    private boolean startAnim = true;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        float xPosition = event.getX();

        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain();
        }
        velocityTracker.addMovement(event);

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (mScrolleAnim != null)
                    clearAnimation();
                isActionUp = false;
                scroller.forceFinished(true);

                break;
            case MotionEvent.ACTION_MOVE:
                isActionUp = false;
                mPointXoff = xPosition - mLastX;//計(jì)算滑動(dòng)距離
                mPointX = mPointX + mPointXoff;
                postInvalidate();

                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                isActionUp = true;
                countVelocityTracker(event);//控制快速滑動(dòng)
                startAnim();
//                startAnim = true;
                return false;
            default:
                break;
        }

        mLastX = xPosition;
        return true;
    }


    @Override
    public void computeScroll() {

        if (scroller.computeScrollOffset()) {
            float mPointXoff = (scroller.getFinalX() - scroller.getCurrX());
            mPointX = mPointX + mPointXoff*functionSpeed();
            float absmPointX = Math.abs(mPointX);
            float mmPointX = (mMaxValue - mMinValue) * mUnit / 2;
            if (absmPointX < mmPointX) {//在視圖范圍內(nèi)
                startAnim();
            }

        }
        super.computeScroll();
    }

    /**
     * 控制滑動(dòng)速度
     *
     * @return
     */
    private float functionSpeed() {
        return 0.5f;
    }

    private void countVelocityTracker(MotionEvent event) {
        velocityTracker.computeCurrentVelocity(800, 800);
        float xVelocity = velocityTracker.getXVelocity();
        if (Math.abs(xVelocity) > minvelocity) {
            scroller.fling(0, 0, (int) xVelocity, 0, Integer.MIN_VALUE,
                    Integer.MAX_VALUE, 0, 0);
        }
    }

    public int dip2px(float dpValue) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public float getFontlength(Paint paint, String str) {
        return paint.measureText(str);
    }

    public float getFontHeight(Paint paint) {
        Paint.FontMetrics fm = paint.getFontMetrics();
        return fm.descent - fm.ascent;
    }


    private ScrolleAnim mScrolleAnim;

    private class ScrolleAnim extends Animation {

        float fromX = 0f;
        float desX = 0f;

        public ScrolleAnim(float d, float f) {
            fromX = f;
            desX = d;
        }


        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);


            mPointX = fromX + (desX - fromX) * interpolatedTime;//計(jì)算動(dòng)畫每貞滑動(dòng)的距離

            invalidate();


        }
    }


    private void startAnim() {
        float absmPointX = Math.abs(mPointX);
        float mmPointX = (mMaxValue - mMinValue) * mUnit / 2;
        if (absmPointX > mmPointX) {//超出視圖范圍
            if (mPointX > 0) {//最左
                moveToX(mMiddleValue - mMinValue, 300);
            } else {//最右
                moveToX(mMiddleValue - mMaxValue, 300);
            }


        } else {
            int space = (int) (Math.rint(mPointX / mUnit));//四舍五入計(jì)算出往左還是往右滑動(dòng)
            moveToX(space, 200);

        }
    }


    private void moveToX(int distance, int time) {
        if (mScrolleAnim != null)
            clearAnimation();
        mScrolleAnim = new ScrolleAnim((distance * mUnit), mPointX);
        mScrolleAnim.setDuration(time);
        startAnimation(mScrolleAnim);
        if (mOnSelect != null)
            mOnSelect.onSelectItem(listValue.get(mMiddleValue - distance - 1));


    }

    private List<String> listValue = new ArrayList<>();
    private onSelect mOnSelect = null;

    public void showValue(List<String> list, onSelect monSelect) {
        mOnSelect = monSelect;
        listValue.clear();
        listValue.addAll(list);
        mMaxValue = listValue.size();
        mMinValue = 1;
        mMiddleValue = (mMaxValue + mMinValue) / 2;


    }

    public interface onSelect {
        void onSelectItem(String value);
    }


}


效果.gif

下載代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末箕戳,一起剝皮案震驚了整個(gè)濱河市漂羊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌走越,老刑警劉巖耻瑟,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谆构,居然都是意外死亡框都,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門熬尺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人季二,你說我怎么就攤上這事揭措。” “怎么了绊含?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵艺挪,是天一觀的道長。 經(jīng)常有香客問我麻裳,道長,這世上最難降的妖魔是什么妙蔗? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任疆瑰,我火速辦了婚禮,結(jié)果婚禮上穆役,老公的妹妹穿的比我還像新娘寸五。我一直安慰自己梳杏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布十性。 她就那樣靜靜地躺著劲适,像睡著了一般。 火紅的嫁衣襯著肌膚如雪霞势。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天支示,我揣著相機(jī)與錄音,去河邊找鬼。 笑死攒庵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浓冒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼闲擦,長吁一口氣:“原來是場噩夢啊……” “哼场梆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起寞忿,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤顶岸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后辖佣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杯拐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年藕施,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裳食。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诲祸,死狀恐怖浊吏,靈堂內(nèi)的尸體忽然破棺而出救氯,到底是詐尸還是另有隱情,我是刑警寧澤墩衙,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布甲抖,位于F島的核電站,受9級(jí)特大地震影響准谚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柱衔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一唆铐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧或链,春花似錦、人聲如沸澳盐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筛婉。三九已至,卻和暖如春爽撒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背硕勿。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留扼褪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓话浇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親幔崖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,133評(píng)論 25 707
  • 2016.10.22王鵬《卓越培訓(xùn)師的設(shè)計(jì)秘籍》工作坊總結(jié):今天至順義航港之家大酒店參加這個(gè)叫“中國培訓(xùn)周:新精英...
    prana閱讀 103評(píng)論 0 0
  • 七絕·詩評(píng)4(振波浪清二讀“白樂天”) 樂天有幸慰英靈刻伊,千載知音幾度惺; 一曲琵琶悲世事捶箱,相憐寄語賦詩銘智什! 七絕 ...
    補(bǔ)缺樓丨胡德棒閱讀 430評(píng)論 0 3
  • 我就是大女子主義啊,我不僅是大女子主義尘分,我還直男審美,那你想我是個(gè)男的培愁、還是女的? 女人是移動(dòng)的atm? 1.天吶...
    霞_8b8c閱讀 235評(píng)論 0 0
  • 《十月的天空》根據(jù)美國一個(gè)航天科學(xué)家真人真事改編拍攝梨撞,上個(gè)世紀(jì)五十年代雹洗,主人公Homer是煤林鎮(zhèn)煤礦負(fù)責(zé)人的兒子卧波,...
    小蘋果搞事情閱讀 202評(píng)論 0 0