【移动应用开发技术】怎么用Android实现比较炫酷的自定义View_第1页
【移动应用开发技术】怎么用Android实现比较炫酷的自定义View_第2页
【移动应用开发技术】怎么用Android实现比较炫酷的自定义View_第3页
【移动应用开发技术】怎么用Android实现比较炫酷的自定义View_第4页
【移动应用开发技术】怎么用Android实现比较炫酷的自定义View_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】怎么用Android实现比较炫酷的自定义View

这篇文章主要介绍了怎么用Android实现比较炫酷的自定义View的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Android实现比较炫酷的自定义View文章都会有所收获,下面我们一起来看看吧。1.1、控件效果要实现的自定义控件效果大致如下,实现过程中用到了比较多的自定义View的API,觉得比较有代表性,就分享出来也当做学习总结项目代码已上传/DaLeiGe/And…1.2、从功能上分析一下这个控件,大致有以下特点随机运动粒子从圆周向圆心运动,并与切线方向有正负30°的角度差,粒子透明度、半径、运动速度随机,运动超过一定距离或者时间消失背景圆有一个从内到外的渐变色计时模式下圆环有一个颜色渐变的顺时针rotate动画整个背景圆颜色随着扇形角度变化而变化指针颜色变化数字变化是上下切换动画1.3、从结构上分析这个控件可以拆分为两个部分,由背景圆+数字控件两个部分构成的组合控件,之所以把数字控件单独拆分出来,也是为了方便做数字上下跳动的动画,毕竟通过控制drawText的位置实现动画感觉不方便,直接通过View的属性动画更好实现2.1、实现粒子运动使用AnimPoint.java表示运动粒子,它具有x,y坐标,半径,角度,运动速度,透明度等属性,通过这些属性就可以画出一个静态的粒子public

class

AnimPoint

implements

Cloneable

{

/**

*

粒子原点x坐标

*/

private

float

mX;

/**

*

粒子原点y坐标

*/

private

float

mY;

/**

*

粒子半径

*/

private

float

radius;

/**

*

粒子初始位置的角度

*/

private

double

anger;

/**

*

一帧移动的速度

*/

private

float

velocity;

/**

*

总共移动的帧数

*/

private

int

num

=

0;

/**

*

透明度

0~255

*/

private

int

alpha

=

153;

/**

*

随机偏移角度

*/

private

double

randomAnger

=

0;

}粒子的初始位置位于随机角度的圆周,且一个粒子具有随机的半径,透明度,速度等,通过init()方法,实现初始化粒子如下public

void

init(Random

random,

float

viewRadius)

{

anger

=

Math.toRadians(random.nextInt(360));

velocity

=

random.nextFloat()

*

2F;

radius

=

random.nextInt(6)

+

5;

mX

=

(float)

(viewRadius

*

Math.cos(anger));

mY

=

(float)

(viewRadius

*

Math.sin(anger));

//随机偏移角度-30°~30°

randomAnger

=

Math.toRadians(30

-

random.nextInt(60));

alpha

=

153

+

random.nextInt(102);

}想让粒子运动起来,使用update更新粒子的这些坐标属性就能实现,比如粒子现在坐标在(5,5),通过update()改变粒子的坐标到(6,6),结合属性动画不停地调用update()则就能不停的改变x,y的坐标,实现粒子运动,然后当粒子移动超过一定距离,或者调用update超过一定次数,再重新调用init()让粒子重新从圆周上开始下一个生命周期运动public

void

updatePoint(Random

random,

float

viewRadius)

{

//每一帧偏移的像素大小

float

distance

=

1F;

double

moveAnger

=

anger

+

randomAnger;

mX

=

(float)

(mX

-

distance

*

Math.cos(moveAnger)

*

velocity);

mY

=

(float)

(mY

-

distance

*

Math.sin(moveAnger)

*

velocity);

//模拟半径逐渐变小

radius

=

radius

-

0.02F

*

velocity;

num++;

//如果到了最大值

则重新给运动粒子一个轨迹属性

int

maxDistance

=

180;

int

maxNum

=

400;

if

(velocity

*

num

>

maxDistance

||

num

>

maxNum)

{

num

=

0;

init(random,

viewRadius);

}

}在View中大致实现如下/**

*

初始化动画

*/

