android_87_自定义ProgressBar

来源:互联网 发布:高盛 知乎 编辑:程序博客网 时间:2024/06/05 06:03

参考:

http://sumile.cn/archives/1523.html


效果:





步骤:










attrs.xml

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="progress_unreach_color" format="color"></attr>    <attr name="progress_unreach_height" format="dimension"></attr>    <attr name="progress_reach_color" format="color"></attr>    <attr name="progress_reach_height" format="dimension"></attr>    <attr name="progress_text_color" format="color"></attr>    <attr name="progress_text_size" format="dimension"></attr>    <attr name="progress_text_offset" format="dimension"></attr>        <declare-styleable name="SGHProgressBar">        <attr name="progress_unreach_color"></attr>        <attr name="progress_unreach_height"></attr>        <attr name="progress_reach_color"></attr>        <attr name="progress_reach_height"></attr>        <attr name="progress_text_color"></attr>        <attr name="progress_text_size"></attr>        <attr name="progress_text_offset"></attr>    </declare-styleable>                <declare-styleable name="SGCircleProgressBar">        <attr name="progress_radius" format="dimension"></attr>    </declare-styleable>                                        </resources>


在布局文件中使用

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"          xmlns:tools="http://schemas.android.com/tools"                xmlns:sumile="http://schemas.android.com/apk/res-auto"                  android:layout_width="match_parent"          android:layout_height="match_parent"          android:paddingBottom="@dimen/activity_vertical_margin"          android:paddingLeft="@dimen/activity_horizontal_margin"          android:paddingRight="@dimen/activity_horizontal_margin"          android:paddingTop="@dimen/activity_vertical_margin"          tools:context="com.sg31.multiplethreaddownload.MainActivity" android:orientation="vertical"          >                <Button              android:layout_width="wrap_content"              android:layout_height="wrap_content"              android:text="开始下载"               android:onClick="downloadBtnClicked"            android:visibility="invisible"              />          <ProgressBar               android:id="@+id/progressBar2"              android:layout_width="match_parent"              android:layout_height="wrap_content"              style="@android:style/Widget.ProgressBar.Horizontal"                        android:visibility="invisible"              />          <TextView               android:id="@+id/tv"              android:layout_width="wrap_content"              android:layout_height="wrap_content"                        android:visibility="invisible"                          />                          <com.sg31.view.SGCircleProgressBar          android:id="@+id/progressBar"               android:layout_width="match_parent"            sumile:progress_radius="200dp"            android:layout_height="wrap_content"            sumile:progress_text_color="#000000"            sumile:progress_unreach_color="#1fff"            sumile:progress_reach_color="#3A99D8"            android:progress="0"            android:indeterminate="false"                                      android:gravity="center_horizontal"android:layout_gravity="center_horizontal"            />    </LinearLayout>  



自定义水平进度条:

