Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Android自定義控件:進度條的四種實現方式

Android自定義控件:進度條的四種實現方式

編輯:關於android開發

Android自定義控件:進度條的四種實現方式


最近一直在學習自定義控件,搜了許多大牛們Blog裡分享的小教程,也上GitHub找了一些類似的控件進行學習。發現讀起來都不太好懂,就想寫這麼一篇東西作為學習筆記吧。

一、控件介紹:

進度條在App中非常常見,例如下載進度、加載圖片、打開文章、打開網頁等等……都需要這麼一個效果讓用戶知道我們的App正在讀取,以構造良好的交互。如果沒有這樣一個效果的話,用戶沒法知道東西有沒有下載好、圖片加載了沒有、文章打開了沒……會讓用戶很不爽。基於這樣的情景我們的UI設計師們創造了這樣一個控件。

二、這篇文章會涉及的知識點:

跟我一樣剛入門的Android菜鳥們,我推薦大家先了解一下這些知識點再往下看。這些知識點我也會推薦一些博客給大家看看,更推薦大家看文檔裡的解釋,當然大牛們可以直接無視……

1、ClipDrawable類:能夠對一個drawable類進行剪切操作(即只顯示某一部分的區域,另一部分隱藏),顯示多大的區域由level控制(level取值是0~10000)

【博客:http://blog.csdn.net/lonelyroamer/article/details/8244777】、沒文檔的可以在這看【http://www.apihome.cn/api/android/ClipDrawable.html】

2、自定義View:guolin大神的深入學習View四部曲

【Android LayoutInflater原理分析,帶你一步步深入了解View】

【Android視圖繪制流程完全解析,帶你一步步深入了解View】

【Android視圖狀態及重繪流程分析,帶你一步步深入了解View】

【Android自定義View的實現方法,帶你一步步深入了解View】

3、沒看過我寫的:Android自定義控件——老版優酷三級菜單的話,或許需要看看這個:

【RotateAnimation詳解——http://blog.csdn.net/u012403246/article/details/41415799】

三、Android上的實現方式:

(前三種方法比較簡單,第四種方法是GitHub項目的解析,對前三種沒興趣可以直接跳到後邊……)

 

1、效果圖:\

 

將進度條的變換過程分解為一幀一幀的圖片,將這些一幀一幀的圖片連起來構成一個動畫。常用於:手機閱讀網頁、逛社區時,加載圖片、文章等不需要清楚知道加載進度,但是需要知道是否進行加載的情景。

 

這種方法實現可以通過創建一個animation-list的XML文件,然後給系統API提供的ProgressBar的indeterminateDrawable屬性就可以了。(這個屬性應該是類似於設置一個動畫吧……)

在CODE上查看代碼片派生到我的代碼片 2、效果圖:\

 