private

void

initAnim()

{

//绘制运动的粒子

AnimPoint

mAnimPoint

=

new

AnimPoint();

for

(int

i

=

0;

i

<

pointCount;

i++)

{

//通过clone创建对象,避免重复创建

AnimPoint

cloneAnimPoint

=

mAnimPoint.clone();

//先给每个粒子初始化各类属性

cloneAnimPoint.init(mRandom,

mRadius

-

mOutCircleStrokeWidth

/

2F);

mPointList.add(cloneAnimPoint);

}

//画运动粒子

mPointsAnimator

=

ValueAnimator.ofFloat(0F,

1F);

mPointsAnimator.setDuration(Integer.MAX_VALUE);

mPointsAnimator.setRepeatMode(ValueAnimator.RESTART);

mPointsAnimator.setRepeatCount(ValueAnimator.INFINITE);

mPointsAnimator.addUpdateListener(animation

->

{

for

(AnimPoint

point

:

mPointList)

{

//通过属性动画不停的计算下粒子的下一个坐标

point.updatePoint(mRandom,

mRadius);

}

invalidate();

});

mPointsAnimator.start();

}

@Override

protected

void

onDraw(final

Canvas

canvas)

{

super.onDraw(canvas);

canvas.save();

canvas.translate(mCenterX,

mCenterY);

//画运动粒子

for

(AnimPoint

animPoint

:

mPointList)

{

mPointPaint.setAlpha(animPoint.getAlpha());

canvas.drawCircle(animPoint.getmX(),

animPoint.getmY(),

animPoint.getRadius(),

mPointPaint);

}

}2.2、实现渐变色圆实现圆从内到外渐变使用RadialGradient大致实现方式如下float[]

mRadialGradientStops

=

{0F,

0.69F,

0.86F,

0.94F,

0.98F,

1F};

mRadialGradientColors[0]

=

transparentColor;

mRadialGradientColors[1]

=

transparentColor;

mRadialGradientColors[2]

=

parameter.getInsideColor();

mRadialGradientColors[3]

=

parameter.getOutsizeColor();

mRadialGradientColors[4]

=

transparentColor;

mRadialGradientColors[5]

=

transparentColor;

mRadialGradient

=

new

RadialGradient(

0,

0,

mCenterX,

mRadialGradientColors,

mRadialGradientStops,

Shader.TileMode.CLAMP);

mSweptPaint.setShader(mRadialGradient);

...

//onDraw()绘制

canvas.drawCircle(0,

0,

mCenterX,

mSweptPaint);2.3、展示背景圆的扇形区域原本想通过DrawArc实现这个效果,但是DrawArc无法实现到圆心的区域那么如何实现这么一个不规则的形状呢,可以使用canvas.clipPath()实现裁剪不规则的形状,所以只要得到扇形的Path就能实现,通过圆点+弧形再闭合path就能实现/**

*

绘制扇形path

*

*

@param

r

半径

*

@param

startAngle

开始角度

*

@param

sweepAngle

扫过的角度

*/

private

void

getSectorClip(float

r,

float

startAngle,

float

sweepAngle)

{

mArcPath.reset();

mArcPath.addArc(-r,

-r,

r,

r,

startAngle,

sweepAngle);

mArcPath.lineTo(0,

0);

mArcPath.close();

}

//然后再onDraw()中,裁剪画布

canvas.clipPath(mArcPath);2.4、实现指针变色指针是不规则形状,无法通过绘制几何图形实现,所以选用drawBitmap实现至于如何实现bitmap指针图片的颜色变化呢,原本的方案是使用AvoidXfermode改变指定像素通道范围内的颜色,但是AvoidXfermode在API24已经被移除,所以这方案无效最终采用图层混合模式实现指针图片变色通过PorterDuff.Mode.MULTIPLY模式可以实现bitmap颜色,源图像为要修改的指针颜色,目标图像为白色指针,通过获取两个图像的重叠部分实现变色大致实现如下/**

*

初始化指针图片的Bitmap

*/

private

void

initBitmap()

{

float

f

=

130F

/

656F;

mBitmapDST

=

BitmapFactory.decodeResource(getResources(),

R.drawable.indicator);

float

mBitmapDstHeight

=

width

*

f;

float

mBitmapDstWidth

=

mBitmapDstHeight

*

mBitmapDST.getWidth()

/

mBitmapDST.getHeight();

//初始化指针的图层混合模式

mXfermode

=

new

PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);

mPointerRectF

=

new

RectF(0,

0,

mBitmapDstWidth,

mBitmapDstHeight);

mBitmapSRT

=

Bitmap.createBitmap((int)

mBitmapDstWidth,

(int)

mBitmapDstHeight,

Bitmap.Config.ARGB_8888);

mBitmapSRT.eraseColor(mIndicatorColor);

}

@Override

protected

void

onDraw(final

Canvas

canvas)

{

super.onDraw(canvas);

//画指针

canvas.translate(mCenterX,

mCenterY);

canvas.rotate(mCurrentAngle

/

10F);

canvas.translate(-mPointerRectF.width()

/

2,

-mCenterY);

mPointerLayoutId

=

canvas.saveLayer(mPointerRectF,

mBmpPaint);

mBitmapSRT.eraseColor(mIndicatorColor);

canvas.drawBitmap(mBitmapDST,

null,

mPointerRectF,

mBmpPaint);

mBmpPaint.setXfermode(mXfermode);

canvas.drawBitmap(mBitmapSRT,

null,

mPointerRectF,

mBmpPaint);

mBmpPaint.setXfermode(null);

canvas.restoreToCount(mPointerLayoutId);

}2.5、实现背景圆颜色随扇形角度变化把圆形控件拆成3600°,每一个角度对应控件一种具体颜色值,那么如何计算特定角度他具体的颜色值呢?参考属性动画中的变色动画android.animation.ArgbEvaluator实现方式,计算两个颜色中具体某一个点的颜色值方式如下public

Object

evaluate(float

fraction,

Object

startValue,

Object

endValue)

{

int

startInt

=

(Integer)

startValue;

float

startA

=

((startInt

>>

24)

&

0xff)

/

255.0f;

float

startR

=

((startInt

>>

16)

&

0xff)

/

255.0f;

float

startG

=

((startInt

>>

8)

&

0xff)

/

255.0f;

float

startB

=

(

startInt

&

0xff)

/

255.0f;

int

endInt

=

(Integer)

endValue;

float

endA

=

((endInt

>>

24)

&

0xff)

/

255.0f;

float

endR

=

((endInt

>>

16)

&

0xff)

/

255.0f;

float

endG

=

((endInt

>>

8)

&

0xff)

/

255.0f;

float

endB

=

(

endInt

&

0xff)

/

255.0f;

//

convert

from

sRGB

to

linear

startR

=

(float)

Math.pow(startR,

2.2);

startG

=

(float)

Math.pow(startG,

2.2);

startB

=

(float)

Math.pow(startB,

2.2);

endR

=

(float)

Math.pow(endR,

2.2);

endG

=

(float)

Math.pow(endG,

2.2);

endB

=

(float)

Math.pow(endB,

2.2);

//

compute

the

interpolated

color

in

linear

space

float

a

=

startA

+

fraction

*

(endA

-

startA);

float

r

=

startR

+

fraction

*

(endR

-

startR);

float

g

=

startG

+

fraction

*

(endG

-

startG);

float

b

=

startB

+

fraction

*

(endB

-

startB);

//

convert

back

to

sRGB

in

the

[0..255]

range

a

=

a

*

255.0f;

r

=

(float)

Math.pow(r,

1.0

/

2.2)

*

255.0f;

g

=

(float)

Math.pow(g,

1.0

/

2.2)

*

255.0f;

b

=

(float)

Math.pow(b,

1.0

/

2.2)

*

255.0f;

return

Math.round(a)

<<

24

|

Math.round(r)

<<

16

|

Math.round(g)

<<

8

|

Math.round(b);

}控件中总共有四个颜色段,3600/4=900,所以fraction=progressValue%900/900;然后判断当前的角度位于第几段颜色值中,通过android.animation.ArgbEvaluator.evaluate(floatfraction,ObjectstartValue,ObjectendValue)就能回去具体的颜色值大致实现过程如下private

