最终效果:
/** * 自定义计时控件 */ public class TimeView extends View { private TextPaint mTextPaint; private Paint mCricPaint; private Paint mArcPaint; /** * 测量的文字(通过文字测量宽度) */ String mContent = "跳过"; /** * 文字的宽度 */ private int mTextLong; /** *内圆的半径 */ private int mInRadius; /** * 外圆的半径 */ private int mOutRadius; /** * 文字的边距 */ private int mTextPadding = 5; /** * 内外圆之间的间隔 */ private int mCriclePadding = 5; /** * 外圆到画布的距离 */ private int mCanvasPadding = 10; private RectF mRectf; private int mDegree = 0; public TimeView(Context context) { super(context); } public TimeView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); // 获取属性数组 TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.TimeView); int inCircleColor = typedArray.getColor(R.styleable.TimeView_inCircleColor, Color.GRAY); int outCircleColor = typedArray.getColor(R.styleable.TimeView_outCircleColor, Color.RED); int textColor = typedArray.getColor(R.styleable.TimeView_textColor, Color.WHITE); int textSize = typedArray.getInteger(R.styleable.TimeView_size, 50); mTextPaint = new TextPaint(); mTextPaint.setTextSize(textSize); mTextPaint.setColor(textColor); mCricPaint = new Paint(); mCricPaint.setFlags(Paint.ANTI_ALIAS_FLAG); mCricPaint.setColor(inCircleColor); mCricPaint.setAlpha(100); mArcPaint = new Paint(); mArcPaint.setFlags(Paint.ANTI_ALIAS_FLAG); mArcPaint.setColor(outCircleColor); mArcPaint.setStyle(Paint.Style.STROKE); mArcPaint.setStrokeWidth(10); mTextLong = (int) mTextPaint.measureText(mContent); mInRadius = (2*mTextPadding + mTextLong)/2; mOutRadius = mInRadius + mCriclePadding; // 内圆到画布的距离 int i = mOutRadius + mCanvasPadding - mInRadius; mRectf = new RectF(i-5, i-5, mInRadius*2+i+5, mInRadius*2+i+5 ); // typedArray对象使用完要进行回收 typedArray.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension((mOutRadius+mCanvasPadding)*2,(mOutRadius+mCanvasPadding)*2); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(mOutRadius+mCanvasPadding,mOutRadius+mCanvasPadding,mInRadius,mCricPaint); // 旋转canvas的坐标系 canvas.save(); canvas.rotate(-90,mOutRadius+mCanvasPadding,mOutRadius+mCanvasPadding); canvas.drawArc(mRectf,0,mDegree,false,mArcPaint); // 恢复坐标系 canvas.restore(); // 文字底部到baseline的距离(正数) float descent = mTextPaint.descent(); // 文字顶部到baseline的距离 (负数) float ascent = mTextPaint.ascent(); int height = canvas.getHeight(); int width = canvas.getWidth(); // Log.i("520","descent:" + descent+""); // Log.i("520","ascent:" + ascent+""); canvas.drawText(mContent,(width/2-mTextLong/2)*1.0f,height/2+(descent-ascent)/2-descent,mTextPaint); } public void setDegree(int degree){ this.mDegree = degree; invalidate(); } @Override public boolean onTouchEvent(MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: setAlpha(0.3f); break; case MotionEvent.ACTION_UP: setAlpha(1.0f); break; default: break; } return true; } }
attrs文件的配置
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="TimeView" > <attr name="inCircleColor" format="color" /> <attr name="outCircleColor" format="color" /> <attr name="textColor" format="color" /> <attr name="size" format="integer" /> </declare-styleable> </resources>
MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private TimeView mTimeView; private Handler mHandler; private int mDegree = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.net_bt).setOnClickListener(this); mTimeView = findViewById(R.id.custom_tv); mHandler = new MyHandler(this); mHandler.post(new Runnable() { @Override public void run() { mDegree += 30; // 使用message保存数据 Message message = mHandler.obtainMessage(0); message.arg1 = mDegree; // 发送消息 mHandler.sendMessage(message); // 递归 if(mDegree>=360){ // 移除这个任务 mHandler.removeCallbacks(this); } mHandler.postDelayed(this,100); } }); } // 使用static切断对MainActivity的引用,将Mainactivity设置为弱引用来取值,防止内存的泄露 static class MyHandler extends Handler{ private WeakReference<MainActivity> main; public MyHandler(MainActivity main){ this.main = new WeakReference<MainActivity>(main); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if(main == null){ return; } MainActivity mainActivity = main.get(); switch(msg.what){ case 0: mainActivity.mTimeView.setDegree(msg.arg1); break; default: break; } } } }