1. 程式人生 > >android progressbar 使用自定義圖片時的左右兩端圓角效果實現

android progressbar 使用自定義圖片時的左右兩端圓角效果實現

              前幾天一直在折騰progressbar的圓角進度條動畫,各種爬貼摸索,幾經折騰找到一種比較方便的方法實現,這裡做下筆記,避免下次折騰。原生的progressbar的條形進度條的進度左右是直角的,沒有圓角效果的。首先我們來認識一下progressbar。要認識這個progressbar,就需要Read the fucking source code.(盜用了某大牛的話了偷笑)。


            setProgressDrawable和setIndeterminateDrawable。前者一般使用於確定時間,或者長度顯示使用的,比如,下載檔案的大小是固定的,這時可以使用條形的progressbar顯示下載的檔案大小。但是有一些卻不是固定的,比如請求網路資料時候的時間,下載檔案需要的時間,這種顯示就不是固定的時間,會隨著網速的變化而變化的,這種時候就使用setIndeterminateDrawable方法。官方的api也說明了很仔細了,in indeterminate mode不確定模式。

      progressbar在使用純色的時候,我們是可以控制進度條兩端的圓角大小的。先看看系統樣式的原始碼情況:

條形進度條樣式Widget.ProgressBar.Horizontal

<span style="background-color: rgb(249, 249, 249);"><style
 name="Widget.ProgressBar.Horizontal">
    <item name="android:indeterminateOnly">false</item>

    </span><span style="background-color: rgb(102, 255, 255);"><item name="android:progressDrawable">@android:drawable/progress_horizontal</item></span><span style="background-color: rgb(249, 249, 249);">

    <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>

    <item name="android:minHeight">20dip</item>

    <item name="android:maxHeight">20dip</item>

    <item name="android:mirrorForRtl">true</item>
</style></span>
樣式比較簡單,我們一看就懂。再看<itemname="android:progressDrawable">@android:drawable/progress_horizontal</item>的裡面的設定情況:
<!--?xml version="1.0"encoding="utf-8"?-->
<!-- Copyright (C) 2008The Android Open Source Project
 
     Licensed under the Apache License, Version 2.0(the "License");
     you may not use thisfile except in compliance with the License.
     You may obtain a copy of the License at
 
          http://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License forthe specific language governing permissions and
     limitations under the License.
-->
 
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dip"><span style="font-family: Arial, Helvetica, sans-serif;"><!-- 就是這裡控制圓角大小--></span>
            <gradient android:startcolor="#ff9d9e9d"android:centercolor="#ff5a5d5a"android:centery="0.75"android:endcolor="#ff747674"android:angle="270">
        </gradient></corners></shape>
    </item>
    
    <item android:id="@android:id/secondaryProgress">
        <clip>
            <shape>
                <corners android:radius="5dip"><span style="font-family: Arial, Helvetica, sans-serif;"><!-- 就是這裡控制圓角大小--></span>
<span style="font-family: Arial, Helvetica, sans-serif;"></span>
                <gradient android:startcolor="#80ffd300"android:centercolor="#80ffb600"android:centery="0.75"android:endcolor="#a0ffcb00"android:angle="270">
            </gradient></corners></shape>
        </clip>
    </item>
    
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <corners android:radius="5dip"><!-- 就是這裡控制圓角大小-->
                <gradient android:startcolor="#ffffd300"android:centercolor="#ffffb600"android:centery="0.75"android:endcolor="#ffffcb00"android:angle="270">
            </gradient></corners></shape>
        </clip>
    </item>
    
</layer-list>

看,就是corners部分控制左右兩端圓角大小的。這裡我強調了是在使用純色的情況下,是這兩個地方可以控制,我們不用修改什麼,在純色的情況下,可以使用顏色值來設定,就跟系統設定方法一致,是可以控制圓角大小的。但是這種控制方式,不適合使用自定義圖片的型別,比如圖片帶花紋的情況,這時候要使用另外的方法設定了,這也是我這次記錄的重點。

       那麼使用自定義圖片的時候要怎麼控制progressbar兩角的圓角大小呢。使用上述方法是控制不了兩端圓角的了,可以設定看一下效果。

xml設定如下的效果:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:id="@android:id/background">
        <shape>
            <corners 
                android:topLeftRadius="15dp"
                android:topRightRadius="15dp"
                android:bottomLeftRadius="15dp"
                android:bottomRightRadius="15dp"
                />
            <solid android:color="#bfbfbf" />
        </shape>
    </item>
    <item
        android:id="@android:id/progress"
        android:drawable="@drawable/progress_loading"<!--這個是一個自定義的png檔案,上面條紋的一小節-->
        >
        <shape>
            <corners 
                android:topLeftRadius="15dp"
                android:topRightRadius="15dp"
                android:bottomLeftRadius="15dp"
                android:bottomRightRadius="15dp"
                />
        </shape>
    </item> 
