关闭

Android 百分比、水波、流量剩余进度球的实现

标签: android流量进度球百分比球剩余流量
1760人阅读 评论(1) 收藏 举报
分类:

因为项目中有遇到进度的一个需求,目前类似的在实现网络进度加载和流量用量上比较多,所有今天就把代码贴出来,希望有需要的朋友能够有用到。

1.首先是主页面

package com.zhanglu.percentageball;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;

/**
 * 
 * 项目名称:PercentageBall 类名称:MainActivity 类描述: 主页面 创建人:zhanglu 创建时间:2016-6-1 上午9:04:46 修改人:zhanglu 修改时间:2016-6-1 上午9:04:46 修改备注:
 * 
 * @version
 * 
 */
public class MainActivity extends Activity implements OnClickListener {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		findViewById(R.id.button1).setOnClickListener(this);
		findViewById(R.id.button2).setOnClickListener(this);
		findViewById(R.id.button3).setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.button1:
			startActivity(new Intent(this, PercentageBallActivty.class));
			break;
		case R.id.button2:
			startActivity(new Intent(this, PercentageBallListviewActivty.class));
			break;
		case R.id.button3:
			startActivity(new Intent(this, PercentageBallWaveActivty.class));
			break;
		default:
			break;
		}

	}

}



2.下面是静止页面的代码

package com.zhanglu.percentageball;

import com.zhanglu.percentageball.view.PercentageBallView;

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

/**    
 *     
 * 项目名称:PercentageBall    
 * 类名称:PercentageBallActivty    
 * 类描述:    动态波浪球
 * 创建人:zhanglu   
 * 创建时间:2016-6-1 上午9:30:53    
 * 修改人:zhanglu   
 * 修改时间:2016-6-1 上午9:30:53    
 * 修改备注:    
 * @version     
 *     
 */
public class PercentageBallActivty extends Activity {

	private PercentageBallView pv;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.percentage_ball_activty);
		pv = (PercentageBallView) findViewById(R.id.ball);
		pv.setmWaterLevel(0.5f, "10/5");
		pv.startWave();// 开始执行
	}

}
 

下面是实现波浪球的效果代码

package com.zhanglu.percentageball.view;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Build;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ProgressBar;

import com.zhanglu.percentageball.R;


