前几天一直在折腾progressbar的圆角进度条动画,各种爬贴摸索,几经折腾找到一种比较方便的方法实现,这里做下笔记,避免下次折腾。原生的progressbar的条形进度条的进度左右是直角的,没有圆角效果的。首先我们来认识一下progressbar。要认识这个progressbar,就需要Read the fucking source code.(盗用了某大牛的话了)。
先参考google的api 文档说明(地址在这里:https://developer.android.com/intl/zh-cn/reference/android/widget/ProgressBar.html),这里不一一介绍,只是介绍和做这个效果相关的,需要看其他就自己去看了。这里简单说一下这两个的区别,
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>
样式比较简单,我们一看就懂。再看 < item name = "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源码:点击下载