android 自定义view-水波纹进度球
- 格式:docx
- 大小:695.48 KB
- 文档页数:9
Android 自定义View——动态进度条这个是看了梁肖的demo,根据他的思路自己写了一个,但是我写的这个貌似计算还是有些问题,从上面的图就可以看出来,左侧、顶部、右侧的线会有被截掉的部分,有懂得希望能给说一下,改进一下,这个过程还是有点曲折的,不过还是觉得收获挺多的。
比如通动画来进行动态的展示(之前做的都是通过Handler进行更新的所以现在换一种思路觉得特别好),还有圆弧的起止角度,矩形区域的计算等!关于绘制我们可以循序渐进,比如最开始先画圆,然后再画周围的线,最后设置动画部分就可以了。
不多说了,上代码了。
代码自定义Viewpublic class ColorProgressBar extends View{//下面这两行在本demo中没什么用,只是前几天看别人的代码时学到的按一定尺寸,设置其他尺寸的方式,自动忽略或者学习一下也不错// private int defaultStepIndicatorNum= (int) TypedValue.applyDimension(PLEX_UNIT_DIP,40,getResources().getDisplay Metrics());// int mCircleRadius=0.28f*defaultStepIndicatorNum;//布局的宽高private int mWidth;private int mHeight;//直径private int mDiameter=500;//底层圆画笔private Paint mPaintbg;//顶层圆的画笔private Paint mPaintft;//周围线的画笔private Paint mPaintLine;//外层线条的长度private int mLongItem=dip2px(20);//线条与圆的间距private int mDistanceItem=dip2px(10);//进度条的最大宽度(取底层进度条与顶层进度条宽度最大的)private int mProgressWidth;//底层圆的颜色private int mBackColor;//顶层圆的颜色private int mFrontColor;//底层圆、顶层圆的宽度private float mBackWidth;private float mFrontWidth;//设置进度private float currentvalue;//通过动画演示进度private ValueAnimator animator;private int curvalue;public ColorProgressBar(Context context) {this(context,null,0);}public ColorProgressBar(Context context, AttributeSet attrs) {this(context, attrs,0);}public ColorProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.ColorProgressBar);mBackColor= ta.getColor(R.styleable.ColorProgressBar_back_color, Color.BLACK);mFrontColor=ta.getColor(R.styleable.ColorProgressBar_front_color,mBackColor);mBackWidth=ta.getDimension(R.styleable.ColorProgressBar_back_width,dip2px(10));mFrontWidth=ta.getDimension(R.styleable.ColorProgressBar_front_width,dip2px(10));mProgressWidth=mBackWidth>mFrontWidth?(int)mBackWidth:(int)mFrontWidth;//注意释放资源ta.recycle();init();}/*** 都是画笔初始化*/private void init() {mPaintbg=new Paint(Paint.ANTI_ALIAS_FLAG);mPaintbg.setStrokeWidth(mProgressWidth);mPaintbg.setColor(mBackColor);mPaintbg.setStrokeCap(Paint.Cap.ROUND);mPaintbg.setStyle(Paint.Style.STROKE);mPaintft=new Paint(Paint.ANTI_ALIAS_FLAG);mPaintft.setColor(mFrontColor);mPaintft.setStyle(Paint.Style.STROKE);mPaintft.setStrokeWidth(mFrontWidth);mPaintft.setStrokeCap(Paint.Cap.ROUND);mPaintLine=new Paint(Paint.ANTI_ALIAS_FLAG);mPaintLine.setColor(Color.BLACK);mPaintLine.setStrokeWidth(5);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 宽度=高度=(长指针+指针与圆盘的间距+进度条的粗细+半径)*2Log.e("测量数据","LongItem:"+mLongItem+"mDistanceItem:"+mDistanceItem+"mProgressWidth:"+mProgressWidth+"mDia meter:"+mDiameter/2);mWidth=(int)2*(mLongItem+mDistanceItem+mProgressWidth*2+mDiameter/2);mHeight=(int)2*(mLongItem+mDistanceItem+mProgressWidth*2+mDiameter/2);Log.e("自定义View","高度"+mHeight+"宽度"+mWidth);setMeasuredDimension(mWidth,mHeight);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制底层圆弧,矩形的具体计算见图片canvas.drawArc(newRectF(mProgressWidth/2+mDistanceItem+mLongItem,mProgressWidth/2+mDistanceItem+mLon gItem,mWidth-mProgressWidth/2-mDistanceItem-mLongItem,mHeight-mProgressWidth/2-mDist anceItem-mLongItem),0,360,true,mPaintbg);// SweepGradient gradient=new SweepGradient();//绘制边缘线canvas.save();canvas.rotate(144,mWidth/2,mHeight/2);for(int i=0;i<=30;i++){canvas.rotate(-9,mWidth/2,mHeight/2);if(i%5==0){canvas.drawLine(mWidth/2,5,mWidth/2,mLongItem,mPaintbg);}else {canvas.drawLine(mWidth/2,25,mWidth/2,mLongItem,mPaintLine);}}canvas.restore();//给画笔设置渐变SweepGradient sweepGradient=new SweepGradient(mWidth/2,mHeight/2,Color.RED,Color.YELLOW);mPaintft.setShader(sweepGradient);//绘制顶层圆弧,currentvalue在改变时呈现动态效果canvas.drawArc(newRectF(mProgressWidth/2+mDistanceItem+mLongItem,mProgressWidth/2+mDistanceItem+mLon gItem,gressWidth/2-mDistanceItem-mLongItem,mHeight-mProgr essWidth/2-mDistanceItem-mLongItem),135,currentvalue,false,mPaintft);mPaintft.setTextSize(100);mPaintft.setTextAlign(Paint.Align.CENTER);//绘制文本canvas.drawText(String.format("%.0f",currentvalue),mWidth/2,mHeight/2+50,mPaintft);invalidate();}/*** 设置动画* @param value*/public void setCurrentValue(float value){// currentvalue=value;animator=ValueAnimator.ofFloat(currentvalue,value);animator.setDuration(3000);animator.setTarget(currentvalue);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator) {currentvalue= (float) valueAnimator.getAnimatedValue();curvalue=curvalue/10;}});animator.start();}private int dip2px(float dip){float density=getContext().getResources().getDisplayMetrics().density;return (int)(dip*density+0.5f);}}Activity调用@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.colorprogressbar);mBtStart1= (ton) findViewById(R.id.bt1);bar1= (ColorProgressBar) findViewById(R.id.cp1);mBtStart1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {bar1.setCurrentValue(270);}});}自定义属性<declare-styleable name="ColorProgressBar"><attr name="back_color" format="color"></attr><attr name="front_color" format="color"></attr><attr name="back_width" format="dimension"></attr><attr name="front_width" format="dimension"></attr></declare-styleable>布局注意:为了使用自定义属性需要添加一行代码(AS)布局<LinearLayoutxmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><Buttonandroid:id="@+id/bt1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="start1"/><workdemo.colorprogressbar.widget.ColorProgressBar android:id="@+id/cp1"android:layout_width="232dp"android:layout_height="match_parent"android:layout_gravity="center_horizontal"app:back_color="@color/colorPrimary"app:front_color="@color/colorAccent"android:background="@mipmap/ic_launcher"/> </LinearLayout>。
android自定义组件(手机加速球+水面波动效果)自定义View确定一个正方形public class WaterView extends View {private int len;public WaterView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width = MeasureSpec.getSize(widthMeasureSpec);int height = MeasureSpec.getSize(heightMeasureSpec);//以最小值为正方形的长len = Math.min(width, height);//设置测量高度和宽度(必须要调用,不然无效果)setMeasuredDimension(len, len);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);}}同样这里集成了View,并通过设置测量值,限定空间为正方形。
布局中使用:<?xml version="1.0" encoding="utf-8"?>android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/ll_parent"android:orientation="vertical"android:background="@color/colorPrimary"android:padding="20dp"tools:context="com.example.huaweiview.MainActivity"><com.example.huaweiview.WaterViewandroid:layout_gravity="center"android:background="@color/colorAccent"android:layout_width="200dp"android:layout_height="300dp" /></LinearLayout>ok,我们设置的长度和宽度并不一样,但是他显示的是一个正方形,并且,根据上一篇博客的介绍,它是有自己的坐标系的,我们绘制的所有东西都在这个坐标系内,并且依靠它去确定位置。
android setprogressdrawable用法-回复Android的setProgressDrawable()方法是用于设置进度条的Drawable 样式的。
进度条是在Android应用程序中常用的一种视图,用于显示任务或操作的进度。
setProgressDrawable()方法有两个参数,分别是start和end。
start 参数是一个Drawable对象,表示进度条的开始部分的样式;end参数是一个Drawable对象,表示进度条的结束部分的样式。
要使用setProgressDrawable()方法,首先需要创建一个进度条的样式。
可以使用XML定义样式,也可以使用代码创建Drawable对象。
下面将详细介绍这两种方式。
一、使用XML定义样式1. 创建一个名为progress_bar_style.xml的XML文件,放在res/drawable文件夹下。
2. 在progress_bar_style.xml文件中,定义进度条的样式。
可以使用<layer-list>标签来定义多层次的Drawable对象。
例如,下面的XML代码定义了一个带有两层样式的进度条:xml<layer-list xmlns:android="<item android:id="@android:id/background"android:drawable="@drawable/progress_background" /> <item android:id="@android:id/progress"android:drawable="@drawable/progress_bar" /></layer-list>这个样式包含了两个Drawable对象。
第一个是ID为android:id/background的Drawable对象,表示进度条的背景部分的样式;第二个是ID为android:id/progress的Drawable对象,表示进度条的进度部分的样式。
Android⾃定义View实现⽔平带数字百分⽐进度条这个进度条可以反映真实进度,并且完成百分⽐的⽂字时随着进度增加⽽移动的,所在位置也恰好是真实完成的百分⽐位置,效果如下:思路如下:第⼀部分是左侧的蓝⾊直线,代表已经完成的进度;第⼆部分是右侧灰⾊的直线,代表未完成的进度;第三部分是红⾊的百分⽐的数字百分⽐⽂本,显⽰当前确切的完成进度。
最关键的部分就是要确定百分⽐⽂本的确切位置,这⾥⽤了paint的getTextBounds⽅法,得到⽂本的宽⾼,然后再精确确定它的位置。
view代码如下:public class NumberProgressView extends View {/*** 进度条画笔的宽度(dp)*/private int paintProgressWidth = 3;/*** ⽂字百分⽐的字体⼤⼩(sp)*/private int paintTextSize = 20;/*** 左侧已完成进度条的颜⾊*/private int paintLeftColor = 0xff67aae4;/*** 右侧未完成进度条的颜⾊*/private int paintRightColor = 0xffaaaaaa;/*** 百分⽐⽂字的颜⾊*/private int paintTextColor = 0xffff0077;/*** Contxt*/private Context context;/*** 主线程传过来进程 0 - 100*/private int progress;/*** 得到⾃定义视图的宽度*/private int viewWidth;/*** 得到⾃定义视图的Y轴中⼼点*/private int viewCenterY;/*** 画左边已完成进度条的画笔*/private Paint paintleft = new Paint();/*** 画右边未完成进度条的画笔*/private Paint paintRight = new Paint();/*** 画中间的百分⽐⽂字的画笔private Paint paintText = new Paint();/*** 要画的⽂字的宽度*/private int textWidth;/*** 画⽂字时底部的坐标*/private float textBottomY;/*** 包裹⽂字的矩形*/private Rect rect = new Rect();/*** ⽂字总共移动的长度(即从0%到100%⽂字左侧移动的长度)*/private int totalMovedLength;public NumberProgressView(Context context, AttributeSet attrs) {super(context, attrs);this.context = context;// 构造器中初始化数据initData();}/*** 初始化数据*/private void initData() {//设置进度条画笔的宽度int paintProgressWidthPx = Utils.dip2px(context, paintProgressWidth);//设置百分⽐⽂字的尺⼨int paintTextSizePx = Utils.sp2px(context, paintTextSize);// 已完成进度条画笔的属性paintleft.setColor(paintLeftColor);paintleft.setStrokeWidth(paintProgressWidthPx);paintleft.setAntiAlias(true);paintleft.setStyle(Style.FILL);// 未完成进度条画笔的属性paintRight.setColor(paintRightColor);paintRight.setStrokeWidth(paintProgressWidthPx);paintRight.setAntiAlias(true);paintRight.setStyle(Style.FILL);// 百分⽐⽂字画笔的属性paintText.setColor(paintTextColor);paintText.setTextSize(paintTextSizePx);paintText.setAntiAlias(true);paintText.setTypeface(Typeface.DEFAULT_BOLD);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);getWidthAndHeight();}/*** 得到视图等的⾼度宽度尺⼨数据*/private void getWidthAndHeight() {//得到包围⽂字的矩形的宽⾼paintText.getTextBounds("000%", 0, "000%".length(), rect);textWidth = rect.width();textBottomY = viewCenterY + rect.height() / 2;//得到⾃定义视图的⾼度int viewHeight = getMeasuredHeight();viewWidth = getMeasuredWidth();viewCenterY = viewHeight / 2;totalMovedLength = viewWidth - textWidth;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//得到float型进度float progressFloat = progress / 100.0f;//当前⽂字移动的长度float currentMovedLentgh = totalMovedLength * progressFloat;//画左侧已经完成的进度条,长度为从Veiw左端到⽂字的左侧canvas.drawLine(0, viewCenterY, currentMovedLentgh, viewCenterY, paintleft);//画右侧未完成的进度条,这个进度条的长度不是严格按照百分⽐来缩放的,因为⽂字的长度会变化,所以它的长度缩放⽐例也会变化 if (progress < 10) {canvas.drawLine(currentMovedLentgh + textWidth * 0.5f, viewCenterY, viewWidth, viewCenterY, paintRight);} else if (progress < 100) {canvas.drawLine(currentMovedLentgh + textWidth * 0.75f, viewCenterY, viewWidth, viewCenterY, paintRight);} else {canvas.drawLine(currentMovedLentgh + textWidth, viewCenterY, viewWidth, viewCenterY, paintRight);}//画⽂字(注意:⽂字要最后画,因为⽂字和进度条可能会有重合部分,所以要最后画⽂字,⽤⽂字盖住重合的部分)canvas.drawText(progress + "%", currentMovedLentgh, textBottomY, paintText);}/*** @param progress 外部传进来的当前进度*/public void setProgress(int progress) {this.progress = progress;invalidate();}}调⽤者activity的代码,设置进度条的进度:public class NumberProgressBarActivity extends Activity {protected static final int WHAT_INCREASE = 1;private NumberProgressView np_numberProgressBar;private int progress;private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {progress++;np_numberProgressBar.setProgress(progress);handler.sendEmptyMessageDelayed(WHAT_INCREASE, getRadomNumber(50, 200));if (progress >= 100) {handler.removeMessages(WHAT_INCREASE);}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_number_progress_bar);np_numberProgressBar = (NumberProgressView) findViewById(R.id.np_numberProgressBar);Button btn_numberProgressBar = (Button) findViewById(R.id.btn_numberProgressBar);btn_numberProgressBar.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {increase();}});}private void increase() {progress = 0;np_numberProgressBar.setProgress(0);handler.removeMessages(WHAT_INCREASE);handler.sendEmptyMessage(WHAT_INCREASE);}/*** 得到两个整数之间的⼀个随机数** @param start 较⼩的数* @param end 较⼤的数* @return*/public int getRadomNumber(int start, int end) {return (int) (start + Math.random() * (end - start));}}⼯具⽅法:/*** 将dip或dp值转换为px值,保证尺⼨⼤⼩不变*/public static int dip2px(Context context, float dipValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dipValue * scale + 0.5f);}/*** 将sp值转换为px值,保证⽂字⼤⼩不变*/public static int sp2px(Context context, float spValue) {final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;return (int) (spValue * fontScale + 0.5f);}布局:<LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.example.viewdemo.view.NumberProgressViewandroid:id="@+id/np_numberProgressBar"android:layout_width="wrap_content"android:layout_height="100dp"android:layout_margin="20dp"android:background="#33890075"/><Buttonandroid:id="@+id/btn_numberProgressBar"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="开始"/></LinearLayout>以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
在某些操作的进度中的可视指示器,为用户呈现操作的进度,还它有一个次要的进度条,用来显示中间进度,如在流媒体播放的缓冲区的进度。
一个进度条也可不确定其进度。
在不确定模式下,进度条显示循环动画。
这种模式常用于应用程序使用任务的长度是未知的。
进度条也就是一个表示运转的过程,例如发送短信,连接网络等等,表示一个过程正在执行中1、android.widget. ProgressBar,继承自android.view.View 。
在android.widget包中。
对应对话框progressDialog。
ProgressBar有两种展示方式,表盘形式(普通、小、大)和条形填充形式。
在layout定义时,需要通过设施style属性类设置展示方式。
ProgressBar的样式有四种:android:progressBarStyle:默认进度条样式,不确定模式android:progressBarStyleHorizontal:水平进度条样式android:progressBarStyleLarge :大号进度条样式,也是不确定进度模式android:progressBarStyleSmall :小号进度条样式,也是不确定进度模式二、XML重要属性android:max-- 这事进度条长度最大值android:progress--设定度条当前进度值android:secondaryProgress--第二进度条进度值android:progressBarStyle:默认进度条样式android:progressBarStyleHorizontal:水平样式style="?android:attr/ProgressBarStyleLarge" --- 属性风格类型--大圆圈,如下图style=”?android:attr/progressBarStyleSmall”--- 属性风格类型--小圆圈,如下图:style="?android:attr/ProgressBarStyleHorizontal" --水平进度条 --如下图:几秒钟之后自动滚到到如下:也可以用下面的形式代替上面的形式的:?三、重要方法getMax():返回这个进度条的范围的上限getProgress():返回当前进度值getSecondaryProgress():返回次要当前进度值incrementProgressBy(int diff):指定增加的进度--即步长isIndeterminate():指示进度条是否在不确定模式下setIndeterminate(boolean indeterminate):设置不确定模式下setVisibility(int v):设置该进度条是否可视四、重要事件onSizeChanged(int w, int h, int oldw, int oldh):当进度值改变时引发此事件接下来看案例:1.定义一个布局文件progressbar.xml?2.之后定义java 文件:ProgressBarDemo.java?运行效果如下:二:用图片实现滚动效果:1.添加图片到drawable下2.自定义图片资源文件iamge_progress.xml ?1 2 3 4 5 6 7 <?xml version="1.0" encoding="utf-8"?><animated-rotatexmlns:android=""android:drawable="@drawable/image_progress" android:pivotX="50%"android:pivotY="50%"/>3.定义布局文件,progress.xml?1 2 3 4 5 6 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center">7 8 9 10 11 <ProgressBarandroid:indeterminateDrawable="@drawable/drawable_progress" android:layout_height="100dp"android:layout_width="100dp"/></LinearLayout>运行效果如下:三》自定义渐变色进度条:定义drawable资源文件color_ProgressBar.xml ?1 2 3 4 5 6 7 8 91011 <?xml version="1.0" encoding="utf-8"?><layer-list xmlns:android=""><item android:id="@android:id/background"> <shape><corners android:radius="5dip" /><gradient android:startColor="#ff9d9e9d" android:centerColor="#ff5a5d5a"android:centerY="0.75"android:endColor="#ff747674"android:angle="270"/>2.定义对应的不布局文件:progressbar.xml在此文件中引用我们定义的drawable资源配置文件?或者在代码中给进度条设置自定义资源文件:效果如下:四:自定义progressbar颜色:1.定义一个图片资源文件:?2.定义布局文件:?1 2 3 4 5 <progressBarandroid:id="@+id/color_ProgressBar2"android:indeterminateDrawable="@drawable/color_progress2" android:layout_height="wrap_content"android:layout_width="wrap_content"/>3.效果:。
自定义view实现水波纹效果我先们来学习效果1:效果1实现本质:用一张波形图和一个圆形图的图片,然后圆形图在波形图上方,然后使用安卓的图片遮罩模式desIn(不懂?那么先记住有这样一个遮罩模式).(只显示上部图像和下部图像公共部分的下半部分),是不是很难懂?那么我在说清一点并且配图.假设圆形图在波形图上面,那么只会显示两者相交部分的波形图下面是解释效果图(正方形蓝色图片在黄色圆形上面):这次的实现我们都选择继承view,在实现的过程中我们需要关注如下几个方法:1.onMeasure():最先回调,用于控件的测量;2.onSizeChanged():在onMeasure后面回调,可以拿到view的宽高等数据,在横竖屏切换时也会回调;3.onDraw():真正的绘制部分,绘制的代码都写到这里面;先来看看我们定义的变量://波形图Bitmap waveBitmap;//圆形遮罩图Bitmap circleBitmap;//波形图srcRect waveSrcRect;//波形图dstRect waveDstRect;//圆形遮罩srcRect circleSrcRect;//圆形遮罩dstRect circleDstRect;//画笔Paint mpaint;//图片遮罩模式PorterDuffXfermode mode;//控件的宽int viewWidth;//控件的高int viewHeight;//图片过滤器PaintFlagsDrawFilter paintFlagsDrawFilter ;//每次移动的距离int speek = 10 ;//当前移动距离int nowOffSet;介绍一个方法:void android.graphics.Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)11此方法的参数:参数1:你的图片参数2:矩形.也就是说此矩形决定你画出图片参数1 的哪个位置,比如说你的矩形是设定是Rect rect= new Rect(0,0,图片宽,图片高) 那么将会画出图片全部参数3:矩形.决定你图片缩放比例和在view中的位置.假设你的矩形Rect rect= new Rect(0,0,100,100) 那么你将在自定义view中(0,0)点到(100,100)绘画此图片并且如果图片大于(小于)此矩形那么按比例缩小(放大)来看看初始化方法//初始化private void init() {mpaint = new Paint();//处理图片抖动mpaint.setDither(true);//抗锯齿mpaint.setAntiAlias(true);//设置图片过滤波mpaint.setFilterBitmap(true);//设置图片遮罩模式mode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);//给画布直接设定参数paintFlagsDrawFilter = new PaintFlagsDrawFilter(0, Paint.DITHER_FLAG|Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);//初始化图片//使用drawable获取的方式,全局只会生成一份,并且系统会进行管理,//而BitmapFactory.decode()出来的则decode多少次生成多少张,务必自己进行recycle;//获取波形图waveBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.wave_2000)).getBitmap();//获取圆形遮罩图circleBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.circle_500)).getBitmap();//不断刷新波形图距离读者可以先不看这部分内容因为需要结合其他方法new Thread(){public void run() {while (true) {try {//移动波形图nowOffSet=nowOffSet+speek;//如果移动波形图的末尾那么重新来if (nowOffSet>=waveBitmap.getWidth()) {nowOffSet=0;}sleep(30);postInvalidate();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}};}.start();}以下获取view的宽高并设置对应的波形图和圆形图矩形(会在onMesure回调后执行)@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//获取view宽高viewWidth = w;viewHeight = h ;//波形图的矩阵初始化waveSrcRect = new Rect();waveDstRect = new Rect(0,0,w,h);//圆球矩阵初始化circleSrcRect = new Rect(0,0,circleBitmap.getWidth(),circleBitmap.getHeight());circleDstRect = new Rect(0,0,viewWidth,viewHeight);}那么最后来看看绘画部分吧@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//给图片直接设置过滤效果canvas.setDrawFilter(paintFlagsDrawFilter);//给图片上色canvas.drawColor(Color.TRANSPARENT);//添加图层注意!!!!!使用图片遮罩模式会影响全部此图层(也就是说在canvas.restoreToCount 所有图都会受到影响)int saveLayer = canvas.saveLayer(0,0, viewWidth,viewHeight,null, Canvas.ALL_SA VE_FLAG);//画波形图部分矩形waveSrcRect.set(nowOffSet, 0, nowOffSet+viewWidth/2, viewHeight);//画矩形canvas.drawBitmap(waveBitmap,waveSrcRect,waveDstRect,mpaint);//设置图片遮罩模式mpaint.setXfermode(mode);//画遮罩canvas.drawBitmap(circleBitmap, circleSrcRect, circleDstRect,mpaint);//还原画笔模式mpaint.setXfermode(null);//将图层放上canvas.restoreToCount(saveLayer);}最后看下完整的代码package com.fmy.shuibo1;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PaintFlagsDrawFilter;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.Rect;import android.graphics.drawable.BitmapDrawable;import android.icu.text.TimeZoneFormat.ParseOption;import android.util.AttributeSet;import android.view.View;public class MySinUi extends View{//波形图Bitmap waveBitmap;//圆形遮罩图Bitmap circleBitmap;//波形图srcRect waveSrcRect;//波形图dstRect waveDstRect;//圆形遮罩srcRect circleSrcRect;//圆形遮罩dstRect circleDstRect;//画笔Paint mpaint;//图片遮罩模式PorterDuffXfermode mode;//控件的宽int viewWidth;//控件的高int viewHeight;//图片过滤器PaintFlagsDrawFilter paintFlagsDrawFilter ;//每次移动的距离int speek = 10 ;//当前移动距离int nowOffSet;public MySinUi(Context context, AttributeSet attrs) { super(context, attrs);init();}//初始化private void init() {mpaint = new Paint();//处理图片抖动mpaint.setDither(true);//抗锯齿mpaint.setAntiAlias(true);//设置图片过滤波mpaint.setFilterBitmap(true);//设置图片遮罩模式mode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);//给画布直接设定参数paintFlagsDrawFilter = new PaintFlagsDrawFilter(0, Paint.DITHER_FLAG|Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);//初始化图片//使用drawable获取的方式,全局只会生成一份,并且系统会进行管理,//而BitmapFactory.decode()出来的则decode多少次生成多少张,务必自己进行recycle;//获取波形图waveBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.wave_2000)).getBitmap();//获取圆形遮罩图circleBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.circle_500)).getBitmap();//不断刷新波形图距离读者可以先不看这部分内容因为需要结合其他方法new Thread(){public void run() {while (true) {try {//移动波形图nowOffSet=nowOffSet+speek;//如果移动波形图的末尾那么重新来if (nowOffSet>=waveBitmap.getWidth()) {nowOffSet=0;}sleep(30);postInvalidate();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}};}.start();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//给图片直接设置过滤效果canvas.setDrawFilter(paintFlagsDrawFilter);//给图片上色canvas.drawColor(Color.TRANSPARENT);//添加图层注意!!!!!使用图片遮罩模式会影响全部此图层(也就是说在canvas.restoreToCount 所有图都会受到影响)int saveLayer = canvas.saveLayer(0,0, viewWidth,viewHeight,null, Canvas.ALL_SA VE_FLAG);//画波形图部分矩形waveSrcRect.set(nowOffSet, 0, nowOffSet+viewWidth/2, viewHeight);//画矩形canvas.drawBitmap(waveBitmap,waveSrcRect,waveDstRect,mpaint);//设置图片遮罩模式mpaint.setXfermode(mode);//画遮罩canvas.drawBitmap(circleBitmap, circleSrcRect, circleDstRect,mpaint);//还原画笔模式mpaint.setXfermode(null);//将图层放上canvas.restoreToCount(saveLayer);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {ged(w, h, oldw, oldh);//获取view宽高viewWidth = w;viewHeight = h ;//波形图的矩阵初始化waveSrcRect = new Rect();waveDstRect = new Rect(0,0,w,h);//圆球矩阵初始化circleSrcRect = new Rect(0,0,circleBitmap.getWidth(),circleBitmap.getHeight());circleDstRect = new Rect(0,0,viewWidth,viewHeight);}}学习效果2:此方法实现原理:运用三角函数画出两个不同速率正弦函数图我们先来复习三角函数吧正余弦函数方程为:y = Asin(wx+b)+h ,这个公式里:w影响周期,A影响振幅,h影响y位置,b为初相;w:周期就是一个完整正弦曲线图此数值越大sin的周期越小(cos越大)如下图:A:振幅两个山峰最大的高度.如果A越大两个山峰越高和越低h:你正弦曲线和y轴相交点.(影响正弦图初始高度的位置)b:初相会让你图片向x轴平移具体大家可以百度学习,我们在学编程,不是数学为什么要两个正弦图画?好看…..先来看看变量:// 波纹颜色private static final int WA VE_PAINT_COLOR = 0x880000aa;// 第一个波纹移动的速度private int oneSeep = 7;// 第二个波纹移动的速度private int twoSeep = 10;// 第一个波纹移动速度的像素值private int oneSeepPxil;// 第二个波纹移动速度的像素值private int twoSeepPxil;// 存放原始波纹的每个y坐标点private float wave[];// 存放第一个波纹的每一个y坐标点private float oneWave[];// 存放第二个波纹的每一个y坐标点private float twoWave[];// 第一个波纹当前移动的距离private int oneNowOffSet;// 第二个波纹当前移动的private int twoNowOffSet;// 振幅高度private int amplitude = 20;// 画笔private Paint mPaint;// 创建画布过滤private DrawFilter mDrawFilter;// view的宽度private int viewWidth;// view高度private int viewHeight;画初始的波形图并且保存到数组中// 大小改变@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh);// 获取view的宽高viewHeight = h;viewWidth = w;// 初始化保存波形图的数组wave = new float[w];oneWave = new float[w];twoWave = new float[w];// 设置波形图周期float zq = (float) (Math.PI * 2 / w);// 设置波形图的周期for (int i = 0; i < viewWidth; i++) {wave[i] = (float) (amplitude * Math.sin(zq * i));}}初始化各种// 初始化private void init() {// 创建画笔mPaint = new Paint();// 设置画笔颜色mPaint.setColor(W A VE_PAINT_COLOR);// 设置绘画风格为实线mPaint.setStyle(Style.FILL);// 抗锯齿mPaint.setAntiAlias(true);// 设置图片过滤波和抗锯齿mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);// 第一个波的像素移动值换算成手机像素值让其在各个手机移动速度差不多oneSeepPxil = dpChangPx(oneSeep);// 第二个波的像素移动值twoSeepPxil = dpChangPx(twoSeep);}// 绘画方法@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.setDrawFilter(mDrawFilter);oneNowOffSet =oneNowOffSet+oneSeepPxil;twoNowOffSet = twoNowOffSet+twoSeepPxil;if (oneNowOffSet>=viewWidth) {oneNowOffSet = 0;}if (twoNowOffSet>=viewWidth) {twoNowOffSet = 0;}//此方法会让两个保存波形图的数组更新头到NowOffSet变成尾部,尾部的变成头部实现动态移动reSet();Log.e("fmy", Arrays.toString(twoWave));for (int i = 0; i < viewWidth; i++) {canvas.drawLine(i, viewHeight, i, viewHeight-400-oneWave[i], mPaint);canvas.drawLine(i, viewHeight, i, viewHeight-400-twoWave[i], mPaint);}postInvalidate();}来看看能让两个数组重置的public void reSet() {// one是指走到此处的波纹的位置(这个理解方法看个人了)int one = viewWidth - oneNowOffSet;// 把未走过的波纹放到最前面进行重新拼接System.arraycopy(wave, oneNowOffSet, oneWave, 0, one);// 把已走波纹放到最后System.arraycopy(wave, 0, oneWave, one, oneNowOffSet);// one是指走到此处的波纹的位置(这个理解方法看个人了)int two = viewWidth - twoNowOffSet;// 把未走过的波纹放到最前面进行重新拼接System.arraycopy(wave, twoNowOffSet, twoWave, 0, two);// 把已走波纹放到最后System.arraycopy(wave, 0, twoWave, two, twoNowOffSet);}最后大家看下完整代码package com.exam1ple.myshuibo2;import java.util.Arrays;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.DrawFilter;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.PaintFlagsDrawFilter;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.Rect;import android.graphics.drawable.BitmapDrawable;import android.icu.text.TimeZoneFormat.ParseOption;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.util.Log;import android.view.View;import android.view.WindowManager;public class MyUi2 extends View {// 波纹颜色private static final int WA VE_PAINT_COLOR = 0x880000aa;// 第一个波纹移动的速度private int oneSeep = 7;// 第二个波纹移动的速度private int twoSeep = 10;// 第一个波纹移动速度的像素值private int oneSeepPxil;// 第二个波纹移动速度的像素值private int twoSeepPxil;// 存放原始波纹的每个y坐标点private float wave[];// 存放第一个波纹的每一个y坐标点private float oneWave[];// 存放第二个波纹的每一个y坐标点private float twoWave[];// 第一个波纹当前移动的距离private int oneNowOffSet;// 第二个波纹当前移动的private int twoNowOffSet;// 振幅高度private int amplitude = 20;// 画笔private Paint mPaint;// 创建画布过滤private DrawFilter mDrawFilter;// view的宽度private int viewWidth;// view高度private int viewHeight;// xml布局构造方法public MyUi2(Context context, AttributeSet attrs) {super(context, attrs);init();}// 初始化private void init() {// 创建画笔mPaint = new Paint();// 设置画笔颜色mPaint.setColor(W A VE_PAINT_COLOR);// 设置绘画风格为实线mPaint.setStyle(Style.FILL);// 抗锯齿mPaint.setAntiAlias(true);// 设置图片过滤波和抗锯齿mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);// 第一个波的像素移动值换算成手机像素值让其在各个手机移动速度差不多oneSeepPxil = dpChangPx(oneSeep);// 第二个波的像素移动值twoSeepPxil = dpChangPx(twoSeep);}// 绘画方法@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.setDrawFilter(mDrawFilter);oneNowOffSet =oneNowOffSet+oneSeepPxil;twoNowOffSet = twoNowOffSet+twoSeepPxil;if (oneNowOffSet>=viewWidth) {oneNowOffSet = 0;}if (twoNowOffSet>=viewWidth) {twoNowOffSet = 0;}reSet();Log.e("fmy", Arrays.toString(twoWave));for (int i = 0; i < viewWidth; i++) {canvas.drawLine(i, viewHeight, i, viewHeight-400-oneWave[i], mPaint);canvas.drawLine(i, viewHeight, i, viewHeight-400-twoWave[i], mPaint);}postInvalidate();}public void reSet() {// one是指走到此处的波纹的位置(这个理解方法看个人了)int one = viewWidth - oneNowOffSet;// 把未走过的波纹放到最前面进行重新拼接System.arraycopy(wave, oneNowOffSet, oneWave, 0, one);// 把已走波纹放到最后System.arraycopy(wave, 0, oneWave, one, oneNowOffSet);// one是指走到此处的波纹的位置(这个理解方法看个人了)int two = viewWidth - twoNowOffSet;// 把未走过的波纹放到最前面进行重新拼接System.arraycopy(wave, twoNowOffSet, twoWave, 0, two);// 把已走波纹放到最后System.arraycopy(wave, 0, twoWave, two, twoNowOffSet);}// 大小改变@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);// 获取view的宽高viewHeight = h;viewWidth = w;// 初始化保存波形图的数组wave = new float[w];oneWave = new float[w];twoWave = new float[w];// 设置波形图周期float zq = (float) (Math.PI * 2 / w);// 设置波形图的周期for (int i = 0; i < viewWidth; i++) {wave[i] = (float) (amplitude * Math.sin(zq * i));}}// dp换算成px 为了让移动速度在各个分辨率的手机的都差不多public int dpChangPx(int dp) {DisplayMetrics metrics = new DisplayMetrics();((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics( metrics);return (int) (metrics.density * dp + 0.5f);}}。
Android⾃定义Material进度条效果⾸先看下效果图布局⽂件:<LinearLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"xmlns:app="/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#ffffff"android:gravity="center"android:orientation="vertical"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin" ><com.example.mytest.view.CircleProgressBarandroid:id="@+id/progress1"android:layout_width="60dp"android:layout_height="60dp"app:mlpb_progress_color="#566da9"app:mlpb_progress_stoke_width="3dp" /></LinearLayout>声明属性<declare-styleable name="CircleProgressBar"><attr name="mlpb_inner_radius" format="dimension"/><attr name="mlpb_background_color" format="color"/><attr name="mlpb_progress_color" format="color"/><attr name="mlpb_progress_stoke_width" format="dimension"/><attr name="mlpb_show_arrow" format="boolean"/><attr name="mlpb_enable_circle_background" format="boolean"/><attr name="mlpb_arrow_width" format="dimension"/><attr name="mlpb_arrow_height" format="dimension"/><attr name="mlpb_progress" format="integer"/><attr name="mlpb_max" format="integer"/><attr name="mlpb_progress_text_size" format="dimension"/><attr name="mlpb_progress_text_color" format="color"/><!--<attr name="mlpb_progress_text_offset" format="dimension"/>--><attr name="mlpb_progress_text_visibility" format="enum"><enum name="visible" value="0"/><enum name="invisible" value="1"/></attr></declare-styleable>⾃定义控件:/** Copyright (C) 2014 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** /licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions andpackage com.example.mytest.view;import android.content.Context;import android.content.res.Resources;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RadialGradient;import android.graphics.Shader;import android.graphics.drawable.Drawable;import android.graphics.drawable.ShapeDrawable;import android.graphics.drawable.shapes.OvalShape;import .Uri;import android.support.v4.view.ViewCompat;import android.util.AttributeSet;import android.view.animation.Animation;import android.widget.ImageView;import com.example.mytest.R;/*** Private class created to work around issues with AnimationListeners being* called before the animation is actually complete and support shadows on older * platforms.*/public class CircleProgressBar extends ImageView {private static final int KEY_SHADOW_COLOR = 0x1E000000;private static final int FILL_SHADOW_COLOR = 0x3D000000;// PXprivate static final float X_OFFSET = 0f;private static final float Y_OFFSET = 1.75f;private static final float SHADOW_RADIUS = 3.5f;private static final int SHADOW_ELEVATION = 4;private static final int DEFAULT_CIRCLE_BG_LIGHT = 0xFFFAFAFA;private static final int DEFAULT_CIRCLE_DIAMETER = 56;private static final int STROKE_WIDTH_LARGE = 3;public static final int DEFAULT_TEXT_SIZE = 9;private Animation.AnimationListener mListener;private int mShadowRadius;private int mBackGroundColor;private int mProgressColor;private int mProgressStokeWidth;private int mArrowWidth;private int mArrowHeight;private int mProgress;private int mMax;private int mDiameter;private int mInnerRadius;private Paint mTextPaint;private int mTextColor;private int mTextSize;private boolean mIfDrawText;private boolean mShowArrow;private MaterialProgressDrawable mProgressDrawable;private ShapeDrawable mBgCircle;private boolean mCircleBackgroundEnabled;private int[] mColors = new int[]{Color.BLACK};public CircleProgressBar(Context context) {super(context);init(context, null, 0);}public CircleProgressBar(Context context, AttributeSet attrs) {super(context, attrs);init(context, attrs, 0);}super(context, attrs, defStyleAttr);init(context, attrs, defStyleAttr);}private void init(Context context, AttributeSet attrs, int defStyleAttr) {final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar, defStyleAttr, 0);// <attr name="mlpb_inner_radius" format="dimension"/>// <attr name="mlpb_background_color" format="color"/>// <attr name="mlpb_progress_color" format="color"/>// <attr name="mlpb_progress_stoke_width" format="dimension"/>// <attr name="mlpb_arrow_width" format="dimension"/>// <attr name="mlpb_arrow_height" format="dimension"/>//// <attr name="mlpb_progress" format="integer"/>// <attr name="mlpb_max" format="integer"/>////// <attr name="mlpb_progress_text_size" format="dimension"/>// <attr name="mlpb_progress_text_color" format="color"/>//// <attr name="mlpb_progress_text_offset" format="dimension"/>//// <attr name="mlpb_progress_text_visibility" format="enum">// <enum name="visible" value="0"/>// <enum name="invisible" value="1"/>// </attr>final float density = getContext().getResources().getDisplayMetrics().density;mBackGroundColor = a.getColor(R.styleable.CircleProgressBar_mlpb_background_color, DEFAULT_CIRCLE_BG_LIGHT);mProgressColor = a.getColor(R.styleable.CircleProgressBar_mlpb_progress_color, DEFAULT_CIRCLE_BG_LIGHT);mColors = new int[]{mProgressColor};mInnerRadius = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_mlpb_inner_radius, -1);mProgressStokeWidth = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_mlpb_progress_stoke_width, (int) (STROKE_WIDTH_LARGE * density));mArrowWidth = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_mlpb_arrow_width, -1);mArrowHeight = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_mlpb_arrow_height, -1);mTextSize = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_mlpb_progress_text_size, (int) (DEFAULT_TEXT_SIZE * density));mTextColor = a.getColor(R.styleable.CircleProgressBar_mlpb_progress_text_color, Color.BLACK);mShowArrow = a.getBoolean(R.styleable.CircleProgressBar_mlpb_show_arrow, false);mCircleBackgroundEnabled = a.getBoolean(R.styleable.CircleProgressBar_mlpb_enable_circle_background, true); mProgress = a.getInt(R.styleable.CircleProgressBar_mlpb_progress, 0);mMax = a.getInt(R.styleable.CircleProgressBar_mlpb_max, 100);int textVisible = a.getInt(R.styleable.CircleProgressBar_mlpb_progress_text_visibility, 1);if (textVisible != 1) {mIfDrawText = true;}mTextPaint = new Paint();mTextPaint.setStyle(Paint.Style.FILL);mTextPaint.setColor(mTextColor);mTextPaint.setTextSize(mTextSize);mTextPaint.setAntiAlias(true);a.recycle();mProgressDrawable = new MaterialProgressDrawable(getContext(), this);super.setImageDrawable(mProgressDrawable);}private boolean elevationSupported() {return android.os.Build.VERSION.SDK_INT >= 21;@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);if (!elevationSupported()) {setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight()+ mShadowRadius * 2);}}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);final float density = getContext().getResources().getDisplayMetrics().density;mDiameter = Math.min(getMeasuredWidth(), getMeasuredHeight());if (mDiameter <= 0) {mDiameter = (int) density * DEFAULT_CIRCLE_DIAMETER;}if (getBackground() == null && mCircleBackgroundEnabled) {final int shadowYOffset = (int) (density * Y_OFFSET);final int shadowXOffset = (int) (density * X_OFFSET);mShadowRadius = (int) (density * SHADOW_RADIUS);if (elevationSupported()) {mBgCircle = new ShapeDrawable(new OvalShape());ViewCompat.setElevation(this, SHADOW_ELEVATION * density);} else {OvalShape oval = new OvalShadow(mShadowRadius, mDiameter - mShadowRadius * 2);mBgCircle = new ShapeDrawable(oval);ViewCompat.setLayerType(this, YER_TYPE_SOFTWARE, mBgCircle.getPaint()); mBgCircle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,KEY_SHADOW_COLOR);final int padding = (int) mShadowRadius;// set padding so the inner image sits correctly within the shadow.setPadding(padding, padding, padding, padding);}mBgCircle.getPaint().setColor(mBackGroundColor);setBackgroundDrawable(mBgCircle);}mProgressDrawable.setBackgroundColor(mBackGroundColor);mProgressDrawable.setColorSchemeColors(mColors);mProgressDrawable.setSizeParameters(mDiameter, mDiameter,mInnerRadius <= 0 ? (mDiameter - mProgressStokeWidth * 2) / 4 : mInnerRadius,mProgressStokeWidth,mArrowWidth < 0 ? mProgressStokeWidth * 4 : mArrowWidth,mArrowHeight < 0 ? mProgressStokeWidth * 2 : mArrowHeight);if (isShowArrow()) {mProgressDrawable.showArrowOnFirstStart(true);mProgressDrawable.setArrowScale(1f);mProgressDrawable.showArrow(true);}super.setImageDrawable(null);super.setImageDrawable(mProgressDrawable);mProgressDrawable.setAlpha(255);if(getVisibility()==VISIBLE) {mProgressDrawable.start();}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (mIfDrawText) {String text = String.format("%s%%", mProgress);int x = getWidth() / 2 - text.length() * mTextSize / 4;int y = getHeight() / 2 + mTextSize / 4;canvas.drawText(text, x, y, mTextPaint);}}@Overridefinal public void setImageResource(int resId) {}return mShowArrow;}public void setShowArrow(boolean showArrow) {this.mShowArrow = showArrow;}@Overridefinal public void setImageURI(Uri uri) {super.setImageURI(uri);}@Overridefinal public void setImageDrawable(Drawable drawable) {}public void setAnimationListener(Animation.AnimationListener listener) {mListener = listener;}@Overridepublic void onAnimationStart() {super.onAnimationStart();if (mListener != null) {mListener.onAnimationStart(getAnimation());}}@Overridepublic void onAnimationEnd() {super.onAnimationEnd();if (mListener != null) {mListener.onAnimationEnd(getAnimation());}}/*** Set the color resources used in the progress animation from color resources. * The first color will also be the color of the bar that grows in response* to a user swipe gesture.** @param colorResIds*/public void setColorSchemeResources(int... colorResIds) {final Resources res = getResources();int[] colorRes = new int[colorResIds.length];for (int i = 0; i < colorResIds.length; i++) {colorRes[i] = res.getColor(colorResIds[i]);}setColorSchemeColors(colorRes);}/*** Set the colors used in the progress animation. The first* color will also be the color of the bar that grows in response to a user* swipe gesture.** @param colors*/public void setColorSchemeColors(int... colors) {mColors = colors;if (mProgressDrawable != null) {mProgressDrawable.setColorSchemeColors(colors);}}/*** Update the background color of the mBgCircle image view.*/public void setBackgroundColor(int colorRes) {if (getBackground() instanceof ShapeDrawable) {final Resources res = getResources();public boolean isShowProgressText() {return mIfDrawText;}public void setShowProgressText(boolean mIfDrawText) {this.mIfDrawText = mIfDrawText;}public int getMax() {return mMax;}public void setMax(int max) {mMax = max;}public int getProgress() {return mProgress;}public void setProgress(int progress) {if (getMax() > 0) {mProgress = progress;}}public boolean circleBackgroundEnabled() {return mCircleBackgroundEnabled;}public void setCircleBackgroundEnabled(boolean enableCircleBackground) { this.mCircleBackgroundEnabled = enableCircleBackground;}@Overridepublic int getVisibility() {return super.getVisibility();}@Overridepublic void setVisibility(int visibility) {super.setVisibility(visibility);if (mProgressDrawable != null) {mProgressDrawable.setVisible(visibility == VISIBLE, false);if (visibility != VISIBLE) {mProgressDrawable.stop();} else {if (mProgressDrawable.isRunning()) {mProgressDrawable.stop();}mProgressDrawable.start();}}}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();if (mProgressDrawable != null) {mProgressDrawable.stop();mProgressDrawable.setVisible(getVisibility() == VISIBLE, false);requestLayout();}}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();if (mProgressDrawable != null) {mProgressDrawable.stop();private class OvalShadow extends OvalShape {private RadialGradient mRadialGradient;private int mShadowRadius;private Paint mShadowPaint;private int mCircleDiameter;public OvalShadow(int shadowRadius, int circleDiameter) {super();mShadowPaint = new Paint();mShadowRadius = shadowRadius;mCircleDiameter = circleDiameter;mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2,mShadowRadius, new int[]{FILL_SHADOW_COLOR, Color.TRANSPARENT}, null, Shader.TileMode.CLAMP);mShadowPaint.setShader(mRadialGradient);}@Overridepublic void draw(Canvas canvas, Paint paint) {final int viewWidth = CircleProgressBar.this.getWidth();final int viewHeight = CircleProgressBar.this.getHeight();canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius),mShadowPaint);canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);}}}在java代码中设置进度条上的颜⾊值progress1 = (CircleProgressBar) findViewById(R.id.progress1);progress1.setColorSchemeResources(android.R.color.holo_green_light,android.R.color.holo_orange_light,android.R.color.holo_red_light); 以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
Android⾃定义控件:进度条的四种实现⽅式(ProgressWheel的解析)最近⼀直在学习⾃定义控件,搜了许多⼤⽜们Blog⾥分享的⼩教程,也上GitHub找了⼀些类似的控件进⾏学习。
发现读起来都不太好懂,就想写这么⼀篇东西作为学习笔记吧。
⼀、控件介绍:进度条在App中⾮常常见,例如下载进度、加载图⽚、打开⽂章、打开⽹页等等……都需要这么⼀个效果让⽤户知道我们的App正在读取,以构造良好的交互。
如果没有这样⼀个效果的话,⽤户没法知道东西有没有下载好、图⽚加载了没有、⽂章打开了没……会让⽤户很不爽。
基于这样的情景我们的UI设计师们创造了这样⼀个控件。
⼆、这篇⽂章会涉及的知识点:跟我⼀样刚⼊门的Android菜鸟们,我推荐⼤家先了解⼀下这些知识点再往下看。
这些知识点我也会推荐⼀些博客给⼤家看看,更推荐⼤家看⽂档⾥的解释,当然⼤⽜们可以直接⽆视……1、ClipDrawable类:能够对⼀个drawable类进⾏剪切操作(即只显⽰某⼀部分的区域,另⼀部分隐藏),显⽰多⼤的区域由level控制(level取值是0~10000)【博客:/lonelyroamer/article/details/8244777】、没⽂档的可以在这看【/api/android/ClipDrawable.html】2、⾃定义View:guolin⼤神的深⼊学习View四部曲【 —— /guolin_blog/article/details/12921889】【 —— /guolin_blog/article/details/16330267】【 —— /guolin_blog/article/details/17045157】——/guolin_blog/article/details/17357967】3、没看过我写的:Android⾃定义控件——⽼版优酷三级菜单的话,或许需要看看这个:【RotateAnimation详解——】三、Android上的实现⽅式:(前三种⽅法⽐较简单,第四种⽅法是GitHub项⽬的解析,对前三种没兴趣可以直接跳到后边……)1、效果图:将进度条的变换过程分解为⼀帧⼀帧的图⽚,将这些⼀帧⼀帧的图⽚连起来构成⼀个动画。
1 2 3 4 5 6 7 ng.Object↳ android.view.View↳ android.widget.ProgressBar 直接子类AbsSeekBar间接子类RatingBar, SeekBar在某些操作的进度中的可视指示器,为用户呈现操作的进度,还它有一个次要的进度条,用来显示中间进度,如在流媒体播放的缓冲区的进度。
一个进度条也可不确定其进度。
在不确定模式下,进度条显示循环动画。
这种模式常用于应用程序使用任务的长度是未知的。
进度条也就是一个表示运转的过程,例如发送短信,连接网络等等,表示一个过程正在执行中1、android.widget. ProgressBar,继承自android.view.View 。
在android.widget包中。
对应对话框ProgressDialog。
ProgressBar有两种展示方式,表盘形式(普通、小、大)和条形填充形式。
在layout定义时,需要通过设施style属性类设置展示方式。
ProgressBar的样式有四种:android:progressBarStyle:默认进度条样式,不确定模式android:progressBarStyleHorizontal:水平进度条样式android:progressBarStyleLarge :大号进度条样式,也是不确定进度模式android:progressBarStyleSmall :小号进度条样式,也是不确定进度模式二、XML重要属性android:max-- 这事进度条长度最大值android:progress--设定度条当前进度值android:secondaryProgress--第二进度条进度值android:progressBarStyle:默认进度条样式android:progressBarStyleHorizontal:水平样式style="?android:attr/progressBarStyleLarge" --- 属性风格类型--大圆圈,如下图style=”?android:attr/progressBarStyleSmall”--- 属性风格类型--小圆圈,如下图:style="?android:attr/progressBarStyleHorizontal" --水平进度条 --如下图:几秒钟之后自动滚到到如下:也可以用下面的形式代替上面的形式的:? 1 2 3 <ProgressBar style="@android:style/Widget.ProgressBar.Inverse"/>//中<ProgressBar style="@android:style/rge.Inverse"/> //大圆 <ProgressBar style="@android:style/Widget.ProgressBar.Small.Inverse"/> //小圆三、重要方法getMax():返回这个进度条的范围的上限getProgress():返回当前进度值getSecondaryProgress():返回次要当前进度值incrementProgressBy(int diff):指定增加的进度--即步长isIndeterminate():指示进度条是否在不确定模式下setIndeterminate(boolean indeterminate):设置不确定模式下setVisibility(int v):设置该进度条是否可视四、重要事件onSizeChanged(int w, int h, int oldw, int oldh):当进度值改变时引发此事件接下来看案例:1.定义一个布局文件progressbar.xml?1234 <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="/apk/res/android" android:layout_width="fill_parent"6 7 8 9101112131415161718192021222324252627282930313233343536373839404142434445464748><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/startText"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="垂直的----标题上面也有一个进度条哦"android:textColor="#CD0000"android:background="#BC8F8F"/><!-- style=”?android:attr/progressBarStyleLarge”大圆圈 --><ProgressBarandroid:id="@+id/progtessBer_btn_id1"android:layout_width="wrap_content"android:layout_height="wrap_content"style="?android:attr/progressBarStyleLarge"/><!-- style=”?android:attr/progressBarStyleSmall”小圆圈 --><ProgressBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"style="?android:attr/progressBarStyleSmall"android:layout_gravity="center_horizontal"/><TextViewandroid:id="@+id/startText1"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="水平的"android:textColor="#aaaaaa"/><!-- style="?android:attr/progressBarStyleHorizontal" 水平进度条 --> <ProgressBarandroid:id="@+id/progtessBer_btn_id2"android:layout_width="fill_parent"android:layout_height="wrap_content"style="?android:attr/progressBarStyleHorizontal"/><TextViewandroid:layout_width="fill_parent"50515253android:text="@string/progress_text"/></LinearLayout></ScrollView>2.之后定义java文件:ProgressBarDemo.java ?1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637 package com.dream.app.start.first.prograssbar;import com.dream.app.start.MenuDemo;import com.dream.app.start.R;import com.dream.app.start.R.id;import yout;import com.dream.app.start.utils.PublicClass;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.os.Handler;import android.text.method.ScrollingMovementMethod;import android.view.View;import android.view.View.OnClickListener;import android.view.Window;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;public class ProgressBarDemo extends PublicClass {private ProgressBar progressbar,progressbar_1;Button btn1,btn2;private int prostatus=0;//创建一个handler对象private Handler handler=new Handler();@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);//在标题条里放置进度条。
Android实现圆圈扩散⽔波动画效果两种⽅法两种⽅式实现类似⽔波扩散效果,先上图为敬1. ⾃定义view实现2. 动画实现⾃定义view实现思路分析:通过canvas画圆,每次改变圆半径和透明度,当半径达到⼀定程度,再次从中⼼开始绘圆,达到不同层级的效果,通过不断绘制达到view扩散效果private Paint centerPaint; //中⼼圆paintprivate int radius = 100; //中⼼圆半径private Paint spreadPaint; //扩散圆paintprivate float centerX;//圆⼼xprivate float centerY;//圆⼼yprivate int distance = 5; //每次圆递增间距private int maxRadius = 80; //最⼤圆半径private int delayMilliseconds = 33;//扩散延迟间隔,越⼤扩散越慢private List<Integer> spreadRadius = new ArrayList<>();//扩散圆层级数,元素为扩散的距离private List<Integer> alphas = new ArrayList<>();//对应每层圆的透明度style⽂件⾥⾃定义属性<declare-styleable name="SpreadView"><!--中⼼圆颜⾊--><attr name="spread_center_color" format="color" /><!--中⼼圆半径--><attr name="spread_radius" format="integer" /><!--扩散圆颜⾊--><attr name="spread_spread_color" format="color" /><!--扩散间距--><attr name="spread_distance" format="integer" /><!--扩散最⼤半径--><attr name="spread_max_radius" format="integer" /><!--扩散延迟间隔--><attr name="spread_delay_milliseconds" format="integer" /></declare-styleable>初始化public SpreadView(Context context) {this(context, null, 0);}public SpreadView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public SpreadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SpreadView, defStyleAttr, 0);radius = a.getInt(R.styleable.SpreadView_spread_radius, radius);maxRadius = a.getInt(R.styleable.SpreadView_spread_max_radius, maxRadius);int centerColor = a.getColor(R.styleable.SpreadView_spread_center_color, ContextCompat.getColor(context, R.color.colorAccent)); int spreadColor = a.getColor(R.styleable.SpreadView_spread_spread_color, ContextCompat.getColor(context, R.color.colorAccent)); distance = a.getInt(R.styleable.SpreadView_spread_distance, distance);a.recycle();centerPaint = new Paint();centerPaint.setColor(centerColor);centerPaint.setAntiAlias(true);//最开始不透明且扩散距离为0alphas.add(255);spreadRadius.add(0);spreadPaint = new Paint();spreadPaint.setAntiAlias(true);spreadPaint.setAlpha(255);spreadPaint.setColor(spreadColor);}确定圆⼼位置@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);//圆⼼位置centerX = w / 2;centerY = h / 2;}⾃定义view的绘制@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);for (int i = 0; i < spreadRadius.size(); i++) {int alpha = alphas.get(i);spreadPaint.setAlpha(alpha);int width = spreadRadius.get(i);//绘制扩散的圆canvas.drawCircle(centerX, centerY, radius + width, spreadPaint);//每次扩散圆半径递增,圆透明度递减if (alpha > 0 && width < 300) {alpha = alpha - distance > 0 ? alpha - distance : 1;alphas.set(i, alpha);spreadRadius.set(i, width + distance);}}//当最外层扩散圆半径达到最⼤半径时添加新扩散圆if (spreadRadius.get(spreadRadius.size() - 1) > maxRadius) {spreadRadius.add(0);alphas.add(255);}//超过8个扩散圆,删除最先绘制的圆,即最外层的圆if (spreadRadius.size() >= 8) {alphas.remove(0);spreadRadius.remove(0);}//中间的圆canvas.drawCircle(centerX, centerY, radius, centerPaint);//TODO 可以在中间圆绘制⽂字或者图⽚//延迟更新,达到扩散视觉差效果postInvalidateDelayed(delayMilliseconds);}xml样式<com.airsaid.diffuseview.widget.SpreadViewandroid:id="@+id/spreadView"android:layout_width="match_parent"android:layout_height="wrap_content"app:spread_center_color="@color/colorAccent"app:spread_delay_milliseconds="35"app:spread_distance="5"app:spread_max_radius="90"app:spread_radius="100"app:spread_spread_color="@color/colorAccent" />效果图中⼼圆处可以⾃定义写⽂字,画图⽚等等...动画实现思路分析:通过动画实现,imageView不停做动画缩放+渐变最中⼼的imageView保持不变中间⼀层imageView从原始放⼤到1.4倍,同时从不透明变为半透明最外层的imageView从1.4倍放⼤到1.8倍,同时从半透明变为全透明利⽤shape画⼀个圆,作为动画基础视图<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="/apk/res/android"><corners android:radius="65dp"/><solid android:color="@color/colorAccent"/></shape>布局视图<FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><!--中⼼imageView--><ImageViewandroid:id="@+id/iv_wave"android:layout_width="130dp"android:layout_height="130dp"android:layout_gravity="center"android:background="@drawable/shape_circle" /><!--中间的imageView--><ImageViewandroid:id="@+id/iv_wave_1"android:layout_width="130dp"android:layout_height="130dp"android:layout_gravity="center"android:background="@drawable/shape_circle" /><!--最外层imageView--><ImageViewandroid:id="@+id/iv_wave_2"android:layout_width="130dp"android:layout_height="130dp"android:layout_gravity="center"android:background="@drawable/shape_circle" /></FrameLayout>中间imageView的动画private void setAnim1() {AnimationSet as = new AnimationSet(true);//缩放动画,以中⼼从原始放⼤到1.4倍ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.4f, 1.0f, 1.4f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f,ScaleAnimation.RELATIVE_TO_SELF, 0.5f);//渐变动画AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.5f);scaleAnimation.setDuration(800);scaleAnimation.setRepeatCount(Animation.INFINITE);alphaAnimation.setRepeatCount(Animation.INFINITE);as.setDuration(800);as.addAnimation(scaleAnimation);as.addAnimation(alphaAnimation);iv1.startAnimation(as);}最外层imageView的动画private void setAnim2() {AnimationSet as = new AnimationSet(true);//缩放动画,以中⼼从1.4倍放⼤到1.8倍ScaleAnimation scaleAnimation = new ScaleAnimation(1.4f, 1.8f, 1.4f, 1.8f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f,ScaleAnimation.RELATIVE_TO_SELF, 0.5f);//渐变动画AlphaAnimation alphaAnimation = new AlphaAnimation(0.5f, 0.1f);scaleAnimation.setDuration(800);scaleAnimation.setRepeatCount(Animation.INFINITE);alphaAnimation.setRepeatCount(Animation.INFINITE);as.setDuration(800);as.addAnimation(scaleAnimation);as.addAnimation(alphaAnimation);iv2.startAnimation(as);}效果图相⽐较⽽⾔,⾃定义view的效果更好点,动画实现起来更⽅便点。
android 自定义view-水波纹进度球在我们的日常开发中自定义控件还是用的挺多的,设计师或者产品为了更好的漂亮,美观,交互都会做一些牛逼的ui效果图,但是最后实现的还是我们程序员啊。
所以说自定义view你还是得会的。
今天我们要实现的这个view没有太多交互性的view,所以就继承view自定义view的套路,套路很深∙获取我们自定义属性attrs(可省略)∙重写onMeasure方法,计算控件的宽和高∙重写onDraw方法,绘制我们的控件这么看来,自定义view的套路很清晰嘛。
我们看下今天的效果图,其中一个是放慢的效果(时间调的长)我们按照套路来。
一.自定义属性<declare-styleable name="WaveProgressView"><attr name="radius" format="dimension|reference" /><attr name="radius_color" format="color|reference" /><attr name="progress_text_color" format="color|reference" /><attr name="progress_text_size" format="dimension|reference" /> <attr name="progress_color" format="color|reference" /><attr name="progress" format="float" /><attr name="maxProgress" format="float" /></declare-styleable>看下效果图我们就知道因该需要哪些属性。
就不说了。
然后就是获取我们的这些属性,就是用TypedArray来获取。
当然是在构造中获取,一般我们会复写构造方法,少参数调用参数多的,然后走到参数最多的那个。
下面是获取自定义属性的代码:TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.WaveProgressView, defStyleAttr,R.style.WaveProgressViewDefault);radius = (int)a.getDimension(R.styleable.WaveProgressView_radius, radius);textColor =a.getColor(R.styleable.WaveProgressView_progress_text_color, 0);textSize =a.getDimensionPixelSize(R.styleable.WaveProgressView_progress_text_si ze, 0);progressColor =a.getColor(R.styleable.WaveProgressView_progress_color, 0);radiusColor =a.getColor(R.styleable.WaveProgressView_radius_color, 0);progress = a.getFloat(R.styleable.WaveProgressView_progress, 0); maxProgress =a.getFloat(R.styleable.WaveProgressView_maxProgress, 100);a.recycle();注:R.style.WaveProgressViewDefault是这个控件的默认样式。
二.onMeasure测量我们重写这个方法主要是根据父控件的宽和高来设置自己的宽和高。
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//计算宽和高int exceptW = getPaddingLeft() + getPaddingRight() + 2 * radius;int exceptH = getPaddingTop() + getPaddingBottom() + 2 * radius;int width = resolveSize(exceptW, widthMeasureSpec);int height = resolveSize(exceptH, heightMeasureSpec);int min = Math.min(width, height);this.width = this.height = min;//计算半径,减去padding的最小值int minLR = Math.min(getPaddingLeft(), getPaddingRight());int minTB = Math.min(getPaddingTop(), getPaddingBottom());minPadding = Math.min(minLR, minTB);radius = (min - minPadding * 2) / 2;setMeasuredDimension(min, min);}首先该控件的宽和高肯定是一样的,因为是个圆嘛。
其实是宽和高与半径和内边距(padding)有关,这里的内边距,我们取上下左右最小的一个。
宽和高也选择取最小的。
this.width = this.height = min; 包含左右边距。
resolveSize这个方法很好的为我们实现了我们想要的宽和高我慢看下源码。
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {finalint specMode = MeasureSpec.getMode(measureSpec);finalint specSize = MeasureSpec.getSize(measureSpec);finalint result;switch (specMode) {case MeasureSpec.AT_MOST:if (specSize < size) {result = specSize | MEASURED_STATE_TOO_SMALL;} else {result = size;}break;case MeasureSpec.EXACTLY:result = specSize;break;case MeasureSpec.UNSPECIFIED:default:result = size;}return result | (childMeasuredState & MEASURED_STATE_MASK);}如果我们自己写也是这样写。
最后通过setMeasuredDimension设置宽和高。
三.onDraw绘制关于绘制有很多android 提供了很多API,这里就不多说了。
绘制首先就是一些画笔的初始化。
需要提一下绘制path路径的画笔设置为PorterDuff.Mode.SRC_IN模式,这个模式只显示重叠的部分。
pathPaint = newPaint(Paint.ANTI_ALIAS_FLAG);pathPaint.setColor(progressColor);pathPaint.setDither(true);pathPaint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC_IN));我们要将所有的绘制绘制到一个透明的bitmap上,然后将这个bitmap绘制到canvas上。
if (bitmap == null) {bitmap = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888);bitmapCanvas = newCanvas(bitmap);}为了方便计算和绘制,我将坐标系平移padding的距离bitmapCanvas.save();//移动坐标系bitmapCanvas.translate(minPadding, minPadding);// .... some thingbitmapCanvas.restore();3.1绘制圆bitmapCanvas.drawCircle(radius, radius, radius, circlePaint);3.2绘制PATH 路径.一是要实现波纹的左右飘,和上下的振幅慢慢的减小绘制这个之前我们需要知道二阶贝塞尔曲线的大致原理。
简单的说就是知道:P1起始点,P2是终点,P1是控制点.利用塞尔曲线的公式就可以得道沿途的一些点,最后把点连起来就是喽。
下面这个图片来于网络:二阶贝塞尔曲线在android-sdk里提供了绘制贝塞尔曲线的函数rQuadTo方法public void rQuadTo(float dx1, float dy1, float dx2, float dy2)∙dx1:控制点X坐标,表示相对上一个终点X坐标的位移坐标,可为负值,正值表示相加,负值表示相减;∙dy1:控制点Y坐标,相对上一个终点Y坐标的位移坐标。
同样可为负值,正值表示相加,负值表示相减;∙dx2:终点X坐标,同样是一个相对坐标,相对上一个终点X坐标的位移值,可为负值,正值表示相加,负值表示相减;∙dy2:终点Y坐标,同样是一个相对,相对上一个终点Y坐标的位移值。
可为负值,正值表示相加,负值表示相减;这四个参数都是传递的都是相对值,相对上一个终点的位移值。
要实现振幅慢慢的减小我们可以调节控制点的y坐标即可,即:float percent=progress * 1.0f / maxProgress;就可以得到[0,1]的一个闭区间,[0,1]这货好啊,我喜欢,可以来做很多事情。
这样我们就可以根据percent来调节控制点的y坐标了。