在上篇文章中,我向大家介绍了如何通过自定义View一步步画出一个漂亮的圆形时钟。如果你还没看的话,我不建议你接着往下看,因为这篇文章是接着上篇的文章,如果直接看的话可能会不知所云,所以还是建议你先看一下我之前的这篇文章Android自定义控件之圆形时钟。如果看过上篇文章的话,我们知道这个时钟还是有几个问题的。第一,很明显就是时钟只是静态的还不能动。第二,秒针、分针和时针之间没有联系,即分针和时针的位置应该和秒针有关的。

我们先来看看效果,

静态的:

动态的:

首先我们来分析如何让秒针动起来。不知道大家熟不熟悉逐帧动画,不熟悉也没关系,大家小时候上学一定有这样的经历:在一本厚厚的书上,在每一页的同一位置,画有略有不同的图案,然后拨动整本书,之后便会奇迹般的呈现出一幅动画。其实这就是逐帧动画的原理,我们看到的动画是由一幅幅图像组成,之所以我们感觉不出来,是因为这些图像闪的太快啦,就拿前面书本的例子,如果你将书拨动的越快,那么你看到的动画就越流畅,相反,如果速度很慢的话,就会明显看到纸张上的图案。那么可能有人要问了,这个速度到底快到什么程度,我们才能感觉到是一幅动画呢?一般来讲,我们肉眼能分辨的帧数是24帧,什么意思呢?还拿这个例子讲解,如果一秒钟,你一共拨动了24页或者更多,那么你就能看到一幅流畅的动画,完全感觉不到纸张的存在;如果页数不到24页,那么我们的肉眼就能看到一张张纸翻过。我们指针的运动其实也是同样的道理。秒针走完一圈是60秒,而一圈是360度,那么我们可以算出一秒钟,其实就是360度/60秒 = 6度。也就是说,每经过一秒钟,我们将秒针的角度加上6度,然后重新调用onDraw方法重绘一次秒针。这样通过不断的重绘,我们的指针也就动起来了。那么如何准确的控制这一秒呢?这里我们用到了定时器Timer。代码如下:

private float mSecondDegree;//秒针的度数

private Timer mTimer = new Timer();

private TimerTask task = new TimerTask() {

@Override

public void run() {//具体的定时任务逻辑

if (mSecondDegree == 360) {//因为圆一圈为360度,所以走满一圈角度清零

mSecondDegree = 0;

}

mSecondDegree = mSecondDegree + 6;

postInvalidate();

}

};

/**

*开启定时器

*/

public void start() {

mTimer.schedule(task,0,1000);

}

@Override

protected void onDraw(Canvas canvas) {

mPaint.setStrokeWidth(2);

canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 3, mPaint);

mPaint.setStrokeWidth(5);

canvas.drawPoint(getWidth() / 2, getHeight() / 2, mPaint);

mPaint.setStrokeWidth(1);

canvas.translate(getWidth() / 2, getHeight() / 2);

for (int i = 0; i < 360; i++) {

if (i % 30 == 0) {//长刻度

canvas.drawLine(getWidth() / 3 - 25, 0,

getWidth() / 3, 0, mPaint);

} else if (i % 6 == 0) {//中刻度

canvas.drawLine(getWidth() / 3 - 14, 0,

getWidth() / 3, 0, mPaint);

} else {//短刻度

canvas.drawLine(getWidth() / 3 - 9, 0,

getWidth() / 3, 0, mPaint);

}

canvas.rotate(1);

}

canvas.save();

mPaint.setTextSize(25);

mPaint.setStyle(Paint.Style.FILL);

for (int i = 0; i < 12; i++) {

if (i == 0) {

drawNum(canvas, i * 30, 12 + "", mPaint);

} else {

drawNum(canvas, i * 30, i + "", mPaint);

}

}

canvas.restore();

//秒针

canvas.save();

mPaint.setColor(Color.RED);

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeWidth(2);

canvas.rotate(mSecondDegree);

canvas.drawLine(0, 0, 0,

-190, mPaint);

canvas.restore();

//分针

canvas.save();