</layer-list>

看,這時候corners 控制圓角效果已經不起作用了,這個時候就要使用另外的方法了,當然,系統並沒有提供這樣的方法。細心的同學可能發現了上面條紋進圖條左邊有一點點的小圓角效果,沒錯,我們沒有看錯,是有那麼一點點的圓角效果,這和corners設定的大小沒有關係,即使設定為150dp也是不起作用的,這是怎麼回事?這個時候,我們只能去看看原始碼是怎麼回事了。

在構造方法裡面我們找到這裡是設定progressdrawable的:

Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);//這裡是找到drawable
        if (drawable != null) {
            drawable = tileify(drawable, false);//就是這裡了
            // Calling this method can set mMaxHeight, make sure the corresponding
            // XML attribute for mMaxHeight is read after calling this method
            setProgressDrawable(drawable);//在這裡設定
        }
進去tileify(drawable,boolean)方法裡面看看:
/**
     * Converts a drawable to a tiled version of itself. It will recursively
     * traverse layer and state list drawables.
     */
    private Drawable tileify(Drawable drawable, boolean clip) {
        
        if (drawable instanceof LayerDrawable) {
            LayerDrawable background = (LayerDrawable) drawable;
            final int N = background.getNumberOfLayers();
            Drawable[] outDrawables = new Drawable[N];
            
            for (int i = 0; i < N; i++) {
                int id = background.getId(i);
                outDrawables[i] = tileify(background.getDrawable(i),
                        (id == R.id.progress || id == R.id.secondaryProgress));
            }

            LayerDrawable newBg = new LayerDrawable(outDrawables);
            
            for (int i = 0; i < N; i++) {
                newBg.setId(i, background.getId(i));
            }
            
            return newBg;
            
        } else if (drawable instanceof StateListDrawable) {
            StateListDrawable in = (StateListDrawable) drawable;
            StateListDrawable out = new StateListDrawable();
            int numStates = in.getStateCount();
            for (int i = 0; i < numStates; i++) {
                out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip));
            }
            return out;
            
        } else if (drawable instanceof BitmapDrawable) {
            final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();
            if (mSampleTile == null) {
                mSampleTile = tileBitmap;
            }
            
            final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());//仔細找,發現這貨很可疑,進去看看就知道了

            final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
                    Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
            shapeDrawable.getPaint().setShader(bitmapShader);

            return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
                    ClipDrawable.HORIZONTAL) : shapeDrawable;
        }
        
        return drawable;
    }
getDrawableShape()方法:
Shape getDrawableShape() {
        final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
        return new RoundRectShape(roundedCorners, null, null);
    }
看到了沒有,就是這個default方法,導致的那個條紋效果左端有一點點的圓角效果,其實這個是和drawable的子類有關的,這裡就不再深入去drawable先了。既然知道了原因,很自然的,就要改變這個值了,但是,這個方法,物件不能設定,繼承progressbar的子類也不能過載,這是default屬性方法的特性。但是沒有關係,我們只需寫一個繼承progressbar的子類,並在實現getDrawableShape()方法即可。:
public class CustomProgressBar extends ProgressBar {

	public EwCustomProgressBar(Context context) {
		super(context);
	}

	public EwCustomProgressBar(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public EwCustomProgressBar(Context context, AttributeSet attrs, int defStyle) {
		this(context, attrs, defStyle,0);

	}

	public EwCustomProgressBar(Context context, AttributeSet attrs, int defStyle, int styleRes) {
        
		super(context, attrs, defStyle);
	}

	@Override
	protected synchronized void onMeasure(int widthMeasureSpec,
			int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}
	
	public final int roundCorners = 15;//就是改變這個值,就可以改變自定義progressbar左右兩端的圓角大小了,使用於自定義圖片的情況,
	Shape getDrawableShape() {
        final float[] roundedCorners = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 };
        for(int i=0;i<roundedCorners.length;i++){
        	roundedCorners[i] = dp2px(getContext(), roundCorners);
        }
        return new RoundRectShape(roundedCorners, null, null);
    }
}
這樣就行了,只需要再寫一次Shape getDrawableShape()就可以了,當然這裡只能在類裡面修改圓角的角度大小,不是很方便,這個就很簡單了,我們再為自定義的CustomProgressBar新增一個設定圓角的屬性,再自定義的類裡面獲取這個屬性值就可以了,這樣我們就很方便使用了。這是效果。這次就篇幅就到這裡了,下次再介紹滾動過程左側也是圓形效果的實現,效果如圖:


這個實現就留到下次說了.....

本次效果的demo原始碼:點選下載