/**    
 *     
 * 项目名称:PercentageBall    
 * 类名称:PercentageBallView    
 * 类描述:    实现效果类
 * 创建人:zhanglu   
 * 创建时间:2016-6-1 上午11:20:17    
 * 修改人:zhanglu   
 * 修改时间:2016-6-1 上午11:20:17    
 * 修改备注:    
 * @version     
 *     
 */
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class PercentageBallView extends View {

	private Context mContext;

	private int mScreenWidth;
	private int mScreenHeight;

	private Paint mRingPaint;
	private Paint mCirclePaint;
	private Paint mWavePaint;
	private Paint flowPaint;

	private int mRingSTROKEWidth = 8;
	private int mCircleSTROKEWidth = 8;
	private int mLineSTROKEWidth = 1;

	private Handler mHandler;
	private long c = 0L;
	private boolean mStarted = false;
	private final float f = 0.033F;
	private int mAlpha = 50;// 透明度
	private float mAmplitude = 0.0F; // 振幅
	private float mWaterLevel = 0.0F;// 水高(0~1)
	private Path mPath;

	// 绘制文字显示在圆形中间,只是我没有设置,我觉得写在布局上也挺好的
	private String flowNum = "";// 2/10

	/**
	 * @param context
	 */
	public PercentageBallView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		mContext = context;
		init(mContext);
	}

	/**
	 * @param context
	 * @param attrs
	 */
	public PercentageBallView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		mContext = context;
		init(mContext);
	}

	/**
	 * @param context
	 * @param attrs
	 * @param defStyleAttr
	 */
	public PercentageBallView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		mContext = context;
		init(mContext);
	}

	public void setmWaterLevel(float mWaterLevel, String str) {
		this.mWaterLevel = mWaterLevel;
		this.flowNum = str;
	}

	public void setmWaterWave(float mWaterLevel, String str, float mWater) {
		this.mWaterLevel = mWaterLevel;
		this.flowNum = str;
		this.mAmplitude = mWater;
	}

	private void init(Context context) {
		// 外圈
		mRingPaint = new Paint();
		mRingPaint.setColor(Color.rgb(75, 210, 243));
		mRingPaint.setStyle(Paint.Style.STROKE);
		mRingPaint.setAntiAlias(true);
		mRingPaint.setStrokeWidth(mRingSTROKEWidth);

		// 内圈
		mCirclePaint = new Paint();
		mCirclePaint.setColor(Color.WHITE);
		mCirclePaint.setStyle(Paint.Style.STROKE);
		mCirclePaint.setAntiAlias(true);
		mCirclePaint.setStrokeWidth(mCircleSTROKEWidth);
		// 文字
		flowPaint = new Paint();
		flowPaint.setColor(Color.BLACK);
		flowPaint.setStyle(Paint.Style.FILL);
		flowPaint.setAntiAlias(true);
		flowPaint.setTextSize(24);

		// 内填充
		mWavePaint = new Paint();
		mWavePaint.setStrokeWidth(1.0F);
		mWavePaint.setColor(Color.rgb(75, 210, 243));
		// mWavePaint.setAlpha(mAlpha);
		mPath = new Path();

		mHandler = new Handler() {
			@Override
			public void handleMessage(android.os.Message msg) {
				if (msg.what == 0) {
					invalidate();
					if (mStarted) {
						// 不断发消息给自己,使自己不断被重绘
						mHandler.sendEmptyMessageDelayed(0, 60L);
					}
				}
			}
		};
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int width = measure(widthMeasureSpec, true);
		int height = measure(heightMeasureSpec, false);
		if (width < height) {
			setMeasuredDimension(width, width);
		} else {
			setMeasuredDimension(height, height);
		}

	}

	/**
	 * @category 测量
	 * @param measureSpec
	 * @param isWidth
	 * @return
	 */
	private int measure(int measureSpec, boolean isWidth) {
		int result;
		int mode = MeasureSpec.getMode(measureSpec);
		int size = MeasureSpec.getSize(measureSpec);
		int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();
		if (mode == MeasureSpec.EXACTLY) {
			result = size;
		} else {
			result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();
			result += padding;
			if (mode == MeasureSpec.AT_MOST) {
				if (isWidth) {
					result = Math.max(result, size);
				} else {
					result = Math.min(result, size);
				}
			}
		}
		return result;
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		// TODO Auto-generated method stub
		super.onSizeChanged(w, h, oldw, oldh);
		mScreenWidth = w;
		mScreenHeight = h;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		// 得到控件的宽高
		int width = getWidth();
		int height = getHeight();
		setBackgroundColor(Color.WHITE);// 可以自定义色值
		// 计算当前油量线和水平中线的距离
		float centerOffset = Math.abs(mScreenWidth / 2 * mWaterLevel - mScreenWidth / 4);
		// 计算油量线和与水平中线的角度
		float horiAngle = (float) (Math.asin(centerOffset / (mScreenWidth / 4)) * 180 / Math.PI);
		// 扇形的起始角度和扫过角度
		float startAngle, sweepAngle;
		if (mWaterLevel > 0.5F) {
			startAngle = 360F - horiAngle;
			sweepAngle = 180F + 2 * horiAngle;
		} else {
			startAngle = horiAngle;
			sweepAngle = 180F - 2 * horiAngle;
		}

		float num = flowPaint.measureText(flowNum);

		// 如果未开始(未调用startWave方法),绘制一个扇形
		if ((!mStarted) || (mScreenWidth == 0) || (mScreenHeight == 0)) {
			// 绘制,即水面静止时的高度
			RectF oval = new RectF(mScreenWidth / 4, mScreenHeight / 4, mScreenWidth * 3 / 4, mScreenHeight * 3 / 4);
			canvas.drawArc(oval, startAngle, sweepAngle, false, mWavePaint);
			return;
		}
		// 绘制,即水面静止时的高度
		// 绘制,即水面静止时的高度
		RectF oval = new RectF(mScreenWidth / 4, mScreenHeight / 4, mScreenWidth * 3 / 4, mScreenHeight * 3 / 4);
		canvas.drawArc(oval, startAngle, sweepAngle, false, mWavePaint);

		if (this.c >= 8388607L) {
			this.c = 0L;
		}
		// 每次onDraw时c都会自增
		c = (1L + c);
		float f1 = mScreenHeight * (1.0F - (0.25F + mWaterLevel / 2)) - mAmplitude;
		// 当前油量线的长度
		float waveWidth = (float) Math.sqrt(mScreenWidth * mScreenWidth / 16 - centerOffset * centerOffset);
		// 与圆半径的偏移量
		float offsetWidth = mScreenWidth / 4 - waveWidth;

		int top = (int) (f1 + mAmplitude);
		mPath.reset();
		// 起始振动X坐标,结束振动X坐标
		int startX, endX;
		if (mWaterLevel > 0.50F) {
			startX = (int) (mScreenWidth / 4 + offsetWidth);
			endX = (int) (mScreenWidth / 2 + mScreenWidth / 4 - offsetWidth);
		} else {
			startX = (int) (mScreenWidth / 4 + offsetWidth - mAmplitude);
			endX = (int) (mScreenWidth / 2 + mScreenWidth / 4 - offsetWidth + mAmplitude);
		}
		// 波浪效果
		while (startX < endX) {
			int startY = (int) (f1 - mAmplitude * Math.sin(Math.PI * (2.0F * (startX + this.c * width * this.f)) / width));
			canvas.drawLine(startX, startY, startX, top, mWavePaint);
			startX++;
		}
		canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2, mScreenWidth / 4 + mRingSTROKEWidth / 2, mRingPaint);

		canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2, mScreenWidth / 4, mCirclePaint);

		// 放到这里绘制文字,不然会因为前面的绘图遮挡住文字
		canvas.drawText(flowNum, mScreenWidth * 4 / 8 - num / 2, mScreenHeight * 4 / 8, flowPaint);
		canvas.restore();
	}

	@Override
	public Parcelable onSaveInstanceState() {
		Parcelable superState = super.onSaveInstanceState();
		SavedState ss = new SavedState(superState);
		ss.progress = (int) c;
		return ss;
	}

	@Override
	public void onRestoreInstanceState(Parcelable state) {
		SavedState ss = (SavedState) state;
		super.onRestoreInstanceState(ss.getSuperState());
		c = ss.progress;
	}

	@Override
	protected void onAttachedToWindow() {
		super.onAttachedToWindow();
		// 关闭硬件加速,防止异常unsupported operation exception
		this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
	}

	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();
	}

	/**
	 * 开始波动
	 */
	public void startWave() {
		if (!mStarted) {
			this.c = 0L;
			mStarted = true;
			this.mHandler.sendEmptyMessage(0);
		}
	}

	/**
	 * @category 停止波动
	 */
	public void stopWave() {
		if (mStarted) {
			this.c = 0L;
			mStarted = false;
			this.mHandler.removeMessages(0);
		}
	}

	/**
	 * @category 保存状态
	 */
	static class SavedState extends BaseSavedState {
		int progress;

		/**
		 * Constructor called from {@link ProgressBar#onSaveInstanceState()}
		 */
		SavedState(Parcelable superState) {
			super(superState);
		}

		/**
		 * Constructor called from {@link #CREATOR}
		 */
		private SavedState(Parcel in) {
			super(in);
			progress = in.readInt();
		}

		@Override
		public void writeToParcel(Parcel out, int flags) {
			super.writeToParcel(out, flags);
			out.writeInt(progress);
		}

		public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
			public SavedState createFromParcel(Parcel in) {
				return new SavedState(in);
			}

			public SavedState[] newArray(int size) {
				return new SavedState[size];
			}
		};
	}

}


