android仿winphone进度指示器的实现

    转载注明出处。

    这里说的效果指的是winphone的忽快忽慢的小圆点(恩,你懂得吧= =)。分为转圈和直线两种。在网上看到有人实现了直线的,于是下下来源码看了一下,顺带实现了转圈的。感觉还算好看。在这里做个记录。顺便谢谢那个慷慨开源的哥们儿~~、

   先说说直线运动小圆点的原理吧。定义一个数组分别对于小圆点,再定义一个同样大小的数组规定每个点的Visibility情况。由后者控制每个点的可见性。定义直线上3/10的区域是慢速区域,其他为快速区。开启一个线程,线程每运行一次画面刷新一次。每个点依次出发,之间有一定时间间隔。根据点运行的距离判断下一次点应该在哪。下面是代码。在原作的基础上我给出了更详细的注释。读者应该能一遍看懂了~工程文件我就不提供了,大家自己实现吧。 

WP7ProgressView
  1 package com.example.dialog;
  2 
  3 import android.content.Context;
  4 import android.graphics.Canvas;
  5 import android.graphics.Color;
  6 import android.graphics.Paint;
  7 import android.graphics.RectF;
  8 import android.os.Handler;
  9 import android.util.AttributeSet;
 10 import android.util.Log;
 11 import android.view.View;
 12 
 13 public class WP7ProgessView extends View {
 14 
 15     private int mColor = Color.RED;
 16     private int mBackgroundColor = Color.BLACK;
 17     private Paint mPainter = null;
 18 
 19     private static final int RECT_NUMBER = 5;
 20     private static int[] sPos = new int[RECT_NUMBER];
 21     private static int[] sVisible = new int[RECT_NUMBER];
 22 
 23     private static final int RECT_WIDTH = 5; // px
 24 
 25     private static final float SLOW_RANGE = 0.30f; // 有30%的空间是慢的
 26 
 27     private static final int UPDATE_PROGRESS_INTERVAL = 28; // ms
 28 
 29     private static final int STANDARD_SCREEN_W = 480; // px
 30 
 31     private UpdateProgessThread mThread = null;
 32 
 33     public WP7ProgessView(Context context) {
 34         this(context, null);
 35     }
 36 
 37     public WP7ProgessView(Context context, AttributeSet attrs) {
 38         this(context, attrs, 0);
 39     }
 40 
 41     public WP7ProgessView(Context context, AttributeSet attrs, int defStyle) {
 42         super(context, attrs, defStyle);
 43     }
 44 
 45     public WP7ProgessView(Context context, int color) {
 46         this(context, null, 0);
 47         mColor = color;
 48         mPainter = new Paint();
 49         mPainter.setAntiAlias(true);
 50         mPainter.setColor(mColor);
 51         mPainter.setStyle(Paint.Style.FILL);
 52         setBackgroundColor(mBackgroundColor);
 53     }
 54 
 55     public void setColor(int color) {
 56         mColor = color;
 57         mPainter.setColor(mColor);
 58     }
 59 
 60     public int getColor() {
 61         return mColor;
 62     }
 63 
 64     @Override
 65     public void draw(Canvas canvas) {
 66         canvas.drawColor(mBackgroundColor);
 67         super.draw(canvas);
 68 
 69         for (int i = 0; i < RECT_NUMBER; i++) {
 70             if (sVisible[i] == View.VISIBLE) {
 71                 drawRect(canvas, sPos[i]);
 72             }
 73         }
 74     }
 75 
 76     private void drawRect(Canvas canvas, int pos) {
 77         int m = getHeight() / 2;
 78         int w = RECT_WIDTH / 2;
 79         canvas.drawRect(new RectF(pos - w, m - w, pos + w, m + w), mPainter);
 80     }
 81 
 82     @Override
 83     protected void onAttachedToWindow() {
 84         super.onAttachedToWindow();
 85         mThread = new UpdateProgessThread("WP7ProgessView", this);
 86         mThread.start();
 87     }
 88 
 89     @Override
 90     protected void onDetachedFromWindow() {
 91         mThread.stopping();
 92         mThread = null;
 93         super.onDetachedFromWindow();
 94     }
 95 
 96     @Override
 97     protected void onWindowVisibilityChanged(int visibility) {
 98         // TODO Auto-generated method stub
 99         super.onWindowVisibilityChanged(visibility);
100     }
101 
102     private static final class UpdateProgessThread extends Thread {
103         private WP7ProgessView host = null;//就理解为宿主吧。。
104         private int mInvisibleCount = 0; // 控制是不是重新开始一次循环,当都不可见了则说明需要重新开始了
105         private int mPosToShowNext = 0; // 显示下一个方块坐标
106         private int mIndexToShowNext = 0; // 检查显示下一个方块的方块坐标,如果为0则表示刚刚开始
107 
108         private long mSleepTime = 0;
109 
110         private boolean running = true;
111 
112         public UpdateProgessThread(String name, WP7ProgessView v) {
113             super(name);
114             host = v;
115             for (int i = 0; i < RECT_NUMBER; i++) {
116 
117                 // 让所有的小方块不显示
118                 sPos[i] = -RECT_WIDTH;
119                 sVisible[i] = View.GONE;
120             }
121 
122             // 至少先让第一个可见
123             sVisible[0] = View.VISIBLE;
124         }
125 
126         public void run() {
127             int w = host.getWidth();
128             Log.d("XXXXX", "w = " + w);
129             int slowRange = 0;
130             int fastRange1 = 0;
131 
132             while (running) {
133                 if (w <= 0) {//一般不会小于0只会等于0.此时这个view还没有被绘制,以下是准备操作
134                     w = host.getWidth();
135                     slowRange = (int) (w * SLOW_RANGE);
136                     fastRange1 = (w - slowRange) / 2;
137                     Log.e("width", w + "   " + slowRange + "   " + fastRange1);
138                 }
139                 if (w > 0 && mSleepTime <= 0) {
140                     mSleepTime = (long) (UPDATE_PROGRESS_INTERVAL
141                             * STANDARD_SCREEN_W / w);
142                 }
143                 // int fastRange2 = fastRange1;
144 
145                 if (mPosToShowNext == 0) {
146                     /**
147                      * 现在我们来决定五个小方框的出现时机,研究了Windows Phone 7的progress
148                      * dialog,它的效果是
149                      * 至少能让五个小方块在同时出现在慢区域。所以在第一个方块走出慢区域的时候,第五个必然要进入慢区域.
150                      * 算法是五个方块都要在慢区,所以他们的间隔是 slowRange/6, 快区是慢区的4/3倍,所以显示间隔就是
151                      * slowRange * (1/6) * (4/3)。
152                      */
153                     mPosToShowNext = (int) (slowRange * 2.0 / 9);
154                 }
155                 
156                 if (mIndexToShowNext < RECT_NUMBER - 1 ) {
157                     /**
158                      * 如果指定的方块跑过了显示下一个方块的点,则显示下一个方块。
159                      */
160                     //因为第一个点已经是可见的,所以是RECT_NUMBER - 1
161                     if (sPos[mIndexToShowNext] > mPosToShowNext) {
162                         mIndexToShowNext++;
163                         sVisible[mIndexToShowNext] = View.VISIBLE;
164                         Log.e("index", "mIndexToShowNext:" + mIndexToShowNext);
165                     }
166                 }
167 
168                 try {
169                     // 更新界面的时间间隔。
170                     Thread.sleep(mSleepTime);
171                 } catch (InterruptedException e) {
172                     e.printStackTrace();
173                 }
174 
175                 for (int i = 0; i < RECT_NUMBER; i++) {
176                     if (sVisible[i] != View.VISIBLE) {
177                         // 如果这个点是看不见的,则不更新了。
178                         Log.e("i", i + "");
179                         continue;
180                     }
181                     if (sPos[i] <= fastRange1
182                             || sPos[i] >= (fastRange1 + slowRange)) {
183                         // 让它移动快点。
184                         sPos[i] += (2 * RECT_WIDTH);
185                     } else if ((sPos[i] - fastRange1) < slowRange) {
186                         // 让它移动慢点。
187                         sPos[i] += RECT_WIDTH;
188                     }
189                     // 如果已经移出边界,则让它看不见
190                     if (sPos[i] > w) {
191                         sVisible[i] = View.GONE;
192                         sPos[i] = -RECT_WIDTH;
193                     }
194                 }
195 
196                 mInvisibleCount = 0;
197 
198                 for (int i = 0; i < RECT_NUMBER; i++) {
199                     if (sVisible[i] != View.VISIBLE) {
200                         mInvisibleCount++;
201                     }
202                 }
203 
204                 // 如果需要重新开始,则初始化一些变量。
205                 if (mInvisibleCount >= RECT_NUMBER) {
206                     mIndexToShowNext = 0;
207                     sVisible[0] = View.VISIBLE;
208                 }
209 
210                 host.postInvalidate();
211             }
212         }
213 
214         public void stopping() {
215             running = false;//方便控制线程运行与否。
216             // stop();
217         }
218     }
219 
220 }

    同时提供我稍作修改的一个Circle类。它是转圆圈的ProgressView。这个Circle类是一个自定义View,在每次返回键被按了时再进去会重新启动一个线程,看起来就是圈圈转的更快了。还有就是我的onMeasure方法没有重写,,懒得写了。所以在drawCircle

方法里你会看到我x,y坐标都加了十,那是为了偏离边界好看清楚我的圆。。。如果要用你可以自行修改这俩小bug.其它的我还没发现。其实bug1在每次activity的onStop()里把线程stopThread()一下就好了。下面是代码。这个注释就少了。可以参照上面看。主要变化的就是把他的距离变成了我的弧度,他只跑一圈我为了和winphone一样让小球跑两圈。恩请看代码:

Cirlcle
  1 package com.example.dialogcircle;
  2 
  3 import android.content.Context;
  4 import android.content.res.TypedArray;
  5 import android.graphics.Canvas;
  6 import android.graphics.Color;
  7 import android.graphics.Paint;
  8 import android.util.AttributeSet;
  9 import android.util.Log;
 10 import android.view.View;
 11 
 12 public class Circle extends View{
 13     
 14     private static final int UPDATE_PROGRESS_INTERVAL = 30;//ms
 15     private static final float DEGREE = (float) 720.0;//一周360度
 16     private static final float SLOWRANGE = (float) (360.0 * 0.3f);//慢的空间
 17     private static final float FASTRANGE = (float) ((360.0 - SLOWRANGE) / 2);//慢的空间
 18     private static final float SLICE = 5;//每次递增度数为10度
 19     private static final float RADIUS = 5;//小圆的半径
 20     
 21     private int mColor = Color.WHITE;
 22     private float mRadius = 0;
 23     
 24     
 25     private Paint mPainter = null;
 26     
 27     private static int dotNum = 4;
 28     
 29     private static float[] degOfDot;
 30     private static int[] visibleDot;
 31     
 32     private Context mContext;
 33     
 34     public static UpdateThread mThread = null;
 35     
 36     public Circle(Context context) {
 37         super(context);
 38     }
 39 
 40     public Circle(Context context, AttributeSet attrs){
 41         super(context, attrs);
 42         TypedArray paramsArray = context.obtainStyledAttributes(attrs, 
 43                 R.styleable.Circle);
 44         mColor = paramsArray.getColor(R.styleable.Circle_color, Color.WHITE);
 45         mRadius = paramsArray.getFloat(R.styleable.Circle_radius, 120.0f);
 46         dotNum = paramsArray.getInt(R.styleable.Circle_dotNum, 4);
 47         
 48         mPainter = new Paint();
 49         mPainter.setAntiAlias(true);
 50         mPainter.setStyle(Paint.Style.FILL);
 51         mPainter.setColor(mColor);
 52         
 53         degOfDot = new float[dotNum];
 54         visibleDot = new int[dotNum];
 55         paramsArray.recycle();
 56         
 57         mThread = new UpdateThread(this);
 58         mThread.start();
 59         if(!mThread.isAlive())mThread.notify();
 60     }
 61     public Circle(Context context, AttributeSet attrs, int defStyle){
 62         super(context, attrs, defStyle);
 63     }
 64     
 65     public void draw(Canvas canvas){
 66         super.draw(canvas);
 67         for(int i = 0; i < dotNum; i++){
 68             if(visibleDot[i] == View.VISIBLE){
 69                 drawCircle(canvas, degOfDot[i]);
 70             }
 71         }
 72     }
 73     private void drawCircle(Canvas canvas, float deg){
 74         float cx = 0, cy = 0;
 75         cx = (float) (mRadius * ( 1.0 + Math.sin( deg / 180.0 * Math.PI))) + 10;
 76         cy = (float) (mRadius * ( 1.0 - Math.cos( deg / 180.0 * Math.PI))) + 10;
 77         canvas.drawCircle(cx, cy , RADIUS, mPainter);
 78     }
 79     
 80     
 81     static final class UpdateThread extends Thread{
 82         private Circle host = null;
 83         private int mInvisibleCount = 0;//是否重新开始循环
 84         private float mDegToShowNext = 0;//下一个圆角度
 85         private int mIndexToShowNext = 0;
 86         
 87         private long mSleepTime = UPDATE_PROGRESS_INTERVAL;
 88         
 89         private boolean running = true;
 90         
 91         public UpdateThread(Circle c){
 92             super();
 93             host = c;
 94             for(int i = 0; i < dotNum; i++){
 95                 degOfDot[i] = 0;
 96                 visibleDot[i] = View.GONE;
 97             }
 98             visibleDot[0] = View.VISIBLE;
 99         }
100         public void run(){
101             while(running){
102                 if(mDegToShowNext == 0){
103                     mDegToShowNext = (float)(SLOWRANGE * 2.0 / 9);
104                 }
105                 if(mIndexToShowNext < dotNum - 1){
106                     if(degOfDot[mIndexToShowNext] > mDegToShowNext){
107                         mIndexToShowNext++;
108                         visibleDot[mIndexToShowNext] = View.VISIBLE;
109                     }
110                 }
111                 try {
112                     Thread.sleep(mSleepTime);
113                 } catch (InterruptedException e) {
114                     // TODO: handle exception
115                     e.printStackTrace();
116                 }
117      
118                 for(int i = 0; i < dotNum; i++){
119                     if(visibleDot[i] != View.VISIBLE){
120                         continue;
121                     }
122                     //以下是运动范围分类
123                     if(degOfDot[i] <= FASTRANGE
124                             || (degOfDot[i] >= (FASTRANGE + SLOWRANGE) 
125                             && degOfDot[i] < (360.0 + FASTRANGE / 2.0))
126                             || (degOfDot[i] >= (360.0 + FASTRANGE / 2.0 + SLOWRANGE))
127                             &&(degOfDot[i] < 720.0) ){
128                         degOfDot[i] += (2.0 * SLICE);
129                     }else if ((degOfDot[i] < FASTRANGE + SLOWRANGE) 
130                             && (degOfDot[i] > FASTRANGE)
131                             || (degOfDot[i] < (360.0 + FASTRANGE / 2.0 + SLOWRANGE))
132                             && (degOfDot[i] >= (360.0 + FASTRANGE / 2.0))){
133                         // 让它移动慢点。
134                         degOfDot[i] += SLICE;
135                     }
136                     if(degOfDot[i] >= DEGREE){
137                         visibleDot[i] = View.GONE;
138                         degOfDot[i] = 0;
139                     }
140                 }
141     
142                 mInvisibleCount = 0;
143                 
144                 for(int i = 0; i < dotNum; i++){
145                     if(visibleDot[i] != View.VISIBLE){
146                         mInvisibleCount++;
147                     }
148                 }
149                 if(mInvisibleCount >= dotNum){
150                     mIndexToShowNext = 0;
151                     visibleDot[0] = View.VISIBLE;
152                 }
153                 host.postInvalidate();
154             }
155         }
156         public void stopThread(){
157             running = false;
158         }
159     }
160 }

    有任何问题请留言。为了大家的可持续发展我不提供源代码。程序员不能懒啊><

posted @ 2012-11-08 19:47  __木头鱼__  阅读(2154)  评论(0编辑  收藏  举报