ProgressParameter

getProgressParameter(float

progressValue)

{

float

fraction

=

progressValue

%

900

/

900;

if

(progressValue

<

900)

{

//第一个颜色段

mParameter.setInsideColor(evaluate(fraction,

insideColor1,

insideColor2));

mParameter.setOutsizeColor(evaluate(fraction,

outsizeColor1,

outsizeColor2));

mParameter.setProgressColor(evaluate(fraction,

progressColor1,

progressColor2));

mParameter.setPointColor(evaluate(fraction,

pointColor1,

pointColor2));

mParameter.setBgCircleColor(evaluate(fraction,

bgCircleColor1,

bgCircleColor2));

mParameter.setIndicatorColor(evaluate(fraction,

indicatorColor1,

indicatorColor2));

}

else

if

(progressValue

<

1800)

{

//第二个颜色段

mParameter.setInsideColor(evaluate(fraction,

insideColor2,

insideColor3));

mParameter.setOutsizeColor(evaluate(fraction,

outsizeColor2,

outsizeColor3));

mParameter.setProgressColor(evaluate(fraction,

progressColor2,

progressColor3));

mParameter.setPointColor(evaluate(fraction,

pointColor2,

pointColor3));

mParameter.setBgCircleColor(evaluate(fraction,

bgCircleColor2,

bgCircleColor3));

mParameter.setIndicatorColor(evaluate(fraction,

indicatorColor2,

indicatorColor3));

}

else

if

(progressValue

<

2700)

{

//第三个颜色段

mParameter.setInsideColor(evaluate(fraction,

insideColor3,

insideColor4));

mParameter.setOutsizeColor(evaluate(fraction,

outsizeColor3,

outsizeColor4));

mParameter.setProgressColor(evaluate(fraction,

progressColor3,

progressColor4));

mParameter.setPointColor(evaluate(fraction,

pointColor3,

pointColor4));

mParameter.setBgCircleColor(evaluate(fraction,

bgCircleColor3,

bgCircleColor4));

mParameter.setIndicatorColor(evaluate(fraction,

indicatorColor3,

indicatorColor4));

}

else

{

//第四个颜色段

mParameter.setInsideColor(evaluate(fraction,

insideColor4,

insideColor5));

mParameter.setOutsizeColor(evaluate(fraction,

outsizeColor4,

outsizeColor5));

mParameter.setProgressColor(evaluate(fraction,

progressColor4,

progressColor5));

mParameter.setPointColor(evaluate(fraction,

pointColor4,

pointColor5));

mParameter.setBgCircleColor(evaluate(fraction,

bgCircleColor4,

bgCircleColor5));

mParameter.setIndicatorColor(evaluate(fraction,

indicatorColor4,

indicatorColor5));

}

return

mParameter;

}3.1、属性动画+2个TextView实现数字上下切换动画实现数字切换动画,原本打算用RecycleView实现,但是考虑到动效上将来可能面临UI小姐姐各种骚操作,所以最终决定就用两个TextView做上下translation动画,这样可控性高,对View执行属性动画也简单NumberView使用FrameLayout包裹两个TextView,widget_progress_number_item_layout.xml<?xml

version="1.0"

encoding="utf-8"?>

<FrameLayout

xmlns:android="/apk/res/android"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

<TextView

android:id="@+id/tv_number_one"

style="@style/progress_text_font"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center"

android:gravity="center"

android:padding="0dp"

androi

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论