自定义带动画的不规则分段显示进度条

本文介绍如何在Android中实现一个带有动画的不规则分段进度条,常见于游戏初始化加载场景。通过创建多层自定义View,包括上下文布局、动画展示层、动态效果等,实现独特的加载效果。

转载请注明出处:http://blog.csdn.net/binbinqq86/article/details/46313437

这种效果在游戏中见到的比较多,一般用在初始化加载的时候。最近项目中需要用到这种效果,于是就自己实现了一个,废话不多说,先上效果图


怎么样,如果你感兴趣,就继续向下看吧~得意

首先说说实现的原理:整个界面上下是两个文本布局,没什么说的,主要是中间的动画展示进度条层。不要小看这一层哦,它一共包含了上、中、下三层,每一层都是一个自定义的View。最下层就是那个5段的总进度加上蓝色段分割线,而中间层是动画向前冲的那层,最上层就是蓝色走过的每两段之间的竖直白色分割线~好晕睡觉

下面还是直接看实际代码:

package com.binbin.tprogressbar;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import com.binbin.mywidget.R;
/**
 * 三层分段组合进度条最底层背景层
 * @author tianbin
 *
 * Created on 2015-3-12 下午2:21:22
 */
public class ProgressBottomView extends View {
	
	private Paint mPaint;
	/**
	 * 边框颜色
	 */
	private int lineColor=0xFFA0DB58;

	/**临时的画布对象*/
	private Bitmap mBitmap;

	/**临时的画布*/
	private Canvas mCanvas;
	
	/**画布宽高*/
	private float height,width;
	/**两边圆的直径*/
	private float rad;
	/**画笔宽*/
	private float penWidth;
	
	public ProgressBottomView(Context context) {
		this(context, null);
	}

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

	public ProgressBottomView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		rad=getResources().getDimension(R.dimen.dpradius);
		penWidth=getResources().getDimension(R.dimen.penwidth);
		height=getResources().getDimension(R.dimen.height);
	}

	@SuppressLint("DrawAllocation") 
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		width=getMeasuredWidth();
		mPaint = new Paint();
		mPaint.setColor(lineColor);
		mPaint.setStrokeWidth(penWidth);
		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setAntiAlias(true);
		mPaint.setSubpixelText(true);
		mBitmap = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_4444);
		mCanvas = new Canvas(mBitmap);
		//画左边半圆
		RectF rf=new RectF(0, 0,rad ,height);
		/**
		 *  oval :指定圆弧的外轮廓矩形区域。
			startAngle: 圆弧起始角度,单位为度。
			sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。
			useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。
		 */
		mCanvas.drawArc(rf, 90, 180, false, mPaint);
		//画右边半圆
		RectF rf1=new RectF(width-rad, 0,width ,height);
		mCanvas.drawArc(rf1, -90, 180, false, mPaint);
		mPaint.setStyle(Paint.Style.FILL);
		//画上下两条线
		mCanvas.drawLine(rad/2f, 0, width-rad/2f, 0, mPaint);
		mCanvas.drawLine(rad/2f, height, width-rad/2f, height, mPaint);
		//画垂直的分割线
		for(int i=0;i<TProgressView.duanNum-1;i++){
			//画垂直的分割线
			mCanvas.drawLine(width*(i+1)/TProgressView.duanNum, 0, width*(i+1)/TProgressView.duanNum, height, mPaint);
		}
		//把准备好的bitmap绘制到当前画布上
		canvas.drawBitmap(mBitmap, 0, 0, null);
	}
	
	public int getLineColor() {
		return lineColor;
	}

	/**
	 * 设置边框颜色
	 * @param color
	 */
	public void setLineColor(int color){
		this.lineColor=color;
		invalidate();
	}
	
}
package com.binbin.tprogressbar;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import com.binbin.mywidget.R;
/**
 * 三层分段组合进度条中间滑动层
 * @author tianbin
 *
 * Created on 2015-3-12 下午2:21:56
 */
public class ProgressMiddleView extends View {
	private Paint mPaint;
	/**
	 * 线颜色
	 */
	private int lineColor=0xFFA0DB58;

