相信很多人在项目里都想过自定义进度条,不是那种普通的,看看上面的截图,没错,就是这样。前段时间项目刚好需要做这样的进度条,可惜没有什么头绪,思前顾后只有两个方法。
方法一:这张进度条的图片1进度条部分镂空,其他部分显示一个没有透明度的颜色,在该图片里底部放一个底色图片2,图片1和图片2之间放一张宽度为0的图片3,图片3的宽度等于进度*图片1的宽度。通过这样来实现进度条变化的动画;
方法二:进度条图片1进度条部分不镂空,其他部分为空,进度变化时对图片1进行从左到右的垂直颜色变化。
看到这两个方法后相信大家肯定觉得方法二比较好,可是方法二怎么做呢?是的,小编开始也纠结了很久,鉴于网上实现方法二的例子非常少,最后还是采用了方法一;不过方法一有个很致命的缺点,就是图片1非镂空部分不能为有透明度,不然很容易和后面的图片2、3叠加在一起。还好,黄天不负有心人,最后还是实现了方法二。下面我给大家分别细细讲解下方法一和方法二的实现:
方法一:
该方法依赖于布局:这里我弄了一个FrameLayout,这个是我做其他事情需要用到的。该布局需要使用3个view,而且也需要这个进度条镂空才行,镂空后该进度条其余部分必须使用非透明色值。这个是它的缺陷。
<RelativeLayout
android:id="@+id/lay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/progress_seek_bar"
android:layout_centerHorizontal="true"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:background="@drawable/progress_hud_bg"
android:paddingBottom="40dp"
android:paddingLeft="60dp"
android:paddingRight="60dp"
android:paddingTop="40dp" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/lay1"
android:layout_alignLeft="@+id/lay1"
android:layout_alignRight="@+id/lay1"
android:layout_alignTop="@+id/lay1"
android:background="#ffffff" />
<ImageView
android:id="@+id/background"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/lay1"
android:layout_alignLeft="@+id/lay1"
android:layout_alignTop="@+id/lay1"
android:background="#26a390" />
<FrameLayout
android:id="@+id/lay1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true" >
<ImageView
android:id="@+id/spinnerImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/icon_normal" />
</FrameLayout>
</RelativeLayout>
其中id为background的这个ImageView宽度设置0,在代码中动态设置他的宽度。
private void init() {
mImageView = (ImageView) findViewById(R.id.spinnerImageView);
mBackground = (ImageView) findViewById(R.id.background);
mimageView = (ImageView) findViewById(R.id.imageview);
// 绘制完view后取得mImageView的宽度
ViewTreeObserver vto2 = mImageView.getViewTreeObserver();
vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
mImageView.getViewTreeObserver().removeGlobalOnLayoutListener(
this);
mWidth = mImageView.getWidth();
}
});
SeekBar progressSeekBar = (SeekBar) findViewById(R.id.progress_seek_bar);
progressSeekBar.setOnSeekBarChangeListener(this);
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
// 取得宽度的值
int bgWidth = (progress * mWidth) / 100;
// 设置宽度
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mBackground
.getLayoutParams();
params.width = bgWidth;
mBackground.setLayoutParams(params);
// 方法二设置进度
Bitmap bitmap = setVolume(progress);
mimageView.setImageBitmap(bitmap);
}
方法一就不多说,实现很简单,童鞋们就自己去体验了。
方法二:
方法二的处理就灵活多了。
在进入该activity时调用该方法初始化一张bitmap,并复制一张可修改的bitmap。之后我们就以该bitmap进行操作了。
private void initBitmap() {
// 在这里创建了一张bitmap
mBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.normal);
mBitmap.getConfig();
// 复制一张可修改的bitmap
mBitmapCp = mBitmap.copy(Config.ARGB_8888, true);
// 将这张bitmap设置为背景图片
BitmapDrawable bd = new BitmapDrawable(mBitmapCp);
bd.setAntiAlias(true);
bd.setFilterBitmap(true);
mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
}
之后我们就可以调用setVolume方法,volume是进度。如果童鞋们根据我的步骤做,这时会发现return回来的bitmap有很严重的锯齿,我那时也是卡在这里一段时间。后来我想到一个方法,通过蒙版来处理
// 通过百分比 根据图片宽高算出实际填充宽度
public int getValue(int volume) {
return mBitmapWidth - (mBitmapWidth * volume / 100);
}
// 修改图片颜色
@SuppressWarnings("deprecation")
public Bitmap setVolume(int volume) {
int startX = 0;
int endX = 0;
if (mBackVolume < volume) {
startX = getValue(volume);
endX = getValue(mBackVolume);
}
// 从左到右填充颜色,如改为for (int i = startX; i < endX; i++) 则为从右到左
for (int i = mBitmapWidth - endX; i < mBitmapWidth - startX; i++) {
for (int j = 0; j < mBitmapHeight; j++) {
// 将需要填充的颜色值如果不是
// 在这说明一下 如果color 是全透明 或者全黑 返回值为 0
// getPixel()不带透明通道 getPixel32()才带透明部分 所以全透明是0x00000000
// 而不透明黑色是0xFF000000 如果不计算透明部分就都是0了
int color = mBitmapCp.getPixel(i, j);
if (color != 0) {
mBitmapCp.setPixel(i, j, 0xff26a390);
}
}
}
mBackVolume = volume;
return mBitmapCp;
}
把上面的return mBitmapCp;改成
Bitmap mask = BitmapFactory.decodeResource(getResources(),
R.drawable.normal);
return MaskBitmap(mBitmapCp, mask, mask.getWidth(), mask.getHeight(),
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
使用PorterDuffXfermode对图片进行蒙版处理,删去对比原图多出来的部分,并且使用Canvas和Paint,这时我们就可以设置防止锯齿。
/**
* 蒙版去除锯齿
*
* @param bitmap
* @param mask
* @param size
* @param mode
* @return
*/
private static Bitmap MaskBitmap(Bitmap bitmap, Bitmap mask, int width,
int height, Xfermode mode) {
if (null == bitmap || mask == null) {
return null;
}
// 定义期望大小的bitmap
Bitmap dstBmp = Bitmap.createBitmap(width, height, Config.ARGB_8888);
// 定义一个画布
Canvas canvas = new Canvas(dstBmp);
// 创建一个取消锯齿画笔
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 定义需要绘制的某图片上的那一部分矩形空间
Rect src = new Rect(0, 0, mask.getWidth(), mask.getHeight());
// 定义需要将上面的矩形绘制成新的矩形大小
Rect dst = new Rect(0, 0, width, height);
// 将蒙版图片绘制成imageview本身的大小,这样从大小才会和UE标注的一样大
canvas.drawBitmap(mask, src, dst, paint);
// 设置两张图片的相交模式
paint.setXfermode(mode);
// 将src修改为需要添加mask的bitmap大小,因为是要将此bitmap整个添加上蒙版
src.right = bitmap.getWidth();
src.bottom = bitmap.getHeight();
// 在已经绘制的mask上叠加bitmap
canvas.drawBitmap(bitmap, src, dst, paint);
return dstBmp;
}
好,本次讲解基本完成,有什么问题或者不懂的,欢迎留言。
欢迎大家加我,相互学习,相互讨论
如有转载,请注明出处:http://blog.csdn.net/hjhrq1991