1 import android.annotation.TargetApi; 2 import android.content.Context; 3 import android.content.res.Resources; 4 import android.content.res.TypedArray; 5 import android.graphics.Canvas; 6 import android.graphics.Paint; 7 import android.graphics.RectF; 8 import android.os.Build; 9 import android.util.AttributeSet; 10 import android.view.View; 11 12 /** 13 * 类似螺纹的加载view 14 * 可以自定义的属性:颜色、旋转速度(X弧度/s) 15 */ 16 public class WhorlView extends View { 17 private static final int CIRCLE_NUM = 3; 18 19 public static final int FAST = 1; 20 public static final int MEDIUM = 0; 21 public static final int SLOW = 2; 22 23 private static final int PARALLAX_FAST = 60; 24 private static final int PARALLAX_MEDIUM = 72; 25 private static final int PARALLAX_SLOW = 90; 26 27 private static final float SWEEP_ANGLE = 90f; 28 private static final float STOKE_WIDTH = 5f; 29 private static final long REFRESH_DURATION = 16L; 30 31 private long mCircleTime; 32 private int[] mLayerColors = new int[CIRCLE_NUM]; 33 private int mCircleSpeed; 34 private int mParallaxSpeed; 35 36 public WhorlView(Context context) { 37 this(context, null, 0); 38 } 39 40 public WhorlView(Context context, AttributeSet attrs) { 41 this(context, attrs, 0); 42 } 43 44 public WhorlView(Context context, AttributeSet attrs, int defStyleAttr) { 45 super(context, attrs, defStyleAttr); 46 Resources res = getResources(); 47 final int defaultSmallColor = res.getColor(R.color.material_red); 48 final int defaultMiddleColor = res.getColor(R.color.material_green); 49 final int defaultBigColor = res.getColor(R.color.material_blue); 50 //默认外层最慢180度/s 51 final int defaultCircleSpeed = 270; 52 if (attrs != null) { 53 final TypedArray typedArray = context.obtainStyledAttributes( 54 attrs, R.styleable.WhorlView_Style); 55 mLayerColors[0] = typedArray.getColor(R.styleable.WhorlView_Style_WhorlView_SmallWhorlColor, defaultSmallColor); 56 mLayerColors[1] = typedArray.getColor(R.styleable.WhorlView_Style_WhorlView_MiddleWhorlColor, defaultMiddleColor); 57 mLayerColors[2] = typedArray.getColor(R.styleable.WhorlView_Style_WhorlView_BigWhorlColor, defaultBigColor); 58 mCircleSpeed = typedArray.getInt(R.styleable.WhorlView_Style_WhorlView_CircleSpeed, defaultCircleSpeed); 59 int index = typedArray.getInt(R.styleable.WhorlView_Style_WhorlView_Parallax, 0); 60 setParallax(index); 61 typedArray.recycle(); 62 } else { 63 mLayerColors[0] = defaultSmallColor; 64 mLayerColors[1] = defaultMiddleColor; 65 mLayerColors[2] = defaultBigColor; 66 mCircleSpeed = defaultCircleSpeed; 67 mParallaxSpeed = PARALLAX_MEDIUM; 68 } 69 } 70 72 private void setParallax(int index) { 73 switch (index) { 74 case FAST: 75 mParallaxSpeed = PARALLAX_FAST; 76 break; 77 case MEDIUM: 78 mParallaxSpeed = PARALLAX_MEDIUM; 79 break; 80 case SLOW: 81 mParallaxSpeed = PARALLAX_SLOW; 82 break; 83 default: 84 throw new IllegalStateException("no such parallax type"); 85 } 86 } 87 88 @Override 89 protected void onDraw(Canvas canvas) { 90 super.onDraw(canvas); 91 for (int i = 0; i < CIRCLE_NUM; i++) { 92 float angle = (mCircleSpeed + mParallaxSpeed * (CIRCLE_NUM - i - 1)) * mCircleTime * 0.001f; 93 drawArc(canvas, i, angle); 94 } 95 } 96 97 private boolean mIsCircling = false; 98 99 /**100 * 旋转开始 <功能简述> 101 */102 public void start() {103 mIsCircling = true;104 new Thread(new Runnable() {105 106 @Override107 public void run() {108 mCircleTime = 0L;109 while (mIsCircling) {110 invalidateWrap();111 mCircleTime = mCircleTime + REFRESH_DURATION;112 try {113 Thread.sleep(REFRESH_DURATION);114 } catch (InterruptedException e) {115 e.printStackTrace();116 }117 }118 }119 }).start();120 }121 122 public void stop() {123 mIsCircling = false;124 mCircleTime = 0L;125 invalidateWrap();126 }127 128 public boolean isCircling(){129 return mIsCircling;130 }131 132 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)133 private void invalidateWrap() {134 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {135 postInvalidateOnAnimation();136 } else {137 postInvalidate();138 }139 }140 141 /**142 * 画弧143 *144 * @param canvas145 * @param index 由内而外146 * @param startAngle147 */148 private void drawArc(Canvas canvas, int index, float startAngle) {149 Paint paint = checkArcPaint(index);150 //最大圆是view的边界151 RectF oval = checkRectF(calcuRadiusRatio(index));152 canvas.drawArc(oval, startAngle, SWEEP_ANGLE, false, paint);153 }154 155 private Paint mArcPaint;156 157 private Paint checkArcPaint(int index) {158 if (mArcPaint == null) {159 mArcPaint = new Paint();160 } else {161 mArcPaint.reset();162 }163 mArcPaint.setColor(mLayerColors[index]);164 mArcPaint.setStyle(Paint.Style.STROKE);165 mArcPaint.setStrokeWidth(STOKE_WIDTH);166 mArcPaint.setAntiAlias(true);167 return mArcPaint;168 }169 170 private RectF mOval;171 172 private RectF checkRectF(float radiusRatio) {173 if (mOval == null) {174 mOval = new RectF();175 }176 float start = getMinLength() * 0.5f * (1 - radiusRatio) + STOKE_WIDTH;177 float end = getMinLength() - start;178 mOval.set(start, start, end, end);179 return mOval;180 }181 182 private static final float RADIUS_RATIO_P = 0.2f;183 184 /**185 * 计算每一圈的半径比例186 *187 * @param index188 * @return189 */190 private float calcuRadiusRatio(int index) {191 return 1f - (CIRCLE_NUM - index - 1) * RADIUS_RATIO_P;192 }193 194 private int getMinLength() {195 return Math.min(getWidth(), getHeight());196 }197 198 @Override199 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {200 int minSize = (int) (STOKE_WIDTH * 4 * CIRCLE_NUM);201 int wantSize = (int) (STOKE_WIDTH * 8 * CIRCLE_NUM);202 int size = measureSize(widthMeasureSpec, wantSize, minSize);203 setMeasuredDimension(size, size);204 }205 206 /**207 * 测量view的宽高208 *209 * @param measureSpec210 * @param wantSize211 * @param minSize212 * @return213 */214 public static int measureSize(int measureSpec, int wantSize, int minSize) {215 int result = 0;216 int specMode = MeasureSpec.getMode(measureSpec);217 int specSize = MeasureSpec.getSize(measureSpec);218 219 if (specMode == MeasureSpec.EXACTLY) {220 // 父布局想要view的大小221 result = specSize;222 } else {223 result = wantSize;224 if (specMode == MeasureSpec.AT_MOST) {225 // wrap_content226 result = Math.min(result, specSize);227 }228 }229 //测量的尺寸和最小尺寸取大230 return Math.max(result, minSize);231 }232 } 功能简述>
values/attrs.xml
1 23 4 145 6 7 8 9 1310 11 12
以上是自定义控件代码和资源文件。下面是如何使用自定义控件。
1 public class MainActivity extends Activity { 2 @Override 3 protected void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 setContentView(R.layout.activity_main); 6 final WhorlView whorlView = (WhorlView) this.findViewById(R.id.whorl); 8 whorlView.setOnClickListener(new View.OnClickListener() { 9 @Override10 public void onClick(View v) {11 if (whorlView.isCircling()) {12 whorlView.stop();13 } else {14 whorlView.start();15 }16 }17 });18 }19 }
values/color.xml
1 23 #F44336 4#4CAF50 5#5677fc 6