	/**临时的画布对象*/
	private Bitmap mBitmap;

	/**临时的画布*/
	private Canvas mCanvas;
	
	/**画布宽高*/
	private float height,width;
	/**画笔宽*/
	private float penWidth;
	/**两边圆的直径*/
	private float rad;
	
	public ProgressMiddleView(Context context) {
		this(context, null);
	}

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

	public ProgressMiddleView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		penWidth=getResources().getDimension(R.dimen.penwidth);
		height=getResources().getDimension(R.dimen.height);
		rad=getResources().getDimension(R.dimen.dpradius);
	}

	@SuppressLint("DrawAllocation") 
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		width=getMeasuredWidth();
		//把准备好的bitmap绘制到当前画布上
		if(mBitmap==null){
			return;
		}
		canvas.drawBitmap(mBitmap, 0, 0, null);
	}
	
	/**
	 * 设置画线颜色
	 * @param lineColor
	 */
	public void setLineColor(int lineColor) {
		this.lineColor = lineColor;
	}

	public int getLineColor() {
		return lineColor;
	}

	/**
	 * 根据当前进度坐标画进度条
	 * @param x 当前进度在屏幕上的坐标
	 */
	public void drawMiddle(int x){
		if(width<=0||height<=0){
			return;
		}
		mPaint = new Paint();
		mPaint.setColor(lineColor);
		mPaint.setStrokeWidth(penWidth);
		mPaint.setStyle(Paint.Style.FILL);
		mPaint.setAntiAlias(true);
		mPaint.setSubpixelText(true);
		mBitmap = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_4444);
		mCanvas = new Canvas(mBitmap);
		/**画左边圆*/
		mCanvas.drawCircle(rad/2, rad/2, rad/2, mPaint);
		/**画中间矩形*/
		mCanvas.drawRect(rad/2, 0, x-rad, height, mPaint);
		/**画右边圆*/
		mCanvas.drawCircle(x-rad, rad/2, rad/2, mPaint);
		invalidate();
	}
	
}

package com.binbin.tprogressbar;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import com.binbin.mywidget.R;
/**
 * 三层分段组合进度条最顶层分割白线层
 * @author tianbin
 *
 * Created on 2015-3-12 下午2:21:56
 */
public class ProgressTopView extends View {
	private Paint mPaint;
	/**
	 * 线颜色
	 */
	private int lineColor=0xFFFFFFFF;

	/**临时的画布对象*/
	private Bitmap mBitmap;

	/**临时的画布*/
	private Canvas mCanvas;
	
	/**画布宽高*/
	private float height,width;
	/**画笔宽*/
	private float penWidth;
	
	public ProgressTopView(Context context) {
		this(context, null);
	}

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

	public ProgressTopView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		penWidth=getResources().getDimension(R.dimen.penwidth);
		height=getResources().getDimension(R.dimen.height);
	}

	@SuppressLint("DrawAllocation") 
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		width=getMeasuredWidth();
		//把准备好的bitmap绘制到当前画布上
		if(mBitmap==null){
			return;
		}
		canvas.drawBitmap(mBitmap, 0, 0, null);
	}
	
	/**
	 * 设置画线颜色
	 * @param lineColor
	 */
	public void setLineColor(int lineColor) {
		this.lineColor = lineColor;
	}

	public int getLineColor() {
		return lineColor;
	}

	/**
	 * 根据数量画分割线
	 * @param num
	 */
	public void drawLine(int num){
		if(width<=0||height<=0){
			return;
		}
		mPaint = new Paint();
		mPaint.setColor(lineColor);
		mPaint.setStrokeWidth(penWidth);
		mPaint.setStyle(Paint.Style.FILL);
		mPaint.setAntiAlias(true);
		mBitmap = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_4444);
		mCanvas = new Canvas(mBitmap);
		for(int i=0;i<num;i++){
			//画垂直的分割线
			mCanvas.drawLine(width*(i+1)/TProgressView.duanNum, 0, width*(i+1)/TProgressView.duanNum, height, mPaint);
		}
		invalidate();
	}
	
}
三层的代码就这么多,怎么样,是不是有点眉目了,下面就用一个自定义view把这三层view整合起来,这样便于在代码中引用:

