android 自定义view-水波纹进度球
Android 自定义View——动态进度条
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自定义组件(手机加速球+水面波动效果)
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用法-回复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实现水平带数字百分比进度条
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>以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
AndroidProgressBar自定义图片进度,自定义渐变色进度条
在某些操作的进度中的可视指示器,为用户呈现操作的进度,还它有一个次要的进度条,用来显示中间进度,如在流媒体播放的缓冲区的进度。
一个进度条也可不确定其进度。
在不确定模式下,进度条显示循环动画。
这种模式常用于应用程序使用任务的长度是未知的。
进度条也就是一个表示运转的过程,例如发送短信,连接网络等等,表示一个过程正在执行中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实现水波纹效果
自定义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进度条效果
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的解析)
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、效果图:将进度条的变换过程分解为⼀帧⼀帧的图⽚,将这些⼀帧⼀帧的图⽚连起来构成⼀个动画。
Android ProgressBar自定义图片进度,自定义渐变色进度条
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实现圆圈扩散水波动画效果两种方法
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的效果更好点,动画实现起来更⽅便点。
自定义view的绘制流程
自定义view的绘制流程自定义View的绘制流程。
在Android开发中,自定义View是非常常见的需求,通过自定义View可以实现各种炫酷的效果,满足各种个性化的设计需求。
但是,自定义View的绘制流程并不是那么简单,需要我们对View的绘制机制有一定的了解,才能够高效地实现自定义View的绘制。
本文将从自定义View的基本概念开始,逐步介绍自定义View的绘制流程。
1. 自定义View的基本概念。
自定义View是指在Android开发中,通过继承View或者ViewGroup来创建自定义的UI控件。
通过自定义View,我们可以实现各种各样的UI效果,比如自定义的按钮样式、进度条样式、图表样式等。
自定义View的核心就是重写View或者ViewGroup的绘制方法,以实现我们所需的UI效果。
2. 自定义View的绘制流程。
自定义View的绘制流程可以分为以下几个步骤:(1)onMeasure,测量View的大小。
在onMeasure方法中,我们需要通过调用setMeasuredDimension方法来设置View的大小。
在这个方法中,我们需要考虑View的宽度和高度,以及View的测量模式。
通常情况下,我们需要根据View的内容和父容器的大小来计算View的大小,并根据测量模式来确定View的最终大小。
(2)onLayout,确定View的位置。
在onLayout方法中,我们需要通过调用layout方法来确定View在父容器中的位置。
在这个方法中,我们需要考虑View的左上右下四个位置,以及父容器的大小和布局方式,来确定View的最终位置。
(3)onDraw,绘制View的内容。
在onDraw方法中,我们需要通过Canvas来绘制View的内容。
在这个方法中,我们可以使用各种绘制方法来绘制文字、图形、图片等内容,以实现我们所需的UI效果。
在绘制过程中,我们还可以通过Paint来设置绘制的样式、颜色、字体等属性,以实现更加丰富的UI效果。
花样AndroidProgressBar史上最强大讲解定制ProgressBar
花样AndroidProgressBar史上最强大讲解定制ProgressBar展开全文1.1 定制动画文件[html] view plain copy1.<ProgressBar2.android:layout_width="wrap_content"3.android:layout_height="wrap_content"4.android:indeterminateDrawable="@drawable/progress_ my_style"5.style="?android:attr/progressBarStyle"6./>注:style="?android:attr/progressBarStyle" 这是默认样式,可换改step2: 在drawable文件夹下建立progress_my_style.xml文件:内容可如下:[html] view plain copy1.<animated-rotate xmlns:android="/apk/res/android"2.android:drawable="@drawable/spinner_color"3.android:pivotX="50%"4.android:pivotY="50%"5./>注意:1. android:drawable="@drawable/spinner_color" 这里,你需要在drawable下建立spinner_color.png图片(自己画成你所想到的效果(渐变))2. progress_my_style.xml文件内容参考至D:\andrirod\android-sdk-windows\platforms\android-7\data\res\drawable\progress_medium_white.xml文件内容, android:framesCount与android:frameDuratiion是frameworks内部的属性,无法直接使用,所以省略)以上是在XML中直接定义,下面是我在代码里直接定义的:[java] view plaincopy1.mProgressBar.setIndeterminateDrawable(AppConfig.getR esources().getDrawable(R.drawable.style_common_processbar));style_common_processbar.xml还是需要定义在xml里的[html] view plaincopy1.<?xml version="1.0"encoding="UTF-8"?>2.<animated-rotate3.android:drawable="@drawable/icon"4.android:pivotX="50.0%"5.android:pivotY="50.0%"6.xmlns:android="/apk/res/android">7.</animated-rotate>结果是就是用图片icon旋转来表示进度,图片制作参考android 的 drawable_hdpi 下的 spinner_black_48.png1.2 定制颜色和形状利用shape来定制颜色和形状[java] view plaincopy1.// 定制模式2.mProgressBar.setIndeterminateDrawable(AppConfig.getR esources().getDrawable(R.drawable.style_common_processbar));style_common_processbar.xml[html] view plaincopy1.<?xml version="1.0"encoding="UTF-8"?>2.<rotate3.xmlns:android="/apk/res/android"4.android:duration="30"5.android:fromDegrees="0.0"6.android:toDegrees="360.0"7.android:pivotX="50.0%"8.android:pivotY="50.0%"9.android:repeatCount="infinite">10.<shape11.android:shape="ring"12.android:innerRadiusRatio="3.2"13.android:thicknessRatio="5.333"14.android:useLevel="false">15.<size16.android:height="16.0dip"17.android:width="16.0dip"/>18.<gradient19.android:startColor="#4cffffff"20.android:endColor="#ffff0000"21.android:useLevel="false"22.android:type="sweep"23.android:centerY="0.5"24.android:centerColor="#4cffffff"/>25.</shape>26.</rotate>通过修改android:shape=["rectangle" | "oval" | "line" | "ring"] 来修改形状gradient来修改颜色属性2.progress mode | 有百分比进度的进度条代码调用:mProgressBar.setProgressDrawable(AppConfig.getResource s().getDrawable(R.drawable.color));color.xml[html] view plaincopy1.<?xml version="1.0"encoding="UTF-8"?>2.<layer-list xmlns:android="/apk/res/android">3.<item android:id="@android:id/background"android:dr awable="@drawable/bg"/>4.<item android:id="@android:id/secondaryProgress"and roid:drawable="@drawable/secondary"/>5.<item android:id="@android:id/progress"android:drawa ble="@drawable/progress"/>6.</layer-list>/xpwang168/article/details/6678465。
Android通过自定义view实现水波纹效果案例详解
Android通过⾃定义view实现⽔波纹效果案例详解在实际的开发中,很多时候还会遇到相对⽐较复杂的需求,⽐如产品妹纸或UI妹纸在哪看了个让⼈兴奋的效果,兴致⾼昂的来找你,看了之后⽬的很明确,当然就是希望你能给她;在这样的关键时候,⾝⼦板就⼀定得硬了,可千万别说不⾏,爷们⼉怎么能说不⾏呢;好了,为了让⼤家都能给妹纸们想要的,后⾯会逐渐分享⼀些⽐较⽐较不错的效果,⽬的只有⼀个,通过⾃定义view实现我们所能实现的动效;今天主要分享⽔波纹效果:1. 标准正余弦⽔波纹;2. ⾮标准圆形液柱⽔波纹;虽说都是⽔波纹,但两者在实现上差异是⽐较⼤的,⼀个通过正余弦函数模拟⽔波纹效果,另外⼀个会运⽤到图像的混合模式(PorterDuffXfermode);先看效果:⾃定义View根据实际情况可以选择继承⾃View、TextView、ImageView或其他,我们先只需要了解如何利⽤android给我们提供好的利刃去满⾜UI妹纸;这次的实现我们都选择继承view,在实现的过程中我们需要关注如下⼏个⽅法:1. onMeasure():最先回调,⽤于控件的测量;2. onSizeChanged():在onMeasure后⾯回调,可以拿到view的宽⾼等数据,在横竖屏切换时也会回调;3. onDraw():真正的绘制部分,绘制的代码都写到这⾥⾯;既然如此,我们先复写这三个⽅法,然后来实现如上两个效果;⼀:标准正余弦⽔波纹这种⽔波纹可以⽤具体函数模拟出具体的轨迹,所以思路基本如下:1. 确定⽔波函数⽅程2. 根据函数⽅程得出每⼀个波纹上点的坐标;3. 将⽔波进⾏平移,即将⽔波上的点不断的移动;4. 不断的重新绘制,⽣成动态⽔波纹;有了上⾯的思路,我们⼀步⼀步进⾏实现:正余弦函数⽅程为:y = Asin(wx+b)+h ,这个公式⾥:w影响周期,A影响振幅,h影响y位置,b为初相;根据上⾯的⽅程选取⾃⼰觉得中意的波纹效果,确定对应参数的取值;然后根据确定好的⽅程得出所有的⽅程上y的数值,并将所有y值保存在数组⾥:// 将周期定为view总宽度mCycleFactorW = (float) (2 * Math.PI / mTotalWidth);// 根据view总宽度得出所有对应的y值for (int i = 0; i < mTotalWidth; i++) {mYPositions[i] = (float) (STRETCH_FACTOR_A * Math.sin(mCycleFactorW * i) + OFFSET_Y);}根据得出的所有y值,则可以在onDraw中通过如下代码绘制两条静态波纹:for (int i = 0; i < mTotalWidth; i++) {// 减400只是为了控制波纹绘制的y的在屏幕的位置,⼤家可以改成⼀个变量,然后动态改变这个变量,从⽽形成波纹上升下降效果// 绘制第⼀条⽔波纹canvas.drawLine(i, mTotalHeight - mResetOneYPositions[i] - 400, i,mTotalHeight,mWavePaint);// 绘制第⼆条⽔波纹canvas.drawLine(i, mTotalHeight - mResetTwoYPositions[i] - 400, i,mTotalHeight,mWavePaint);}这种⽅式类似于数学⾥⾯的细分法,⼀条波纹,如果横向以⼀个像素点为单位进⾏细分,则形成view总宽度条直线,并且每条直线的起点和终点我们都能知道,在此基础上我们只需要循环绘制出所有细分出来的直线(直线都是纵向的),则形成了⼀条静态的⽔波纹;接下来我们让⽔波纹动起来,之前⽤了⼀个数组保存了所有的y值点,有两条⽔波纹,再利⽤两个同样⼤⼩的数组来保存两条波纹的y值数据,并不断的去改变这两个数组中的数据:private void resetPositonY() {// mXOneOffset代表当前第⼀条⽔波纹要移动的距离int yOneInterval = mYPositions.length - mXOneOffset;// 使⽤System.arraycopy⽅式重新填充第⼀条波纹的数据System.arraycopy(mYPositions, mXOneOffset, mResetOneYPositions, 0, yOneInterval);System.arraycopy(mYPositions, 0, mResetOneYPositions, yOneInterval, mXOneOffset);int yTwoInterval = mYPositions.length - mXTwoOffset;System.arraycopy(mYPositions, mXTwoOffset, mResetTwoYPositions, 0,yTwoInterval);System.arraycopy(mYPositions, 0, mResetTwoYPositions, yTwoInterval, mXTwoOffset);}如此下来只要不断的改变这两个数组的数据,然后不断刷新,即可⽣成动态⽔波纹了;刷新可以调⽤invalidate()或postInvalidate(),区别在于后者可以在⼦线程中更新UI整体代码如下:public class DynamicWave extends View {// 波纹颜⾊private static final int WAVE_PAINT_COLOR = 0x880000aa;// y = Asin(wx+b)+hprivate static final float STRETCH_FACTOR_A = 20;private static final int OFFSET_Y = 0;// 第⼀条⽔波移动速度private static final int TRANSLATE_X_SPEED_ONE = 7;// 第⼆条⽔波移动速度private static final int TRANSLATE_X_SPEED_TWO = 5;private float mCycleFactorW;private int mTotalWidth, mTotalHeight;private float[] mYPositions;private float[] mResetOneYPositions;private float[] mResetTwoYPositions;private int mXOffsetSpeedOne;private int mXOffsetSpeedTwo;private int mXOneOffset;private int mXTwoOffset;private Paint mWavePaint;private DrawFilter mDrawFilter;public DynamicWave(Context context, AttributeSet attrs) {super(context, attrs);// 将dp转化为px,⽤于控制不同分辨率上移动速度基本⼀致mXOffsetSpeedOne = UIUtils.dipToPx(context, TRANSLATE_X_SPEED_ONE);mXOffsetSpeedTwo = UIUtils.dipToPx(context, TRANSLATE_X_SPEED_TWO);// 初始绘制波纹的画笔mWavePaint = new Paint();// 去除画笔锯齿mWavePaint.setAntiAlias(true);// 设置风格为实线mWavePaint.setStyle(Style.FILL);// 设置画笔颜⾊mWavePaint.setColor(WAVE_PAINT_COLOR);mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 从canvas层⾯去除绘制时锯齿canvas.setDrawFilter(mDrawFilter);resetPositonY();for (int i = 0; i < mTotalWidth; i++) {// 减400只是为了控制波纹绘制的y的在屏幕的位置,⼤家可以改成⼀个变量,然后动态改变这个变量,从⽽形成波纹上升下降效果 // 绘制第⼀条⽔波纹canvas.drawLine(i, mTotalHeight - mResetOneYPositions[i] - 400, i,mTotalHeight,mWavePaint);// 绘制第⼆条⽔波纹canvas.drawLine(i, mTotalHeight - mResetTwoYPositions[i] - 400, i,mTotalHeight,mWavePaint);}// 改变两条波纹的移动点mXOneOffset += mXOffsetSpeedOne;mXTwoOffset += mXOffsetSpeedTwo;// 如果已经移动到结尾处,则重头记录if (mXOneOffset >= mTotalWidth) {mXOneOffset = 0;}if (mXTwoOffset > mTotalWidth) {mXTwoOffset = 0;}// 引发view重绘,⼀般可以考虑延迟20-30ms重绘,空出时间⽚postInvalidate();}private void resetPositonY() {// mXOneOffset代表当前第⼀条⽔波纹要移动的距离int yOneInterval = mYPositions.length - mXOneOffset;// 使⽤System.arraycopy⽅式重新填充第⼀条波纹的数据System.arraycopy(mYPositions, mXOneOffset, mResetOneYPositions, 0, yOneInterval);System.arraycopy(mYPositions, 0, mResetOneYPositions, yOneInterval, mXOneOffset);int yTwoInterval = mYPositions.length - mXTwoOffset;System.arraycopy(mYPositions, mXTwoOffset, mResetTwoYPositions, 0,yTwoInterval);System.arraycopy(mYPositions, 0, mResetTwoYPositions, yTwoInterval, mXTwoOffset);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);// 记录下view的宽⾼mTotalWidth = w;mTotalHeight = h;// ⽤于保存原始波纹的y值mYPositions = new float[mTotalWidth];// ⽤于保存波纹⼀的y值mResetOneYPositions = new float[mTotalWidth];// ⽤于保存波纹⼆的y值mResetTwoYPositions = new float[mTotalWidth];// 将周期定为view总宽度mCycleFactorW = (float) (2 * Math.PI / mTotalWidth);// 根据view总宽度得出所有对应的y值for (int i = 0; i < mTotalWidth; i++) {mYPositions[i] = (float) (STRETCH_FACTOR_A * Math.sin(mCycleFactorW * i) + OFFSET_Y);}}⼆:⾮标准圆形液柱⽔波纹前⾯的波形使⽤函数模拟,这个我们换种⽅式,采⽤图进⾏实现,先⽤PS整张不像波纹的波纹图;为了衔接紧密,⾸尾都⽐较平,并⾼度⼀致;思路:1. 使⽤⼀个圆形图作为遮罩过滤波形图;2. 平移波纹图,即不断改变绘制的波纹图的区域,即srcRect;3. 当⼀个周期绘制完,则从波纹图的最前⾯重新计算;⾸先初始化bitmap:private void initBitmap() {mSrcBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.wave_2000)).getBitmap();mMaskBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.circle_500)).getBitmap();}使⽤drawable获取的⽅式,全局只会⽣成⼀份,并且系统会进⾏管理,⽽BitmapFactory.decode()出来的则decode多少次⽣成多少张,务必⾃⼰进⾏recycle;然后绘制波浪和遮罩图,绘制时设置对应的混合模式:/** 将绘制操作保存到新的图层*/int sc = canvas.saveLayer(0, 0, mTotalWidth, mTotalHeight, null, Canvas.ALL_SAVE_FLAG);// 设定要绘制的波纹部分mSrcRect.set(mCurrentPosition, 0, mCurrentPosition + mCenterX, mTotalHeight);// 绘制波纹部分canvas.drawBitmap(mSrcBitmap, mSrcRect, mDestRect, mBitmapPaint);// 设置图像的混合模式mBitmapPaint.setXfermode(mPorterDuffXfermode);// 绘制遮罩圆canvas.drawBitmap(mMaskBitmap, mMaskSrcRect, mMaskDestRect,mBitmapPaint);mBitmapPaint.setXfermode(null);canvas.restoreToCount(sc);为了形成动态的波浪效果,单开⼀个线程动态更新要绘制的波浪的位置:new Thread() {public void run() {while (true) {// 不断改变绘制的波浪的位置mCurrentPosition += mSpeed;if (mCurrentPosition >= mSrcBitmap.getWidth()) {mCurrentPosition = 0;}try {// 为了保证效果的同时,尽可能将cpu空出来,供其他部分使⽤Thread.sleep(30);} catch (InterruptedException e) {}postInvalidate();}};}.start();主要过程就以上这些,全部代码如下:public class PorterDuffXfermodeView extends View {private static final int WAVE_TRANS_SPEED = 4;private Paint mBitmapPaint, mPicPaint;private int mTotalWidth, mTotalHeight;private int mCenterX, mCenterY;private int mSpeed;private Bitmap mSrcBitmap;private Rect mSrcRect, mDestRect;private PorterDuffXfermode mPorterDuffXfermode;private Bitmap mMaskBitmap;private Rect mMaskSrcRect, mMaskDestRect;private PaintFlagsDrawFilter mDrawFilter;private int mCurrentPosition;public PorterDuffXfermodeView(Context context, AttributeSet attrs) {super(context, attrs);initPaint();initBitmap();mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);mSpeed = UIUtils.dipToPx(mContext, WAVE_TRANS_SPEED);mDrawFilter = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, Paint.DITHER_FLAG);new Thread() {public void run() {while (true) {// 不断改变绘制的波浪的位置mCurrentPosition += mSpeed;if (mCurrentPosition >= mSrcBitmap.getWidth()) {mCurrentPosition = 0;}try {// 为了保证效果的同时,尽可能将cpu空出来,供其他部分使⽤Thread.sleep(30);} catch (InterruptedException e) {}postInvalidate();}};}.start();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 从canvas层⾯去除锯齿canvas.setDrawFilter(mDrawFilter);canvas.drawColor(Color.TRANSPARENT);/** 将绘制操作保存到新的图层*/int sc = canvas.saveLayer(0, 0, mTotalWidth, mTotalHeight, null, Canvas.ALL_SAVE_FLAG);// 设定要绘制的波纹部分mSrcRect.set(mCurrentPosition, 0, mCurrentPosition + mCenterX, mTotalHeight);// 绘制波纹部分canvas.drawBitmap(mSrcBitmap, mSrcRect, mDestRect, mBitmapPaint);// 设置图像的混合模式mBitmapPaint.setXfermode(mPorterDuffXfermode);// 绘制遮罩圆canvas.drawBitmap(mMaskBitmap, mMaskSrcRect, mMaskDestRect,mBitmapPaint);mBitmapPaint.setXfermode(null);canvas.restoreToCount(sc);}// 初始化bitmapprivate void initBitmap() {mSrcBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.wave_2000)).getBitmap();mMaskBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.circle_500)).getBitmap();}// 初始化画笔paintprivate void initPaint() {mBitmapPaint = new Paint();// 防抖动mBitmapPaint.setDither(true);// 开启图像过滤mBitmapPaint.setFilterBitmap(true);mPicPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPicPaint.setDither(true);mPicPaint.setColor(Color.RED);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mTotalWidth = w;mTotalHeight = h;mCenterX = mTotalWidth / 2;mCenterY = mTotalHeight / 2;mSrcRect = new Rect();mDestRect = new Rect(0, 0, mTotalWidth, mTotalHeight);int maskWidth = mMaskBitmap.getWidth();int maskHeight = mMaskBitmap.getHeight();mMaskSrcRect = new Rect(0, 0, maskWidth, maskHeight);mMaskDestRect = new Rect(0, 0, mTotalWidth, mTotalHeight);}}到此这篇关于Android 通过⾃定义view实现⽔波纹效果案例详解的⽂章就介绍到这了,更多相关Android 通过⾃定义view实现⽔波纹效果内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。
Android七种进度条的样式
Android七种进度条的样式当⼀个应⽤在后台执⾏时,前台界⾯就不会有什么信息,这时⽤户根本不知道程序是否在执⾏、执⾏进度如何、应⽤程序是否遇到错误终⽌等,这时需要使⽤进度条来提⽰⽤户后台程序执⾏的进度。
Android系统提供了两⼤类进度条样式,长形进度条(progress-BarStyleHorizontal)和圆形进度条(progressBarStyleLarge)。
进度条⽤处很多,⽐如,应⽤程序装载资源和⽹络连接时,可以提⽰⽤户稍等,这⼀类进度条只是代表应⽤程序中某⼀部分的执⾏情况,⽽整个应⽤程序执⾏情况呢,则可以通过应⽤程序标题栏来显⽰⼀个进度条,这就需要先对窗⼝的显⽰风格进⾏设置"requestWindowFeature(Window.FEATURE_PROGRESS)"。
先看下⾯效果图:例1:(默认样式(中等圆形))Xml代码<ProgressBarandroid:id="@+id/progressBar1"android:layout_width="wrap_content"android:layout_height="wrap_content"/>例2:(超⼤圆形)例1:(默认样式(中等圆形))Xml代码<ProgressBarandroid:id="@+id/progressBar1"android:layout_width="wrap_content"android:layout_height="wrap_content"/>例2:(超⼤圆形)Xml代码<ProgressBarandroid:id="@+id/progressBar2"android:layout_width="wrap_content"android:layout_height="wrap_content"style="?android:attr/progressBarStyleLarge"/>例3:(⼩号圆形)Xml代码<ProgressBarandroid:id="@+id/progressBar3"android:layout_width="wrap_content"android:layout_height="wrap_content"style="?android:attr/progressBarStyleSmall"/>例4:(标题⼩号圆形)Xml代码<ProgressBarandroid:id="@+id/progressBar4"android:layout_width="wrap_content"android:layout_height="wrap_content"style="?android:attr/progressBarStyleSmallTitle"/>例4-在标题中使⽤⼩号圆形的使⽤代码:Java代码@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//设置标题不确定性进度条风格requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(yout.progress_bars);//显⽰标题不确定性进度条setProgressBarIndeterminateVisibility(true);//关闭标题不确定性进度条//setProgressBarIndeterminateVisibility(false);}例5:(长⽅形进度条)Xml代码<ProgressBarandroid:id="@+id/progressBar5"android:layout_width="200dp"android:layout_height="wrap_content"style="?android:attr/progressBarStyleHorizontal"android:max="100"android:progress="50"android:secondaryProgress="70"/>android:max="100" 最⼤进度值100android:progress="50" 当前初始化进度值50android:secondaryProgress="70" 当前初始化第2进度值70例5-在标题中使⽤长⽅形进度条的代码:Java代码@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//设置标题进度条风格requestWindowFeature(Window.FEATURE_PROGRESS);setContentView(yout.progress_bars);//显⽰标题进度setProgressBarVisibility(true);//设置标题当前进度值为5000(标题进度最⼤值默认为10000)setProgress(5000);//关闭标题进度//setProgressBarVisibility(false);}例6:(进度对话框-圆形进度条)Java代码ProgressDialog dialog = new ProgressDialog(this);//设置进度条风格,风格为圆形,旋转的dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);//设置ProgressDialog 标题dialog.setTitle("进度对话框");//设置ProgressDialog 提⽰信息dialog.setMessage("圆形进度条");//设置ProgressDialog 标题图标dialog.setIcon(android.R.drawable.ic_dialog_map);//设置ProgressDialog 的⼀个Buttondialog.setButton("确定", new ProgressDialog.OnClickListener(){@Overridepublic void onClick(DialogInterface dialog, int which) {}});//设置ProgressDialog 的进度条是否不明确dialog.setIndeterminate(false);//设置ProgressDialog 是否可以按退回按键取消dialog.setCancelable(true);//显⽰dialog.show();例7:(进度对话框-长⽅形进度条)Java代码ProgressDialog dialog = new ProgressDialog(this);//设置进度条风格,风格为圆形,旋转的dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//设置ProgressDialog 标题dialog.setTitle("进度对话框");//设置ProgressDialog 提⽰信息dialog.setMessage("长⽅形进度条");//设置ProgressDialog 标题图标dialog.setIcon(android.R.drawable.ic_dialog_alert);//设置ProgressDialog的最⼤进度dialog.setMax(100);//设置ProgressDialog 的⼀个Buttondialog.setButton("确定", new ProgressDialog.OnClickListener(){@Overridepublic void onClick(DialogInterface dialog, int which) {}});//设置ProgressDialog 是否可以按退回按键取消dialog.setCancelable(true);//显⽰dialog.show();//设置ProgressDialog的当前进度dialog.setProgress(50);通过本篇⽂章介绍了Android 七种进度条的样式,希望⼤家喜欢。
Android之给控件添加水纹波效果
Android之给控件添加水纹波效果1 问题给控件添加水纹波效果,点击起来像点中了,不然效果太粗糙了,没反应。
2 实现给控件添加如下属性android:background="?android:attr/selectableItemBackgro und"波纹有边界android:background="?android:attr/selectableItemBackgro undBorderless"波纹超出边界设置水纹波颜色android:colorControlHighlight:3 需要注意地方1)在哪个控件上点击需要有效果必须设置为点击事件所以只需要为View 设置android:clickable="true"或者.setOnClickListener(null);2)自己测试双层view包裹没效果,比如下面<LinearLayoutandroid:background="?android:attr/selectableItemBackground"> <LinearLayout><AppCompatTextView></AppCompatTextView></LinearLayout></LinearLayout>3) 自己测试单层view包裹有效果,比如下面<LinearLayout><LinearLayoutandroid:background="?android:attr/selectableItemBackground"> <AppCompatTextView></AppCompatTextView></LinearLayout></LinearLayout>。
Android自定义View实现圆形进度条
Android⾃定义View实现圆形进度条本⽂实例为⼤家分享了Android⾃定义View实现圆形进度条的具体代码,供⼤家参考,具体内容如下效果如下:主要代码CircularProgressView.javapublic class CircularProgressView extends View {private Paint mBackPaint, mProgPaint; // 绘制画笔private RectF mRectF; // 绘制区域private int[] mColorArray; // 圆环渐变⾊private int mProgress; // 圆环进度(0-100)/*** 绘制弧线的画笔*/private Paint progressPaint;/*** 圆弧圆⼼位置*/private int centerX, centerY;/*** 圆弧的半径*/private int circleRadius;public CircularProgressView(Context context) {this(context, null);}public CircularProgressView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public CircularProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);@SuppressLint("Recycle")TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircularProgressView);// 初始化背景圆环画笔mBackPaint = new Paint();mBackPaint.setStyle(Paint.Style.STROKE); // 只描边,不填充mBackPaint.setStrokeCap(Paint.Cap.ROUND); // 设置圆⾓mBackPaint.setAntiAlias(true); // 设置抗锯齿mBackPaint.setDither(true); // 设置抖动mBackPaint.setStrokeWidth(typedArray.getDimension(R.styleable.CircularProgressView_backWidth, 5));mBackPaint.setColor(typedArray.getColor(R.styleable.CircularProgressView_progbgColor, Color.LTGRAY));// 初始化进度圆环画笔mProgPaint = new Paint();mProgPaint.setStyle(Paint.Style.STROKE); // 只描边,不填充mProgPaint.setStrokeCap(Paint.Cap.ROUND); // 设置圆⾓mProgPaint.setAntiAlias(true); // 设置抗锯齿mProgPaint.setDither(true); // 设置抖动mProgPaint.setStrokeWidth(typedArray.getDimension(R.styleable.CircularProgressView_progWidth, 10));mProgPaint.setColor(typedArray.getColor(R.styleable.CircularProgressView_progColor, Color.BLUE));//初始化结束位置⼩圆点progressPaint = new Paint();progressPaint.setStyle(Paint.Style.FILL); // 填充progressPaint.setStrokeCap(Paint.Cap.ROUND); // 设置圆⾓progressPaint.setAntiAlias(true); // 设置抗锯齿progressPaint.setDither(true); // 设置抖动progressPaint.setStrokeWidth(typedArray.getDimension(R.styleable.CircularProgressView_progWidth, 10));progressPaint.setColor(Color.WHITE);// 初始化进度圆环渐变⾊int startColor = typedArray.getColor(R.styleable.CircularProgressView_progStartColor, -1);int firstColor = typedArray.getColor(R.styleable.CircularProgressView_progFirstColor, -1);if (startColor != -1 && firstColor != -1) mColorArray = new int[]{startColor, firstColor};else mColorArray = null;// 初始化进度mProgress = typedArray.getInteger(R.styleable.CircularProgressView_progress, 0);typedArray.recycle();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int viewWide = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();int viewHigh = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();int mRectLength = (int) ((viewWide > viewHigh ? viewHigh : viewWide) - (mBackPaint.getStrokeWidth() > mProgPaint.getStrokeWidth() ? mBackPaint.getStrokeWidth() : mProgPaint.getStrokeWidth())); int mRectL = getPaddingLeft() + (viewWide - mRectLength) / 2;int mRectT = getPaddingTop() + (viewHigh - mRectLength) / 2;mRectF = new RectF(mRectL, mRectT, mRectL + mRectLength, mRectT + mRectLength);centerX = getMeasuredWidth() / 2;centerY = getMeasuredHeight() / 2;//计算圆弧半径和圆⼼点circleRadius = Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2;circleRadius-=8;// 设置进度圆环渐变⾊if (mColorArray != null && mColorArray.length > 1)mProgPaint.setShader(new LinearGradient(0, 0, 0, getMeasuredWidth(), mColorArray, null, Shader.TileMode.MIRROR));}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawArc(mRectF, 0, 360, false, mBackPaint);canvas.drawArc(mRectF, 270, 360 * mProgress / 100, false, mProgPaint);//绘制结束位置⼩圆形progressPaint.setStrokeWidth(10);progressPaint.setStyle(Paint.Style.FILL);float swipe = 360 * mProgress / 100;Log.d("=================", swipe + " mProgress");float radians = (float) (((swipe - 90) / 2) / 180 * 2 * Math.PI);float endX;float endY;endX = centerX + circleRadius * (float) Math.cos(radians);endY = centerY + circleRadius * (float) Math.sin(radians);if (mProgress!=0) {canvas.drawCircle(endX, endY, 8, progressPaint);}}/*** 获取当前进度** @return 当前进度(0-100)*/public int getProgress() {return mProgress;}/*** 设置当前进度** @param progress 当前进度(0-100)*/public void setProgress(int progress) {this.mProgress = progress;invalidate();}/*** 设置当前进度,并展⽰进度动画。
Android开发之进度条ProgressBar的示例代码
Android开发之进度条ProgressBar的⽰例代码说明ProgressBar⼀般⽤于显⽰⼀个过程,例如数据加载过程,⽂件下载进度,⾳乐播放进度等。
默认形式ProgressBar默认⽅式下,ProgressBar显⽰为圆形进度,循环转圈,不显⽰具体的进度值,控制其显隐藏即可,如下适⽤于界⾯加载//xml中<ProgressBarandroid:layout_width="wrap_content"android:layout_height="wrap_content" />//代码中控制显隐藏mProgressBar = (ProgressBar) findViewById(R.id.progress_bar_main);mProgressBar.setVisibility(View.VISIBLE);横向ProgressBar横向带进度的进度条,通过设置ProgressBar的Style为style="?android:attr/progressBarStyleHorizontal"max属性指定进度条总进度值,progress设置当前进度值,也可以说是初始进度值//xml中<ProgressBarandroid:id="@+id/progress_bar_h"style="?android:attr/progressBarStyleHorizontal"android:layout_width="85dp"android:layout_height="10dp"android:layout_gravity="center"android:max="100"android:progress="50"/>设进度条背景系统⾃带的进度条的颜⾊⽐较单调,实际开发中使⽤较少,可以⾃定义进度条背景,新建⼀个progressbar_bg.xml⽂件<?xml version="1.0" encoding="UTF-8"?><layer-list xmlns:android="/apk/res/android"><!-- 设置背景⾊ --><item android:id="@android:id/background"android:drawable="@mipmap/feed_grow_progress_bar"></item><!-- 设置进度条颜⾊ --><item android:id="@android:id/progress"><clip><shape><gradientandroid:endColor="#fff000"android:startColor="#fff000" /></shape></clip></item></layer-list>gradient可以设置进度条的渐变⾊, android:endColor和 android:startColor可以设置渐变开始和结束的颜⾊。
详解Android如何自定义view实现圆形进度条
详解Android如何⾃定义view实现圆形进度条Android中实现进度条有很多种⽅式,⾃定义进度条⼀般是继承progressBar或继承view来实现,本篇中讲解的是第⼆种⽅式。
先上效果图:实现圆形进度条总体来说并不难,还是跟往常⼀样继承view,初始化画笔,按下⾯的步骤⼀步步来就好了。
对初学者来说动画效果可能⽐较陌⽣,我们可以使⽤属性动画中的valueAnimator来实现动画效果。
实现步骤:1、画出⼀个灰⾊的圆环作为背景。
2、画出上层的圆环覆盖下⽅的圆环。
3、加⼊动画效果值得注意的是怎么设置圆环和⽂字的位置。
画出矩形只需要传⼊矩形对⾓线的坐标即可,如果不加以处理的话画出来的圆环的边缘是不完整的,刚开始接触⾃定义view 的同学们⼀定要先好好看看Android坐标系相关内容,不然很难理解位置参数为什么这样设置。
完整代码:package com.example.floatingwindow.widget;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.util.AttributeSet;import android.util.TypedValue;import android.view.View;import android.view.animation.DecelerateInterpolator;import androidx.annotation.Nullable;import com.example.floatingwindow.R;public class ProgressBarView extends View {private Paint mPaintBack;private Paint mPaint;private Paint mPaintText;private float process;private int strokeWidth = 15;private int textSize = 20;private long duration = 3000;private float startDegree = 0;private float endDegree = 360;private String text = "完成";private String defaultText = "0%";public ProgressBarView(Context context) {super(context);init();}public ProgressBarView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public ProgressBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mPaintBack = new Paint();mPaintBack.setColor(getResources().getColor(R.color.gray));mPaintBack.setStyle(Paint.Style.STROKE);mPaintBack.setAntiAlias(true);mPaintBack.setStrokeCap(Paint.Cap.ROUND);mPaintBack.setStrokeWidth(strokeWidth);mPaint = new Paint();mPaint.setColor(getResources().getColor(R.color.purple_200));mPaint.setStyle(Paint.Style.STROKE);mPaint.setAntiAlias(true);mPaint.setStrokeCap(Paint.Cap.ROUND);mPaint.setStrokeWidth(strokeWidth);mPaintText = new Paint();mPaintText.setAntiAlias(true);mPaintText.setStyle(Paint.Style.STROKE);mPaintText.setColor(Color.BLACK);mPaintBack.setStrokeCap(Paint.Cap.ROUND);mPaintText.setTextSize(sp2px(textSize));}public void setStrokeWidth(int width) {strokeWidth = width;}public void setTextSize(int textSize) {this.textSize = textSize;}public void setDuration(long duration) {this.duration = duration;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//创建圆环矩形RectF rectF = new RectF(strokeWidth, strokeWidth, getWidth() - strokeWidth, getHeight() - strokeWidth); //画出灰⾊进度条作为背景canvas.drawArc(rectF, 0, 360, false, mPaintBack);//画进度条canvas.drawArc(rectF, 0, process, false, mPaint);//计算进度int percent = (int) (process / 360 * 100);//设置⽂字在canvas中的位置Paint.FontMetrics fm = mPaintText.getFontMetrics();int mTxtWidth = (int) mPaintText.measureText(text, 0, defaultText.length());int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);int x = getWidth() / 2 - mTxtWidth / 2;int y = getHeight() / 2 + mTxtHeight / 4;if (percent < 100) {canvas.drawText(percent + "%", x, y, mPaintText);} else {canvas.drawText(text, x, y, mPaintText);}}/*** 设置动画效果*/public void start() {ValueAnimator valueAnimator = ValueAnimator.ofFloat(startDegree, endDegree);valueAnimator.setDuration(duration);valueAnimator.setInterpolator(new DecelerateInterpolator());valueAnimator.addUpdateListener(animation -> {process = (float) animation.getAnimatedValue();invalidate();});valueAnimator.start();}private int sp2px(int sp) {return (int) TypedValue.applyDimension(PLEX_UNIT_SP, sp,getResources().getDisplayMetrics());}}最后就是动画效果了,使⽤valueanimator,传⼊开始和结束的进度以及执⾏时间。
Android实现波浪球效果
Android实现波浪球效果波浪球的效果⼀直都是想模仿的对象,在最近⼀段时间⾥模仿了这⼀界⾯,其实所⽤知识并不多。
1)、波浪的效果是利⽤三⾓函数来实现的,在⾃定义view中创建容量为width的数组,由y=Asin(Kx+T)+H得到每个x相对应的y值,然后存⼊数组⾥⾯。
2)、利⽤Android中Canvas提供的drawLine来从上部向下画线,每个像素上画完线之后就组成了图像。
3)、⾃定义view中的变量都与width建⽴了⽐例关系,可以任意⼤⼩,且效果⼀致。
4)、分别对y=Asin(Kx+T)+H中的T与H做valueAnimation的数字动画,就能实现波浪和上升的效果。
5)、利⽤Android中Paint的setXfermode来实现圆形效果。
6)、给外部提供了外部接⼝,可以对内部状态进⾏改变。
具体代码如下:1 界⾯XML代码:<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:id="@+id/activity_wave_view"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.app_switchbutton.WaveViewActivity"><Buttonandroid:id="@+id/begainButtton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="开始"android:layout_below="@+id/waveview"android:layout_alignParentStart="true" /><com.example.app_switchbutton.waveViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/waveview"android:layout_alignParentTop="true"android:layout_alignParentStart="true"android:layout_marginStart="11dp" /></RelativeLayout>2、waveView的java代码:package com.example.app_switchbutton;import android.animation.ValueAnimator;import android.content.Context;import android.content.pm.ProviderInfo;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;/*** Created by 尽途 on 2017/4/30.*/public class waveView extends View {private int widthSize;private int heightSize;private float[] mContentOneYs=null;private float[] mContentTwoYs=null;private float[] restoreOnes=null;private float[] restoreTwos=null;private float n=0.5f;private int SWINGONE;private int SWINGTWO;private int OFFSETONE=0;private int OFFSETTWO=0;private Paint mPaint1;private Paint mPaint2;private Paint circlePaint;private Canvas bitmapCanvas;private Bitmap bitmap;private float endValue;public waveView(Context context){super(context);init();}public waveView(Context context, AttributeSet attributeSet){super(context,attributeSet);init();}private void init(){mPaint1=new Paint();mPaint1.setColor(Color.parseColor("#AB9DCF"));mPaint1.setStrokeWidth(4);mPaint1.setAlpha(240);mPaint1.setStyle(Paint.Style.FILL);mPaint1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));mPaint2=new Paint();mPaint2.setColor(Color.parseColor("#A2D1F3"));mPaint2.setStrokeWidth(4);mPaint2.setAlpha(240);mPaint2.setStyle(Paint.Style.FILL);mPaint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));circlePaint=new Paint();circlePaint.setAntiAlias(true);circlePaint.setColor(Color.GRAY);// circlePaint.setAlpha(56);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {widthSize=MeasureSpec.getSize(widthMeasureSpec);heightSize=widthSize;setMeasuredDimension(widthSize,heightSize);bitmap=Bitmap.createBitmap(widthSize,heightSize, Bitmap.Config.ARGB_8888);//实现圆球效果 bitmapCanvas=new Canvas(bitmap);SWINGONE=widthSize/20;SWINGTWO=widthSize/10;}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);getPoints();}private void getPoints(){mContentOneYs=new float[widthSize];mContentTwoYs=new float[widthSize];restoreOnes=new float[widthSize];restoreTwos=new float[widthSize];for (int i=0;i<widthSize;i++){mContentOneYs[i]=getposition1(i,SWINGONE,OFFSETONE,(int)(widthSize*n));mContentTwoYs[i]=getposition2(i,SWINGTWO,OFFSETTWO,(int)(widthSize*n));}}private float getposition1(int x,int swing,int offset,int baseHeight){float cycle=(float)(2*Math.PI)/widthSize;return (float)Math.sin(cycle*x+offset)*swing+baseHeight;}private float getposition2(int x,int swing,int offset,int baseHeight){float cycle=(float)(2*Math.PI)/widthSize;return (float)Math.cos(cycle*x+offset)*swing+baseHeight;}@Overrideprotected void onDraw(Canvas canvas) {bitmapCanvas.drawCircle(widthSize/2,heightSize/2,widthSize/2,circlePaint);//实现圆球效果canvas.save();getPoints();for (int i=0;i<widthSize;i++){final float x=i;final float y1=mContentOneYs[i];final float y2=mContentTwoYs[i];bitmapCanvas.drawLine(x,y1,x,heightSize,mPaint2);//实现了线的绘制最终⽣成了图,在画的球上画的线 bitmapCanvas.drawLine(x,y2,x,heightSize,mPaint1);}canvas.drawBitmap(bitmap,0,0,null);//将球画在了主界⾯的View中。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
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坐标了。