前言:

上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性。本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解。有关原理知识请参考Android自定义控件之基本原理(一)这篇文章。

需求产生背景:

为何要引入自定义属性?当Android提供的原生属性不能满足实际的需求的时候,比如我们需要自定义圆形百分比半径大小、圆形背景、圆形显示的位置、圆形进度的背景等等。这个时候就需要我们自定义属性了。

自定义属性步骤:

1.)在res/values文件下添加一个attrs.xml文件,如果项目比较大的话,会导致attrs.xml代码相当庞大,这时可以根据相应的功能模块起名字,方便查找,例如:登录模块相关attrs_login.xml

 
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundImageView">
<attr name="borderRadius" />
<attr name="type" />
</declare-styleable> </resources>
 

2.)如何声明一组属性

使用<declare-styleable name="PercentView"></declare-styleable>来定义一个属性集合,name就是属性集合的名字,这个名字一定要起的见名知意。

  <declare-styleable name="PercentView">
<!--添加属性-->
</declare-styleable>

然后就是定义属性值了,通过<attr name="textColor" format="color" /> 方式定义属性值,属性名字同样也要起的见名知意,format表示这个属性的值的类型,类型有以下几种:

  • reference:引用资源

  • string:字符串

  • Color:颜色

  • boolean:布尔值

  • dimension:尺寸值

  • float:浮点型

  • integer:整型

  • fraction:百分数

  • enum:枚举类型

  • flag:位或运算

基于上面的要求,我们可以定义一下百分比控件属性
 
    <declare-styleable name="PercentView">
<attr name="percent_circle_gravity"><!--圆形绘制的位置-->
<flag name="left" value="0" />
<flag name="top" value="1" />
<flag name="center" value="2" />
<flag name="right" value="3" />
<flag name="bottom" value="4" />
</attr>
<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" /><!--圆形背景色-->
</declare-styleable>
 

3.)布局中如何使用

 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:lee="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <com.whoislcj.views.PercentView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_margin="10dp"
android:background="@color/red"
android:padding="10dp"
lee:percent_background_color="@color/gray"
lee:percent_circle_gravity="left"
lee:percent_circle_progress="30"
lee:percent_circle_radius="50dp"
lee:percent_progress_color="@color/blue" /> </LinearLayout>
 

为属性集设置一个属性集名称,我这里用的lee,我这是因为实在想不起使用什么属性集名称了,建议在真正的项目中使用项目的缩写,比如微信可能就是使用wx。

4.)自定义控件中如何获取自定义属性

每一个属性集合编译之后都会对应一个styleable对象,通过styleable对象获取TypedArray typedArray,然后通过键值对获取属性值,这点有点类似SharedPreference的取法。

 
  TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PercentView);
if (typedArray != null) {
backgroundColor = typedArray.getColor(R.styleable.PercentView_percent_background_color, Color.GRAY);
progressColor = typedArray.getColor(R.styleable.PercentView_percent_progress_color, Color.BLUE);
radius = typedArray.getDimension(R.styleable.PercentView_percent_circle_radius, 0);
progress = typedArray.getInt(R.styleable.PercentView_percent_circle_progress, 0);
gravity = typedArray.getInt(R.styleable.PercentView_percent_circle_gravity, CENTER);
typedArray.recycle();
}
 

5.)完整示例

 
public class PercentView extends View {
private final static String TAG = PercentView.class.getSimpleName();
private Paint mPaint;
private int backgroundColor = Color.GRAY;
private int progressColor = Color.BLUE;
private float radius;
private int progress;
private float centerX = 0;
private float centerY = 0;
public static final int LEFT = 0;
public static final int TOP = 1;
public static final int CENTER = 2;
public static final int RIGHT = 3;
public static final int BOTTOM = 4;
private int gravity = CENTER;
private RectF rectF; //用于定义的圆弧的形状和大小的界限 public PercentView(Context context) {
super(context);
init();
} public PercentView(Context context, AttributeSet attrs) {
super(context, attrs);
initParams(context, attrs);
init();
} public PercentView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initParams(context, attrs);
init();
} private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
rectF = new RectF();
} private void initParams(Context context, AttributeSet attrs) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
rectF = new RectF();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PercentView);
if (typedArray != null) {
backgroundColor = typedArray.getColor(R.styleable.PercentView_percent_background_color, Color.GRAY);
progressColor = typedArray.getColor(R.styleable.PercentView_percent_progress_color, Color.BLUE);
radius = typedArray.getDimension(R.styleable.PercentView_percent_circle_radius, 0);
progress = typedArray.getInt(R.styleable.PercentView_percent_circle_progress, 0);
gravity = typedArray.getInt(R.styleable.PercentView_percent_circle_gravity, CENTER);
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://
break;
case MeasureSpec.AT_MOST:
break;
case MeasureSpec.UNSPECIFIED:
break;
}
Log.e(TAG, "onMeasure--widthSize-->" + widthSize);
Log.e(TAG, "onMeasure--heightMode-->" + heightMode);
Log.e(TAG, "onMeasure--heightSize-->" + heightSize);
int with = getWidth();
int height = getHeight();
Log.e(TAG, "onDraw---->" + with + "*" + height);
centerX = with / 2;
centerY = with / 2;
switch (gravity) {
case LEFT:
centerX = radius + getPaddingLeft();
break;
case TOP:
centerY = radius + getPaddingTop();
break;
case CENTER:
break;
case RIGHT:
centerX = with - radius - getPaddingRight();
break;
case BOTTOM:
centerY = height - radius - getPaddingBottom();
break;
}
float left = centerX - radius;
float top = centerY - radius;
float right = centerX + radius;
float bottom = centerY + radius;
rectF.set(left, top, right, bottom);
} @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.e(TAG, "onLayout");
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(backgroundColor);
// FILL填充, STROKE描边,FILL_AND_STROKE填充和描边
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(centerX, centerY, radius, mPaint);
mPaint.setColor(progressColor); double percent = progress * 1.0 / 100;
int angle = (int) (percent * 360);
canvas.drawArc(rectF, 270, angle, true, mPaint); //根据进度画圆弧
}
}
 

运行结果:

根据不同的配置显示的两种效果

小结:

通过自定义属性可以达到自定义的控件也能像原生的控件一样实现可配置。但是在实际的项目开发中,像本文介绍的这种自定义控件使用频率并不是最高的,使用频率较高的是通过自定义一个组合控件的方式,来达到布局文件的复用,以减少项目维护成本以及开发成本,下篇文章将重点介绍如何自定义控件组合。

Android自定义控件之自定义属性(二)的更多相关文章

  1. Android自定义控件之自定义属性

    前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性.本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解.有关原理知识请参考Android自定义控 ...

  2. Android自定义控件及自定义属性

    Android自定义控件及自定义属性 自定义控件 创建自定义控件 自定义一个类,继承View 继承View还是哪个类,取决于你要实现一个什么样的控件 如果你要实现的是一个线性布局的组合控件,就可以继承 ...

  3. Android自定义控件View(二)继承控件

    在前一篇博客中学习了Android自定义控件View的流程步骤和注意点,不了解的童鞋可以参考Android自定义控件View(一).这一节开始学习自定义控件View(二)之继承系统已有的控件.我们来自 ...

  4. android 自定义控件,自定义属性设置

    做listView的上拉下拉刷新,网上找了个历程.但是有些界面只有上拉刷新,有些界面是下拉刷新.觉得应该在xml里定义一个属性控制上下拉使能. 0.关于自定义控件: 自定义控件设计主要方式有:a) 继 ...

  5. android自定义控件(三) 自定义属性

    书接上回 在xml里建立属性,然后java代码里用typedArray获得这些属性,得到属性后,利用属性做一些事.例:得到xml里的color,赋给paint. 1.在res/values/下新建at ...

  6. Android自定义控件之自定义ViewGroup实现标签云

    前言: 前面几篇讲了自定义控件绘制原理Android自定义控件之基本原理(一),自定义属性Android自定义控件之自定义属性(二),自定义组合控件Android自定义控件之自定义组合控件(三),常言 ...

  7. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  8. Android自定义控件之自定义组合控件(三)

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  9. Android自定义控件之基本原理

    前言: 在日常的Android开发中会经常和控件打交道,有时Android提供的控件未必能满足业务的需求,这个时候就需要我们实现自定义一些控件,今天先大致了解一下自定义控件的要求和实现的基本原理. 自 ...

随机推荐

  1. loj2276 「HAOI2017」新型城市化

    给出的图是一个二分图(显然--吗),一个图的最大团=其补图的最大独立集,因此二分图的最大独立集就是补图的最大团. 欲使补图最大团变大,则要最大独立集变大.二分图最大独立集=点数-最小点覆盖.最小点覆盖 ...

  2. Oracle 10g Data Pump Expdp/Impdp 详解

    Data Pump 介绍 在第一部分看了2段官网的说明, 可以看出数据泵的工作流程如下: (1)在命令行执行命令 (2)expdp/impd 命令调用DBMS_DATAPUMP PL/SQL包. 这个 ...

  3. 深入了解SEO

    为什么要SEO,SEO的作用是什么?SEO(Search Engine Optimization)是为了让自己的IT产品优先能被搜索引擎找到,通过搜索引擎搜索推荐给网民浏览(特点就是精准找到用户群体) ...

  4. Java多线程框架源码阅读之---ReentrantLock

    ReentrantLock基于Sync内部类来完成锁.Sync有两个不同的子类NonfairSync和FairSync.Sync继承于AbstractQueuedSynchronizer. Reent ...

  5. Tinkoff Internship Warmup Round 2018 and Codeforces Round #475 (Div. 2)

    A. Splits time limit per test 1 second memory limit per test 256 megabytes input standard input outp ...

  6. HAZU校赛 Problem K: Deadline

    Problem K: Deadline Time Limit: 2 Sec Memory Limit: 1280 MB Submit: 1106 Solved: 117 [Submit][Status ...

  7. HDU——2723Electronic Document Security(STL map嵌套set做法)

    Electronic Document Security Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K ( ...

  8. UVA11367 Full Tank? 【分层图最短路】

    题目 After going through the receipts from your car trip through Europe this summer, you realised that ...

  9. maven打包加入依赖包以及加入本地依赖包的方法

    Maven引入本地Jar包并打包进War包中 1.概述 在平时的开发中,有一些Jar包因为种种原因,在Maven的中央仓库中没有收录,所以就要使用本地引入的方式加入进来. 2. 拷贝至项目根目录 项目 ...

  10. UVa11424 GCD - Extreme (I)

    直接两重循环O(n^2)算gcd……未免太耗时 枚举因数a和a的倍数n,考虑gcd(i,n)==a的i数量(i<=n) 由于gcd(i,n)==a等价于gcd(i/a,n/a)==1,所以满足g ...