自定义View实现点值选择效果(二)

来源:互联网 发布:新时代证券交易软件 编辑:程序博客网 时间:2024/05/14 19:25

在前几天写的博客中,通过使用自定义View画了一个可以实现点值选择的seekBar,但是在实际使用中还是会有一点问题,在点击处于连个值之间时,如何进行选择等。本篇博客对上次的自定义View进行了重新编码,最新的效果如下:
这里写图片描述
这里写图片描述

只是对上次自定义的View进行了一些细节方面的优化,在这里就不详细叙述了,看一下具体实现代码:

import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;/** * Created by Administrator on 2017/8/27 0027. */public class LevelSeekBar extends View {    private int mTextSize = 12;    private String[] labels = new String[]{"L1","L2","L3","L4","L5"};    private float mPrewidth;    private int mWidth;    private int mHeight;    private int mDotSize;    private Paint mPaint;    private int mThumbnailSize;    private int count = 4;    private Bitmap mBasicLine;    private int mLine_position_y;    private int mLine_position_x;    private int mDensity = 2;    private float progress = 50;    private float mMaxProgress = 100.0f;    private float mThumbnail_position_x;    private int mLineWidth;    private int mLineStroke = 4;    private OnLevelSeekBarChangeListener mListener;    private boolean mMustBeSelected = true;    private int mPreProgress;    public interface  OnLevelSeekBarChangeListener{        void onStartChanged(int progress);        void onProgressChanged(int process);        void onEndProgressChanged(int progress);    }    public LevelSeekBar(Context context) {        super(context);        init();    }    public LevelSeekBar(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    public LevelSeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    public LevelSeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        init();    }    private void init(){        mTextSize = 12*mDensity;        mDotSize = 8*mDensity;        mThumbnailSize = 16*mDensity;        mPaint = new Paint();        mPaint.setAntiAlias(true);        mPaint.setDither(true);        mPaint.setFilterBitmap(true);        mPaint.setColor(Color.RED);        count = labels.length-1;        mLine_position_x = mThumbnailSize;        mLineStroke = 4*mDensity;        mPreProgress = (int)(mMaxProgress/count);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        mWidth = MeasureSpec.getSize(widthMeasureSpec);        int height = MeasureSpec.getSize(heightMeasureSpec);        mHeight = height>calculateHeight()?height:calculateHeight();        mPrewidth = (mWidth-2*mThumbnailSize)/count;        mLine_position_y = mHeight-(mThumbnailSize);        mLineWidth = mWidth-mThumbnailSize*2;        mBasicLine = getBasicLine(mWidth,mHeight);        setMeasuredDimension(mWidth,mHeight);    }    private int calculateHeight(){        int height = mThumbnailSize*2+mTextSize+15;        return height;    }    @Override    protected void onDraw(Canvas canvas) {        mBasicLine = getBasicLine(mWidth,mHeight);        canvas.drawBitmap(mBasicLine,0,0,mPaint);        mThumbnail_position_x = (progress/mMaxProgress)*mLineWidth+mThumbnailSize;        canvas.drawCircle(mThumbnail_position_x,mLine_position_y,mThumbnailSize,mPaint);    }    private Bitmap getBasicLine(int width,int height){        Bitmap b = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);        Canvas c = new Canvas(b);        Paint p = new Paint();        p.setColor(Color.GRAY);        p.setStrokeWidth(mLineStroke);        p.setTextSize(mTextSize);        p.setFakeBoldText(true);        c.drawLine(mLine_position_x,mLine_position_y,mWidth-mLine_position_x,mLine_position_y,p);        for(int i = 0;i<labels.length;i++){            c.drawCircle(mLine_position_x+mPrewidth*i,mLine_position_y,mDotSize,p);        }        for(int i = 0;i<labels.length;i++){            Rect r = new Rect();            p.getTextBounds(labels[i],0,labels[i].length(),r);            float mPos = mLine_position_x+mPrewidth*i;            Log.i("zyq","progress :1111111 = "+progress);            if(Math.abs(progress - mPreProgress*i)<1){                p.setColor(Color.RED);            }else{                p.setColor(Color.GRAY);            }            c.drawText(labels[i],mPos-r.width()/2,mHeight-(2*mThumbnailSize+15),p);        }        p.setColor(Color.GRAY);        return b;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        float x ;        float dest_x;        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                x = event.getX()-getPaddingLeft()-mThumbnailSize;                if(mMustBeSelected){                    dest_x = ((int)((x+mPrewidth/2)/mPrewidth))*mPrewidth;                    x = dest_x;                }                progress = ((x/mLineWidth)*mMaxProgress);                invalidate();                if(mListener != null){                    mListener.onStartChanged((int)progress);                }                break;            case MotionEvent.ACTION_MOVE:                x = event.getX()-getPaddingLeft()-mThumbnailSize;                progress = ((x/mLineWidth)*mMaxProgress);                invalidate();                if(mListener != null){                    mListener.onProgressChanged((int)progress);                }                break;            case MotionEvent.ACTION_UP:                x = event.getX()-getPaddingLeft()-mThumbnailSize;                if(mMustBeSelected){                    dest_x = ((int)((x+mPrewidth/2)/mPrewidth))*mPrewidth;                    x = dest_x;                }                progress = ((x/mLineWidth)*mMaxProgress);                invalidate();                if(mListener != null){                    mListener.onEndProgressChanged((int)progress);                }                break;        }        return true;    }    public void setLevelSeekBarChangeListener(OnLevelSeekBarChangeListener l){        mListener = l;    }    public void setCanSelected(boolean selectable){        mMustBeSelected = selectable;        if(mMustBeSelected){            float x =  (progress*mLineWidth/mMaxProgress);            float des_x = ((int)((x+mPrewidth/2)/mPrewidth))*mPrewidth;            progress = ((des_x/mLineWidth)*mMaxProgress);            Log.i("zyq","progress = "+progress);        }        invalidate();    }    public void setLabels(String[] infos){        labels = infos;        init();        mPrewidth = (mWidth-2*mThumbnailSize)/count;        if(mMustBeSelected){            float x =  (progress*mLineWidth/mMaxProgress);            float des_x = ((int)((x+mPrewidth/2)/mPrewidth))*mPrewidth;            progress = ((des_x/mLineWidth)*mMaxProgress);            Log.i("zyq","progress = "+progress);        }        invalidate();    }    public boolean getSelectable() {        return mMustBeSelected;    }}

在这里有一个地方需要注意下,在刚开始的时候,labels中的字符串数量为5,可以把seekBar的进度条分为四份,此时得到的每段长度极有可能是整数,在计算过程中,即使出现数据精度的丢失,那么丢失的数据在UI上也体现的不怎么明显,但是一旦要把seekBar分成奇数份,数据就会出现丢失严重的情况,在UI上反应就是Thumbnail绘制的位置不准确,特别是在点值选择的情况下,Thumbnail无法在准确的位置绘制,所以在某些变量的类型使用时需要考虑到底是使用int类型还是float类型。

其他的就没有什么好说的了,关于这个自定义的View还有很多需要优化的地方,在这里大神就不要喷我了,有兴趣的朋友可以关注我一下,有什么问题大家可以相互讨论,谢谢查阅本篇博客!!!