mPaint.setColor(Color.BLACK);

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeWidth(4);

canvas.rotate(30);

canvas.drawLine(0, 0, 0,

-130, mPaint);

canvas.restore();

//时针

canvas.save();

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeWidth(7);

canvas.rotate(90);

canvas.drawLine(0, 0, 0,

-90, mPaint);

canvas.restore();

在代码中我们看到,我们先创建了一个Timer,又创建了一个定时任务TimerTask,然后重写里面的run()方法,这个run方法中其实就是我们每隔一秒要处理的事情,这里代码也很简单,就是每隔一秒钟,我们就把秒针的度数加上6度,然后调用postInvalidate();调用这个方法就会执行onDraw方法让画布重绘,当然invalidate()也会调用onDraw方法,两者区别就是,invalidate()要在主线程调用,而postInvalidate()在子线程中调用,我们开启了一个定时器,相当于开启了一个子线程,所以这里要用postInvalidate()方法。我们的onDraw方法中代码基本和上篇文章一样,而且讲的也非常详细了,这里就不在赘述了,具体可以戳这里Android自定义控件之圆形时钟。唯一不同的就是在画秒针的地方, 我们多了这句代码: canvas.rotate(mSecondDegree);即在画秒针之前我们让画布旋转了mSecondDegree度,这里的mSecondDegree就是我们在定时任务中计算得来的。最后我们就可以启动这个定时器啦,启动也很简单,只需要调用定时器Timer的schedule方法,这里我们传入三个参数,第一个就是我们的定时任务task,第二个表示启动定时器后多少毫秒开始工作,传入0代表,一调用schedule这个方法就立即开启定时任务;第三个参数就是任务的执行间隔,单位也是毫秒,由于是秒针,所以每秒要重绘一次,这里自然就是1000毫秒啦。好了接下来我们在MainActivity中调用start方法来启动这个定时器就可以了。

public class MainActivity extends AppCompatActivity {

private TimeView time_view;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

time_view = (TimeView)findViewById(R.id.time_view);

time_view.start();

}

}

我们来看一下效果图:

怎么样,秒针动起来了!!是不是有点小兴奋。。虽然这是简单的一小步骤,但确实我们学习知识的一大步,我们从画静态的图形,过渡到能做出动画了。好了秒针是动起来,但别忘了还有分针和时针这两家伙,我们该如何让它们也动起来呢?相信聪明的你一定学会了举一反三了。我们在上面算出,每一秒钟,秒针是要加6度,那么我们只要算出每秒钟,分针和时针要加多少度,问题就迎刃而解了。那么具体怎么算呢?先来看分针,我们知道,分钟走一圈是60分钟吧,而圆的一圈是360度,那么一分钟,其实就是分针走了360度/60分钟 = 6度,而一分钟等于60秒,所以对应一秒钟就是,6度/60秒 = 0.1度/秒,即每隔一秒钟就让分针的度数加0.1度。这样我们就知道了分针每秒要加的度数。接下里看一下时针,同理,时针走一圈是12小时,那么每小时就走360度/12小时,而每小时等于3600秒,所以每秒钟也就是360度/(12*3600秒) = 1/120度/秒。这样,每隔一秒钟,分针和时针要加相应的度数也都已经求出来啦,代码相信你也一定会了,和秒针一样的逻辑,

private TimerTask task = new TimerTask() {

@Override

public void run() {

if (mSecondDegree == 360) {

mSecondDegree = 0;

}

if (mMinDegree == 360) {

mMinDegree = 0;

}

if (mHourDegree == 360) {

mHourDegree = 0;

}

mSecondDegree = mSecondDegree + 6;//秒针

mMinDegree = mMinDegree + 0.1f;//分针

mHourDegree = mHourDegree + 1.0f/240;//时针

postInvalidate();

}

};

在onDraw方法中,画布旋转的具体度数就由定时任务中算出来的度数。

//分针

canvas.save();

mPaint.setColor(Color.BLACK);

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeWidth(4);

canvas.rotate(mMinDegree);//定时任务中算出分针的度数

canvas.drawLine(0, 0, 0,

-130, mPaint);

