自定义控件 --- 加载旋转图标_加载

  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             @Override
107             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 canvas
145      * @param index      由内而外
146      * @param startAngle
147      */
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 index
188      * @return
189      */
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     @Override
199     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 measureSpec
210      * @param wantSize
211      * @param minSize
212      * @return
213      */
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_content
226                 result = Math.min(result, specSize);
227             }
228         }
229         //测量的尺寸和最小尺寸取大
230         return Math.max(result, minSize);
231     }
232 }

values/attrs.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
 3     <declare-styleable name="WhorlView_Style">
 4         <attr name="WhorlView_SmallWhorlColor" format="color" />
 5         <attr name="WhorlView_MiddleWhorlColor" format="color" />
 6         <attr name="WhorlView_BigWhorlColor" format="color" />
 7         <attr name="WhorlView_CircleSpeed" format="integer" />
 8         <attr name="WhorlView_Parallax">
 9             <enum name="fast" value="1" />
10             <enum name="medium" value="0" />
11             <enum name="slow" value="2" />
12         </attr>
13     </declare-styleable>
14 </resources>

 

以上是自定义控件代码和资源文件。下面是如何使用自定义控件。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="50dp" >
    <com.dr.WhorlView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/whorl"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        app:WhorlView_CircleSpeed="270"
        app:WhorlView_Parallax="fast"
        app:WhorlView_MiddleWhorlColor="@color/material_red"
        app:WhorlView_SmallWhorlColor="@color/material_green" /> 
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/whorl"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="18dp"
        android:text="加载中..."
        android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
 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             @Override
10             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 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3     <color name="material_red">#F44336</color>
4     <color name="material_green">#4CAF50</color>
5     <color name="material_blue">#5677fc</color>
6 </resources>