Android自定义视图
Android框架为我们提供了大量的视图类来帮助我们做好展示信息以及同用户进行交互的工作。然后有时候,我们的app或许需要一些在Android内建视图之外特殊的视图,那么此时我们就需要自定义视图。下面我们来看看如何构建一个具有鲁棒性和可重用的视图。本文主要结合谷歌官方文档和API介绍自定义视图。
第一步:建立一个视图类
1.1 继承自View作为View的一个子类
class PieChart extends View {
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
1.2 自定义属性
<resources>
<declare-styleable name="PieChart">
<attr name="showText" format="boolean" />
<attr name="labelPosition" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>
</resources>
<?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/com.example.customviews">
<com.example.customviews.charting.PieChart
custom:showText="true"
custom:labelPosition="left" />
</LinearLayout>
1.3 应用自定义属性
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0, 0);
try {
mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
} finally {
a.recycle();
}
}
1.4 添加属性和事件
public boolean isShowText() {
return mShowText;
}
public void setShowText(boolean showText) {
mShowText = showText;
invalidate();
requestLayout();
}
1.5 无障碍
第二步:自定义绘图
2.1 重载onDraw()方法
2.2 创建Drawing对象
private void init() {
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(mTextColor);
if (mTextHeight == 0) {
mTextHeight = mTextPaint.getTextSize();
} else {
mTextPaint.setTextSize(mTextHeight);
}
mPiePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPiePaint.setStyle(Paint.Style.FILL);
mPiePaint.setTextSize(mTextHeight);
mShadowPaint = new Paint(0);
mShadowPaint.setColor(0xff101010);
mShadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));
...
2.3 处理布局事件
// Account for padding
float xpad = (float)(getPaddingLeft() + getPaddingRight());
float ypad = (float)(getPaddingTop() + getPaddingBottom());
// Account for the label
if (mShowText) xpad += mTextWidth;
float ww = (float)w - xpad;
float hh = (float)h - ypad;
// Figure out how big we can make the pie.
float diameter = Math.min(ww, hh);
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Try for a width based on our minimum
int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
int w = resolveSizeAndState(minw, widthMeasureSpec, 1);
// Whatever the width ends up being, ask for a height that would let the pie
// get as big as it can
int minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop();
int h = resolveSizeAndState(MeasureSpec.getSize(w) - (int)mTextWidth, heightMeasureSpec, 0);
setMeasuredDimension(w, h);
}
1.正如前面提及的,这些计算中需要考虑padding。2.resolveSizeAndState()方法用来确认最后个的宽度和高度。它会通过比较期望的大小和传递进onMeasure()方法的spec值来返回一个合适的View.MeasureSpec。除非有引入一个不同大小的限制,否则它会去期望的数值。
3.onMeasure()方法没有返回值。它会在通过调用setMeasuredDimension()方法来传递结果。setMeasuredDimension()是强制需要的,如果没有调用它,会抛出运行时异常。
2.4 绘制
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw the shadow
canvas.drawOval(
mShadowBounds,
mShadowPaint
);
// Draw the label text
canvas.drawText(mData.get(mCurrentItem).mLabel, mTextX, mTextY, mTextPaint);
// Draw the pie slices
for (int i = 0; i < mData.size(); ++i) {
Item it = mData.get(i);
mPiePaint.setShader(it.mShader);
canvas.drawArc(mBounds,
360 - it.mEndAngle,
it.mEndAngle - it.mStartAngle,
true, mPiePaint);
}
// Draw the pointer
canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint);
canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint);
}
其中slice的角度计算如下
for (Item it : mData) {
it.mStartAngle = currentAngle;
it.mEndAngle = (int) ((float) currentAngle + it.mValue * 360.0f / mTotal);
currentAngle = it.mEndAngle;
...
}
参考:http://developer.android.com/training/custom-views/index.html
Android自定义视图的更多相关文章
- Android自定义视图教程
Android自定义视图教程 Android的UI元素都是基于View(屏幕中单个元素)和ViewGroup(元素的集合),Android有许多自带的组件和布局,比如Button.TextView.R ...
- Android自定义视图四:定制onMeasure强制显示为方形
这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...
- Android自定义视图三:给自定义视图添加“流畅”的动画
这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...
- Android自定义视图二:如何绘制内容
这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...
- Android自定义视图一:扩展现有的视图,添加新的XML属性
这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...
- 【转】ANDROID自定义视图——onLayout源码 流程 思路详解
转载(http://blog.csdn.net/a396901990) 简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量——onMeasure():决定View的大小 2.布局 ...
- 【转】ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解
原文地址:http://blog.csdn.net/a396901990/article/details/36475213 简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量—— ...
- ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解
简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3. ...
- ANDROID自定义视图——onMeasure流程,MeasureSpec详解
简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量——onMeasure():决定View的大小 2.布局——onLayout():决定View在ViewGroup中的位置 3. ...
随机推荐
- Opencv YAML和XML格式文件操作详解
本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/47660943 本文参考Opencv 2 ...
- HDU——T 2647 Reward
http://acm.hdu.edu.cn/showproblem.php?pid=2647 Time Limit: 2000/1000 MS (Java/Others) Memory Limi ...
- HDU 5373(2015多校7)-The shortest problem(模拟%11)
题目地址:pid=5373">HDU 5373 题意:给你一个数n和操作次数t,每次操作将n的各位数之和求出来放在n的末尾形成新的n,问t次操作后得到的n能否够被11整除. 思路:就是 ...
- easyui combobox keyhandler使用
easyui combobox keyhandler使用 在combo组件中有属性: keyHandler : { up : function() { console.log('upupup'); ...
- 设计模式之Flyweight模式(笔记)
享元模式:运用共享技术有效地支持大量细粒度的对象. 适用场合:假设一个应用程序适用了大量的对象.而大量的这些对象造成了非常大的存储开销时就应该考虑使用. 首先定义一个IFlyweight接口 publ ...
- Git简介以及与SVN的区别
Git是由著名Linux内核(Kernel)开发者LinusTorvalds为了便利维护Linux而开发的. Git是一个分布式的版本控制系统.作为一个分布式的版本控制系统,在Git中并不存在主库这样 ...
- BZOJ1901 ZOJ2112 线段树+treap (线段树套线段树)
BZOJ1901: 线段树套线段树做法: (外层线段树 里层动态开节点的权值线段树) 有一个小小的trick 可以省掉二分变成nlog^2n的 就是把查询的区间都取出来- logn个一起走- 2016 ...
- SQL server无法启动服务,提示“错误1069: 由于登录失败而无法启动服务”
原因:大部分情况是你修改了服务器系统的登录密码,而导致SQL服务无法启动. 解决方法:将sql server(mssql server)服务的登录密码改为系统登录密码或本地登录,如下操作步骤: 在wi ...
- 应对加密js的三种方法
经常遇到网页在登录后会对用户输入的帐号和密码通过js进行加密,导致模拟登录这类网站时受到阻碍 这里小记一下当前解决该问题的三种方法 1.利用python实现js同等加密. 2.利用selenium模拟 ...
- 学习《Python金融实战》中文版PDF+英文版PDF+源代码
学习python处理金融数据,建议学习<Python金融实战>,比较实用,只不过Yahoo财经的API改了,书里的方法不再有效要改一改,还有就是会有一些代码缩进小问题,总体上对金融分析很实 ...