canvas.restore();

//时针

canvas.save();

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeWidth(7);

canvas.rotate(mHourDegree);//定时任务中算出时针的度数

canvas.drawLine(0, 0, 0,

-90, mPaint);

canvas.restore();

这样我们的三个指针就都可以动起来了,当然分针和时针动的不是很明显,你可以过上一段时间在来看一下。

好了,时钟总算是动起来了,不过还有个问题,就是我们无法给它设置时间。接下来,我们来看看如何给他设置时间。设置时间说白了就是给每个指针的角度设置一个具体值。首先我们再来理一遍关系:秒针一秒钟走6度(一圈60秒共走了360度);分针一钟也是走6度(一圈60分钟走共走了360度);而时针一小时走30度(一圈12小时共走了360度)。所以我们就可以根据具体的时间来求出各指针的角度。比如我们要设置时间:1点30分30秒,那么根据上述关系求时针的角度为1*30 = 30度;分针的角度为30*6 = 180度;秒针的角度为30*6=180度;

自定义TimeView里的代码如下:

public void setTime(int hour, int min, int second) {

mMinDegree = min * 6f;

mHourDegree = hour * 30f;

mSecondDegree = second * 6f;

invalidate();//重绘控件

}

然后我们在MainActivity中调用上面的方法

public class MainActivity extends AppCompatActivity {

private TimeView time_view;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

time_view = (TimeView)findViewById(R.id.time_view);

time_view.setTime(1,30, 30);

time_view.start();

}

}

运行后看一下效果:

额。。是不是看着特变扭,没错细心的你一定看出来了,分针在30分的时候,时钟却还是在1点整,秒针都走了30多秒了,分针确还停在30分钟的位置上。所以我们上面角度计算的还是有点问题,我们知道30分30秒其实就是30.5分钟,而我们计算时仅仅只算了30分钟的角度,少了那0.5分钟。所以我们还是得把传入的秒转换为分钟,即mMinDegree = (min + second * 1.0f/60f) *6f;同理时针的角度和分针秒针都有关,我们得把传入的分和秒也都转换为小时再计算它的角度,即mHourDegree = (hour + min * 1.0f/60f + second * 1.0f/3600f)*30f;

修改后的代码:

public void setTime(int hour, int min, int second) {

if (hour >= 24 || hour < 0 || min >= 60 || min < 0 || second >= 60 || second < 0) {

Toast.makeText(getContext(), "时间不合法",

Toast.LENGTH_SHORT).show();

return;

}

if (hour >= 12) {//这里我们采用24小时制

mIsNight = true;//添加一个变量,用于记录是否为下午。

mHourDegree = (hour + min * 1.0f/60f + second * 1.0f/3600f - 12)*30f;

} else {

mIsNight = false;

mHourDegree = (hour + min * 1.0f/60f + second * 1.0f/3600f )*30f;

}

mMinDegree = (min + second * 1.0f/60f) *6f;

mSecondDegree = second * 6f;

invalidate();

}

代码还是很简洁的,这里我们采用的是24小时制,给时分秒加了边界的判断,然后当传入的小时大于12时,就让它减去12小时计算它的角度,并且我们定义一个变量mIsNight,这个变量用于标志是否为下午,当传入的小时大于12个小时,使他为true,这个变量会在后面获取时钟时间时用到。好了我们再重新运行下代码,效果如下:

这样三个指针的位置就比较合理了。

好了,知道了如何设置时间后我们再来看看如何获取当前时间。其实也很简单,上面我们知道时针走30度时一个小时,也就是3600秒,所以一度就是3600秒/30度 = 120秒。我们就可以根据时针走的度数,来求出一共是多少秒。比如时针正好为90度,也就是整3点钟的位置,那么我们可求出共有3 * 3600秒 = 10800秒。这样我们定义一个记录总秒数的变量mTotalSecond = mHourDegree * 120。具体代码如下

public float getTimeTotalSecond() {

if (mIsNight) {//判断是否为下午,是的话再加12个小时

mTotalSecond = mHourDegree * 120 + 12 * 3600;

return mTotalSecond;

} else {

mTotalSecond = mHourDegree * 120;

return mTotalSecond;

}

}

有了总秒数,时分秒就比较好求了,具体代码如下:

public int getHour() {//获取小时

return (int) (getTimeTotalSecond() / 3600);

}

public int getMin() {//获取分钟

return (int) ((getTimeTotalSecond() - getHour() * 3600) / 60);

}

public int getSecond() {//获取秒钟

return (int) (getTimeTotalSecond() - getHour() * 3600 - getMin() * 60);

}

这样我们的时钟就可以进行设置和获取时间的操作了。有了基本的功能,我们再来看一下样式方面,我们自定义的控件说到底是拿来用的,不同的人有不同的喜好,比如有的人想将时钟边框的颜色设置成黑的,有的人就喜欢红色。所以接下来我们看看,如何在XML布局文件里自由设置样式,比如时钟边框的颜色。首先,我们在values文件夹下新建一个attrs.xml文件,里面的内容为

//自定义属性

我们可以看到,在资源标签下有一个declare-styleable标签,名字可以任意取,这里就叫TimeView。然后在这个标签下有个attr标签,这个标签就是我们自定义的属性,这里就拿边框颜色为例,名字可以任意起,易读就可以了,这里就叫borderColor由于 我们定义的属性和颜色相关,这里的format就是color,format还有很多其他格式,如果是布尔型,那么它就是boolean,如果是长度的话就是dimension,当然还有很多其他格式,大家可以查阅官网,这里就不细讲了。然后我们在初始自定义控件的时候添加如下代码

private void init(Context context, AttributeSet attrs) {

TypedArray ta = context.obtainStyledAttributes(attrs,

R.styleable.TimeView);

borderColor = ta.getColor(R.styleable.TimeView_borderColor,

Color.BLACK);//获取布局文中设置的颜色,默认设置为黑色

ta.recycle();

}

获取颜色后,我们就要在画边框之前将画笔设置成获取到的颜色代码如下:

mPaint.setColor(borderColor);//将画笔颜色设置成获取到的颜色

mPaint.setStrokeWidth(2);

canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 3, mPaint);

mPaint.setColor(Color.BLACK);

接着我们就可以在XML文件中设置自己喜欢的颜色了,完整的布局文件:

android:layout_width="match_parent"

android:layout_height="match_parent"

xmlns:custom="http://schemas.android.com/apk/res-auto"

android:orientation="vertical"

android:background="#fff"

>

android:layout_gravity="center_horizontal"

android:id="@+id/time_view"

custom:borderColor="#ff0000"

android:layout_width="match_parent"

android:layout_height="match_parent"/>

我们要在根布局加上了这么一句话xmlns:custom="http://schemas.android.com/apk/res-auto",只有加上这句话,你才能使用自已之前定义的属性,这里的custom可以是任意的,但必须和下面的要保持一致。好了,看一下效果吧:

可以看到,设置的还是挺成功的,当然你还可以添加其他的属性来丰富你的样式,这里就不在一一演示了。这里我们的宽高是和父布局一样大小,如果你觉得太大,可以让它的宽高变小点,比如我们可以给它的宽高都设置成300dp,我们来看一下效果:

可以看到时钟变小了,我们再将宽高都改为wrap_content试试吧:

。。额,好像不起作用,明明是wrap_content,怎么还是和match_parent的效果一样,这究竟是怎么回事呢?

其实这就牵扯到自定义view的测量,我们先来看看一个控件展示在手机屏幕上的几个过程,或者说是执行哪些方法:

onMeasure-----------告诉系统这个自定义控件多大

onLayou -----------告诉系统这个控件放哪。单独的一个view不需要调用这个方法,主要是针对自定义ViewGroup的,关于自定义ViewGroup,后续会有文章详细讲解,这里就先不讲了。

onDraw -----------告诉系统这个控件展示的内容,这个方法在上一篇Android自定义控件之圆形时钟讲的也是比较详细了,这里就不在赘述了。