运行后效果如下:



好,我们实现了静态的图后,再来实现动态波动的


因为项目需要,是要在listview中实现,所有现在把效果加到listview中,下面是实现代码:

package com.zhanglu.percentageball;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import com.zhanglu.percentageball.view.PercentageBallView;


/**    
 *     
 * 项目名称:PercentageBall    
 * 类名称:PercentageBallListviewActivty    
 * 类描述:    实现在listview中波动效果
 * 创建人:zhanglu   
 * 创建时间:2016-6-1 上午11:25:15    
 * 修改人:zhanglu   
 * 修改时间:2016-6-1 上午11:25:15    
 * 修改备注:    
 * @version     
 *     
 */
public class PercentageBallListviewActivty extends Activity {

	private Context mContext;
	private ListView listview;
	private ArrayList<pvinfo> mlist = new ArrayList<pvinfo>();
	private MyAdapter mMyAdapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.percentage_ball__listview_activty);
		mContext = getApplicationContext();
		listview = (ListView) findViewById(R.id.listView1);

		mMyAdapter = new MyAdapter(mlist);
		listview.setAdapter(mMyAdapter);

	}

	@Override
	protected void onResume() {
		super.onResume();
		for (int i = 0; i < 30; i++) {
			pvinfo pv = new pvinfo();
			pv.setStr((i + 1) + "/" + 30);
			mlist.add(pv);
		}
		mMyAdapter.notifyDataSetChanged();
	}

	public class MyAdapter extends BaseAdapter {
		private ArrayList<pvinfo> mlist = null;

		public MyAdapter(ArrayList<pvinfo> mlist2) {
			this.mlist = mlist2;
		}

		public int getCount() {

			return mlist.size();
		}

		public Object getItem(int pos) {
			return mlist.get(pos);
		}

		public long getItemId(int pos) {
			return pos;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			final ViewHolder viewHolder;
			if (convertView == null) {
				convertView = LayoutInflater.from(mContext).inflate(R.layout.percentage_ball__listview_item, null);
				viewHolder = new ViewHolder();
				viewHolder.pv = (PercentageBallView) convertView.findViewById(R.id.ball);
				viewHolder.textView1 = (TextView) convertView.findViewById(R.id.textView1);
				convertView.setTag(viewHolder);
			} else {
				viewHolder = (ViewHolder) convertView.getTag();
			}
			viewHolder.textView1.setText("这是第" + (position+1) + " item ");
			float n = (float) position / 30;
			viewHolder.pv.setmWaterWave(n, mlist.get(position).getStr(),8f);
			viewHolder.pv.startWave();
			return convertView;
		}

		public class ViewHolder {
			public PercentageBallView pv;
			public TextView textView1;

		}

	}

	public class pvinfo {
		private float valve;
		private String str;

		public float getValve() {
			return valve;
		}

		public void setValve(float valve) {
			this.valve = valve;
		}

		public String getStr() {
			return str;
		}

		public void setStr(String str) {
			this.str = str;
		}

		public pvinfo(float valve, String str) {
			super();
			this.valve = valve;
			this.str = str;
		}

		public pvinfo() {
			super();
		}

	}

}

来张效果图:


代码下载地址:http://download.csdn.net/detail/meburningg/9537522


1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:3242次
    • 积分:61
    • 等级:
    • 排名:千里之外
    • 原创:3篇
    • 转载:0篇
    • 译文:0篇
    • 评论:1条
    文章分类
    文章存档
    最新评论