編輯:關於Android編程
譯於20160718,原文android.support.v4.widget.MaterialProgressDrawable.java
class MaterialProgressDrawable(譯注:注意不是public的哦)
extends Drawable
implements Animatable
Java.lang.Object
?android.graphics.drawable.Drawable
?android.support.v4.widget.MaterialProgressDrawable
為Material主題設置的Fancy(譯注:花式的、花樣的、奇特的)的進度條
//①創建drawable並設給view
ImageView imageView=...;
progress = new MaterialProgressDrawable(getContext(),imageView);
imageView.setImageDrawable(progress);
//②設置相關屬性
//背景
progress.setBackgroundColor(0xFF000000);
//顏色
int[] colors = {0xFFFF0000,0xFF00FF00,0xFF0000FF};
progress.setColorSchemeColors(colors);
//尺寸
progress.updateSizes(MaterialProgressDrawable.DEFAULT);
//旋轉角度,0-1
progress.setProgressRotation(0f);
//圓環范圍,0-1
progress.setStartEndTrim(0f, 1f);
//箭頭大小,0-1
progress.setArrowScale(0f);
//透明度,0-255
progress.setAlpha(100);
//③start
progress.start();
源碼分析
構造方法
調用構造方法後,要記得把這個drawable設給view,如imageView.setImageDrawable(drawable);
public static final int LARGE = 0;
public static final int DEFAULT = 1;
private final int[] COLORS = new int[] {
Color.BLACK
};
public MaterialProgressDrawable(Context context, View parent) {
mParent = parent;
mResources = context.getResources();
mRing = new Ring(mCallback);
mRing.setColors(COLORS);
updateSizes(DEFAULT);
setupAnimators();
}
初始化動畫
private void setupAnimators() {
final Ring ring = mRing;
final Animation animation = new Animation() {
@Override
public void applyTransformation(float interpolatedTime, Transformation t) {
if (mFinishing) {
applyFinishTranslation(interpolatedTime, ring);
} else {
//初始化,最小弧度等於寬度
// The minProgressArc is calculated from 0 to create an
// angle that matches the stroke width.
final float minProgressArc = getMinProgressArc(ring);
final float startingEndTrim = ring.getStartingEndTrim();
final float startingTrim = ring.getStartingStartTrim();
final float startingRotation = ring.getStartingRotation();
//更新顏色,後面有補充1
updateRingColor(interpolatedTime, ring);
//前50%,移動start trim
// Moving the start trim only occurs in the first 50% of a
// single ring animation
if (interpolatedTime <= START_TRIM_DURATION_OFFSET) {
//這個計算使得在這50%的時間段內可以完成0-1的動畫展
// scale the interpolatedTime so that the full
// transformation from 0 - 1 takes place in the
// remaining time
final float scaledTime = (interpolatedTime)
/ (1.0f - START_TRIM_DURATION_OFFSET);
//對MATERIAL_INTERPOLATOR的補充2
final float startTrim = startingTrim
+ ((MAX_PROGRESS_ARC - minProgressArc) * MATERIAL_INTERPOLATOR
.getInterpolation(scaledTime));
ring.setStartTrim(startTrim);
}
//後50%移動end trim
// Moving the end trim starts after 50% of a single ring
// animation completes
if (interpolatedTime > END_TRIM_START_DELAY_OFFSET) {
// scale the interpolatedTime so that the full
// transformation from 0 - 1 takes place in the
// remaining time
final float minArc = MAX_PROGRESS_ARC - minProgressArc;
float scaledTime = (interpolatedTime - START_TRIM_DURATION_OFFSET)
/ (1.0f - START_TRIM_DURATION_OFFSET);
final float endTrim = startingEndTrim
+ (minArc * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime));
ring.setEndTrim(endTrim);
}
//補充3
final float rotation = startingRotation + (0.25f * interpolatedTime);
ring.setRotation(rotation);
float groupRotation = ((FULL_ROTATION / NUM_POINTS) * interpolatedTime)
+ (FULL_ROTATION * (mRotationCount / NUM_POINTS));
setRotation(groupRotation);
}
}
};
animation.setRepeatCount(Animation.INFINITE);
animation.setRepeatMode(Animation.RESTART);
animation.setInterpolator(LINEAR_INTERPOLATOR);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mRotationCount = 0;
}
@Override
public void onAnimationEnd(Animation animation) {
// do nothing
}
@Override
public void onAnimationRepeat(Animation animation) {
ring.storeOriginals();
//設置下一個顏色
ring.goToNextColor();
//結束角設為開始角
ring.setStartTrim(ring.getEndTrim());
if (mFinishing) {
// finished closing the last ring from the swipe gesture; go
// into progress mode
mFinishing = false;
animation.setDuration(ANIMATION_DURATION);
ring.setShowArrow(false);
} else {
//+1求余
mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
}
}
});
mAnimation = animation;
}
補充1更新環的顏色。
這裡要看一下這個方法
/**
*在動畫的最後25%的時間內,更新環的顏色
*環的新顏色是當前環的顏色到下一個顏色的漸變
*/
private void updateRingColor(float interpolatedTime, Ring ring) {
if (interpolatedTime > COLOR_START_DELAY_OFFSET) {
// scale the interpolatedTime so that the full
// transformation from 0 - 1 takes place in the
// remaining time
ring.setColor(evaluateColorChange((interpolatedTime - COLOR_START_DELAY_OFFSET)
/ (1.0f - COLOR_START_DELAY_OFFSET), ring.getStartingColor(),
ring.getNextColor()));
}
}
//看一個漸變方法值得學習
// Adapted from ArgbEvaluator.java
private int evaluateColorChange(float fraction, int startValue, int endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
(int)((startR + (int)(fraction * (endR - startR))) << 16) |
(int)((startG + (int)(fraction * (endG - startG))) << 8) |
(int)((startB + (int)(fraction * (endB - startB))));
}
補充2
這個interpolator
private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
/**
*和android.R.interpolator#fast_out_slow_in一致的interpolator
*用查找表格來表示貝塞爾曲線在(0.0)到(1,1)的控制點
*/
public class FastOutSlowInInterpolator extends LookupTableInterpolator {}
abstract class LookupTableInterpolator implements Interpolator {
private final float[] mValues;
private final float mStepSize;
public LookupTableInterpolator(float[] values) {
mValues = values;
mStepSize = 1f / (mValues.length - 1);
}
@Override
public float getInterpolation(float input) {
if (input >= 1.0f) {
return 1.0f;
}
if (input <= 0f) {
return 0f;
}
//計算索引,用length-2,以防後面的越界,後面+1了
// Calculate index - We use min with length - 2 to avoid IndexOutOfBoundsException when
// we lerp (linearly interpolate) in the return statement
int position = Math.min((int) (input * (mValues.length - 1)), mValues.length - 2);
//因為表離散,計算小的偏移
// Calculate values to account for small offsets as the lookup table has discrete values
float quantized = position * mStepSize;
float diff = input - quantized;
float weight = diff / mStepSize;
// Linearly interpolate between the table values
return mValues[position] + weight * (mValues[position + 1] - mValues[position]);
//(譯注:不會翻譯,意思就是說,因為計算所求的input比例的位置,和查詢表中的位置有差值,要根據這個差值求得相差的值。具體做法是位置*步長,求得位置所占比例,(input比例-位置所占比例)/步長,得到權重。(下一位置的值-當前位置的值)*權重,得到差值)
//示例如下[0,2,4,6,8]
//要求0.8位的值
//position=min(0.8*4,3)=3
//quantized=3*1/4=0.75,
//diff=0.8-0.75=0.05
//weight=0.05/0.25=0.2
//也就是說,和0.8相近的是0.75位置的數6,它和6相差0.05個步長,這個步長的值為
//(8-6)*0.2=0.4
//所以0.8的值應為6.4
//並不是很難,主要是數學的知識,我的數學似乎也不是很好……
}
}
補充3
這裡設置了2個旋轉,一個是環自己,一個是動畫的旋轉。
如果注釋掉動畫的旋轉,環在慢慢變短、變長的同時,還會慢慢地旋轉。
後面這個動畫的旋轉在draw方法中調用
c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
private static final float FULL_ROTATION = 1080.0f;
/** The number of points in the progress "star". */
private static final float NUM_POINTS = 5f;
float groupRotation = ((FULL_ROTATION / NUM_POINTS) * interpolatedTime)+ (FULL_ROTATION * (mRotationCount / NUM_POINTS));
//不是很明白,可能是這樣的意思,在5次內轉1080度,當前要轉的度數就等於轉次的度數乘以時間,加上已經轉了多少圈對應的度數,即1080/5*時間+1080*(次數/5)
原理分析
看了上面的源碼,我們整理一下,原理也是很清晰呢。
用動畫控制環,
在動畫中,讓環先變短,再變長。
同時,讓環和drawable都旋轉。
環
draw
/**
* Draw the progress spinner
*/
public void draw(Canvas c, Rect bounds) {
final RectF arcBounds = mTempBounds;
arcBounds.set(bounds);
arcBounds.inset(mStrokeInset, mStrokeInset);
final float startAngle = (mStartTrim + mRotation) * 360;
final float endAngle = (mEndTrim + mRotation) * 360;
float sweepAngle = endAngle - startAngle;
mPaint.setColor(mCurrentColor);
//畫弧,主要實現
c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
//畫三角
drawTriangle(c, startAngle, sweepAngle, bounds);
if (mAlpha < 255) {
mCirclePaint.setColor(mBackgroundColor);
mCirclePaint.setAlpha(255 - mAlpha);
//畫圓,就是背景的
c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2,
mCirclePaint);
}
}
drawTriangle
//看不懂,哈哈。看了好幾遍還是看不懂,等我以後學成再來看。
private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) {
if (mShowArrow) {
if (mArrow == null) {
mArrow = new Path();
mArrow.setFillType(Path.FillType.EVEN_ODD);
} else {
mArrow.reset();
}
// Adjust the position of the triangle so that it is inset as
// much as the arc, but also centered on the arc.
float inset = (int) mStrokeInset / 2 * mArrowScale;
float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX());
float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY());
// Update the path each time. This works around an issue in SKIA
// where concatenating a rotation matrix to a scale matrix
// ignored a starting negative rotation. This appears to have
// been fixed as of API 21.
mArrow.moveTo(0, 0);
mArrow.lineTo(mArrowWidth * mArrowScale, 0);
mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight
* mArrowScale));
mArrow.offset(x - inset, y);
mArrow.close();
// draw a triangle
mArrowPaint.setColor(mCurrentColor);
c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(),
bounds.exactCenterY());
c.drawPath(mArrow, mArrowPaint);
}
}
一、RxJava概念RxJava官方定義一個在 Java VM 上使用可觀測的序列來組成異步的、基於事件的程序的庫。用一個詞概括:異步,也就是說RxJava也可以理解為一
微信右上角的操作菜單看起來很好用,就照著仿了一下,不過是舊版微信的,手裡剛好有一些舊版微信的資源圖標,給大家分享一下。不知道微信是用什麼實現的,我使用popupwindo
最近,同學的同學找我做了一款簡單的安卓手機軟件,第一次,一個人,做一個完整的項目。所以,在這裡總結一下完整的開發流程和步驟,方便後來人入門學習。其實,我是一個新手,沒有系
Android RecyclerView 是Android5.0推出來的,導入support-v7包即可使用。個人體驗來說,RecyclerView絕對是一款功能強大的控
Android的Notification是android系統中很重要的一