学习安卓笔记之自定义控件(一)
------带三角指示器的ProgressBar
如下图所示:“左图是android自带的进度条,他并没有带有指示的效果。所以我们要实现右图的效果,去自定义一个带有三角指示器的进度条。”
自定义ProgressBar的实现
【第一步:首先我们来准备自定义属性】
创建attrs.xml文件,用于创建我们需要的自定义属性。示意图如下:
创建完attrs.xml之后,我们来继续添加我们需要的自定义属性。代码如下(代码颜色有点对不上,谁知道咋调告诉我一下,谢谢了):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyProgressBar" >
<!-- 进度条默认的样式 -->
<attr name="back_line_color" format="color"/>
<attr name="back_line_height" format="dimension"/>
<!-- 展示进度的样式 -->
<attr name="fore_line_color" format="color"/>
<attr name="fore_line_height" format="dimension"/>
<!-- 三角形的颜色和高度 -->
<attr name="indicator_color" format="color"/>
<attr name="indicator_height" format="dimension"/>
</declare-styleable>
</resources>
【第二步:创建MyProgressBar并继承ProgressBar】
因为代码量不大,所以此处贴出了整个类。代码如下:
package lyan.test.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ProgressBar;
import lyan.test.R;
/**
* 自定义进度条
* Created by Administrator on 2016/9/5.
*/
public class MyProgressBar extends ProgressBar{
/**
* 默认的属性
*/
private static final int DEFAULT_VALUES = 10;//默认的值
private static final int DEFAULT_INDICATOR_HEIGHT = DEFAULT_VALUES * 2;//三角形的底边宽
private static final int DEFAULT_INDICATOR_COLOR = 0xffFF0000;//指示器默认的颜色(红)
private static final int DEFAULT_BACK_LINE_HEIGHT = DEFAULT_VALUES;//进度条默认高度(无进度)
private static final int DEFAULT_BACK_LINE_COLOR = 0xffFF8080;//进度条默认颜色(粉)
private static final int DEFAULT_FORE_LINE_HEIGHT = DEFAULT_VALUES;//进度条默认高度(有进度)
private static final int DEFAULT_FORE_LINE_COLOR = 0xff95CAFF;//进度条默认颜色(蓝)
/**
* 变量
*/
private int indicator_height = dp2px(DEFAULT_INDICATOR_HEIGHT);
private int indicator_color = DEFAULT_INDICATOR_COLOR;
private int back_height = dp2px(DEFAULT_BACK_LINE_HEIGHT);
private int back_color = DEFAULT_BACK_LINE_COLOR;
private int fore_height = dp2px(DEFAULT_FORE_LINE_HEIGHT);
private int fore_color = DEFAULT_FORE_LINE_COLOR;
private int progress_width;//绘制线的宽度
private int triangle_width = indicator_height;//底边宽的一半
/**
* 绘制
*/
private Paint indicator_paint;//绘制指示器的画笔
private Paint back_paint;//绘制进度条的底部
private Paint fore_paint;//绘制进度条的进度
private int line_endX;//进度条的终点坐标
/**
* 构造方法
* @param context
*/
public MyProgressBar(Context context) {
this(context,null);
}
public MyProgressBar(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义属性
obtainAttributes(attrs);
//初始化画笔
initPaint();
}
/**
* 绘制画笔
*/
private void initPaint() {
//绘制指示器的画笔
indicator_paint = new Paint();
indicator_paint.setAntiAlias(true);
indicator_paint.setStyle(Paint.Style.FILL);//填充
indicator_paint.setColor(indicator_color);//设置颜色
//绘制进度条的底部
back_paint = new Paint();
back_paint.setStyle(Paint.Style.FILL);//填充
back_paint.setColor(back_color);//设置颜色
back_paint.setStrokeWidth(back_height);
//绘制进度条的进度
fore_paint = new Paint();
fore_paint.setStyle(Paint.Style.FILL);//填充
fore_paint.setColor(fore_color);//设置颜色
fore_paint.setStrokeWidth(fore_height);
}
/**
* 获取自定义属性
*/
private void obtainAttributes(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(
attrs, R.styleable.MyProgressBar);
//指示器
indicator_height = (int) typedArray.getDimension(
R.styleable.MyProgressBar_indicator_height,indicator_height);
indicator_color = typedArray.getColor(
R.styleable.MyProgressBar_indicator_color,DEFAULT_INDICATOR_COLOR);
//底部进度条
back_height = (int) typedArray.getDimension(
R.styleable.MyProgressBar_back_line_height,back_height);
back_color = typedArray.getColor(
R.styleable.MyProgressBar_back_line_color,DEFAULT_BACK_LINE_COLOR);
//顶部进度
fore_height = (int) typedArray.getDimension(
R.styleable.MyProgressBar_fore_line_height,fore_height);
fore_color = typedArray.getColor(
R.styleable.MyProgressBar_fore_line_color,DEFAULT_FORE_LINE_COLOR);
//回收资源
typedArray.recycle();
}
/**
* 测量
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
progress_width = getMeasuredWidth()
- getPaddingRight() - getPaddingLeft();//进度条的宽度
line_endX = progress_width - triangle_width;//进度条线的终点
}
private int measureHeight(int measureSpec) {
int result;
int specMode = MeasureSpec.getMode(measureSpec);//获取测量高度模式
int specSize = MeasureSpec.getSize(measureSpec);//获取测量高度的值
if (specMode == MeasureSpec.EXACTLY) {//精确的测量模式
result = specSize;
} else {
result = (getPaddingTop() + getPaddingBottom()
+ Math.max(back_height, fore_height)) + indicator_height;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
/**
* 绘制
* @param canvas
*/
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(getPaddingLeft(), getHeight()/2);
canvas.drawLine(triangle_width,0,line_endX,0,back_paint);//底部
float progress_with = getProgress()*1.0f/getMax();
float progressPosX = (int) ((line_endX
- triangle_width) * progress_with);
canvas.drawLine(triangle_width,0,progressPosX
+ triangle_width,0,fore_paint);//顶部
//绘制三角形
setTriangle(canvas , progressPosX);
canvas.restore();
}
/**
* 绘制三角形
* @param canvas
*/
private void setTriangle(Canvas canvas,float progressPosX) {
// 绘制等边三角形
float w = triangle_width/2;
float h = (float) (triangle_width * Math.sin(45));
Path path = new Path();
path.moveTo(progressPosX + triangle_width, - back_height/2);//起点
path.lineTo(w + progressPosX+ triangle_width,-h- back_height/2);
path.lineTo(-w + progressPosX+ triangle_width,-h- back_height/2);
path.close();
canvas.drawPath(path, indicator_paint);
}
/**
* dp转px
* @param dpValues
* @return
*/
private int dp2px(int dpValues) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dpValues,
getResources().getDisplayMetrics());
}
}
至此我们的自定义进度条已经编写完成,接下来我们测试一下我们的成果。
测试MyProgressBar的运行效果
我们先看一下效果
我们在布局文件中添加我们自定义的控件,易用的方式就是包名加上类名。如:我的包名是:lyan.test.view,类名是:MyProgressBar,择控件的引用方式为lyan.test.view.MyProgressBar。让我们看一下布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="lyan.test.MainActivity">
<lyan.test.view.MyProgressBar
android:id="@+id/test_myprogressbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:progress="50"
android:max="100" />
</RelativeLayout>
接下来我们运行下程序,效果如下所示:
到现在我们还没用到我们的自定义属性,我们如何使用我们自定义的属性名,只需要添加一个命名控件就可以使用了。我们看一下示例代码:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="lyan.test.MainActivity">
<lyan.test.view.MyProgressBar
android:id="@+id/test_myprogressbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:progress="50"
android:max="100"
app:back_line_color="#ffff00"
app:fore_line_color="#00ff00"
app:indicator_color="#00ffff"
/>
</RelativeLayout>
接下来我们再运行下程序,效果如下所示:
看来我们的效果并没有问题,接下来我们测试下我们的功能是否好使。
测试MyProgressBar的功能
让我们在MainActivity中添加如下的代码:
package lyan.test;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import lyan.test.view.MyProgressBar;
public class MainActivity extends AppCompatActivity {
private int counts;//计数
private boolean isRunning;//子线程的开关
private MyProgressBar myProgressBar;//声明控件
private Thread thread;//子线程
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 1){
++counts;//模拟进度
if (counts == 100){
isRunning = false;//关闭子线程
return false;
}
myProgressBar.setProgress(counts);
}
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
myProgressBar = (MyProgressBar) findViewById(R.id.test_myprogressbar);
}
/**
* 按钮的点击事件
* @param view
*/
public void start(View view){
if (thread !=null && thread.isAlive()){
Toast.makeText(this,"子线程没有运行结束!",Toast.LENGTH_SHORT).show();
return;
}
//设置一个子线程来模拟进度
thread = new Thread(new Runnable() {
@Override
public void run() {
isRunning = true;
counts = 0;
while (isRunning){
try {
Thread.sleep(100);
handler.sendEmptyMessage(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
Toast.makeText(this,"子线程开始运行了!",Toast.LENGTH_SHORT).show();
}
}
我们在布局中添加一个按钮,代码如下
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="lyan.test.MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start"
android:textSize="20sp"
android:onClick="start"
android:textAllCaps="false"/>
<lyan.test.view.MyProgressBar
android:id="@+id/test_myprogressbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:progress="50"
android:max="100"
app:back_line_color="#ffff00"
app:fore_line_color="#00ff00"
app:indicator_color="#00ffff"
/>
</RelativeLayout>
最后我们运行一下程序,看一下最后的运行效果
如图所示,我们的MyProgressBar效果和功能都已经实现。由于我的gif截图绿色图像有问题,所以我这里把进度条换成了紫红色(#FF00FF)。
至此,我们的自定义进度条已经完成了。