所以今天我们就来看看这个onMeasure方法。 我们要想告诉系统这个控件多大,只用在onMeasure方法中调用setMeasuredDimension(int width,int height),将你想要设置的宽高传入就可以了。比如我们想把宽高都设置为300,代码如下:

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

setMeasuredDimension(300,300);

}

这样控件的宽高就是300啦,我们来运行下:

不过,如果这样设置的话,你自己写的是很爽,但你让别人怎么活。。。。。这样设置的话,别人就无法再在XML文件中进行宽高的设置。。因为不管怎么设置,最后都被你指定为300不变。。。要是有人用这个控件的话,估计都会开始怀疑人生了。。。那么该怎么办呢?这个时候MeasureSpec这个类就闪亮登场了。这个类是一个32位的int值,高两位是测量模式,低30位是测量尺寸。测量模式一共有三种:

EXACTLY

当我们在布局文件中指定宽高为具体的值或者指定为match_parent时,系统用的就是这个模式

AT_MOST

当我们在布局文件中,指定宽高为wrap_content时,就是这个模式。

UNSPECIFIED

这个模式比较特殊,就是view想要多大就有多大,一般不怎么用

如果你在自定义view时不重写onMeasure这个方法,那么系统默认只支持EXACTLY这个模式,即你可以在布局文件中,指定控件宽高一个具体的数值,也可以让它match_parent。但是无法识别wrap_content的,要想让wrap_content有效,我们就要重写onMeasure方法,然后给控件指定一个大小。代码如下:

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

setMeasuredDimension(measureWidth(widthMeasureSpec),

measureHeight(heightMeasureSpec));

}

//自己写的测量宽度的方法

private int measureWidth(int measureSpec) {

int result;

int specSize = MeasureSpec.getSize(measureSpec);

int specMode = MeasureSpec.getMode(measureSpec);

if (specMode == MeasureSpec.EXACTLY) {

result = specSize;

} else {

result = 300;

if (specMode == MeasureSpec.AT_MOST) {

result = Math.min(result, specSize);

}

}

return result;

}

可以看到,我们自己写了一个方法,用于测量控件的宽,由于这个控件是圆形的所以宽高是一样的,这里就只贴出测量宽的代码。我们根据系统传入的MeasureSpec类,来获取测量尺寸和测量模式,即得到specSize 和specMode ,如果我们在XML文件中指定控件的具体数值大小,那么获取到的specSize 就等于这个具体的值,如果是match_parent,那么这个值就是父控件的值。然后我们来看一下获取到的specMode ,如果布局文件指定为match_parent,那么specMode 就等于MeasureSpec.EXACTLY,如果是wrap_content,那么就等于MeasureSpec.AT_MOST。接下来就是判断获取的是什么模式,根据不同的模式来返回具体的测量值。如果是EXACTLY那么测量的结果就是specSize ,如果是AT_MOST,那么我们指定一个具体的值,然后和specSize 比较,较小者作为测量的值。这个测量的代码,其实可以作为一个模板,这样onMeasure这个方法其实也没有什么难的地方,以后要重写onMeasure方法时,套用这个模板就行了,然后在AT_MOST时,指定自己需要的大小。

好了最后我们写个Demo来演示下我们的时钟把,首先看一下XML代码:

android:layout_width="match_parent"

android:layout_height="match_parent"

xmlns:custom="http://schemas.android.com/apk/res-auto"

android:orientation="vertical"

android:background="#fff"

>

android:id="@+id/time_view"

android:layout_width="300dp"

android:layout_height="300dp"

android:layout_gravity="center_horizontal"

custom:secondPointerColor="#fff"

custom:borderColor="#f12"

custom:borderWidth="3dp"

custom:maxScaleColor="#fff"

custom:minScaleColor="#fff"

custom:midScaleColor="#fff"

custom:maxScaleLength="14dp"

custom:midScaleLength="10dp"

custom:minScaleLength="7dp"

custom:centerPointRadiu="2dp"

custom:centerPointType="circle"

custom:centerPointColor="#fff"

custom:secondPointerLength="80dp"

custom:minPointerLength="50dp"

custom:hourPointerLength="30dp"

custom:minPointerColor="#fff"

custom:hourPointerColor="#fff"

