自定义View实现炫酷进度条
2017-03-19 23:29
141 查看
本篇博客主要介绍利用自定义View来实现自己想要的进度加载效果,其中涉及到的内容不会说的很多,但是我会稍微解释一下,其实只要有了思路都好处理。而且个人感觉自己写一个自定义View要比看别人写的自定义View要简单一点,因为你很明白里面属性的意义,看别人的自定义View还需要不断的判断每个属性的作用,然后是各种计算什么的,感觉不是很好!!(这个炫酷的进度条不是我的想法,是我在网上看到的,不过很不好意思,我当时只是从他的git仓库中把代码clone下来,没有保存原文链接,如果作者看到了,可以告诉我一下,我会将原文链接重新添加上去)。不过这篇的自定义View的实现,是我自己写的,没有完全拷贝作者的代码!!!详细情况可以看一下我的代码!!
好了,废话不多说了,先看一下效果图:
具体的代码的执行效果就是如上图所示,效果不是很好,还有非常大的优化空间,有兴趣的朋友可以自己更改我的代码,或者直接推倒重写,谢谢!!!
主布局文件:
在这里主要是将我自定义的View引入到布局中,就不做过多的介绍了!!!
Activity文件:
使用一个定时器来模拟加载时value变化,根据时间不断的调整Value值,给人进度不断变化的感觉。这里的cv就是我们自定义的View。
CustomView文件(这个很关键,主要就是这个文件):
在文件刚开始的时候,定义了各种宽高,比如叶子每次水平移动的距离、垂直移动的距离,旋转的角度、旋转的方向等。基本上来说就是你想控制什么样的动作,就需要你定义什么样的属性,通过属性控制对象的动作。这里的三个图片都是提前准备好的。然后在view中获取这个单个图片对应的bitmap,并获取每个bitmap的宽高,这里的宽高用于以后确定这些bitmap的绘制位置。
在实际的绘制过程中,还需要考虑图层的关系,因为需要有叶子融入的感觉,所以在这里把绘制叶子的步骤放在最前面,这样实现的了叶子融入的感觉。
其次,自定义View中的动画都是通过控制矩阵来实现,比如旋转、移动等。但是字体放大的动画,我试过矩阵,但是不起作用,最后不得已通过改变字体尺寸来实现,希望有知道的朋友可以告诉我一下,不胜感激!!!
其实,其他的动画还好,就是叶子的动画不太好弄,因为需要一种,叶子随机出现的效果,我这里做的不好,希望大神们可以帮忙更改一下!!原作者的叶子动画是与时间关联的,我没有采用,只是限定了叶子出现的位置。具体的情况,还请麻烦各位看代码吧!!!
好了,关于自定义View,暂时就说到这里,以后还将继续介绍有关于自定义View方面的内容,希望大家可以关一下!!!
这是我的微信公众号,如果可以的话,希望您可以帮忙关注一下,这将是对我最大的鼓励了,谢谢!!
代码地址:
https://github.com/zhuyuqiang2017/CustomView
好了,废话不多说了,先看一下效果图:
具体的代码的执行效果就是如上图所示,效果不是很好,还有非常大的优化空间,有兴趣的朋友可以自己更改我的代码,或者直接推倒重写,谢谢!!!
主布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" 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.administrator.customview.MainActivity"> <com.example.administrator.customview.CustomView android:id="@+id/pb" android:layout_width="wrap_content" android:layout_height="wrap_content"> </com.example.administrator.customview.CustomView> <Button android:layout_marginTop="20dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="startProgressAnimation" android:layout_below="@id/pb" android:text="@string/start_animation"/> </RelativeLayout>
在这里主要是将我自定义的View引入到布局中,就不做过多的介绍了!!!
Activity文件:
package com.example.administrator.customview; import android.os.CountDownTimer; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; public class MainActivity extends AppCompatActivity { private CustomView cv; private int progressValue = 0; private boolean mProgressIsRunning = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); cv = (CustomView) findViewById(R.id.pb); } public void startProgressAnimation(View view){ if (mProgressIsRunning){ timer.cancel(); timer.onFinish(); }else{ mProgressIsRunning = true; if (progressValue>=100){ progressValue = 0; } timer.start(); } } private CountDownTimer timer = new CountDownTimer(Integer.MAX_VALUE,100) { @Override public void onTick(long millisUntilFinished) { cv.setProgressValue(progressValue); progressValue ++; } @Override public void onFinish() { mProgressIsRunning = false; } }; }
使用一个定时器来模拟加载时value变化,根据时间不断的调整Value值,给人进度不断变化的感觉。这里的cv就是我们自定义的View。
CustomView文件(这个很关键,主要就是这个文件):
package com.example.administrator.customview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Random; /** * Created by zhuyuqiang on 2017/3/13. */ public class CustomView extends View { private final int WHITE_COLOR = Color.WHITE; private final int ORANGE_COLOR = 0xffffa800; //确定叶子旋转方向 private final int ROTATE_RIGHT = 0; private final int ROTATE_LEFT = 1; private final int ROTATE_DEGREE = 5; //确定叶子垂直方向的振幅大小 private final int SMALL_AMPLITUDE = 0; private final int MIDDLE_AMPLITUDE = 1; private final int BIG_AMPLITUDE = 2; private final int SMALL_Y = 1; private final int MIDDLE_Y = 2; private final int BIG_Y = 3; //确定叶子水平方向的移动大小 private final int SMALL_TRANSITION = 0; private final int MIDDLE_TRANSITION = 1; private final int BIG_TRANSITION = 2; private final int SMALL_X = 5; private final int MIDDLE_X = 10; private final int BIG_X = 15; private Paint mWhitePaint,mOrangePaint,mBitmapPaint; private int mPadding_left,mPadding_top,mPadding_right,mPadding_bottom; private Point mLeftCircleCenter,mRightCircleCenter,mFengshanPoint; private BitmapContainer mLeafContainer,mFengShanContainer,mBg_Container; private Matrix mFengShanRotate,mLeafMatrix,mTextMatrix; private int startDegree = 0; private int mProgressValue = 0; private int radius; private final float MAX_PROGRESS_VALUE = 100; private RectF mLeftArcRect; private Rect mTextRect; private final int max = 10; private int y_step = 2; private final String mFinish= "100%"; private float mTextScale = 0.1f; private int mTextSize = 42; private List<Leaf> mLeaves = new ArrayList<>(); private onProgressChangedListener listener; public interface onProgressChangedListener{ void onProgressValueChanged(int progressValue); } public CustomView(Context context) { this(context,null); } public CustomView(Context context, @Nullable AttributeSet attrs) { this(context, attrs,0); } public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr,0); } public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initAttrs(); initBitmaps(); initPaints(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(getCustomMeasureWidth(widthMeasureSpec),getCustomMeasureHeight(heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); initKeyPoints(); initMatrix(); generateLeaves(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawLeafs(canvas); drawProgress(canvas); canvas.drawBitmap(mBg_Container.src,mPadding_left,mPadding_top,mBitmapPaint); drawFengShan(canvas); postInvalidateDelayed(50); } private void drawLeafs(Canvas canvas) { int mProgressWidth = (int) ((mProgressValue/MAX_PROGRESS_VALUE)*(getWidth()-mPadding_left-mPadding_right-radius)); for (Leaf mLeaf:mLeaves){ canvas.save(); mLeafMatrix.reset(); mLeafMatrix.preRotate(mLeaf.degree,mLeafContainer.width/2,mLeafContainer.height/2); switch (mLeaf.rotateDirection){ case ROTATE_LEFT: mLeaf.degree = mLeaf.degree-ROTATE_DEGREE; break; case ROTATE_RIGHT: mLeaf.degree = mLeaf.degree+ROTATE_DEGREE; break; } mLeafMatrix.postTranslate(mLeaf.position.x-50,mLeaf.position.y-mLeafContainer.height/2); switch (mLeaf.transition){ case SMALL_TRANSITION: mLeaf.position.x = mLeaf.position.x - SMALL_X; break; case MIDDLE_TRANSITION: mLeaf.position.x = mLeaf.position.x - MIDDLE_X; break; case BIG_TRANSITION: mLeaf.position.x = mLeaf.position.x - BIG_X; break; } switch (mLeaf.transition){ case SMALL_AMPLITUDE: if(mLeaf.position.y<(mRightCircleCenter.y-max)){ y_step = -SMALL_Y; }else if (mLeaf.position.y>(mRightCircleCenter.y+max)){ y_step = SMALL_Y; } break; case MIDDLE_AMPLITUDE: if(mLeaf.position.y<(mRightCircleCenter.y-max)){ y_step = -MIDDLE_Y; }else if (mLeaf.position.y>(mRightCircleCenter.y+max)){ y_step = MIDDLE_Y; } break; case BIG_AMPLITUDE: if(mLeaf.position.y<(mRightCircleCenter.y-max)){ y_step = -BIG_Y; }else if (mLeaf.position.y>(mRightCircleCenter.y+max)){ y_step = BIG_Y; } break; } mLeaf.position.y = mLeaf.position.y - y_step; if(mLeaf.position.x > mProgressWidth+mPadding_left+10){ canvas.drawBitmap(mLeafContainer.src,mLeafMatrix,mBitmapPaint); }else{ mLeaf.position.set(mRightCircleCenter.x-radius,mRightCircleCenter.y-mLeafContainer.height/2); mLeaf.transition = new Random().nextInt(3); mLeaf.degree = new Random().nextInt(360); mLeaf.rotateDirection = new Random().nextInt(2); mLeaf.amplitude = new Random().nextInt(3); } canvas.restore(); } } public void setProgressValue(int value){ this.mProgressValue = value; invalidate(); } public int getProgressValue(){ return mProgressValue; } public void setProgressChangeListener(onProgressChangedListener listener){ this.listener = listener; } private void drawProgress(Canvas canvas) { int shouldDrawWidth = (int) ((mProgressValue/MAX_PROGRESS_VALUE)*(getWidth()-mPadding_left-mPadding_right-radius)); Log.d("zyq_progress","shouldDrawWidth = "+shouldDrawWidth); if(shouldDrawWidth<(mLeftCircleCenter.x-mPadding_left)){ float degree = (float) Math.toDegrees(Math.acos((radius - shouldDrawWidth) / (float) radius)); canvas.save(); canvas.drawArc(mLeftArcRect,180-degree,2*degree,false,mOrangePaint); }else{ if (shouldDrawWidth>mRightCircleCenter.x){ shouldDrawWidth = mRightCircleCenter.x; } canvas.save(); canvas.drawArc(mLeftArcRect,90,180,true,mOrangePaint); canvas.drawRect(radius+mPadding_left,mPadding_top,shouldDrawWidth,2*radius+mPadding_top,mOrangePaint); canvas.restore(); } if(listener != null){ listener.onProgressValueChanged(mProgressValue); } } private void drawFengShan(Canvas canvas){ if(mProgressValue<100){ startDegree = startDegree+15; canvas.save(); mFengShanRotate.reset(); mFengShanRotate.preRotate(startDegree,mFengShanContainer.width/2,mFengShanContainer.height/2); mFengShanRotate.postTranslate(mFengshanPoint.x,mFengshanPoint.y); Log.i("zyq","mFengshanPoint.x ="+mFengshanPoint.x+" mFengshanPoint.y="+mFengshanPoint.y); canvas.drawBitmap(mFengShanContainer.src,mFengShanRotate,mBitmapPaint); canvas.restore(); }else{ canvas.save(); mTextMatrix.reset(); if(mTextScale<1){ mWhitePaint.setTextSize(mTextSize*mTextScale); mWhitePaint.getTextBounds(mFinish,0,mFinish.length(),mTextRect); }else{ mWhitePaint.setTextSize(mTextSize); mWhitePaint.getTextBounds(mFinish,0,mFinish.length(),mTextRect); } canvas.drawText(mFinish,0,mFinish.length(),mRightCircleCenter.x-((mTextRect.width())/2), mRightCircleCenter.y+(mTextRect.height()/2),mWhitePaint); mTextScale = mTextScale+0.1f; canvas.restore(); } } private void initKeyPoints(){ mLeftCircleCenter = new Point(); radius = getHeight()/2-mPadding_bottom/2-mPadding_top/2; mLeftCircleCenter.y = radius+mPadding_top; mLeftCircleCenter.x = radius+mPadding_right; Log.d("zyq_point","View,height="+getHeight()+" mLeftCircleCenter.x = "+mLeftCircleCenter.x+"mLeftCircleCenter.y="+mLeftCircleCenter.y); mRightCircleCenter = new Point(); mRightCircleCenter.y = radius+mPadding_top; mRightCircleCenter.x = getWidth()-radius-mPadding_right; Log.d("zyq_point","mRightCircleCenter.x = "+mRightCircleCenter.x+"mRightCircleCenter.y="+mRightCircleCenter.y); mFengshanPoint = new Point(); mFengshanPoint.x = mRightCircleCenter.x-mFengShanContainer.width/2; mFengshanPoint.y = mRightCircleCenter.y-mFengShanContainer.height/2; Log.d("zyq_point","mFengshanPoint.x = "+mFengshanPoint.x+"mFengshanPoint.y="+mFengshanPoint.y); } private void initPaints(){ mWhitePaint = new Paint(); mWhitePaint.setAntiAlias(true); mWhitePaint.setDither(true); mWhitePaint.setTextSize(32); mWhitePaint.setTextAlign(Paint.Align.LEFT); mWhitePaint.setStrokeWidth(6f); mWhitePaint.setColor(WHITE_COLOR); mOrangePaint = new Paint(); mOrangePaint.setAntiAlias(true); mOrangePaint.setDither(true); mOrangePaint.setColor(ORANGE_COLOR); mOrangePaint.setStyle(Paint.Style.FILL); mBitmapPaint = new Paint(); mBitmapPaint.setAntiAlias(true); mBitmapPaint.setDither(true); mBitmapPaint.setFilterBitmap(true); } private void initBitmaps(){ Bitmap mLeaf = ((BitmapDrawable)getResources().getDrawable(R.drawable.leaf_drawable,null)).getBitmap(); Bitmap mFengShan = ((BitmapDrawable)getResources().getDrawable(R.drawable.fengshan_drawable,null)).getBitmap(); Bitmap mbg_kuang = ((BitmapDrawable)getResources().getDrawable(R.drawable.leaf_kuang_drawable,null)).getBitmap(); mLeafContainer = new BitmapContainer(); mLeafContainer.src = mLeaf; mLeafContainer.width = mLeaf.getWidth(); mLeafContainer.height = mLeaf.getHeight(); mFengShanContainer = new BitmapContainer(); mFengShanContainer.src = mFengShan; mFengShanContainer.width = mFengShan.getWidth(); mFengShanContainer.height = mFengShan.getHeight(); Log.i("zyq","fen:width="+mFengShanContainer.width+" height="+mFengShanContainer.height); mBg_Container = new BitmapContainer(); mBg_Container.src = mbg_kuang; mBg_Container.width = mbg_kuang.getWidth(); mBg_Container.height = mbg_kuang.getHeight(); Log.i("zyq_Bg_Container","mBg_Container:width="+mBg_Container.width+" height="+mBg_Container.height); } private void initMatrix(){ mFengShanRotate = new Matrix(); mLeafMatrix = new Matrix(); mTextMatrix = new Matrix(); calculatePosition(); } private void calculatePosition(){ mLeftArcRect = new RectF(); mLeftArcRect.left = mPadding_left; mLeftArcRect.top = mPadding_top; mLeftArcRect.right = 2*radius+mPadding_left; mLeftArcRect.bottom = 2*radius+mPadding_top; mTextRect = new Rect(); mWhitePaint.getTextBounds(mFinish,0,mFinish.length(), mTextRect); } private class BitmapContainer{ Bitmap src; int width; int height; } private class Leaf{ Point position; int degree; long startTime; int rotateDirection; int amplitude; int transition; public Leaf(){ position = new Point(); position.set(mRightCircleCenter.x-radius,mRightCircleCenter.y); degree = new Random().nextInt(360); startTime = System.currentTimeMillis(); rotateDirection = new Random().nextInt(2); amplitude = new Random().nextInt(3); transition = new Random().nextInt(3); } public Leaf(int transition){ position = new Point(); position.set(mRightCircleCenter.x-radius,mRightCircleCenter.y); degree = new Random().nextInt(360); startTime = System.currentTimeMillis(); rotateDirection = new Random().nextInt(2); amplitude = new Random().nextInt(3); this.transition = transition; } } private void generateLeaves() { if(mLeaves.isEmpty()){ for(int i = 0;i<3;i++){ Leaf l = new Leaf(i); mLeaves.add(l); } } } private int getCustomMeasureWidth(int widthMeasureSpec){ int mode = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec); if (mode == MeasureSpec.EXACTLY){ Log.i("zyq_view","view.size = "+size); return size; }else{ Log.i("zyq_view","view.width = "+(mBg_Container.width+getPaddingLeft()+getPaddingRight())); return mBg_Container.width+getPaddingLeft()+getPaddingRight(); } } private int getCustomMeasureHeight(int heightMeasureSpec){ int mode = MeasureSpec.getMode(heightMeasureSpec); int size = MeasureSpec.getSize(heightMeasureSpec); if (mode == MeasureSpec.EXACTLY){ return size; }else{ return mBg_Container.height+getPaddingTop()+getPaddingBottom(); } } private void initAttrs() { mPadding_left = getPaddingLeft(); mPadding_top = getPaddingTop(); mPadding_right = getPaddingRight(); mPadding_bottom = getPaddingBottom(); Log.i("zyq_pad","left="+mPadding_left+" top="+mPadding_top+" right="+mPadding_right+" bottom="+mPadding_bottom); } }
在文件刚开始的时候,定义了各种宽高,比如叶子每次水平移动的距离、垂直移动的距离,旋转的角度、旋转的方向等。基本上来说就是你想控制什么样的动作,就需要你定义什么样的属性,通过属性控制对象的动作。这里的三个图片都是提前准备好的。然后在view中获取这个单个图片对应的bitmap,并获取每个bitmap的宽高,这里的宽高用于以后确定这些bitmap的绘制位置。
在实际的绘制过程中,还需要考虑图层的关系,因为需要有叶子融入的感觉,所以在这里把绘制叶子的步骤放在最前面,这样实现的了叶子融入的感觉。
其次,自定义View中的动画都是通过控制矩阵来实现,比如旋转、移动等。但是字体放大的动画,我试过矩阵,但是不起作用,最后不得已通过改变字体尺寸来实现,希望有知道的朋友可以告诉我一下,不胜感激!!!
其实,其他的动画还好,就是叶子的动画不太好弄,因为需要一种,叶子随机出现的效果,我这里做的不好,希望大神们可以帮忙更改一下!!原作者的叶子动画是与时间关联的,我没有采用,只是限定了叶子出现的位置。具体的情况,还请麻烦各位看代码吧!!!
好了,关于自定义View,暂时就说到这里,以后还将继续介绍有关于自定义View方面的内容,希望大家可以关一下!!!
这是我的微信公众号,如果可以的话,希望您可以帮忙关注一下,这将是对我最大的鼓励了,谢谢!!
代码地址:
https://github.com/zhuyuqiang2017/CustomView
相关文章推荐
- android 自定义View实现进度条增长
- android自定义View实现图片上传进度显示(仿手机QQ上传效果)
- Android 自定义View修炼-仿360手机卫士波浪球进度的实现
- Android自定义View——使用贝塞尔曲线实现流量进度条
- 自定义view实现流程进度的绘制(提现流程进度,转账流程进度等等啦)
- 自定义View实现圆形进度条
- [AndroidUI]自定义view(三):实现圆环进度条
- 自定义View实现圆形进度条及圆形Loading
- SeekBar实现实现进度提示随thum移动,自定义View
- 自定义View---实现半圆环形进度条
- 安卓自定义View实现图片上传进度显示(仿QQ)
- Android:自定义View实现水波进度效果
- 自定义View实现图片上传进度显示
- android自定义View实现图片上传进度显示(仿手机QQ上传效果)
- Android 自定义View修炼-仿360手机卫士波浪球进度的实现
- 安卓自定义View实现图片上传进度显示(仿QQ)
- 学习自定义View(一)实现进度条加载
- Android:自定义View实现绚丽的圆形进度条
- 自定义View实现 “手机淘宝”物流进程模块进度告知UI横向版
- Android Launcher 自定义View 炫酷换壁纸效果,水瓶加水进度显示效果