我的自定义控件之旅——自定义进度条
来源:互联网 发布:java c3p0连接池 编辑:程序博客网 时间:2024/05/21 22:48
上次做一个项目看到一个很简约的进度条,然后就做了一个,结果发现这个进度条网上的做法有好多,不过也不管了,先来看看样子
一、首先讲下要用到的两个函数
在自定义中最重要的就是onMeasure、onDraw以及onLayout了,此次我们用到的是前两个 onMeasur():这个方法是用来测量View的大小的(宽高) 用户在这个函数里先计算好宽和高然后调用setMeasuredDimension(width, height)方法就可以完成绘制 setMeasuredDimension(width, height)方法可以理解为通知view我计算好了并把计算好的宽高传给view onDraw():这个方法是用来绘画的(例如要显示文字就利用画笔画一个文字出来)
二、编写自定义view代码
通过上面的图片可以看到我们需要进度条的高度、字体的大小、进度条的已完成的颜色、未完成的颜色、字体颜色以及起始进度值着六个自定义属性。 1、首先在values目录下新建attrs.xml文件,然后定义上面所讲的六个属性
<?xml version="1.0" encoding="utf-8"?><resources> <attr name="progress_lineheight" format="dimension"></attr> <attr name="progress_textsize" format="dimension"></attr> <attr name="progress_finishlinecolor" format="color"></attr> <attr name="progress_unfinishlinecolor" format="color"></attr> <attr name="progress_textcolort" format="color"></attr> <attr name="progress" format="float"></attr> <declare-styleable name="MyProgress"> <attr name="progress_lineheight" ></attr> <attr name="progress_textsize" ></attr> <attr name="progress_finishlinecolor" ></attr> <attr name="progress_unfinishlinecolor" ></attr> <attr name="progress_textcolort"></attr> <attr name="progress"></attr> </declare-styleable></resources>
2、接下来创建我们自定义控件类MyProgress继承自View(当然也可以继承ProgressBar这样还不用自己考虑进度方面的问题),然后声明6个属性变量,以及6个静态变量(对应6个属性变量的默认值),当然不能忘记将dp、sp等单位转化为px单位了
private static final int LINE_HEIGHT = 2;//进度条高度;dp private static final int TEXT_SIZE = 13;//显示进度的字体的大小;sp private static final int FINISHLINE_COLOR = 0xFFFF0000;//完成部分进度的颜色 private static final int UNFINISHLINE_COLOR = 0xFF00FFFF;//未完成部分进度的颜色 private static final int TEXT_COLOR = FINISHLINE_COLOR;//字体的颜色 private static final int PROGRESS = 0;//进度; private int mLineHeight = dptopx(LINE_HEIGHT); private int mTextSize = sptopx(TEXT_SIZE); private int mFinishLineColor = FINISHLINE_COLOR; private int mUnFinishLineColor = UNFINISHLINE_COLOR; private int mTextColor = TEXT_COLOR; private float mProgress = PROGRESS; /** * dp转px * @param dp * @return */ private int dptopx(int dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); } /** * sp转px * @param sp * @return */ private int sptopx(int sp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); }
然后定义画笔变量,以及构造函数,在构造函数中,我们初始化画笔,获取自定义属性,然后设置画笔的字体大小以便以后计算字体打宽高使用
private Paint mPaint ; public MyProgress(Context context) { this(context,null); } public MyProgress(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyProgress(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); inint(attrs); } private void inint(AttributeSet attrs) { mPaint = new Paint(); obtainStyledAttr(attrs); mPaint.setTextSize(mTextSize); } /** * 获取自定义属性 * @param attrs */ private void obtainStyledAttr(AttributeSet attrs) { TypedArray t = getContext() .obtainStyledAttributes(attrs,R.styleable.MyProgress); mLineHeight= (int) t.getDimension( R.styleable.MyProgress_progress_lineheight,mLineHeight); mTextSize= (int) t.getDimension( R.styleable.MyProgress_progress_textsize,mTextSize); mFinishLineColor = t.getColor( R.styleable.MyProgress_progress_finishlinecolor,mFinishLineColor); mUnFinishLineColor = t.getColor( R.styleable.MyProgress_progress_unfinishlinecolor,mUnFinishLineColor); mTextColor = t.getColor( R.styleable.MyProgress_progress_textcolort,mTextColor); mProgress = t.getFloat( R.styleable.MyProgress_progress,mProgress); t.recycle(); }
接着给进度度添加get和set,get我们*100再输出,set的是后要通知view重绘
public int getprogress() { return (int) (mProgress*100); } public void setprogress(float progress) { this.mProgress = progress; //通知view重绘 invalidate(); }
接下来在onMeasure方法里面计算view的宽和高,widthMeasureSpec和heightMeasureSpec传入的不是一个值,而是两个,我们可以通过MeasureSpec.getMode()和MeasureSpec.getSize()获取类型和大小,另外我们的是条形横向进度条所以宽度一定是给定的不能包裹内容的所以我们只需要获取size就可以了,计算完之后调用setMeasuredDimension(width,heitht)方法通知view就可以了
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int heitht = getmeasure(heightMeasureSpec); //通知view测量完毕,并将数据传给它 setMeasuredDimension(width,heitht); } /** * 计算view的高度 * @param heightMeasureSpec * @return view的高度 */ private int getmeasure(int heightMeasureSpec) { int result = 0; int Mode = MeasureSpec.getMode(heightMeasureSpec); int size = MeasureSpec.getSize(heightMeasureSpec); switch (Mode){ case MeasureSpec.EXACTLY://用户给定精确值 result = size; break; case MeasureSpec.UNSPECIFIED://父容器对于子容器没有任何限制,子容器想要多大就多大 //测量文字的高度 int textHeight = (int) (mPaint.descent()+mPaint.ascent()); //获取文字和进度条两者高度的最大值 result = getPaddingBottom()+getPaddingTop()+Math.max(textHeight,mLineHeight); break; case MeasureSpec.AT_MOST://子容器可以是声明大小内的任意大小 int textheight = (int) (mPaint.descent()+mPaint.ascent()); int maxHeight = getPaddingBottom()+getPaddingTop()+Math.max(textheight,mLineHeight); //获取计算出来的高度和实际可用最大高度的最小值 result = Math.min(maxHeight,size); break; } return result; }
接着是绘画了,绘画分为三个部分,第一部分是画以及完成的进度条部分(首先判断进度是否为0,如果为0就说名没有已经完成的部分,第一部分就不需要画了),首先计算起点,起点X应该是从paddingleft开始,Y嘛就是一半的高度了,然后将画布移过去就好了,然后计算第一部分的长度,第一部分的长度等于(view的宽度-左右padding-字体的长度)*进度/100。
canvas.translate(getPaddingLeft(),getHeight()/2); //两边添加空格使三个部分有分开的感觉 String str = " "+getprogress()+"% "; int strWidth = (int) mPaint.measureText(str); int width = getWidth()-getPaddingLeft()-getPaddingRight()-strWidth; int drawToX = width*getprogress()/100; //已经完成的部分 if (getprogress()!=0){ mPaint.setColor(mFinishLineColor); mPaint.setStrokeWidth(mLineHeight); canvas.drawLine(0,0,drawToX,0,mPaint); }
第二部分是文字部分,文字部分起点是第一部分的结束,就是代码中的drawToX,高度要向下移动字体一半的高度
//进度文字 mPaint.setColor(mTextColor); int y = (int) ((mPaint.descent()+mPaint.ascent())/2); canvas.drawText(str,drawToX,-y,mPaint);
第三部分是未完成部分,和第一部分类似,判断进度是否为100,如果是一百就不需要画了,第三部分的起始点是第一部分的长度+第二部份的长度,结束点是view的宽度-左右padding
//未完成部分 if (getprogress()!=100){ mPaint.setColor(mUnFinishLineColor); mPaint.setStrokeWidth(mLineHeight); canvas.drawLine(drawToX+strWidth,0,width+strWidth,0,mPaint); }
下面是自定义view的全部代码:
package com.linzhou.myview;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.view.View;/* *项目名: MyView *包名: com.linzhou.myview *创建者: linzhou *创建时间:17/08/05 *描述: */public class MyProgress extends View { private static final int LINE_HEIGHT = 2;//进度条高度;dp private static final int TEXT_SIZE = 13;//显示进度的字体的大小;sp private static final int FINISHLINE_COLOR = 0xFFFF0000;//完成部分进度的颜色 private static final int UNFINISHLINE_COLOR = 0xFF00FFFF;//未完成部分进度的颜色 private static final int TEXT_COLOR = FINISHLINE_COLOR;//字体的颜色 private static final int PROGRESS = 0;//进度; private int mLineHeight = dptopx(LINE_HEIGHT); private int mTextSize = sptopx(TEXT_SIZE); private int mFinishLineColor = FINISHLINE_COLOR; private int mUnFinishLineColor = UNFINISHLINE_COLOR; private int mTextColor = TEXT_COLOR; private float mProgress = PROGRESS; private Paint mPaint ; public MyProgress(Context context) { this(context,null); } public MyProgress(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyProgress(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); inint(attrs); } private void inint(AttributeSet attrs) { mPaint = new Paint(); obtainStyledAttr(attrs); mPaint.setTextSize(mTextSize); } /** * 获取自定义属性 * @param attrs */ private void obtainStyledAttr(AttributeSet attrs) { TypedArray t = getContext() .obtainStyledAttributes(attrs,R.styleable.MyProgress); mLineHeight= (int) t.getDimension( R.styleable.MyProgress_progress_lineheight,mLineHeight); mTextSize= (int) t.getDimension( R.styleable.MyProgress_progress_textsize,mTextSize); mFinishLineColor = t.getColor( R.styleable.MyProgress_progress_finishlinecolor,mFinishLineColor); mUnFinishLineColor = t.getColor( R.styleable.MyProgress_progress_unfinishlinecolor,mUnFinishLineColor); mTextColor = t.getColor( R.styleable.MyProgress_progress_textcolort,mTextColor); mProgress = t.getFloat( R.styleable.MyProgress_progress,mProgress); t.recycle(); } public int getprogress() { return (int) (mProgress*100); } public void setprogress(float progress) { this.mProgress = progress; //通知view重绘 invalidate(); } /** * dp转px * @param dp * @return */ private int dptopx(int dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); } /** * sp转px * @param sp * @return */ private int sptopx(int sp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int heitht = getmeasure(heightMeasureSpec); //通知view测量完毕,并将数据传给它 setMeasuredDimension(width,heitht); } /** * 计算view的高度 * @param heightMeasureSpec * @return view的高度 */ private int getmeasure(int heightMeasureSpec) { int result = 0; int Mode = MeasureSpec.getMode(heightMeasureSpec); int size = MeasureSpec.getSize(heightMeasureSpec); switch (Mode){ case MeasureSpec.EXACTLY://用户给定精确值 result = size; break; case MeasureSpec.UNSPECIFIED://父容器对于子容器没有任何限制,子容器想要多大就多大 //测量文字的高度 int textHeight = (int) (mPaint.descent()+mPaint.ascent()); //获取文字和进度条两者高度的最大值 result = getPaddingBottom()+getPaddingTop()+Math.max(textHeight,mLineHeight); break; case MeasureSpec.AT_MOST://子容器可以是声明大小内的任意大小 int textheight = (int) (mPaint.descent()+mPaint.ascent()); int maxHeight = getPaddingBottom()+getPaddingTop()+Math.max(textheight,mLineHeight); //获取计算出来的高度和实际可用最大高度的最小值 result = Math.min(maxHeight,size); break; } return result; } @Override protected void onDraw(Canvas canvas) { canvas.translate(getPaddingLeft(),getHeight()/2); String str = " "+getprogress()+"% "; int strWidth = (int) mPaint.measureText(str); int width = getWidth()-getPaddingLeft()-getPaddingRight()-strWidth; int drawToX = width*getprogress()/100; //已经完成的部分 if (getprogress()!=0){ mPaint.setColor(mFinishLineColor); mPaint.setStrokeWidth(mLineHeight); canvas.drawLine(0,0,drawToX,0,mPaint); } //进度文字 mPaint.setColor(mTextColor); int y = (int) ((mPaint.descent()+mPaint.ascent())/2); canvas.drawText(str,drawToX,-y,mPaint); //未完成部分 if (getprogress()!=100){ mPaint.setColor(mUnFinishLineColor); mPaint.setStrokeWidth(mLineHeight); canvas.drawLine(drawToX+strWidth,0,width+strWidth,0,mPaint); } }}
三、测试代码
MainActivity的布局文件:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:lz="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.linzhou.myview.MainActivity"> <com.linzhou.myview.MyProgress android:id="@+id/pg1" android:layout_width="match_parent" android:layout_marginTop="20dp" android:layout_height="wrap_content" android:padding="10dp" /> <com.linzhou.myview.MyProgress android:id="@+id/pg2" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" lz:progress="0.5" lz:progress_textcolort="@color/coloAccent" lz:progress_finishlinecolor="@color/colorAccent" lz:progress_unfinishlinecolor="@color/coloAccent" /> <Button android:id="@+id/start" android:text="start" android:layout_width="wrap_content" android:layout_height="wrap_content" /></LinearLayout>
MainActivity代码:
package com.linzhou.myview;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;public class MainActivity extends AppCompatActivity { private MyProgress pg1,pg2; private Button start; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Bundle bundle = msg.getData(); int pg = bundle.getInt("pg"); Log.d("linzhou123",pg+""); if (pg>=100) start.setEnabled(true); pg1.setprogress(((float)pg)/100); pg2.setprogress(((float)pg)/100); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pg1 = (MyProgress) findViewById(R.id.pg1); pg2 = (MyProgress) findViewById(R.id.pg2); start = (Button) findViewById(R.id.start); start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { start.setEnabled(false); new Thread(new Runnable() { @Override public void run() { for (int i = 0;i<=100;i++){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } Message message = new Message(); Bundle bundle = new Bundle(); bundle.putInt("pg",i); message.setData(bundle); mHandler.sendMessage(message); } } }).start(); } }); }}
运行图:
阅读全文
1 0
- 我的自定义控件之旅——自定义进度条
- 自定义控件之进度条
- 自定义控件之进度条
- 自定义控件之仿知乎进度条
- 自定义控件之_自定义圆形进度条
- Android自定义控件之自定义圆形进度条
- 自定义的用户控件——一个平滑的进度条
- 随记11——我的简单的自定义View之进度条
- Android自定义控件——时钟、进度条
- 自定义控件—入门圆形进度条
- 我的Android进化论——[View自定义] 画双环进度条
- Android自定义控件之加载进度条的实现
- Android 自定义控件之 带刻度的进度条
- iOS开发系列之常用自定义控件开发集—进度条Loading控件开发
- Android自定义控件之圆形进度条ImageView
- Android自定义控件之百分比圆环进度条
- Android自定义控件之百分比圆环进度条
- Android自定义控件之百分比圆环进度条
- splay小记
- jQuery_extend()参数
- 【Redis】redis实战:在业务中添加缓存机制
- CART剪枝详解
- 编写一个C函数,将”I am from shanghai ”倒置为”shanghai from am I”,及将句子中的单词位置倒置,而不改变单词内部结构.
- 我的自定义控件之旅——自定义进度条
- MySQL入门1——数据类型和简单数据表操作
- xutils网络请求
- spring对rabbitmq RPC的支持
- 泛型-擦出的神秘之处
- Spring MVC中AOP无效、不起作用
- C语言排序之二分法插入排序篇
- 天鹅会面(BFS)
- R 自定义图例(text、label、colour)