custom:isSecondGoSmooth="false"

custom:textColor="#fff"

custom:textSize="20sp"

custom:circleBackground="#0af"/>

android:layout_width="match_parent"

android:layout_height="wrap_content">

android:id="@+id/hour"

android:layout_width="0dp"

android:layout_weight="1"

android:layout_height="wrap_content"

android:gravity="center_horizontal"

android:hint="时"/>

android:id="@+id/min"

android:layout_width="0dp"

android:layout_weight="1"

android:layout_height="wrap_content"

android:gravity="center_horizontal"

android:hint="分"/>

android:id="@+id/second"

android:layout_width="0dp"

android:layout_weight="1"

android:layout_height="wrap_content"

android:gravity="center_horizontal"

android:hint="秒"/>

android:layout_margin="10dp"

android:id="@+id/set_time"

android:textColor="#fff"

android:background="@color/colorPrimary"

android:layout_gravity="center_horizontal"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="设置时间"/>

android:layout_margin="10dp"

android:id="@+id/get_time"

android:textColor="#fff"

android:background="@color/colorPrimary"

android:layout_gravity="center_horizontal"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="获取时间"/>

布局文件也很简单,一个是我们的自定义控件,可以看到我添加很多别的样式。然后下面三个EditText分别可以输入时分秒,然后下面两个Button,用来设置和获取时间。接下来,看一下MainActivity中的代码:

public class MainActivity extends AppCompatActivity {

private TimeView time_view;

private EditText hour;

private EditText min;

private EditText second;

private Button set_time;

private Button get_time;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

time_view = (TimeView)findViewById(R.id.time_view);

hour = (EditText)findViewById(R.id.hour);

min = (EditText)findViewById(R.id.min);

second = (EditText)findViewById(R.id.second);

set_time = (Button)findViewById(R.id.set_time);

get_time = (Button)findViewById(R.id.get_time);

set_time.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

time_view.setTime(Integer.parseInt(hour.getText().toString()),

Integer.parseInt(min.getText().toString()),

Integer.parseInt(second.getText().toString()));

}

});

get_time.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Toast.makeText(MainActivity.this,

time_view.getHour()+":"+time_view.getMin()+":"+time_view.getSecond()+"",

Toast.LENGTH_SHORT).show();

}

});

time_view.setTime(1,30,30);//设置了默认时间

time_view.start();

}

}

代码还是很简单的,分别给两个按钮添加点击事件,然后给时钟一个初始的时间,并调用start方法,让它动起来。我们来看一下效果吧!

演示

好了,这个自定义时钟到此就告一段落了,通过这篇文章以及上一篇的Android自定义控件之圆形时钟简单介绍了Android自定义view的一些基础知识,也算是自己学习的一点总结吧

完整代码已上传到github:https://github.com/Lloyd0577/CustomClockForAndroid,欢迎Star、Fork (__)

