Android自定义圆形进度条
2017-05-25 16:42
309 查看
自定义View是Android进阶的必备技能,只要掌握了自定义view的三个方法,自定义view其实很简单,下面间通过一个示例来说明
首先需要在values文件夹下建一个attrs的文件
其中attr为定义属性,name为属性名称。
xml布局文件
根布局文件里写明命名空间,可以写成xmlns:app=”http://schemas.android.com/apk/res-auto”系统自动识别,也可以写成 xmlns:xxx=”http://schemas.android.com/apk/res/com.cdemo.customview”,其中”xxx”名字你自己定义,“com.cdemo.customview”对应你的包名,这里我使用app命名空间自动识别。
下面开始Java代码编写
通过TypedArray获取刚刚在xml文件中定义的属性和设定的值
onMeasure方法是自定义View中需要重写的方法,其中int widthMeasureSpec, int heightMeasureSpec是父布局传进来的两个参数,由ViewGroup中的layout_width,layout_height和padding以及View自身的layout_margin共同决定,指定View需要多少空间。
这个值由高32位和低16位组成,高32位保存的值叫specMode,可以通过如代码中所示的MeasureSpec.getMode()获取;低16位为specSize,同样可以由MeasureSpec.getSize()获取。
MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值,如:200dp或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
以上写的代码全部服务于setMeasuredDimension函数指定最终需要的空间大小。
下面是 onDraw函数,也是自定义View需要重写的方法:
我用的方法是,先画一个圆环作为底色,再到同样的位置画一个圆环,作为实时进度,中间画一个指示数字,根据任务执行的进度来更新圆环位置
MainAcyivity代码
Activity里面的代码主要就是开一个子线程模拟任务进度,然后更新圆环的进度,直到完成
源码下载
首先需要在values文件夹下建一个attrs的文件
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="titletext" format="string" /> <attr name="titletextcolor" format="color" /> <attr name="titletextsize" format="dimension" /> <attr name="drawcolor" format="color" /> <attr name="drawwidth" format="dimension" /> <attr name="textsize" format="dimension" /> <attr name="textcolor" format="color" /> <!--定义样式,声明属性--> <declare-styleable name="CustomTitleView"> <attr name="titletext" /> <attr name="titletextcolor" /> <attr name="titletextsize" /> </declare-styleable> <declare-styleable name="CustomRoundProgressBar"> <attr name="drawcolor" /> <attr name="drawwidth" /> <attr name="textsize" /> <attr name="textcolor" /> </declare-styleable> </resources>
其中attr为定义属性,name为属性名称。
<declare-styleable name="CustomRoundProgressBar"> 意为是在CustomRoundProgressBar里面声明属性,此为声明属性的固定写法
xml布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.cdemo.customview.MainActivity"> <!-- <com.cdemo.customview.CustomTitleView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:padding="10dp" app:titletext="666666" app:titletextcolor="#ff0000" app:titletextsize="50sp" /> --> <com.cdemo.customview.CustomRoundProgressBar android:id="@+id/customprogressbar" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" app:drawcolor="#4ACC60" app:drawwidth="39dp" app:textcolor="#4ACC60" app:textsize="50sp" /> </RelativeLayout>
根布局文件里写明命名空间,可以写成xmlns:app=”http://schemas.android.com/apk/res-auto”系统自动识别,也可以写成 xmlns:xxx=”http://schemas.android.com/apk/res/com.cdemo.customview”,其中”xxx”名字你自己定义,“com.cdemo.customview”对应你的包名,这里我使用app命名空间自动识别。
下面开始Java代码编写
package com.cdemo.customview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.Typeface; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; /** * Created by yangdi on 2017/5/24. */ public class CustomRoundProgressBar extends View{ // 进度条宽度 private int drawWidth; // 进度条颜色 private int drawColor; // 中间数字颜色 private int textColor; // 中间数字字体大小 private int textSize; // 字的高度 private float mTxtHeight; // 进度条最大值 private int maxProgress; // 进度条当前值 private int currentProgress; private Paint mPaint; String TAG = "CustomRoundProgressBar"; public CustomRoundProgressBar(Context context) { this(context, null); } public CustomRoundProgressBar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomRoundProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 获取在xml文件中定义的属性和值 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomRoundProgressBar, defStyleAttr, 0); drawWidth = typedArray.getDimensionPixelSize(R.styleable.CustomRoundProgressBar_drawwidth, TypedValue.COMPLEX_UNIT_DIP); drawColor = typedArray.getInt(R.styleable.CustomRoundProgressBar_drawcolor, Color.LTGRAY); textColor = typedArray.getColor(R.styleable.CustomRoundProgressBar_textcolor,Color.BLACK); textSize = typedArray.getDimensionPixelSize(R.styleable.CustomRoundProgressBar_textsize, TypedValue.COMPLEX_UNIT_SP); typedArray.recycle(); mPaint = new Paint(); // 抗锯齿 mPaint.setAntiAlias(true); Paint.FontMetrics fm = mPaint.getFontMetrics(); mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent); }
通过TypedArray获取刚刚在xml文件中定义的属性和设定的值
onMeasure方法是自定义View中需要重写的方法,其中int widthMeasureSpec, int heightMeasureSpec是父布局传进来的两个参数,由ViewGroup中的layout_width,layout_height和padding以及View自身的layout_margin共同决定,指定View需要多少空间。
这个值由高32位和低16位组成,高32位保存的值叫specMode,可以通过如代码中所示的MeasureSpec.getMode()获取;低16位为specSize,同样可以由MeasureSpec.getSize()获取。
MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值,如:200dp或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width = 0; int height = 0; if(widthMode == MeasureSpec.EXACTLY){ width = widthSize; }else{ // 这里不考虑wrap_content的情况,如有需要可自己补上 } if(heightMode == MeasureSpec.EXACTLY){ height = heightSize; }else{ // 这里不考虑wrap_content的情况,如有需要可自己补上 } // 设置最终宽高 setMeasuredDimension(width, height); }
以上写的代码全部服务于setMeasuredDimension函数指定最终需要的空间大小。
下面是 onDraw函数,也是自定义View需要重写的方法:
@Override protected void onDraw(Canvas canvas) { // 获取视图宽度/2 int viewWidth = getWidth()/2; // 获取视图高度/2 int viewHeight = getHeight()/2; // 圆形进度条半径为宽度的3倍 int radius = drawWidth*3; // 画最外层圆圈 mPaint.setColor(Color.LTGRAY); // 设置画笔宽度 mPaint.setStrokeWidth(drawWidth); // 绘制空心效果 mPaint.setStyle(Paint.Style.STROKE); // 画板底色 canvas.drawColor(Color.WHITE); canvas.drawCircle(viewWidth, viewHeight, radius, mPaint); // 画中间进度数字 mPaint.setStrokeWidth(0);/* 需要注意的是,上面设置过一次画笔宽度,现在需要设置为0,否则画出的字挤在一起*/ // 画笔颜色 mPaint.setColor(textColor); mPaint.setTextSize(textSize); // 字体 mPaint.setTypeface(Typeface.DEFAULT_BOLD); // 通过画布测量字体所占长度 float textWidth = mPaint.measureText(currentProgress+"%"); canvas.drawText(currentProgress+"%", viewWidth - textWidth/2, viewHeight + textSize/3, mPaint); // 画进度 mPaint.setColor(drawColor); mPaint.setStrokeWidth(drawWidth); // 绘制进度 RectF oval = new RectF(viewWidth - radius, viewHeight - radius, viewWidth + radius, viewHeight + radius); canvas.drawArc(oval, 0, getProgress(), false, mPaint); }
我用的方法是,先画一个圆环作为底色,再到同样的位置画一个圆环,作为实时进度,中间画一个指示数字,根据任务执行的进度来更新圆环位置
public void setMaxProgress(int maxProgress) { this.maxProgress = maxProgress; } public void setCurrentProgress(int currentProgress) { this.currentProgress = currentProgress; postInvalidate(); } private int getProgress(){ if(currentProgress < maxProgress){ return 360*currentProgress/maxProgress; }else{ return 360; } }
MainAcyivity代码
package com.cdemo.customview; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { private CustomRoundProgressBar customRoundProgressBar; private int maxProgress = 100; int currentProgress = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); customRoundProgressBar = (CustomRoundProgressBar) findViewById(R.id.customprogressbar); customRoundProgressBar.setMaxProgress(maxProgress); mHandler.sendEmptyMessage(0x11); } Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 0x11: currentProgress++; customRoundProgressBar.setCurrentProgress(currentProgress); sendEmptyMessageDelayed(0x11, 200); if (currentProgress >= maxProgress) { currentProgress = maxProgress; removeMessages(0x11); } break; } } }; @Override protected void onDestroy() { super.onDestroy(); if(mHandler != null){ mHandler.removeCallbacksAndMessages(null); } } }
Activity里面的代码主要就是开一个子线程模拟任务进度,然后更新圆环的进度,直到完成
源码下载
相关文章推荐
- Android自定义圆形进度条,完成类似LOFTER效果
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- 自定义实现圆形播放进度条(android,飞一般的感觉)
- 自定义实现圆形播放进度条(android,飞一般的感觉)
- Android高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- 【Android 应用开发】 自定义 圆形进度条 组件
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- Android:自定义圆形进度条
- Android自定义圆形进度条,完成类似LOFTER效果
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- 【Android 应用开发】 自定义 圆形进度条 组件
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- 【Android 应用开发】 自定义 圆形进度条 组件