2015年收尾制作了如下几种控件
其中2篇代码被小伙伴放到了CSDN上
http://blog.csdn.net/superloveboy/article/details/49612255
http://blog.csdn.net/superloveboy/article/details/49612877
当然,以上代码由于硬盘格式化了,全没了,看下面的吧
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; }