android 画布控件,Android Canvas控件View控件绘制

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

3dd93ca8d137664da878ba2ea9527087.gif

d8298d1c943f6c694eee56e9d38af4fa.gif

3267ac16e3690737e8041b55732296ed.gif

f61174b9d15bda23bd5a3fa18965d0b9.gif

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

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

acf685d56502db3109623912ed86be64.gif

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

{

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 

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 (point.x 

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();

}

}

我们需要自定义一些属性

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

A

B

C

D

然后是布局文件(片段)

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,不过已经被规避了

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

{

canvas.drawLine(paddingLeft + contentWidth * i / mDivideNumber, paddingTop + gapPadding, paddingLeft + contentWidth * i / mDivideNumber, paddingTop + contentHeight-gapPadding,mSolidPaint);

}

3.速度计算

//减速算法

int speed = Math.abs((mTargetZone-1)*(contentWidth/mDivideNumber)-point.x)/mSpeed;

if (point.x < (mTargetZone - 1) * (contentWidth / mDivideNumber)) {

point.x+=speed;

} else {

point.x-=speed;

}

4.关于绘制,我们得知道,某些ViewGroup不会调用onDraw,因此我们需要设置

setWillNotDraw(false);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值