2015年收尾制作了如下几种控件
其中2篇代码被小伙伴放到了CSDN上
当然,以上代码由于硬盘格式化了,全没了,看下面的吧
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);