在上一篇有關自定義控件的博客裡我們使用了一個RotateAnimation類來實現旋轉效果 (http://blog.csdn.net/u012403246/article/details/41309161),其實,我們在這裡也可以把一張圖片,通過旋轉,達到我們要的效果。本質上和上一種方法沒多大區別。

我們只需要創建一個rotate的XML,對其屬性進行一些簡單的設置,然後加入我們要用的圖片就可以了。

XML:

  在CODE上查看代碼片派生到我的代碼片   android:pivotY="50%" android:fromDegrees="0" android:toDegrees="360" android:interpolator="@android:anim/accelerate_decelerate_interpolator"> [html]   在CODE上查看代碼片派生到我的代碼片 3、效果圖:\

我們可以弄兩張照片,第一張是純黑色的,然後把這張照片中心挖一個圓出來,圓區域弄成白色,挖出來的圓弄成第二張照片。我們不妨疊加顯示兩張照片,剛開始把第二張完全“遮住”,隨著加載進度的增加,我們減少遮住的區域把第二張照片慢慢的顯示出來。

Android上剛好就有這麼一個ClipDrawable類,能夠實現剪裁的過程。我們來看看怎麼通過這樣的方式自定義一個進度條控件。

代碼:


  1. publicclassMyProgressBarextendsFrameLayout{ privatebooleanrunning; privateintprogress=0; privatestaticfinalintMAX_PROGRESS=10000;   privateClipDrawableclip;   privateHandlerhandler=newHandler(){ @Override publicvoidhandleMessage(android.os.Messagemsg){ if(msg.what==0x123) clip.setLevel(progress); } };   publicMyProgressBar(Contextcontext){ this(context,null,0); }   publicMyProgressBar(Contextcontext,AttributeSetattrs){ this(context,null,0); }   publicMyProgressBar(Contextcontext,AttributeSetattrs,intdefStyle){ super(context,attrs,defStyle); Init(context); }   publicvoidInit(Contextcontext){ Viewview=LayoutInflater.from(context).inflate(R.layout.view,null);   ImageViewiv=(ImageView)view.findViewById(R.id.progress_img);   addView(view); clip=(ClipDrawable)iv.getDrawable();   Threadthread=newThread(newRunnable(){   @Override publicvoidrun(){ running=true; while(running){ handler.sendEmptyMessage(0x123); if(progress==MAX_PROGRESS) progress=0; progress+=100; try{ Thread.sleep(18); }catch(InterruptedExceptione){ e.printStackTrace(); } } } }); thread.start(); }   publicvoidstop(){ progress=0; running=false; } }
    通過代碼我們可以看到,邏輯非常簡單,關鍵就在於ClipDrawable的setLevel()方法,這個是設置剪裁效果的。

     

    4、效果圖:\

    實現一個View的子類——Progress Wheel類,實現進度條效果。具體的內容我都寫在了注釋上,如果不了解自定義控件的知識,可以去閱讀guolin博客裡自定義View四部曲的講解,講的挺好的。

    代碼:


    1. publicclassProgressWheelextendsView{   //繪制View用到的各種長、寬帶大小 privateintlayout_height=0; privateintlayout_width=0; privateintfullRadius=100; privateintcircleRadius=80; privateintbarLength=60; privateintbarWidth=20; privateintrimWidth=20; privateinttextSize=20; privatefloatcontourSize=0;   //與頁邊的間距 privateintpaddingTop=5; privateintpaddingBottom=5; privateintpaddingLeft=5; privateintpaddingRight=5;   //View要繪制的顏色 privateintbarColor=0xAA000000; privateintcontourColor=0xAA000000; privateintcircleColor=0x00000000; privateintrimColor=0xAADDDDDD; privateinttextColor=0xFF000000;   //繪制要用的畫筆 privatePaintbarPaint=newPaint(); privatePaintcirclePaint=newPaint(); privatePaintrimPaint=newPaint(); privatePainttextPaint=newPaint(); privatePaintcontourPaint=newPaint();   //繪制要用的矩形 @SuppressWarnings("unused") privateRectFrectBounds=newRectF(); privateRectFcircleBounds=newRectF(); privateRectFcircleOuterContour=newRectF(); privateRectFcircleInnerContour=newRectF();   //動畫 //每次繪制要移動的像素數目 privateintspinSpeed=2; //繪制過程的時間間隔 privateintdelayMillis=0; intprogress=0; booleanisSpinning=false;   //其他 privateStringtext=""; privateString[]splitText={};   /** *ProgressWheel的構造方法 * *@paramcontext *@paramattrs */ publicProgressWheel(Contextcontext,AttributeSetattrs){ super(context,attrs);   parseAttributes(context.obtainStyledAttributes(attrs, R.styleable.ProgressWheel)); }   //---------------------------------- //初始化一些元素 //----------------------------------   /* *調用這個方法時,使View繪制為方形 *From:http://www.jayway.com/2012/12/12/creating-custom-android-views-part-4-measuring-and-how-to-force-a-view-to-be-square/ * */ @Override protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){ //首先我們要調用超類的onMeasure借口 //原因是我們自己去實現一個方法獲得長度、寬度太麻煩了 //使用超類的的方法非常方便而且讓復雜的細節可控 super.onMeasure(widthMeasureSpec,heightMeasureSpec);   //在這裡我們不能使用getWidth()和getHeight()。 //因為這兩個方法只能在View的布局完成後才能使用,而一個View的繪制過程是先繪制元素,再繪制Layout //所以我們必須使用getMeasuredWidth()和getMeasuredHeight() intsize=0; intwidth=getMeasuredWidth(); intheight=getMeasuredHeight(); intwidthWithoutPadding=width-getPaddingLeft()-getPaddingRight(); intheigthWithoutPadding=height-getPaddingTop()-getPaddingBottom();   //最後我們用一些簡單的邏輯去計算View的大小並調用setMeasuredDimension()去設置View的大小 //在比較View的長寬前我們不考慮間距,但當我們設置View所需要繪制的面積時,我們要考慮它 //不考慮間距的View(View內的實際畫面)此時就應該是方形的,但是由於間距的存在,最終View所占的面積可能不是方形的 if(widthWithoutPadding>heigthWithoutPadding){ size=heigthWithoutPadding; }else{ size=widthWithoutPadding; }   //如果你重寫了onMeasure()方法,你必須調用setMeasuredDimension()方法 //這是你設置View大小的唯一途徑 //如果你不調用setMeasuredDimension()方法,父控件會拋出異常,並且程序會崩潰 //如果我們使用了超類的onMeasure()方法,我們就不是那麼需要setMeasuredDimension()方法 //然而,重寫onMeasure()方法是為了改變既有的繪制流程,所以我們必須調用setMeasuredDimension()方法以達到我們的目的 setMeasuredDimension(size+getPaddingLeft()+getPaddingRight(),size+getPaddingTop()+getPaddingBottom()); }   /** *使用onSizeChanged方法代替onAttachedToWindow獲得View的面積 *因為這個方法會在測量了MATCH_PARENT和WRAP_CONTENT後馬上被調用 *使用獲得的面積設置View */ @Override protectedvoidonSizeChanged(intw,inth,intoldw,intoldh){ super.onSizeChanged(w,h,oldw,oldh);   //Sharethedimensions layout_width=w; layout_height=h;   setupBounds(); setupPaints(); invalidate(); }   /** *設置我們想要繪制的progresswheel的顏色 */ privatevoidsetupPaints(){ 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);   circlePaint.setColor(circleColor); circlePaint.setAntiAlias(true); circlePaint.setStyle(Style.FILL);   textPaint.setColor(textColor); textPaint.setStyle(Style.FILL); textPaint.setAntiAlias(true); textPaint.setTextSize(textSize);   contourPaint.setColor(contourColor); contourPaint.setAntiAlias(true); contourPaint.setStyle(Style.STROKE); contourPaint.setStrokeWidth(contourSize); }   /** *設置元素的邊界 */ privatevoidsetupBounds(){ //為了保持寬度和長度的一致,我們要獲得layout_width和layout_height中較小的一個,從而繪制一個圓 intminValue=Math.min(layout_width,layout_height);   //計算在繪制過程中在x,y方向的偏移量 intxOffset=layout_width-minValue; intyOffset=layout_height-minValue;   //間距加上偏移量 paddingTop=this.getPaddingTop()+(yOffset/2); paddingBottom=this.getPaddingBottom()+(yOffset/2); paddingLeft=this.getPaddingLeft()+(xOffset/2); paddingRight=this.getPaddingRight()+(xOffset/2);   intwidth=getWidth();//this.getLayoutParams().width; intheight=getHeight();//this.getLayoutParams().height;   rectBounds=newRectF(paddingLeft, paddingTop, width-paddingRight, height-paddingBottom);   circleBounds=newRectF(paddingLeft+barWidth, paddingTop+barWidth, width-paddingRight-barWidth, height-paddingBottom-barWidth); circleInnerContour=newRectF(circleBounds.left+(rimWidth/2.0f)+(contourSize/2.0f),circleBounds.top+(rimWidth/2.0f)+(contourSize/2.0f),circleBounds.right-(rimWidth/2.0f)-(contourSize/2.0f),circleBounds.bottom-(rimWidth/2.0f)-(contourSize/2.0f)); circleOuterContour=newRectF(circleBounds.left-(rimWidth/2.0f)-(contourSize/2.0f),circleBounds.top-(rimWidth/2.0f)-(contourSize/2.0f),circleBounds.right+(rimWidth/2.0f)+(contourSize/2.0f),circleBounds.bottom+(rimWidth/2.0f)+(contourSize/2.0f));   fullRadius=(width-paddingRight-barWidth)/2; circleRadius=(fullRadius-barWidth)+1; }   /** *從XML中解析控件的屬性 * *@paramatheattributestoparse */ privatevoidparseAttributes(TypedArraya){ barWidth=(int)a.getDimension(R.styleable.ProgressWheel_barWidth, barWidth);   rimWidth=(int)a.getDimension(R.styleable.ProgressWheel_rimWidth, rimWidth);   spinSpeed=(int)a.getDimension(R.styleable.ProgressWheel_spinSpeed, spinSpeed);   delayMillis=a.getInteger(R.styleable.ProgressWheel_delayMillis, delayMillis); if(delayMillis<0){ delayMillis=0; }   barColor=a.getColor(R.styleable.ProgressWheel_barColor,barColor);   barLength=(int)a.getDimension(R.styleable.ProgressWheel_barLength, barLength);   textSize=(int)a.getDimension(R.styleable.ProgressWheel_textSize, textSize);   textColor=(int)a.getColor(R.styleable.ProgressWheel_textColor, textColor);   //如果text是空的,就無視它 if(a.hasValue(R.styleable.ProgressWheel_text)){ setText(a.getString(R.styleable.ProgressWheel_text)); }   rimColor=(int)a.getColor(R.styleable.ProgressWheel_rimColor, rimColor);   circleColor=(int)a.getColor(R.styleable.ProgressWheel_circleColor, circleColor);   contourColor=a.getColor(R.styleable.ProgressWheel_contourColor,contourColor); contourSize=a.getDimension(R.styleable.ProgressWheel_contourSize,contourSize);     //使用TypedArray獲得控件屬性時必須要注意:使用結束後必須回收TypedArray的對象 a.recycle(); }   //---------------------------------- //動畫 //----------------------------------   protectedvoidonDraw(Canvascanvas){ super.onDraw(canvas); //繪制內圓 canvas.drawArc(circleBounds,360,360,false,circlePaint); //繪制邊界 canvas.drawArc(circleBounds,360,360,false,rimPaint); canvas.drawArc(circleOuterContour,360,360,false,contourPaint); canvas.drawArc(circleInnerContour,360,360,false,contourPaint); //繪制條紋 if(isSpinning){ canvas.drawArc(circleBounds,progress-90,barLength,false, barPaint); }else{ canvas.drawArc(circleBounds,-90,progress,false,barPaint); } //繪制我們想要設置的文字(並讓它顯示在圓水平和垂直方向的中心處) floattextHeight=textPaint.descent()-textPaint.ascent(); floatverticalTextOffset=(textHeight/2)-textPaint.descent();   for(Strings:splitText){ floathorizontalTextOffset=textPaint.measureText(s)/2; canvas.drawText(s,this.getWidth()/2-horizontalTextOffset, this.getHeight()/2+verticalTextOffset,textPaint); } if(isSpinning){ scheduleRedraw(); } }   privatevoidscheduleRedraw(){ progress+=spinSpeed; if(progress>360){ progress=0; } postInvalidateDelayed(delayMillis); }   /** *判斷wheel是否在旋轉 */   publicbooleanisSpinning(){ if(isSpinning){ returntrue; }else{ returnfalse; } }   /** *重設進度條的值 */ publicvoidresetCount(){ progress=0; setText("0%"); invalidate(); }   /** *停止進度條的旋轉 */ publicvoidstopSpinning(){ isSpinning=false; progress=0; postInvalidate(); }     /** *讓進度條開啟旋轉模式 */ publicvoidspin(){ isSpinning=true; postInvalidate(); }   /** *讓進度條每次增加1(最大值為360) */ publicvoidincrementProgress(){ isSpinning=false; progress++; if(progress>360) progress=0; setText(Math.round(((float)progress/360)*100)+"%"); postInvalidate(); }     /** *設置進度條為一個確切的數值 */ publicvoidsetProgress(inti){ isSpinning=false; progress=i; postInvalidate(); }   //---------------------------------- //get和set方法 //----------------------------------   /** *設置progressbar的文字並不需要刷新View * *@paramtextthetexttoshow('\n'constitutesanewline) */ publicvoidsetText(Stringtext){ this.text=text; splitText=this.text.split("\n"); }   publicintgetCircleRadius(){ returncircleRadius; }   publicvoidsetCircleRadius(intcircleRadius){ this.circleRadius=circleRadius; }   publicintgetBarLength(){ returnbarLength; }   publicvoidsetBarLength(intbarLength){ this.barLength=barLength; }   publicintgetBarWidth(){ returnbarWidth; }   publicvoidsetBarWidth(intbarWidth){ this.barWidth=barWidth;   if(this.barPaint!=null){ this.barPaint.setStrokeWidth(this.barWidth); } }   publicintgetTextSize(){ returntextSize; }   publicvoidsetTextSize(inttextSize){ this.textSize=textSize;   if(this.textPaint!=null){ this.textPaint.setTextSize(this.textSize); } }   publicintgetPaddingTop(){ returnpaddingTop; }   publicvoidsetPaddingTop(intpaddingTop){ this.paddingTop=paddingTop; }   publicintgetPaddingBottom(){ returnpaddingBottom; }   publicvoidsetPaddingBottom(intpaddingBottom){ this.paddingBottom=paddingBottom; }   publicintgetPaddingLeft(){ returnpaddingLeft; }   publicvoidsetPaddingLeft(intpaddingLeft){ this.paddingLeft=paddingLeft; }   publicintgetPaddingRight(){ returnpaddingRight; }   publicvoidsetPaddingRight(intpaddingRight){ this.paddingRight=paddingRight; }   publicintgetBarColor(){ returnbarColor; }   publicvoidsetBarColor(intbarColor){ this.barColor=barColor;   if(this.barPaint!=null){ this.barPaint.setColor(this.barColor); } }   publicintgetCircleColor(){ returncircleColor; }   publicvoidsetCircleColor(intcircleColor){ this.circleColor=circleColor;   if(this.circlePaint!=null){ this.circlePaint.setColor(this.circleColor); } }   publicintgetRimColor(){ returnrimColor; }   publicvoidsetRimColor(intrimColor){ this.rimColor=rimColor;   if(this.rimPaint!=null){ this.rimPaint.setColor(this.rimColor); } }     publicShadergetRimShader(){ returnrimPaint.getShader(); }   publicvoidsetRimShader(Shadershader){ this.rimPaint.setShader(shader); }   publicintgetTextColor(){ returntextColor; }   publicvoidsetTextColor(inttextColor){ this.textColor=textColor;   if(this.textPaint!=null){ this.textPaint.setColor(this.textColor); } }   publicintgetSpinSpeed(){ returnspinSpeed; }   publicvoidsetSpinSpeed(intspinSpeed){ this.spinSpeed=spinSpeed; }   publicintgetRimWidth(){ returnrimWidth; }   publicvoidsetRimWidth(intrimWidth){ this.rimWidth=rimWidth;   if(this.rimPaint!=null){ this.rimPaint.setStrokeWidth(this.rimWidth); } }   publicintgetDelayMillis(){ returndelayMillis; }   publicvoidsetDelayMillis(intdelayMillis){ this.delayMillis=delayMillis; }   publicintgetContourColor(){ returncontourColor; }   publicvoidsetContourColor(intcontourColor){ this.contourColor=contourColor;   if(contourPaint!=null){ this.contourPaint.setColor(this.contourColor); } }   publicfloatgetContourSize(){ returnthis.contourSize; }   publicvoidsetContourSize(floatcontourSize){ this.contourSize=contourSize;   if(contourPaint!=null){ this.contourPaint.setStrokeWidth(this.contourSize); } } }

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved