Android Canvas控件绘制&自定义View

2015年收尾制作了如下几种控件

Android Canvas控件绘制&自定义View_第1张图片Android Canvas控件绘制&自定义View_第2张图片Android Canvas控件绘制&自定义View_第3张图片

Android Canvas控件绘制&自定义View_第4张图片Android Canvas控件绘制&自定义View_第5张图片Android Canvas控件绘制&自定义View_第6张图片

Android Canvas控件绘制&自定义View_第7张图片Android Canvas控件绘制&自定义View_第8张图片


其中2篇代码被小伙伴放到了CSDN上

http://blog.csdn.net/superloveboy/article/details/49612255

http://blog.csdn.net/superloveboy/article/details/49612877

当然,以上代码由于硬盘格式化了,全没了,看下面的吧

Android Canvas控件绘制&自定义View_第9张图片

public class TabBarView extends View implements  Runnable {
    //画笔
    private Paint mSolidPaint;
    //尺寸
    private float mHeight;
    private float mWidth;
    //中间竖线与边框间隙
    private int gapPadding = 0;
    //平分量
    private int mDivideNumber = 1;
    //边框大小
    private final float mBorderSize = 1.5f;
    //避免重复绘制Bitmap,短暂保存底色bitmap
    private Bitmap srcRoundBitmap;
    //图片混合模式
    private PorterDuffXfermode mPorterDuffXfermode;
    private Point point;
    //内容区域大小
    private int contentWidth;
    private int contentHeight;
    //欢动到的目标区域
    private int mTargetZone;
    //滑动速度
    private int mSpeed;
    //主调颜色
    private int primaryColor;
    //默认字体颜色
    private int textColor;
    //焦点字体颜色
    private int selectedTextColor;
    //item
    private CharSequence[] mStringItems;
    //字体大小
    private float textSize;
    //是否处于滑动
    private boolean isSliding;

    public TabBarView(Context context) {
        super(context);
        init(null, 0);
    }
    public TabBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }
    public TabBarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }
    private void init(AttributeSet attrs, int defStyle) {
        // Load attributes
        final TypedArray a = getContext().obtainStyledAttributes(
                attrs, R.styleable.TabBarView, defStyle, 0);

        //参数值越大,速度越大,速度指数越小
        mSpeed = Math.max(10 - Math.max(a.getInt(R.styleable.TabBarView_speed, 6), 6),1);

        mStringItems = a.getTextArray(R.styleable.TabBarView_tabEntries);
        primaryColor = a.getColor(R.styleable.TabBarView_primaryColor, 0xFF4081);
        textColor = a.getColor(R.styleable.TabBarView_textColor, primaryColor);
        selectedTextColor = a.getColor(R.styleable.TabBarView_selectedTextColor, 0xffffff);
        textSize = a.getDimensionPixelSize(R.styleable.TabBarView_textSize, 30);

        if(mStringItems!=null && mStringItems.length>0)
        {
            mDivideNumber = mStringItems.length;
        }

        a.recycle();

        mSolidPaint = new Paint();
        mSolidPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
        point = new Point(0,0);
        mTargetZone = 1;

        invalidateTextPaintAndMeasurements();

    }

    private void invalidateTextPaintAndMeasurements() {

        mSolidPaint.setColor(primaryColor);
        mSolidPaint.setStrokeWidth(mBorderSize);
        mSolidPaint.setTextSize(textSize);
        mSolidPaint.setStyle(Paint.Style.STROKE);
        mSolidPaint.setXfermode(null);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        contentWidth = getWidth() - paddingLeft - paddingRight;
        contentHeight = getHeight() - paddingTop - paddingBottom;
        int minContentSize = Math.min(contentWidth, contentHeight);

        RectF rectRound = new RectF(paddingLeft,paddingTop,paddingLeft+contentWidth,paddingTop+contentHeight);
        canvas.drawRoundRect(rectRound, minContentSize / 2, minContentSize / 2, mSolidPaint);
        for (int i=1;i<mDivideNumber;i++)
        {
            canvas.drawLine(paddingLeft + contentWidth * i / mDivideNumber, paddingTop + gapPadding, paddingLeft + contentWidth * i / mDivideNumber, paddingTop + contentHeight-gapPadding,mSolidPaint);

        }

        if(srcRoundBitmap==null)
        {
            srcRoundBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
            Canvas srcCanvas = new Canvas(srcRoundBitmap);
            mSolidPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            srcCanvas.drawRoundRect(rectRound, minContentSize / 2, minContentSize / 2, mSolidPaint);
        }

        Bitmap dstBitmap = Bitmap.createBitmap(contentWidth/mDivideNumber,contentHeight, Bitmap.Config.ARGB_8888);
        Canvas dstCanvas = new Canvas(dstBitmap);
        dstCanvas.drawColor(Color.YELLOW);

        Bitmap resultBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas  resultCanvas = new Canvas(resultBitmap);

        resultCanvas.drawBitmap(dstBitmap, paddingLeft + point.x, paddingTop, mSolidPaint);

        mSolidPaint.setXfermode(mPorterDuffXfermode);
        resultCanvas.drawBitmap(srcRoundBitmap, 0, 0, mSolidPaint);
        canvas.drawBitmap(resultBitmap, 0, 0, null);

        invalidateTextPaintAndMeasurements();

        if(mStringItems!=null) {

            for (int i = 0; i < mStringItems.length; i++) {
                String itemChar = mStringItems[i].toString();
                float textX = (contentWidth / mDivideNumber) * i / 2 + paddingLeft + (contentWidth * (i + 1) / mDivideNumber - mSolidPaint.measureText(itemChar)) / 2;
                float textY = paddingTop + (contentHeight - mSolidPaint.getFontMetrics().bottom - mSolidPaint.getFontMetrics().ascent) / 2;
                int color = mSolidPaint.getColor();
                mSolidPaint.setStyle(Paint.Style.FILL);
                if((i+1)==mTargetZone && !isSliding)
                {
                    mSolidPaint.setColor(selectedTextColor);
                }else{
                    mSolidPaint.setColor(textColor);
                }
                canvas.drawText(itemChar, textX, textY, mSolidPaint);
                mSolidPaint.setColor(color);
                mSolidPaint.setStyle(Paint.Style.STROKE);
            }
        }
        recyleBitmap(resultBitmap);
        recyleBitmap(dstBitmap);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event)
    {

        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                if(checkLocationIsOk(event)&&!isSliding)
                {
                    return true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                return checkLocationIsOk(event);
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_OUTSIDE:
                if(checkLocationIsOk(event)&&!isSliding)
                {
                    float x =  event.getX()-getPaddingLeft();
                    mTargetZone = (int)(x/(contentWidth/mDivideNumber))+1;
                    //规避区域超出范围
                    mTargetZone = Math.min(mTargetZone,mDivideNumber);
                    postToMove();
                }
                break;
        }
        return super.dispatchTouchEvent(event);
    }

    private void postToMove()
    {
        if(point.x==(mTargetZone-1)*(contentWidth/mDivideNumber))
        {
            return;
        }
        postDelayed(this,20);
    }

    /**
     * 检测位置是否可用
     * @param event
     * @return
     */
    private boolean  checkLocationIsOk(MotionEvent event)
    {
        float x = event.getX();
        float y = event.getY();
        if(x-getPaddingLeft()>0&&(getPaddingLeft()+contentWidth-x)>0&&y-getPaddingTop()>0 && (getPaddingTop()+contentHeight-y)>0)
        {
            return true;
        }
        return false;
    }

    private void recyleBitmap(Bitmap bmp)
    {
        if(bmp!=null &&!bmp.isRecycled())
        {
            bmp.recycle();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getHandler().removeCallbacksAndMessages(null);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /**
         * 设置宽度
         */
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
        {
            mWidth = specSize;
        }
        else
        {
            // 由图片决定的宽
            int desireByImg = getPaddingLeft() + getPaddingRight()
                    + 380;
            if (specMode == MeasureSpec.AT_MOST)// wrap_content
            {
                mWidth = Math.min(desireByImg, specSize);
            } else
                mWidth = desireByImg;
        }
        /***
         * 设置高度
         */
        specMode = MeasureSpec.getMode(heightMeasureSpec);
        specSize = MeasureSpec.getSize(heightMeasureSpec);
        if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
        {
            mHeight = specSize;
        } else
        {
            int desire = getPaddingTop() + getPaddingBottom()
                    + 80;

            if (specMode == MeasureSpec.AT_MOST)// wrap_content
            {
                mHeight = Math.min(desire, specSize);
            } else
            {
                mHeight = desire;
            }
        }

        setMeasuredDimension((int) mWidth, (int) mHeight);
    }

    @Override
    public void run() {
        //计算速度,先按照最大速度5变化,如果小于5,则表示该减速停靠
        int speed = Math.abs((mTargetZone-1)*(contentWidth/mDivideNumber)-point.x)/mSpeed;
        if(speed==0)
        {
            //减速算法
            speed = Math.abs((mTargetZone-1)*(contentWidth/mDivideNumber)-point.x)%mSpeed;
        }
        if (point.x < (mTargetZone - 1) * (contentWidth / mDivideNumber)) {
            point.x+=speed;
        } else {
            point.x-=speed;
        }
        invalidate();
        if (point.x == (mTargetZone - 1) * (contentWidth / mDivideNumber))
        {
            isSliding = false;
        }else {
            isSliding = true;
            postDelayed(this, 20);
        }
    }

    public void setSelectedTab(int tabIndex)
    {
        mTargetZone = Math.max(Math.min(mDivideNumber,tabIndex+1),1);
        postToMove();
    }

    public void  setTabItems(CharSequence[] mStringItems)
    {
        this.mStringItems = mStringItems;
        invalidate();
    }
}

我们需要自定义一些属性

<declare-styleable name="TabBarView">

    <attr name="speed" format="integer" />
    <attr name="tabEntries" format="reference"/>
    <attr name="primaryColor" format="color|reference"/>
    <attr name="textSize" format="dimension"/>
    <attr name="textColor" format="color|reference"/>
    <attr name="selectedTextColor" format="color|reference"/>

</declare-styleable>

还有部分需要引用的string-array

<string-array name="tabEntries_array">
    <item>A</item>
    <item>B</item>
    <item>C</item>
    <item>D</item>
</string-array>

然后是布局文件(片段)

<com.yyuap.twt.widgets.TabBarView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:padding="10dp"
    app:speed="4"
    app:tabEntries="@array/tabEntries_array"
    app:primaryColor="@color/colorAccent"
    app:textColor="@color/colorPrimaryDark"
    app:selectedTextColor="@android:color/white"
    />


难点

1.主要是Bitmap的合成

PorterDuffXfermode

2.计算内容区域大小和内容区域的位置,当然在点击区域出现了一个小小的bug,不过已经被规避了

minContentSize = Math.(, );
RectF rectRound = RectF(paddingLeft,paddingTop,paddingLeft+,paddingTop+);
canvas.drawRoundRect(rectRound, minContentSize / , minContentSize / , );
(i=;i<;i++)
{
    canvas.drawLine(paddingLeft + * i / , paddingTop + , paddingLeft + * i / , paddingTop + -,);
}

3.速度计算

speed = Math.((-)*(/)-.)/;
(speed==)
{
    speed = Math.((-)*(/)-.)%;
}
(.< (- ) * (/ )) {
    .+=speed;
} {
    .-=speed;
}


你可能感兴趣的:(Android Canvas控件绘制&自定义View)