android 自定义时钟,Android自定义控件之圆形时钟(续)相关推荐

  1. 精通Android自定义View(十二)绘制圆形进度条

    1 绘图基础简析 1 精通Android自定义View(一)View的绘制流程简述 2 精通Android自定义View(二)View绘制三部曲 3 精通Android自定义View(三)View绘制 ...

  2. android自定义带进度条的圆形图片

    前言:在项目听新闻的改版中需要实现环绕圆形新闻图片的进度条功能,作为技术预备工作我就去看了一些网上的相关的原理,做了一个自定义带进度条的圆形图片的demo,并将这个实现写成文章发布出来,谁需要了可以进 ...

  3. android 自定义ImageView控件实现圆形图片-适用于用户头像

    android开发中常常涉及到一种情况,就是将用户上传的图片以圆形样式显示,但是用户上传的图片可以有直角.圆角.正方形等多种不确定样式,这时就用到了自定义ImageView控件,在安卓客户端使接收到的 ...

  4. android 自定义 seekbar,Android自定义控件 带文字提示的SeekBar

    封面 1.写在前面 SeekBar控件在开发中还是比较常见的,比如音视频进度.音量调节等,但是原生控件有时还不能满足我们的需求,今天就来学习一下如何自定义SeekBar控件,本文主要实现了一个带文字指 ...

  5. android 自定义event,Android运用onTouchEvent自定义滑动布局

    写在自定义之前 我们也许会遇到,自定义控件的触屏事件处理,先来了解一下View类中的,onTouch事件和onTouchEvent事件. 1.boolean onTouch(View v, Motio ...

  6. android自定义抽奖,Android自定义view制作抽奖转盘

    本文实例为大家分享了Android自定义view制作抽奖转盘的具体代码,供大家参考,具体内容如下 效果图 TurntableActivity package com.bawei.myapplicati ...

  7. android自定义省略号,Android开发自定义TextView省略号样式的方法

    本文实例讲述了Android开发自定义TextView省略号样式的方法.分享给大家供大家参考,具体如下: 在布局xml中设置textView的字段 android:maxLines="2&q ...

  8. android 自定义paint,Android自定义View中Paint、Rect、Canvas介绍(一)

    自定义View对于新手而言貌似是一个很复杂的东西.格式,各函数的意义.对于大神经常忘记各函数及一些参数的具体写法及意义,刚好在做一个风车效果,把过程及遇到的问题都写下来 1.如何自定义一个View p ...

  9. android 自定义图片,Android自定义图片集合

    本文主要包括以下内容: 使用Xfermode设置圆角图片 使用BitmapShader设置圆角图片 滑动旋转缩放的bimp图片 图片颜色处理(滑动) 图片 + 文字 其中1,2是两种不同方式处理图片圆 ...

最新文章

  1. bat自动输入密码登录_如何制作自动设置计算机管理员密码的脚本
  2. mysql 5.7.15 安装_mysql 5.7.15 安装配置方法图文教程
  3. GLUT及其函数的用法整理
  4. python无法启动该程序因为计算机中丢失_python报错:无法启动此程序,因为计算机中丢失...
  5. python中的集合类型_Python中的集合类型知识讲解
  6. hdu 5542(树状数组优化dp)
  7. docker镜像常见命令
  8. 如何把睡袋转给别人_微信收到的语音如何转给别人?试试这2个方法,没准能帮到你...
  9. DOM(document object model),文档对象模型
  10. foreach 和 for 循环的区别
  11. android窗口动画体系,Android 7.1 GUI系统-窗口管理WMS-动画的执行(七)
  12. 8plus基带电源供电线路_双电源供电与双回路供电的区别是什么?别再傻傻分不清了!...
  13. mysql 内存表 速度_mysql查询速度。为什么用内存表查询tmp表比直接选择慢?
  14. SAP恭贺德国国家足球队夺冠!
  15. 闲聊:Android 平台网络游戏加速器·一(科普文)
  16. 嵌入式硬件入门——74HC138译码器(三个IO实现8选1)
  17. fiddler打开之后google浏览器无法上网的解决办法
  18. 许巍的故乡到底想表达什么?是写给谁的?
  19. 关于MySQL数据类型定义的几个细节-INT(N)/VARCHAR(N)/DECIMAL(M,N)
  20. 身高预测_大部分都很准哦

热门文章

  1. 因为请了三天假,被领导和HR以去年迟到500分钟为由逼我主动离职,只给一个月赔偿!HR还威胁,如果不同意就别在北京混了!...
  2. php-ml 逻辑回归,TensorFlow ML cookbook 第三章6-8节 套索和岭回归、弹性网络回归and Logistic回归...
  3. 借助百度云平台人脸识别sdk完成网页人脸识别登录demo
  4. 华西朱冰梅教授当面教导
  5. 如何在线将PDF转Word?这样转换了解一下
  6. 向市场妥协的新旗舰 iPhone6 Plus评测
  7. 打造国内专业企业研发管理解决方案,ONES 完成华创资本领投 A+轮 600 万美元融资...
  8. 修改/etc/profile导致命令无法使用的处理办法
  9. Window7下创建Mac OS虚拟机
  10. Docker学习:容器五种(3+2)网络模式 | bridge模式 | host模式 | none模式 | container 模式 | 自定义网络模式详解