package com.binbin.tprogressbar;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;

import com.binbin.mywidget.R;
import com.nineoldandroids.animation.IntEvaluator;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;

/**
 * 自定义带动画分段显示进度条,目前暂不支持直接设置段数,需要手动重写
 * @author tianbin
 *
 * Created on 2015-3-12 下午4:29:35
 */
public class TProgressView extends FrameLayout {
	
	/**控件宽度*/
	private int viewWidth;
	/**中间动画层*/
	private ProgressMiddleView progressView;
	/**顶层白色垂直分割线层*/
	private ProgressTopView ptv;
	/**底层*/
	private ProgressBottomView pbv;
	/**当前进度*/
	private int progress;
	/**进度条分为几段(5段有6个等级值,4个垂直分割线)*/
	public static int duanNum=5;
	/**等级分级标准*/
	private int[] positions=new int[duanNum+1];
	/**动画完成时间(ms)*/
	private long animTime=1000;
	
	public TProgressView(Context context) {
		this(context, null);
	}

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

	public TProgressView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		pbv=new ProgressBottomView(context);
		progressView=new ProgressMiddleView(context);
//		progressView.setBackgroundResource(R.drawable.my_of_bg);
		//默认不可见,动画开始时可见
		progressView.setVisibility(8);
		ptv=new ProgressTopView(context);
		
		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TProgressView, defStyle, 0);
		int mBottomColor = a.getColor(R.styleable.TProgressView_bottom_color, pbv.getLineColor());
		int mTopColor = a.getColor(R.styleable.TProgressView_top_color, ptv.getLineColor());
		int mMiddleColor = a.getColor(R.styleable.TProgressView_bottom_color, progressView.getLineColor());
		a.recycle();
		//设置边框颜色
		pbv.setLineColor(mBottomColor);
		ptv.setLineColor(mTopColor);
		progressView.setLineColor(mMiddleColor);
		
		addView(pbv);
		addView(progressView,(int)getResources().getDimension(R.dimen.height),(int)getResources().getDimension(R.dimen.height));
		addView(ptv);
	}
	
	/**
	 * 执行动画
	 * @param target 执行动画的目标控件
	 * @param start 开始坐标
	 * @param end 结束坐标
	 */
	@SuppressLint("NewApi")
	private void performAnimate(final View target, final int start,
			final int end) {
		// 把当前进度等分成多少份
		ValueAnimator valueAnimator = ValueAnimator.ofInt(1, end);
		valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
			// 持有一个IntEvaluator对象,方便下面估值的时候使用
			private IntEvaluator mEvaluator = new IntEvaluator();

			@Override
			public void onAnimationUpdate(ValueAnimator animator) {
				// 获得当前动画的进度值,整型,1-100之间
				int currentValue = (Integer) animator.getAnimatedValue();
				progressView.drawMiddle(currentValue);
				if (currentValue < viewWidth * 1f / duanNum) {
				} else if (currentValue < viewWidth * 2f / duanNum) {
					ptv.drawLine(1);
				} else if (currentValue < viewWidth * 3f / duanNum) {
					ptv.drawLine(2);
				} else if (currentValue < viewWidth * 4f / duanNum) {
					ptv.drawLine(3);
				} else {
					ptv.drawLine(4);
				}
				// 计算当前进度占整个动画过程的比例,浮点型,0-1之间
				float fraction = (float) currentValue / end;
				// 直接调用整型估值器通过比例计算出宽度,然后再设给view
				target.getLayoutParams().width = mEvaluator.evaluate(fraction,
						start, end);
				target.requestLayout();
			}
		});
		valueAnimator.setDuration(animTime).start();
	}
	
	/**
	 * 开始动画
	 */
	public void startAnim(){
		//延时,以便得到viewWidth
		postDelayed(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				progressView.setVisibility(0);
				int pos=calculatePosition();
				if(progress>=positions[positions.length-1]){
					pos+=1;
				}else if(progress>0){
					pos+=10;
				}
				performAnimate(progressView, progressView.getWidth(), pos);
			}
		}, 100);
	}
	
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		// TODO Auto-generated method stub
		super.onLayout(changed, left, top, right, bottom);
		if(changed){
			viewWidth=getMeasuredWidth();
		}
	}

	private int calculatePosition(){
		int pos=0;
		if(viewWidth==0){
			return pos;
		}
		//每一段的长度
		float perLength=viewWidth/duanNum;
		if(progress<positions[0]){
			pos=0;
		}else if(progress<positions[1]){
			pos=(int) (perLength*(float)(progress/(positions[1]-positions[0])));
		}else if(progress<positions[2]){
			pos=(int) (perLength*(1+(float)(progress-positions[1])/(positions[2]-positions[1])));
		}else if(progress<positions[3]){
			pos=(int) (perLength*(2+(float)(progress-positions[2])/(positions[3]-positions[2])));
		}else if(progress<positions[4]){
			pos=(int) (perLength*(3+(float)(progress-positions[3])/(positions[4]-positions[3])));
		}else if(progress<positions[5]){
			pos=(int) (perLength*(4+(float)(progress-positions[4])/(positions[5]-positions[4])));
		}else{
			pos=(int) (perLength*5);
		}
		return pos;
	}

	public int getProgress() {
		return progress;
	}

	/**
	 * 设置当前进度
	 * @param progress
	 */
	public void setProgress(int progress) {
		this.progress = progress;
	}

	public int[] getPositions() {
		return positions;
	}

	/**
	 * 设置每段代表的进度值
	 * @param positions
	 */
	public void setPositions(int[] positions) {
		this.positions = positions;
	}

	public long getAnimTime() {
		return animTime;
	}

	public void setAnimTime(long animTime) {
		this.animTime = animTime;
	}

}

