上一篇博客讲解了给自定义控件添加事件,这篇博客讲解给自定义控件添加属性,首先介绍一下添加自定义属性的基本步骤:
1.在res/values文件下新建一个属性xml文件,如attrs.xml,xml的文件名字可以自己任意取,然后再属性文件里添加
<declare-styleable>标签,如下图所示
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="test">
<attr name="width" format="dimension"/>
</declare-styleable>
</resources>
2.声明属性,比如上面声明了名字为test的一组属性,<attr>标签里的是属性,可以有很多组<attr>属性,属性里有单位(format)和属性(name)名称,属性的单位有以下几种:
reference:引用资源
string:字符串
Color:颜色
boolean:布尔值
dimension:尺寸值
float:浮点型
integer:整型
fraction:百分数
enum:枚举类型
flag:位或运算
3.在控件布局文件里添加属性和再自定义的构造方法里获取属性,下图给出的是本篇博客给出的例子的布局文件属性添加和获取属性的方法:
通过以上几步就可以获取自定义控件的属性了。
下面是给出的一个代码的示例,接着前面两篇博客继续做的工作。
属性文件attrs_percent.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SimpleView">
<attr name="percent_circle_radius" format="dimension" /><!--圆形半径-->
<attr name="percent_circle_progress" format="integer" /><!--当前进度值-->
<attr name="percent_progress_color" format="color" /><!--进度显示颜色-->
<attr name="percent_background_color" format="color" /><!--圆形背景色-->
<attr name="percent_text_color" format="color"/><!--文字颜色-->
<attr name="percent_text_size" format="dimension"/><!--字体大小-->
</declare-styleable>
</resources>
布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:percentview="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="com.example.myapplication.MainActivity">
<com.example.myapplication.SimpleView
android:layout_width="200dp"
android:layout_height="200dp"
percentview:percent_circle_radius="90dp"
percentview:percent_circle_progress="135"
percentview:percent_background_color="#a8f9fc"
percentview:percent_progress_color="#0000ff"
percentview:percent_text_color="#ff0000"
percentview:percent_text_size="25dp"
/>
</RelativeLayout>
自定义控件类代码
package com.example.myapplication;
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.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class SimpleView extends View {
private final static String TAG = SimpleView.class.getSimpleName();
//画笔
private Paint mPaint;
private RectF oval;
//事件处理
private EventHandle mEventHandle;
//鼠标按下位置
private int startX,startY;
//按下鼠标时控件的位置
private int startLeft,startTop;
//状态栏高度
int statusHeight = 0;
//背景颜色
int backgroundColor=Color.GRAY;
int progressColor=Color.RED;
int textColor=Color.WHITE;
int textSize=30;
int circleRadius=2048;
int progress=120;//进度
public SimpleView(Context context) {
super(context);
init();
}
public SimpleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initParameter(context,attrs);
init();
}
public SimpleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initParameter(context,attrs);
init();
}
private void init(){
mPaint = new Paint();
//设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
mPaint.setAntiAlias(true);
mPaint.setTextSize(textSize);
oval=new RectF();
mEventHandle=null;
startY=startX=0;
int resourceId = this.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusHeight = this.getResources().getDimensionPixelSize(resourceId);
}
}
private void initParameter(Context context, AttributeSet attrs)
{
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SimpleView);
if (typedArray != null) {
backgroundColor = typedArray.getColor(R.styleable.SimpleView_percent_background_color, Color.GRAY);
progressColor = typedArray.getColor(R.styleable.SimpleView_percent_progress_color, Color.RED);
circleRadius = (int)typedArray.getDimension(R.styleable.SimpleView_percent_circle_radius, 2048);
progress = typedArray.getInt(R.styleable.SimpleView_percent_circle_progress, 120);
textColor=typedArray.getColor(R.styleable.SimpleView_percent_text_color,Color.WHITE);
textSize=(int)typedArray.getDimension(R.styleable.SimpleView_percent_text_size,30);
if(progress>360)
progress=360;
if(progress<0)
progress=0;
typedArray.recycle();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Log.e(TAG, "onMeasure--widthMode-->" + widthMode);
switch (widthMode) {
case MeasureSpec.EXACTLY:
//精确值模式,当控件的layout_width和layout_height属性指定为具体数值或match_parent时。
break;
case MeasureSpec.AT_MOST:
//最大值模式,当空间的宽高设置为wrap_content时。
break;
case MeasureSpec.UNSPECIFIED:
//未指定模式,View想多大就多大,通常在绘制自定义View时才会用。
break;
}
//取最小边为控件的宽高的最小值
int minWidth=widthSize>heightSize?heightSize:widthSize;
setMeasuredDimension(minWidth,minWidth);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 绘制背景圆
*/
mPaint.setColor(backgroundColor);
// FILL填充, STROKE描边,FILL_AND_STROKE填充和描边
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
int with = getWidth();
int height = getHeight();
Log.e(TAG, "onDraw---->" + with + "*" + height);
float radius = with / 2-5;
//如果设置的半径比较合理,就去设置的半径
if(circleRadius<radius)
radius=circleRadius;
canvas.drawCircle(with / 2, with / 2, radius, mPaint);
/**
* 绘制百分比圆
*/
mPaint.setColor(progressColor);
oval.set(with / 2 - radius, with / 2 - radius, with / 2
+ radius, with / 2 + radius);//用于定义的圆弧的形状和大小的界限
int sweepAngle=progress;
canvas.drawArc(oval, 0, -sweepAngle, true, mPaint); //根据进度画圆弧
double percent=sweepAngle/360.0;
/**
*绘制百分比文本
*/
//设置文本颜色
mPaint.setColor(textColor);
//绘制文本百分比数据
canvas.drawText(String.format("%.2f",percent)+"%",(float)(with/2+radius*Math.cos(sweepAngle*Math.PI/360)/4)
,(float)(with/2-radius*Math.sin(sweepAngle*Math.PI/360)/3),mPaint);
canvas.drawText(String.format("%.2f",1-percent)+"%",(float)(with/2-radius*Math.cos(sweepAngle*Math.PI/360))
,(float)(with/2+radius*Math.sin(sweepAngle*Math.PI/360)/3),mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
startX=(int)event.getRawX();
startY=(int)event.getRawY();
startLeft=(int)(startX-event.getX());
/**
* 这里startTop计算有些偏离,原因在于计算时加入了标题栏和状态栏的高度
* 注意:要是你的Activity没有去掉标题栏,这里还要去掉标题栏的高度
*/
startTop= (int)(startY-event.getY())-statusHeight;//减去状态栏高度
break;
case MotionEvent.ACTION_MOVE:
if(mEventHandle!=null)
{
mEventHandle.onTouchEvent(event);
}else{
int disX=(int)event.getRawX()-startX;//计算偏移的X坐标
int disY=(int)event.getRawY()-startY;//计算偏移的Y坐标;
int left=startLeft+disX;
int top=startTop+disY;
//控件超出屏幕范围取消绘制
//更新控件位置
layout(left,top,left+getWidth(),top+getHeight());
}
break;
case MotionEvent.ACTION_UP:
break;
}
//返回true表示不消耗此事件,事件继续传递,返回flase表示事件消耗
return true;
}
public void setmEventHandle(EventHandle mEventHandle) {
this.mEventHandle = mEventHandle;
}
interface EventHandle{
public void onTouchEvent(MotionEvent event);
}
}
MainActivity.java
package com.example.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏
if (getSupportActionBar() != null){
getSupportActionBar().hide();
}
setContentView(R.layout.activity_main);
}
}
效果图: