自定义View等待旋转
效果图
1 string.xml
<string name="default_progressbar">Default Progressbar:</string>
2 attrs.xml
<resources> <declare-styleable name="ProgressWheel">
<attr name="matProg_progressIndeterminate" format="boolean" />
<attr name="matProg_barColor" format="color" />
<attr name="matProg_rimColor" format="color" />
<attr name="matProg_rimWidth" format="dimension" />
<attr name="matProg_spinSpeed" format="float" />
<attr name="matProg_barSpinCycleTime" format="integer" />
<attr name="matProg_circleRadius" format="dimension" />
<attr name="matProg_fillRadius" format="boolean" />
<attr name="matProg_barWidth" format="dimension" />
<attr name="matProg_linearProgress" format="boolean" />
</declare-styleable> </resources>
3 activity_main.xml
<com.etoury.myapplication.ProgressWheel
android:id="@+id/progress_wheel"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerHorizontal="true"
wheel:matProg_barColor="#5588FF"
wheel:matProg_progressIndeterminate="true" />
4 ProgressWhell.java
public class ProgressWheel extends View {
private static final String TAG = ProgressWheel.class.getSimpleName();
private final int barLength = 16;
private final int barMaxLength = 270;
private final long pauseGrowingTime = 200;
/**
* *********
* DEFAULTS *
* **********
*/
//Sizes (with defaults in DP)
private int circleRadius = 28;
private int barWidth = 4;
private int rimWidth = 4;
private boolean fillRadius = false;
private double timeStartGrowing = 0;
private double barSpinCycleTime = 460;
private float barExtraLength = 0;
private boolean barGrowingFromFront = true;
private long pausedTimeWithoutGrowing = 0;
//Colors (with defaults)
private int barColor = 0xAA000000;
private int rimColor = 0x00FFFFFF; //Paints
private Paint barPaint = new Paint();
private Paint rimPaint = new Paint(); //Rectangles
private RectF circleBounds = new RectF(); //Animation
//The amount of degrees per second
private float spinSpeed = 230.0f;
//private float spinSpeed = 120.0f;
// The last time the spinner was animated
private long lastTimeAnimated = 0; private boolean linearProgress; private float mProgress = 0.0f;
private float mTargetProgress = 0.0f;
private boolean isSpinning = false; private ProgressCallback callback; private boolean shouldAnimate; /**
* The constructor for the ProgressWheel
*/
public ProgressWheel(Context context, AttributeSet attrs) {
super(context, attrs); parseAttributes(context.obtainStyledAttributes(attrs, R.styleable.ProgressWheel)); setAnimationEnabled();
} /**
* The constructor for the ProgressWheel
*/
public ProgressWheel(Context context) {
super(context);
setAnimationEnabled();
} @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) private void setAnimationEnabled() {
int currentApiVersion = Build.VERSION.SDK_INT; float animationValue;
if (currentApiVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
animationValue = Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.ANIMATOR_DURATION_SCALE, 1);
} else {
animationValue = Settings.System.getFloat(getContext().getContentResolver(),
Settings.System.ANIMATOR_DURATION_SCALE, 1);
} shouldAnimate = animationValue != 0;
} //----------------------------------
//Setting up stuff
//---------------------------------- @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int viewWidth = circleRadius + this.getPaddingLeft() + this.getPaddingRight(); int viewHeight = circleRadius + this.getPaddingTop() + this.getPaddingBottom(); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; //Measure Width if (widthMode == MeasureSpec.EXACTLY) { //Must be this size width = widthSize; } else if (widthMode == MeasureSpec.AT_MOST) { //Can't be bigger than... width = Math.min(viewWidth, widthSize); } else { //Be whatever you want width = viewWidth; } //Measure Height if (heightMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.EXACTLY) { //Must be this size height = heightSize; } else if (heightMode == MeasureSpec.AT_MOST) { //Can't be bigger than... height = Math.min(viewHeight, heightSize); } else { //Be whatever you want height = viewHeight; } setMeasuredDimension(width, height); } /** * Use onSizeChanged instead of onAttachedToWindow to get the dimensions of the view, * because this method is called after measuring the dimensions of MATCH_PARENT & WRAP_CONTENT. * Use this dimensions to setup the bounds and paints. */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); setupBounds(w, h); setupPaints(); invalidate(); } /** * Set the properties of the paints we're using to * draw the progress wheel */ private void setupPaints() { barPaint.setColor(barColor); barPaint.setAntiAlias(true); barPaint.setStyle(Style.STROKE); barPaint.setStrokeWidth(barWidth); rimPaint.setColor(rimColor); rimPaint.setAntiAlias(true); rimPaint.setStyle(Style.STROKE); rimPaint.setStrokeWidth(rimWidth); } /** * Set the bounds of the component */ private void setupBounds(int layout_width, int layout_height) { int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); if (!fillRadius) { // Width should equal to Height, find the min value to setup the circle int minValue = Math.min(layout_width - paddingLeft - paddingRight, layout_height - paddingBottom - paddingTop); int circleDiameter = Math.min(minValue, circleRadius * 2 - barWidth * 2); // Calc the Offset if needed for centering the wheel in the available space int xOffset = (layout_width - paddingLeft - paddingRight - circleDiameter) / 2 + paddingLeft; int yOffset = (layout_height - paddingTop - paddingBottom - circleDiameter) / 2 + paddingTop; circleBounds = new RectF(xOffset + barWidth, yOffset + barWidth, xOffset + circleDiameter - barWidth, yOffset + circleDiameter - barWidth); } else { circleBounds = new RectF(paddingLeft + barWidth, paddingTop + barWidth, layout_width - paddingRight - barWidth, layout_height - paddingBottom - barWidth); } } /** * Parse the attributes passed to the view from the XML * * @param a the attributes to parse */ private void parseAttributes(TypedArray a) { // We transform the default values from DIP to pixels DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); barWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, barWidth, metrics); rimWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rimWidth, metrics); circleRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, circleRadius, metrics); circleRadius = (int) a.getDimension(R.styleable.ProgressWheel_matProg_circleRadius, circleRadius); fillRadius = a.getBoolean(R.styleable.ProgressWheel_matProg_fillRadius, false); barWidth = (int) a.getDimension(R.styleable.ProgressWheel_matProg_barWidth, barWidth); rimWidth = (int) a.getDimension(R.styleable.ProgressWheel_matProg_rimWidth, rimWidth); float baseSpinSpeed = a.getFloat(R.styleable.ProgressWheel_matProg_spinSpeed, spinSpeed / 360.0f); spinSpeed = baseSpinSpeed * 360; barSpinCycleTime = a.getInt(R.styleable.ProgressWheel_matProg_barSpinCycleTime, (int) barSpinCycleTime); barColor = a.getColor(R.styleable.ProgressWheel_matProg_barColor, barColor); rimColor = a.getColor(R.styleable.ProgressWheel_matProg_rimColor, rimColor); linearProgress = a.getBoolean(R.styleable.ProgressWheel_matProg_linearProgress, false); if (a.getBoolean(R.styleable.ProgressWheel_matProg_progressIndeterminate, false)) { spin(); } // Recycle a.recycle(); } public void setCallback(ProgressCallback progressCallback) { callback = progressCallback; if (!isSpinning) { runCallback(); } } //---------------------------------- //Animation stuff //---------------------------------- protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawArc(circleBounds, 360, 360, false, rimPaint); boolean mustInvalidate = false; if (!shouldAnimate) { return; } if (isSpinning) { //Draw the spinning bar mustInvalidate = true; long deltaTime = (SystemClock.uptimeMillis() - lastTimeAnimated); float deltaNormalized = deltaTime * spinSpeed / 1000.0f; updateBarLength(deltaTime); mProgress += deltaNormalized; if (mProgress > 360) { mProgress -= 360f; // A full turn has been completed // we run the callback with -1 in case we want to // do something, like changing the color runCallback(-1.0f); } lastTimeAnimated = SystemClock.uptimeMillis(); float from = mProgress - 90; float length = barLength + barExtraLength; if (isInEditMode()) { from = 0; length = 135; } canvas.drawArc(circleBounds, from, length, false, barPaint); } else { float oldProgress = mProgress; if (mProgress != mTargetProgress) { //We smoothly increase the progress bar mustInvalidate = true; float deltaTime = (float) (SystemClock.uptimeMillis() - lastTimeAnimated) / 1000; float deltaNormalized = deltaTime * spinSpeed; mProgress = Math.min(mProgress + deltaNormalized, mTargetProgress); lastTimeAnimated = SystemClock.uptimeMillis(); } if (oldProgress != mProgress) { runCallback(); } float offset = 0.0f; float progress = mProgress; if (!linearProgress) { float factor = 2.0f; offset = (float) (1.0f - Math.pow(1.0f - mProgress / 360.0f, 2.0f * factor)) * 360.0f; progress = (float) (1.0f - Math.pow(1.0f - mProgress / 360.0f, factor)) * 360.0f; } if (isInEditMode()) { progress = 360; } canvas.drawArc(circleBounds, offset - 90, progress, false, barPaint); } if (mustInvalidate) { invalidate(); } } @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (visibility == VISIBLE) { lastTimeAnimated = SystemClock.uptimeMillis(); } } private void updateBarLength(long deltaTimeInMilliSeconds) { if (pausedTimeWithoutGrowing >= pauseGrowingTime) { timeStartGrowing += deltaTimeInMilliSeconds; if (timeStartGrowing > barSpinCycleTime) { // We completed a size change cycle // (growing or shrinking) timeStartGrowing -= barSpinCycleTime; //if(barGrowingFromFront) { pausedTimeWithoutGrowing = 0; //} barGrowingFromFront = !barGrowingFromFront; } float distance = (float) Math.cos((timeStartGrowing / barSpinCycleTime + 1) * Math.PI) / 2 + 0.5f; float destLength = (barMaxLength - barLength); if (barGrowingFromFront) { barExtraLength = distance * destLength; } else { float newLength = destLength * (1 - distance); mProgress += (barExtraLength - newLength); barExtraLength = newLength; } } else { pausedTimeWithoutGrowing += deltaTimeInMilliSeconds; } } /** * Check if the wheel is currently spinning */ public boolean isSpinning() { return isSpinning; } /** * Reset the count (in increment mode) */ public void resetCount() { mProgress = 0.0f; mTargetProgress = 0.0f; invalidate(); } /** * Turn off spin mode */ public void stopSpinning() { isSpinning = false; mProgress = 0.0f; mTargetProgress = 0.0f; invalidate(); } /** * Puts the view on spin mode */ public void spin() { lastTimeAnimated = SystemClock.uptimeMillis(); isSpinning = true; invalidate(); } private void runCallback(float value) { if (callback != null) { callback.onProgressUpdate(value); } } private void runCallback() { if (callback != null) { float normalizedProgress = (float) Math.round(mProgress * 100 / 360.0f) / 100; callback.onProgressUpdate(normalizedProgress); } } /** * Set the progress to a specific value, * the bar will be set instantly to that value * * @param progress the progress between 0 and 1 */ public void setInstantProgress(float progress) { if (isSpinning) { mProgress = 0.0f; isSpinning = false; } if (progress > 1.0f) { progress -= 1.0f; } else if (progress < 0) { progress = 0; } if (progress == mTargetProgress) { return; } mTargetProgress = Math.min(progress * 360.0f, 360.0f); mProgress = mTargetProgress; lastTimeAnimated = SystemClock.uptimeMillis(); invalidate(); } // Great way to save a view's state http://stackoverflow.com/a/7089687/1991053 @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); WheelSavedState ss = new WheelSavedState(superState); // We save everything that can be changed at runtime ss.mProgress = this.mProgress; ss.mTargetProgress = this.mTargetProgress; ss.isSpinning = this.isSpinning; ss.spinSpeed = this.spinSpeed; ss.barWidth = this.barWidth; ss.barColor = this.barColor; ss.rimWidth = this.rimWidth; ss.rimColor = this.rimColor; ss.circleRadius = this.circleRadius; ss.linearProgress = this.linearProgress; ss.fillRadius = this.fillRadius; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { if (!(state instanceof WheelSavedState)) { super.onRestoreInstanceState(state); return; } WheelSavedState ss = (WheelSavedState) state; super.onRestoreInstanceState(ss.getSuperState()); this.mProgress = ss.mProgress; this.mTargetProgress = ss.mTargetProgress; this.isSpinning = ss.isSpinning; this.spinSpeed = ss.spinSpeed; this.barWidth = ss.barWidth; this.barColor = ss.barColor; this.rimWidth = ss.rimWidth; this.rimColor = ss.rimColor; this.circleRadius = ss.circleRadius; this.linearProgress = ss.linearProgress; this.fillRadius = ss.fillRadius; this.lastTimeAnimated = SystemClock.uptimeMillis(); } /** * @return the current progress between 0.0 and 1.0, * if the wheel is indeterminate, then the result is -1 */ public float getProgress() { return isSpinning ? -1 : mProgress / 360.0f; } //---------------------------------- //Getters + setters //---------------------------------- /** * Set the progress to a specific value, * the bar will smoothly animate until that value * * @param progress the progress between 0 and 1 */ public void setProgress(float progress) { if (isSpinning) { mProgress = 0.0f; isSpinning = false; runCallback(); } if (progress > 1.0f) { progress -= 1.0f; } else if (progress < 0) { progress = 0; } if (progress == mTargetProgress) { return; } // If we are currently in the right position // we set again the last time animated so the // animation starts smooth from here if (mProgress == mTargetProgress) { lastTimeAnimated = SystemClock.uptimeMillis(); } mTargetProgress = Math.min(progress * 360.0f, 360.0f); invalidate(); } /** * Sets the determinate progress mode * * @param isLinear if the progress should increase linearly */ public void setLinearProgress(boolean isLinear) { linearProgress = isLinear; if (!isSpinning) { invalidate(); } } /** * @return the radius of the wheel in pixels */ public int getCircleRadius() { return circleRadius; } /** * Sets the radius of the wheel * * @param circleRadius the expected radius, in pixels */ public void setCircleRadius(int circleRadius) { this.circleRadius = circleRadius; if (!isSpinning) { invalidate(); } } /** * @return the width of the spinning bar */ public int getBarWidth() { return barWidth; } /** * Sets the width of the spinning bar * * @param barWidth the spinning bar width in pixels */ public void setBarWidth(int barWidth) { this.barWidth = barWidth; if (!isSpinning) { invalidate(); } } /** * @return the color of the spinning bar */ public int getBarColor() { return barColor; } /** * Sets the color of the spinning bar * * @param barColor The spinning bar color */ public void setBarColor(int barColor) { this.barColor = barColor; setupPaints(); if (!isSpinning) { invalidate(); } } /** * @return the color of the wheel's contour */ public int getRimColor() { return rimColor; } /** * Sets the color of the wheel's contour * * @param rimColor the color for the wheel */ public void setRimColor(int rimColor) { this.rimColor = rimColor; setupPaints(); if (!isSpinning) { invalidate(); } } /** * @return the base spinning speed, in full circle turns per second * (1.0 equals on full turn in one second), this value also is applied for * the smoothness when setting a progress */ public float getSpinSpeed() { return spinSpeed / 360.0f; } /** * Sets the base spinning speed, in full circle turns per second * (1.0 equals on full turn in one second), this value also is applied for * the smoothness when setting a progress * * @param spinSpeed the desired base speed in full turns per second */ public void setSpinSpeed(float spinSpeed) { this.spinSpeed = spinSpeed * 360.0f; } /** * @return the width of the wheel's contour in pixels */ public int getRimWidth() { return rimWidth; } /** * Sets the width of the wheel's contour * * @param rimWidth the width in pixels */ public void setRimWidth(int rimWidth) { this.rimWidth = rimWidth; if (!isSpinning) { invalidate(); } } public interface ProgressCallback { /** * Method to call when the progress reaches a value * in order to avoid float precision issues, the progress * is rounded to a float with two decimals. * * In indeterminate mode, the callback is called each time * the wheel completes an animation cycle, with, the progress value is -1.0f * * @param progress a double value between 0.00 and 1.00 both included */ public void onProgressUpdate(float progress); } static class WheelSavedState extends BaseSavedState { //required field that makes Parcelables from a Parcel public static final Creator<WheelSavedState> CREATOR = new Creator<WheelSavedState>() { public WheelSavedState createFromParcel(Parcel in) { return new WheelSavedState(in); } public WheelSavedState[] newArray(int size) { return new WheelSavedState[size]; } }; float mProgress; float mTargetProgress; boolean isSpinning; float spinSpeed; int barWidth; int barColor; int rimWidth; int rimColor; int circleRadius; boolean linearProgress; boolean fillRadius; WheelSavedState(Parcelable superState) { super(superState); } private WheelSavedState(Parcel in) { super(in); this.mProgress = in.readFloat(); this.mTargetProgress = in.readFloat(); this.isSpinning = in.readByte() != 0; this.spinSpeed = in.readFloat(); this.barWidth = in.readInt(); this.barColor = in.readInt(); this.rimWidth = in.readInt(); this.rimColor = in.readInt(); this.circleRadius = in.readInt(); this.linearProgress = in.readByte() != 0; this.fillRadius = in.readByte() != 0; } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeFloat(this.mProgress); out.writeFloat(this.mTargetProgress); out.writeByte((byte) (isSpinning ? 1 : 0)); out.writeFloat(this.spinSpeed); out.writeInt(this.barWidth); out.writeInt(this.barColor); out.writeInt(this.rimWidth); out.writeInt(this.rimColor); out.writeInt(this.circleRadius); out.writeByte((byte) (linearProgress ? 1 : 0)); out.writeByte((byte) (fillRadius ? 1 : 0)); } }}
自定义View等待旋转的更多相关文章
- ipad上自定义view的旋转适配
ios8横屏时宽高会自动转换,但是ios7不是 CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width; CGFloat scree ...
- Android自定义View之圆环交替 等待效果
学习了前面两篇的知识,对于本篇实现的效果,相信大家都不会感觉太困难,我要实现的效果是什么样呢?下面请先看效果图: 看上去是不很炫的样子,它的实现上也不是很复杂,重点在与onDraw()方法的绘制. 首 ...
- Android 自定义View (三) 圆环交替 等待效果
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24500107 一个朋友今天有这么个需求(下图),我觉得那自定义View来做还是很 ...
- Android 自定义View之BounceProgressBar
之前几天下载了很久没用了的桌面版酷狗来用用的时候,发现其中加载歌曲的等待进度条的效果不错(个人感觉),如下: 然后趁着这周末两天天气较冷,窝在宿舍放下成堆的操作系统作业(目测要抄一节多课的一堆堆文字了 ...
- 通过圆形载入View了解自定义View
这是自定义View的第一篇文章,通过制作简单的自定义View来了解自定义View的流程. 自定义View是Android学习和开发中必不可少的一部分.通过自定义View我们可以制作丰富绚丽的控件,自定 ...
- Android 自定义View合集
自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...
- Android 自定义 view(三)—— onDraw 方法理解
前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...
- iOS开发——UI进阶篇(十八)核心动画小例子,转盘(裁剪图片、自定义按钮、旋转)图片折叠、音量震动条、倒影、粒子效果
一.转盘(裁剪图片.自定义按钮.旋转) 1.裁剪图片 将一张大图片裁剪为多张 // CGImageCreateWithImageInRect:用来裁剪图片 // image:需要裁剪的图片 // re ...
- Android中使用自定义View实现下载进度的显示
一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态.之前在公司的项目中写过一个,今天抽空来整理一下. 一般下载都会有这么几种状态:未开始.等待.正在下载.下载结束,当然有时候会有下载 ...
随机推荐
- 'mysql' 不是内部或外部命令,也不是可运行的程序或批处理文件的解决办法
前言: 本文的解决方法来自http://www.cnblogs.com/xionghui/archive/2012/04/11/2442404.html --感谢! 问题描述:新电脑装mysql后在c ...
- 【POJ】【2960】S-Nim
博弈论 这题跟 BZOJ 1874 取石子游戏 差不多 先暴力求出10000以内的SG函数(利用定义来求即可) 然后每次询问直接将SG值异或起来即可…… Source Code Problem: Us ...
- Calling Lua From a C Program
Introduction From a running C program, you can call a Lua script. The C program can pass arguments t ...
- [转载]Spring Autowire自动装配介绍
转自: http://www.cnblogs.com/zhishan/p/3190757.html 在应用中,我们常常使用<ref>标签为JavaBean注入它依赖的对象.但是对于一个大型 ...
- 2014多校第一场A题 || HDU 4861 Couple doubi
题目链接 题意 : 有K个球,给你一个数P,可以求出K个值,(i=1,2,...,k) : 1^i+2^i+...+(p-1)^i (mod p).然后女朋友先取,再xp取,都希望赢,如果女朋友能赢输 ...
- hdu 1713 相遇周期
求分数的最小公倍数.对于a/b c/d 先化简为最简分数,分数最小公倍数=分子的最小公倍数/分母的最大公约数. ;}
- KMP笔记√//找最大子串,前缀自匹配长度
假设s1里找s2,然后s2进去匹配假设在第三位失配那么说明前两位是匹配成功的 如果这时候将s2后移一位相当于将s2的第一位和s2的第二位比较,如果我们已知s1(1)≠s1(2)那么就可以直接后移两位 ...
- OPenGL中三维图形的矩阵变换
对于二维的图形开发,拿简单的图片显示来说,我们主要的目的:就是在一块显示buffer中,不停的把每个像素进行着色,然后就可以绘制出来了.为了速度,很多其他的加速方法,但原理基本上就是这样了. 很直观, ...
- 【mongoDB中级篇②】索引与expain
索引的操作 数据库百分之八十的工作基本上都是查询,而索引能帮我们更快的查询到想要的数据.但是其降低了数据的写入速度,所以要权衡常用的查询字段,不必在太多字段上建立索引. 在mongoDB中默认是用bt ...
- 安卓--使用Intent实现Activity之间传值与跳转
http://blog.csdn.net/cjjky/article/details/6337447 在一个Android的应用程序中,很少只存在一个Activity,一般都有多个Activity,如 ...