这里以一个进度条的加载为例子,先看效果(运行效果是动态变化的)
一、自定义属性
首先在res->values目录下新建attrs资源文件,如下图:
内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ProgressBar">
<!-- 圆弧的颜色-->
<attr name="innerBackground" format="color"/>
<attr name="outerBackground" format="color"/>
<!-- 圆弧的大小-->
<attr name="roundWidth" format="dimension"/>
<!-- 字体大小、颜色-->
<attr name="progressTextSize" format="dimension"/>
<attr name="progressTextColor" format="color"/>
</declare-styleable>
</resources>
二、在Layout文件中引用自定义View
这里要注意引入自定义View属性的命名空间的方式
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="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=".MainActivity"
android:orientation="vertical">
<com.example.viewtest.ProgressBar
android:id="@+id/progress_bar"
custom:progressTextSize="20sp"
custom:progressTextColor="#ff7788"
custom:innerBackground="#ee9933"
custom:outerBackground="#229933"
custom:roundWidth="5dp"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!-- 测试按钮-->
<Button
android:id="@+id/test"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginTop="30dp"
android:layout_gravity="center"
android:text="测试"
android:background="@color/black"
android:textColor="@color/white"/>
</LinearLayout>
三、 创建一个类继承View
public class ProgressBar extends View {
private static final String TAG = "ProgressBar";
//声明自定义属性
private int mInnerBackground = Color.RED;
private int mOuterBackground = Color.GREEN;
private int mRoundWidth = 30;
private int mProgressTextSize = 70;
private int mProgressTextColor = Color.GREEN;
//创建画笔
private Paint mInnerPaint=new Paint();
private Paint mOutterPaint = new Paint();
private Paint mTextPaint = new Paint();
//进度条的最大进度
private int mMax=100;
//当前进度
private int mCurrentProgress=50;
//new对象的时候调用该方法
public ProgressBar(Context context) {
super(context);
}
//在布局中使用 xml文件中使用
public ProgressBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
// 在layout中调用 自定义样式、有固定style的时候使用
public ProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义属性
TypedArray array= context.obtainStyledAttributes(attrs,R.styleable.ProgressBar);
mInnerBackground=array.getColor(R.styleable.ProgressBar_innerBackground,mInnerBackground);
mOuterBackground=array.getColor(R.styleable.ProgressBar_outerBackground,mOuterBackground);
mProgressTextColor= array.getColor(R.styleable.ProgressBar_progressTextColor,mProgressTextColor);
mProgressTextSize= (int) array.getDimension(R.styleable.ProgressBar_progressTextSize,sp2px(mProgressTextSize));
mRoundWidth= (int) array.getDimension(R.styleable.ProgressBar_roundWidth,dip2px(mRoundWidth));
array.recycle();
// mInnerPaint = new Paint();
}
private float sp2px(int sp) {
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,sp,getResources().getDisplayMetrics());
}
private float dip2px(int dip) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
}
public ProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
/**
* onMeasure
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//拿到宽高
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//存储view 的宽高 在这里取最小值 保证是一个正方形即可
setMeasuredDimension(Math.min(width,height),Math.min(width,height));
}
/**
* onDraw
*/
@Override
protected void onDraw(Canvas canvas) {
mInnerPaint.setAntiAlias(true);//抗锯齿
mInnerPaint.setColor(mInnerBackground);
mInnerPaint.setStrokeWidth(mRoundWidth);//线条宽度
mInnerPaint.setStyle(Paint.Style.STROKE);//空心
mOutterPaint.setAntiAlias(true);//抗锯齿
mOutterPaint.setColor(mOuterBackground);
mOutterPaint.setStrokeWidth(mRoundWidth);//线条宽度
mOutterPaint.setStyle(Paint.Style.STROKE);//空心
mOutterPaint.setAntiAlias(true);//抗锯齿
mTextPaint.setColor(mProgressTextColor);
mTextPaint.setStrokeWidth(mRoundWidth);
mTextPaint.setTextSize(mProgressTextSize);
// super.onDraw(canvas);
//先画内圆
int center = getWidth()/2;
Log.d(TAG, "onDraw: "+center);
canvas.drawCircle(center,center,center-mRoundWidth/2,mInnerPaint);
//再画外层圆
RectF rectF=new RectF(0+mRoundWidth/2,0+mRoundWidth/2,getWidth()-mRoundWidth/2,getHeight()-mRoundWidth/2);
if (mCurrentProgress==0){
return;
}
float percent = (float) mCurrentProgress/mMax;
canvas.drawArc(rectF,0,percent*360,false,mOutterPaint);
//画进度文字
String text=((int)(percent*100))+"%";
Rect textBounds=new Rect();
mTextPaint.getTextBounds(text,0,text.length(),textBounds);
int x= getWidth()/2-textBounds.width()/2;
Paint.FontMetricsInt fontMetrics=mTextPaint.getFontMetricsInt();
int dy=(fontMetrics.bottom-fontMetrics.top)/2 - fontMetrics.bottom;
int baseLineY=getHeight()/2+dy;
canvas.drawText(text,x,baseLineY,mTextPaint);
}
public synchronized void setMax(int max){
if (max<0){
Log.d(TAG, "setMax: max<0 不合法"+max);
}
this.mMax=max;
}
public synchronized void setProgress(int process){
if (process<0){
Log.d(TAG, "setMax: max<0 不合法"+process);
}
this.mCurrentProgress=process;
invalidate();//刷新
}
}
实现自定义View有那些方式
① 继承View,例如折线图等。
② 继承ViewGroup,例如流式布局等。
③ 继承现有的View,例如TextView、ListView等。
四、在Activity中调用
public class MainActivity extends AppCompatActivity {
private ProgressBar mProgressBar;
private Button mTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
}
private void initData() {
mTest=findViewById(R.id.test);
mProgressBar=(ProgressBar) findViewById(R.id.progress_bar);
mProgressBar.setMax(1000);
mTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
test();
}
});
}
private void test() {
//值不断变化
ValueAnimator animator= ObjectAnimator.ofFloat(0,1000);
animator.setDuration(3000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float progress = (float) animation.getAnimatedValue();
mProgressBar.setProgress((int) progress);
}
});
}
}
五、参考
- Android自定义View
- 【Android学习】—自定义view的流程以及实践
- Android自定义View入门(一)
- Android 自定义View 之 Mac地址输入框