下面终于派上用场了,在Activity中引用:

package com.binbin.mywidget;

import com.binbin.tprogressbar.TProgressView;

import android.app.Activity;
import android.os.Bundle;


public class ProgressActivity extends Activity {

	private TProgressView tpv;
	private int[] positions=new int[]{0,50,100,200,450,800};
	private int currentProgress=330;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.progress_main);
        tpv=(TProgressView) findViewById(R.id.tpv);
        tpv.setPositions(positions);
        tpv.setProgress(currentProgress);
        tpv.startAnim();
    }
}

下面是一些布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#fff"
    android:orientation="vertical"
    android:padding="20dp" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="12dp"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tv_degree1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="1级"
            android:textColor="#949494"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_degree2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="2级"
            android:textColor="#949494"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_degree3"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="3级"
            android:textColor="#949494"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_degree4"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="4级"
            android:textColor="#949494"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_degree5"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="5级"
            android:textColor="#949494"
            android:textSize="12sp" />
    </LinearLayout>

    <com.binbin.tprogressbar.TProgressView
        android:id="@+id/tpv"
        app:bottom_color="#021356"
        app:top_color="#fff"
        android:layout_width="match_parent"
        android:layout_height="@dimen/height" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="horizontal" >

            <TextView
                android:id="@+id/tv_pro1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="left|center_vertical"
                android:text="0"
                android:textColor="#949494"
                android:textSize="12sp" />

            <TextView
                android:id="@+id/tv_pro2"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="right|center_vertical"
                android:text="50分"
                android:textColor="#949494"
                android:textSize="12sp" />
        </LinearLayout>

        <TextView
            android:id="@+id/tv_pro3"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right|center_vertical"
            android:text="100分"
            android:textColor="#949494"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_pro4"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right|center_vertical"
            android:text="200分"
            android:textColor="#949494"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_pro5"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right|center_vertical"
            android:text="450分"
            android:textColor="#949494"
            android:textSize="12sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="800分"
            android:gravity="right|center_vertical"
            android:textColor="#949494"
            android:textSize="12sp" />
    </LinearLayout>

</LinearLayout>

其实在实现的过程中,顶层跟底层View可以采用图片的方式(不用自定义View),中间滑动层也可以用一个图片来进行水平方向缩放移动来达到效果,这两种方式均可以~ 大笑


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值