番茄时钟工作法,有没有人认识这小不点的,哈哈,我来给大家解释下什么叫番茄时钟工作法。不过一看就知道是和番茄有关的,什么,你不信,哈哈,我也不信,告诉大家吧!简单来说嘛就是一套时间管理方法,选择一个一个你待完成的任务,例如打飞机。。。(嘘嘘嘘。。。),将番茄时钟,也就是一个计时器而已,设为25分钟,专注打飞机,专注打飞机。。。(打飞机这活中途不能停哦)直到番茄时钟响起就ok了。就这么简单。
喂喂喂,跑题啦!这次我们说的是圆弧按钮哦,也就图1-1这叼样。好,那咋们就进入正题,再说多一句,这番茄时钟工作法挺有用的,对于小编我这种定力不足的人来说,哈哈,犹如看着打码的片子突然变无码了,哈哈哈哈。。。
咋们先来分析下。。。
咋一看去这东西是不是一个圆环呢,啊啊啊,五环,你比四环多一环
,哈哈,小岳岳的歌终于被我用上了。既然是一个圆环,那肯定是由一个外圆和一个内圆组成的吧,中间呢,就是一个类似textView的东西吗?好,有这思路,那我们可以这样弄,先画两个半径不一的圆,再重叠在一起,(可以通过ImageView来弄),让后添加一个textView就大功告成了,这就是自定义控件中通过组合已有的控件来达到自定义控件的效果,这种方法比较简单,哈哈,都说程序员追求简单,但这也太没技术含量了,咱也是想装B的人,好下面就介绍点有技术含量的东西。
这东西所包含的元素,什么外圆,内圆,文字,咱们统统通过android的canvas画出来,哈哈,够逼格没,不过喜欢装逼的人,得先承受得起挫折,鄙视。。。
好,开始了!
咱们把上面一整块东西就看成个变了模样的Button,本来是眉清目秀的小姑娘,要变得如此惊艳,那得彻头彻尾地变啊,哈哈,那咱就自定义一个Button嘛,想她变成哪个美女据变成哪个美女,嘻嘻嘻。。。什么,自定义控件,搞啥子勒,别慌别慌,这东西入门是相当容易的,好,咱就开始入门自定义控件。
什么是自定义控件呢?
根据我的经验,虽然我没啥经验哈,这东西还是挺有用的,你要在手机上弄一些酷炫叼炸天的控件时,那大部分得用到自定义控件的,这东西呢,说复杂,那可以复杂到你头痛,复杂到你要去乖乖地拿起大学的高数,线性代数好好学;说简单呢,就简单的继承、重写,照着模板来就好,还是能弄个不错的东西出来的,拿来骗骗学弟学妹也是可以的哦!
控件的简单继承关系
由上图可知,所有的控件都是继承自View 的,也就是说,View 是鼻祖,对于自定义控件来说,继承的祖先越大,灵活性就越好,越能创造出酷炫叼炸天的控件,但同时,难度也会有所增加,这次咱们就继承View ,然后开始酷炫叼炸天之旅行。
自定义控件的三种形式
- 继承已有的控件来实现自定义控件;
- 通过继承一个布局文件来实现自定义控件;
- 通过继承View类来实现自定义控件。
- 新建CircleButton并继承View,并添加三个构造方法,重写OnDraw() 方法:
/*构造方法*/
public CircleButton(Context context) {
this(context, null);
}
public CircleButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs);//初始化界面,后面有具体实现
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
- 既然是自定义控件,那归根到底,它还是一个控件嘛,和Button、TextView 可都是亲兄弟嘛,兄弟间必然是有相似的特征的,例如属性,点击事件。。。那下面咱们就来自定义属性先。咱们最终做出来的结果就是下图这逼:
那这里包括哪些自定义属性呢?一个是:中间文字的颜色(textColor)、文字的大小(textSize)、文字的内容(text);另一个是:圆环的颜色(backgroundColor)。
好,那咱们就自定义这些属性,该如何定义呢?别急,首先在res/values 下新建atts.xml 文件(前提是该目录下不存在该文件),然后就开始自定义上面的属性了
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--name:属性的名字 format:该属性可以拥有的数据类型-->
<declare-styleable name="CircleButton">
<attr name="backgroundColor" format="color"/>
<attr name="textSize" format="dimension"/>
<attr name="text" format="string"/>
<attr name="textColor" format="color"/>
</declare-styleable>
</resources>
既然定义好了属性,那就得用他们,接下来就是获取自定义属性并赋值,在这里可以通过上下文的obtainStyledAttributes() 方法获取 :
private int backgroundColor;
private float textSize;
private String text;
private int textColor;
......
......
......
/*获取自定义属性并赋值*/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleButton);
backgroundColor = typedArray.getColor(R.styleable.CircleButton_backgroundColor, Color.RED);
textColor = typedArray.getColor(R.styleable.CircleButton_textColor, Color.WHITE);
textSize = typedArray.getDimension(R.styleable.CircleButton_textSize, 80);
text = typedArray.getString(R.styleable.CircleButton_text);
这里需要注意的是通过R.styleable.xxx 来找到属性的,因为在定义属性的时候就声明了
<declare-styleable name="CircleButton">
在获取完后,可以通过typedArray.recycle();
进行回收,优化性能。
- 定义完属性又获取到属性了,那下面就是去应用这些属性了。好,那咱们就先画圆,这得在OnDraw() 内画了,开始展示泡妞的功力了。
既然要画画,那是不是得需要画笔和画纸啊,总不能在哪淫想嘛,那样妹子早就跟别人跑了,这画笔和画纸就是传说中的Paint 和Canvas 了。好,咱们就拿来一根画笔Paint mPaint = new Paint()
,先挑个颜色先mPaint.setColor(backgroundColor)
,为了画出细节,咱们需要挑一个笔尖比较细的画笔mPaint.setStrokeWidth(4)
,这画笔还分风格的,mPaint.setStyle(Paint.Style.STROKE)
,好了,画笔搞好了,接下来就是在画纸上画画了canvas.drawArc(10, 10, getWidth()-10, getHeight()-10, -90, -360+mAngle, true, mPaint)
, OK,打工搞成,详细代码在后面会给出。
这样就把圆给画出来了,下面就是画中间的文字了,同理,需要先配置好画笔,然后再在画纸上画画,不过这里需要注意的是,我们的先确认文字的起始点和长度,这样才能画出好看的文字可通过以下代码获取:
mPaint.getTextBounds(text, 0, text.length(), mRect);
int textWidth = mRect.width();
int textHeight = mRect.height();
接下来就是在画纸上画画
canvas.drawText(text, (getWidth()-textWidth) / 2, (getHeight()+textHeight) / 2, mPaint);
好,到这里为止,已经把一个静态的控件构建好了,接下来就可以在布局中调用了,咱们在acytivity_main.xml 调用吧:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:circleBtn="http://schemas.android.com/apk/res-auto"
android:background="@color/colorPrimary"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.nlte.test_diyview.MainActivity">
<com.nlte.test_diyview.CircleButton
android:id="@+id/btn_circle_gray"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="200dp"
circleBtn:backgroundColor="#b0adad"
circleBtn:textSize="30dp"
circleBtn:textColor="@android:color/holo_red_light" />
<com.nlte.test_diyview.CircleButton
android:id="@+id/btn_circle_red"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="200dp"
circleBtn:backgroundColor="#ff0202"
circleBtn:text="20"
circleBtn:textSize="30dp"
circleBtn:textColor="@android:color/holo_red_light" />
</FrameLayout>
为了后面的方便,在这里我们用FrameLayout, 并在里面添加的颜色不同的两个CircleButton控件,在这里需要注意的是,引用自定义控件是需要添加下面代码:xmlns:circleBtn="http://schemas.android.com/apk/res-auto"
, circleBtn 可以自由定义。
到这里可以说是大功告成了,但这只是一个静态的就控件,没有交互能力,既然如此,那我们还不如用张图片代替就ok了,还高搞这么麻烦干嘛,别急别急,咱们接下来开始让他活起来,简单粗暴点,给他添加一个点击事件,当点击按钮一次,就让里面的数字减一。
好的,接下来开始了!。。。
简单够粗暴,咱们可以直接在CircleButton添加点击事件,并将里面的数字减一,然后再重新刷新视图(修改视图时,一般需要刷新视图),代码如下(在initView()方法内添加):
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
number--;
//刷新视图
invalidate();
}
});
但咱们不能这样子做,咱们是懒人,为了下次在其他地方也能够用这样一个控件,并根据需要来进行不同的时间处理,咱们可以通过接口回调的方法来实现调用者对控件事件的出处理,什么,接口回调,不懂啊,哈哈,悄悄告诉你,我也是半懂半懂,也是套着模板来的:
private CirclebuttonClickListener mListener;
public interface CirclebuttonClickListener{
void circleButtonClick();
}
public void setOnCirclebuttonClickListener(CirclebuttonClickListener listener){
mListener = listener;
}
- 首先定义一个接口 interface CirclebuttonClickListener
- 创建接口变量 CirclebuttonClickListener mListener
- 暴露一个方法给调用者来注册接口回调,通过接口来获得回调者对接口方法的实现
然后刚才的代码就可以改成:
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.circleButtonClick();
}
});
是不是没那么复杂呢?
接下来就就交给别人处理了,自己的任务完成了,(听着咋那么像卖身呢)
好,别人该如何用呢,其实和普通控件添加事件一样,该怎么弄就怎么弄:
private CircleButton mCircleButtonRed;
mCircleButtonRed = (CircleButton) findViewById(R.id.btn_circle_red);
mCircleButtonRed.setOnCirclebuttonClickListener(new CircleButton.CirclebuttonClickListener() {
@Override
public void circleButtonClick() {
//事件处理
}
});
ok,大功告成,l来个效果图
接下来就是完整的代码
MainActivity.java
package com.nlte.test_diyview;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private CircleButton mCircleButtonGray;
private CircleButton mCircleButtonRed;
private static int number = 69;
private float angle = 0;
private float EachAngle = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCircleButtonRed = (CircleButton) findViewById(R.id.btn_circle_red);
mCircleButtonRed.setText(number+"");
//必须要将360*1.0 / number转为float或者double,这样可以避免因为取整的问题而导致没有完全平分360度
EachAngle = Float.parseFloat(String.valueOf(360*1.0 / number));
mCircleButtonRed.setOnCirclebuttonClickListener(new CircleButton.CirclebuttonClickListener() {
@Override
public void circleButtonClick() {
mCircleButtonRed.setText(--number+"");
angle += EachAngle;
System.out.println(angle);
mCircleButtonRed.setAngle(angle);
}
});
}
}
CircleButton
package com.nlte.test_diyview;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
/**
* Founction:
* NLTE
* 2016/5/15 0015
*/
public class CircleButton extends View {
private Paint mPaint;
private Rect mRect;
private float mAngle;//弧的角度
private int backgroundColor;
private float textSize;
private String text;
private int textColor;
/*通过接口回调的方式实现控件的点击事件添加和处理
* 步骤:
* 1.首先定义一个接口 interface CirclebuttonClickListener
* 2.创建接口变量 CirclebuttonClickListener mListener
* 3.暴露一个方法给调用者来注册接口回调,通过接口来获得回调者对接口方法的实现
* */
private CirclebuttonClickListener mListener;
public interface CirclebuttonClickListener{
void circleButtonClick();
}
public void setOnCirclebuttonClickListener(CirclebuttonClickListener listener){
mListener = listener;
}
/*构造方法*/
public CircleButton(Context context) {
this(context, null);
}
public CircleButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs);
}
/**
* Founction:Init the View
* NLTE
* 2016/5/15
* @param context
* @param attrs
*/
private void initView(Context context, AttributeSet attrs) {
mPaint = new Paint();
//给Paint加上抗锯齿标志
mPaint.setAntiAlias(true);
mRect = new Rect();
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//方法一:
/* number--;
//刷新视图
invalidate();*/
//方法二:
mListener.circleButtonClick();
}
});
/*获取自定义属性并赋值*/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleButton);
backgroundColor = typedArray.getColor(R.styleable.CircleButton_backgroundColor, Color.RED);
textColor = typedArray.getColor(R.styleable.CircleButton_textColor, Color.WHITE);
textSize = typedArray.getDimension(R.styleable.CircleButton_textSize, 80);
text = typedArray.getString(R.styleable.CircleButton_text);
if (text == null){
text = "";
}
//回收,避免浪费
typedArray.recycle();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画圆
mPaint.setColor(backgroundColor);
mPaint.setStrokeWidth(4); //设置圆弧的宽度
mPaint.setStyle(Paint.Style.STROKE); //设置圆弧
// canvas.drawCircle(getWidth()/2, getHeight()/2, getWidth()/2-15, mPaint);
/*
* 第一 二个参数:暂且理解为圆的外切正方形左上角的坐标
* 第三 四个参数:暂且理解为圆的外切正方形右下角的坐标
* 第五 六个参数:绘制的起始点和终点
* 第七个参数:是否绘制扇形
* 第八个参数:画笔
* */
canvas.drawArc(10, 10, getWidth()-10, getHeight()-10, -90, -360+mAngle, true, mPaint);
//中间有一个白色的数字 mRect是数字四周的边距
mPaint.setColor(textColor);
mPaint.setTextSize(textSize);
mPaint.setTypeface(Typeface.DEFAULT);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(0.1f);
mPaint.getTextBounds(text, 0, text.length(), mRect);
int textWidth = mRect.width();
int textHeight = mRect.height();
canvas.drawText(text, (getWidth()-textWidth) / 2, (getHeight()+textHeight) / 2, mPaint);
}
public int getBackgroundColor() {
return backgroundColor;
}
@Override
public void setBackgroundColor(int backgroundColor) {
invalidate();
this.backgroundColor = backgroundColor;
}
public float getTextSize() {
return textSize;
}
public void setTextSize(float textSize) {
invalidate();
this.textSize = textSize;
}
public String getText() {
return text;
}
public void setText(String text) {
invalidate();
this.text = text;
}
public int getTextColor() {
return textColor;
}
public void setTextColor(int textColor) {
invalidate();
this.textColor = textColor;
}
public void setAngle(float angle) {
mAngle = angle;
invalidate();
}
}
atts.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--name:属性的名字 format:该属性可以拥有的数据类型-->
<declare-styleable name="CircleButton">
<attr name="backgroundColor" format="color"/>
<attr name="textSize" format="dimension"/>
<attr name="text" format="string"/>
<attr name="textColor" format="color"/>
</declare-styleable>
</resources>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:circleBtn="http://schemas.android.com/apk/res-auto"
android:background="@color/colorPrimary"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.nlte.test_diyview.MainActivity">
<com.nlte.test_diyview.CircleButton
android:id="@+id/btn_circle_gray"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="200dp"
circleBtn:backgroundColor="#b0adad"
circleBtn:textSize="30dp"
circleBtn:textColor="@android:color/holo_red_light" />
<com.nlte.test_diyview.CircleButton
android:id="@+id/btn_circle_red"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="200dp"
circleBtn:backgroundColor="#ff0202"
circleBtn:text="20"
circleBtn:textSize="30dp"
circleBtn:textColor="@android:color/holo_red_light" />
</FrameLayout>