package com.sg31.view;import com.sg31.taiyu.R;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Paint;import android.util.AttributeSet;import android.util.TypedValue;import android.widget.ProgressBar;public class SGHProgressBar extends ProgressBar {private static final int DEFAULT_TEXT_SIZE = 60;private static final int DEFAULT_TEXT_COLOR = 0xffFC00D1;private static final int DEFAULT_Color_UNREACH = 0xFFD3D6DA;private static final int DEFAULT_HEIGHT_UNREACH = 5;private static final int DEFAULT_COLOR_REACH = DEFAULT_TEXT_COLOR;private static final int DEFAULT_HEIGHT_REACH = 5;private static final int DEFAULT_TEXT_OFFSET = 10;protected int mTextSize = spToPx(DEFAULT_TEXT_SIZE);protected int mTextColor = DEFAULT_TEXT_COLOR;protected int mUnreachHeight = dpToPx(DEFAULT_HEIGHT_UNREACH);protected int mUnreachColor = DEFAULT_Color_UNREACH;protected int mReachHeight = dpToPx(DEFAULT_HEIGHT_REACH);protected int mReachColor = DEFAULT_COLOR_REACH;protected int mTextOffset = dpToPx(DEFAULT_TEXT_OFFSET);protected Paint mPaint = new Paint();private int mRealWidth;public SGHProgressBar(Context context) {this(context, null);}public SGHProgressBar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SGHProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);obtainStyleAttrs(attrs);mPaint.setTextSize(mTextSize);}/** * 获得自定义参数 *  * @param attrs */private void obtainStyleAttrs(AttributeSet attrs) {TypedArray ta = getContext().obtainStyledAttributes(attrs,R.styleable.SGHProgressBar);mTextColor = ta.getColor(R.styleable.SGHProgressBar_progress_text_color,mTextColor);mTextSize = (int) ta.getDimension(R.styleable.SGHProgressBar_progress_text_size,mTextSize);mReachColor = ta.getColor(R.styleable.SGHProgressBar_progress_reach_color,mReachColor);mReachHeight = (int) ta.getDimension(R.styleable.SGHProgressBar_progress_reach_height,mReachHeight);mUnreachColor = ta.getColor(R.styleable.SGHProgressBar_progress_unreach_color,mUnreachColor);mUnreachHeight = (int) ta.getDimension(R.styleable.SGHProgressBar_progress_unreach_height,mUnreachHeight);ta.recycle();}@Overrideprotected synchronized void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {// int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int height = measureHeight(heightMeasureSpec);setMeasuredDimension(widthSize, height);mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();}@Overrideprotected synchronized void onDraw(Canvas canvas) {canvas.save();canvas.translate(getPaddingLeft(), getHeight() / 2);boolean noNeedUnreach = false;// draw reach barfloat radio = getProgress() * 1.0f / getMax();float progressX = radio * mRealWidth;String text = getProgress() + "%";float textWidth = mPaint.measureText(text);if (textWidth + progressX > mRealWidth) {progressX = mRealWidth - textWidth;noNeedUnreach = true;}float endX = progressX - mTextOffset / 2;if (endX > 0) {mPaint.setColor(mReachColor);mPaint.setStrokeWidth(mReachHeight);canvas.drawLine(0, 0, endX, 0, mPaint);}// draw textmPaint.setColor(mTextColor);mPaint.setStrokeWidth(mTextSize);int y = (int) (Math.abs(mPaint.descent() + mPaint.ascent()) / 2);canvas.drawText(text, progressX, y, mPaint);// draw unreach barif (!noNeedUnreach) {float start = progressX + mTextOffset / 2 + textWidth;mPaint.setColor(mUnreachColor);mPaint.setStrokeWidth(mUnreachHeight);canvas.drawLine(Math.min(start, mRealWidth), 0, mRealWidth, 0,mPaint);}canvas.restore();}private int measureHeight(int heightMeasureSpec) {int result = 0;int mode = MeasureSpec.getMode(heightMeasureSpec);int size = MeasureSpec.getSize(heightMeasureSpec);if (mode == MeasureSpec.EXACTLY) {result = size;} else {int textSize = (int) (mPaint.descent() - mPaint.ascent());result = getPaddingTop()+ getPaddingBottom()+ Math.max(Math.max(mUnreachHeight, mReachHeight),Math.abs(textSize));if (mode == MeasureSpec.AT_MOST) {result = Math.min(size, result);}}return result;}/** * 工具方法 * @param spValue * @return */protected int spToPx(int spValue) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spValue, getResources().getDisplayMetrics());}protected int dpToPx(int dpValue) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpValue, getResources().getDisplayMetrics());}}



自定义圆进度条:

package com.sg31.view;import com.sg31.taiyu.R;import android.graphics.Paint;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.RectF;import android.util.AttributeSet; public class SGCircleProgressBar extends SGHProgressBar {    private int mRadius = dpToPx(200);    private int mMaxPaintWidth;    public SGCircleProgressBar(Context context) {        this(context, null);    }    public SGCircleProgressBar(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public SGCircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        obtainAttributeValue(attrs);        mReachHeight = mUnreachHeight * 2;        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setAntiAlias(true);        mPaint.setDither(true);        mPaint.setStrokeCap(Paint.Cap.ROUND);    }    private RectF mRectF = new RectF(0, 0, mRadius * 2, mRadius * 2);    @Override    protected synchronized void onDraw(Canvas canvas) {        //计算文字相关高度以及宽度    // (long) progressBar.getProgress() * 100              //    / progressBar.getMax() + "%"        String text = getProgress()* 100 / getMax() + "%";        int textWidth = (int) mPaint.measureText(text);        int textHeight = (int) ((mPaint.descent() + mPaint.ascent()));        canvas.save();        //移动坐标到左上角   左上角的位置为除去padding和画笔宽度的一半        canvas.translate(getPaddingLeft() + mMaxPaintWidth / 2, getPaddingTop() + mMaxPaintWidth / 2);        //画unReachBar        mPaint.setColor(mUnreachColor);        mPaint.setStrokeWidth(mUnreachHeight);        mPaint.setStyle(Paint.Style.STROKE);        canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);        //画reachBar        //计算要画出的角度,度数的百分比乘以360        float sweepAngle = getProgress() * 1.0f / getMax() * 360;        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(mReachColor);        mPaint.setStrokeWidth(mReachHeight);        //画圆  第一个参数是圆的外接正方形,第二三个参数分别为其实度数以及要画的度数,第四个参数表示是否过圆心        canvas.drawArc(mRectF, -90, sweepAngle, false, mPaint);        //画出文字        mPaint.setColor(mTextColor);        mPaint.setTextSize(mTextSize);        mPaint.setStyle(Paint.Style.FILL);        //第一个参数是文字,第二、三个参数是画文字的左上角的坐标(mRadius是圆心,圆心的左上角的横坐标就是所有文字的宽度/2,高度类似)        canvas.drawText(text, mRadius - textWidth / 2, mRadius - textHeight / 2, mPaint);        canvas.restore();    }    @Override    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        mMaxPaintWidth = Math.max(mReachHeight, mUnreachHeight);        //直径(我们根据用户输入的半径算得的直径)        int expect = mRadius * 2 + mMaxPaintWidth + getPaddingRight() + getPaddingLeft();        //将我们的直径传入,根据父控件告诉我们的模式(我自己的模式),算出来我真正的高度和宽度        int width = resolveSize(expect, widthMeasureSpec);        int height = resolveSize(expect, heightMeasureSpec);        //从两者中选出一个小的,作为直径        int realWidth = Math.min(width, height);        //直径减去左右边距,减去画笔宽度(画笔在直径上有两个/边,但是直径是在画笔的中间的,也就是画笔画出来的线其实是横跨在直径的那条线上的),最后除2,算出真实的半径        mRadius = (realWidth - getPaddingLeft() - getPaddingRight() - mMaxPaintWidth) / 2;        setMeasuredDimension(realWidth, realWidth);    }    private void obtainAttributeValue(AttributeSet attrs) {        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SGCircleProgressBar);        mRadius = (int) ta.getDimension(R.styleable.SGCircleProgressBar_progress_radius, mRadius);        ta.recycle();    }}



控制器中使用:

package com.sg31.download;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.InputStream;import java.io.InputStreamReader;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;import com.sg31.play.MoviePlayActivity;import com.sg31.taiyu.R;import com.sg31.utils.Utils;import android.content.Intent;import android.content.SharedPreferences;import android.content.SharedPreferences.Editor;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.support.v7.app.ActionBarActivity;import android.view.KeyEvent;import android.view.View;import android.widget.ProgressBar;import android.widget.TextView;public class SGDownloadActivity extends ActionBarActivity {static int ThreadCount = 1;static int finishedThread = 0;int currentProgress;boolean isFinished;private ProgressBar progressBar;TextView tv;private String lesson;private String startTime;Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {if (msg.what == 2) {Utils.showToast(SGDownloadActivity.this, "下载成功"); //Activity返回时传递数据,也是通过意图对象                  Intent data = new Intent();                  //把要传递的数据封装至意图对象中                  data.putExtra("lesson", lesson);                data.putExtra("startTime", startTime);                //当前Activity销毁时,data这个意图就会传递给启动当前Activity的那个Activity                  setResult(5000, data); finish();// jump();} // 把变量改成long,在long下运算// tv.setText((long) progressBar.getProgress() * 100// / progressBar.getMax() + "%");}};private void jump() {// 下载完成saveVideoNameToUserDefault(lesson);Utils.showToast(SGDownloadActivity.this, "下载成功");startMoviePlayActivity();try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}private void saveVideoNameToUserDefault(String videoName) {SharedPreferences sp = getSharedPreferences("taiyu", MODE_PRIVATE);// 拿到sp的编辑器Editor ed = sp.edit();ed.putString(lesson, videoName);// 提交ed.commit();}private void startMoviePlayActivity() {Intent intent = new Intent();// class:直接指定目标Activity的类名Bundle bundle = new Bundle();bundle.putString("startTime", startTime);bundle.putString("lesson", lesson);// 把bundle对象封装至intent对象中intent.putExtras(bundle);// 显示意图intent.setClass(SGDownloadActivity.this, MoviePlayActivity.class);startActivity(intent);}// 确定下载地址String path;// 只要没有下载完成,就不让返回@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_BACK) { // 监控/拦截/屏蔽返回键return !isFinished;} else if (keyCode == KeyEvent.KEYCODE_MENU) {// MENU键// 监控/拦截菜单键return !isFinished;}return super.onKeyDown(keyCode, event);}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_sgdownload);fetchDataFromIntent();initSubViews();new Thread(new Runnable() {public void run() {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}downloadBtnClicked();}}).start();}private void initSubViews() {// TODO Auto-generated method stub// 控件progressBar = (ProgressBar) findViewById(R.id.progressBar);tv = (TextView) findViewById(R.id.tv);}private void fetchDataFromIntent() {// 获取传递过来的数据Intent intent = getIntent();Bundle bundle = intent.getExtras();lesson = bundle.getString("lesson");startTime = bundle.getString("startTime");path = "http://ojyztd67m.bkt.clouddn.com/" + lesson;}public void downloadBtnClicked() {Thread t = new Thread() {@Overridepublic void run() {// 发送get请求,请求这个地址的资源的长度try {URL url = new URL(path);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5000);conn.setReadTimeout(5000);if (conn.getResponseCode() == 200) {// 拿到所请求资源文件的长度int length = conn.getContentLength();// 设置进度条的最大值就是原文件的总长度progressBar.setMax(length);File file = new File(Environment.getExternalStorageDirectory(),lesson);// 生成临时文件,rwd表示实时写入底层设备RandomAccessFile raf = new RandomAccessFile(file, "rwd");// 设置临时文件的大小raf.setLength(length);raf.close();// 计算出每个线程应该下载多少字节int size = length / ThreadCount;for (int i = 0; i < ThreadCount; i++) {// 计算线程下载的开始位置和结束位置int startIndex = i * size;int endIndex = (i + 1) * size - 1;// 如果是最后一个线程,那么结束位置写死if (i == ThreadCount - 1) {endIndex = length - 1;}// System.out.println("线程" + i + "的下载区间是:" +// startIndex + "---" + endIndex);new DownLoadThread(startIndex, endIndex, i).start();}}} catch (Exception e) {e.printStackTrace();}}};t.start();}// 专门用来下载的类class DownLoadThread extends Thread {int startIndex;int endIndex;int threadId;public DownLoadThread(int startIndex, int endIndex, int threadId) {super();this.startIndex = startIndex;this.endIndex = endIndex;this.threadId = threadId;}@Overridepublic void run() {// 再次发送http请求,下载原文件try {File progressFile = new File(Environment.getExternalStorageDirectory(), threadId+ ".txt");// 判断进度临时文件是否存在if (progressFile.exists()) {FileInputStream fis = new FileInputStream(progressFile);BufferedReader br = new BufferedReader(new InputStreamReader(fis));// 从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置int lastProgress = Integer.parseInt(br.readLine());startIndex += lastProgress;// 把上次下载的进度显示至进度条currentProgress += lastProgress;progressBar.setProgress(currentProgress);// 发送消息,让主线程刷新文本进度handler.sendEmptyMessage(1);fis.close();}System.out.println("线程" + threadId + "的下载区间是:" + startIndex+ "---" + endIndex);HttpURLConnection conn;URL url = new URL(path);conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5000);conn.setReadTimeout(5000);// 核心代码:设置本次http请求所请求的数据的区间conn.setRequestProperty("Range", "bytes=" + startIndex + "-"+ endIndex);// 请求部分数据,相应码是206if (conn.getResponseCode() == 206) {// 流里此时只有1/3原文件的数据InputStream is = conn.getInputStream();byte[] b = new byte[1024];int len = 0;int total = 0;// 拿到临时文件的输出流File file = new File(Environment.getExternalStorageDirectory(), lesson);RandomAccessFile raf = new RandomAccessFile(file, "rwd");// 核心代码:把文件的写入位置移动至startIndexraf.seek(startIndex);while ((len = is.read(b)) != -1) {// 每次读取流里数据之后,同步把数据写入临时文件raf.write(b, 0, len);total += len;System.out.println("线程" + threadId + "下载了" + total);// 每次读取流里数据之后,把本次读取的数据的长度显示至进度条currentProgress += len;progressBar.setProgress(currentProgress);// 发送消息,让主线程刷新文本进度handler.sendEmptyMessage(1);// 生成一个专门用来记录下载进度的临时文件RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd");// 每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中progressRaf.write((total + "").getBytes());progressRaf.close();}System.out.println("线程" + threadId+ "下载完毕-------------------sg31.com");raf.close();finishedThread++;synchronized (path) {if (finishedThread == ThreadCount) {for (int i = 0; i < ThreadCount; i++) {File f = new File(Environment.getExternalStorageDirectory(),i + ".txt");f.delete();}finishedThread = 0;}}// 发送消息,让主线程刷新文本进度handler.sendEmptyMessage(2);}} catch (Exception e) {e.printStackTrace();}}}}














0 0