Android之自定义View的实现
对于学习Android开发的小童鞋对于自定义View一定不会陌生,相信大家对它是又爱又恨,爱它可以跟随我们的心意设计出漂亮的效果;恨它想要完全流畅掌握,需要一定的功夫。对于初学者来说确实很不容易,网上有很多相关的博客文档之类的资料,但是往往对于初学者,只知拷贝修改,不理解其中的深意,最后还是无法更具自己的需要,进行自定义的开发,本篇我们就一同来了解下自定义View的神秘面纱。
开发自定义控件的步骤:
/**
* 当实现自定义View时,需要重写它的三个构造方法
*/
public class MySelfView extends View{ private String mtext;//文本内容
private int msrc;//图片系统内地址 public MySelfView(Context context) {
this(context, null, 0);
} public MySelfView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public MySelfView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
} //初始化操作
private void init(Context context, AttributeSet attrs) {
int textId = attrs.getAttributeResourceValue(null, "Text", 0);
int srcId = attrs.getAttributeResourceValue(null, "Src", 0);
mtext = context.getResources().getText(textId).toString();
msrc = srcId;
} @Override
protected void onDraw(Canvas canvas) {//canvas:画板对象
super.onDraw(canvas);
Paint paint = new Paint();//画笔对象
paint.setColor(Color.BLUE);//设置画笔的颜色
InputStream is = getResources().openRawResource(msrc);
Bitmap mBitmap = BitmapFactory.decodeStream(is);
int bh = mBitmap.getHeight();//获取图片的高
int bw = mBitmap.getWidth();//获取图片的宽
canvas.drawBitmap(mBitmap, 100, 100, paint);//绘制图片
canvas.drawCircle(50, 50, 50, paint);//绘制图形
canvas.drawText(mtext, bw/2, 30, paint);//绘制文字
} }
布局文件:
<LinearLayout 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"
android:orientation="vertical"
tools:context="${relativePackage}.${activityClass}" > <com.example.myselfview.view.MySelfView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Text="@string/hello_world"
Src="@drawable/ic_launcher" /> </LinearLayout>
属性Text, Src在自定义View类的构造方法中读取。
最终的显示效果:

<?xml version="1.0" encoding="utf-8"?>
<resources> <attr name="TitleText" format="string" />
<attr name="TitleColor" format="color" />
<attr name="TitleSize" format="dimension" /> <declare-styleable name="CustomTitleView">
<attr name="TitleText" />
<attr name="TitleColor" />
<attr name="TitleSize" />
</declare-styleable> </resources>
我们定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;不清楚的可以google一把。
2、在View的构造方法中,获得我们的自定义的样式
public class MySelfViewRandomNumber extends View{
/**
* 文本
*/
private String mTitleText;
/**
* 文本的颜色
*/
private int mTitleTextColor;
/**
* 文本的大小
*/
private int mTitleTextSize;
/**
* 绘制时控制文本绘制的范围
*/
private Rect mBound;//设置文本绘制的范围
private Paint mPaint;//画笔对象
public MySelfViewRandomNumber(Context context) {
this(context, null, 0);
}
public MySelfViewRandomNumber(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MySelfViewRandomNumber(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
* 获得我们所定义的自定义样式属性
*/
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyleAttr, 0);
int n = typedArray.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.CustomTitleView_TitleText:
mTitleText = typedArray.getString(attr);
break;
case R.styleable.CustomTitleView_TitleColor:
// 默认颜色设置为黑色
mTitleTextColor = typedArray.getColor(attr, Color.BLACK);
break;
case R.styleable.CustomTitleView_TitleSize:
// 默认设置为16sp,TypeValue也可以把sp转化为px
mTitleTextSize = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
typedArray.recycle();
/**
* 获得绘制文本的宽和高
*/
mPaint = new Paint();
mPaint.setTextSize(mTitleTextSize);
mPaint.setColor(mTitleTextColor);
mBound = new Rect();
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
//添加点击事件
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mTitleText = randomText();
postInvalidate();//更新View视图
}
});
}
//生成随机数
private String randomText() {
Random random = new Random();
Set<Integer> set = new HashSet<Integer>();
while (set.size() < 4) {
int randomInt = random.nextInt(10);
set.add(randomInt);
}
StringBuffer sb = new StringBuffer();
for (Integer i : set) {
sb.append("" + i);
}
return sb.toString();
}
@Override
//获得并设置控件的宽和高
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
float textWidth = mBound.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
float textHeight = mBound.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
}
setMeasuredDimension(width, height);
}
@Override
//绘制控件
protected void onDraw(Canvas canvas) {
mPaint.setColor(Color.YELLOW);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
mPaint.setColor(mTitleTextColor);
canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
}
}
我们重写了3个构造方法,默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造调用我们的三个参数的构造,我们在三个参数的构造中获得自定义属性。
系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。
所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:
重写之前先了解MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
下面是我们重写onMeasure代码:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height ;
if (widthMode == MeasureSpec.EXACTLY)
{
width = widthSize;
} else
{
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
float textWidth = mBounds.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
} if (heightMode == MeasureSpec.EXACTLY)
{
height = heightSize;
} else
{
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
float textHeight = mBounds.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
} setMeasuredDimension(width, height);
}
2、然后在布局中声明我们的自定义View
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.myselfview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <com.example.myselfview.view.MySelfViewRandomNumber
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_centerInParent="true"
custom:TitleText="3254"
custom:TitleColor="#ff0000"
custom:TitleSize="16sp"/> </RelativeLayout>
一定要引入 xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"我们的命名空间,后面的包路径指的是项目的package
附上我们的运行效果图:

关于自定义View的设计与学习就先总结到这里,本篇仅仅为了入门了解,对于自定义View还有很多的知识需要学习,希望在接下来的时间,大家多多交流,相互学习。
Android之自定义View的实现的更多相关文章
- Android读取自定义View属性
Android读取自定义View属性 attrs.xml : <?xml version="1.0" encoding="utf-8"?> < ...
- Android之自定义View学习(一)
Android之自定义View学习(一) Canvas常用方法: 图片来源 /** * Created by SiberiaDante on 2017/6/3. */ public class Bas ...
- Android中自定义View的MeasureSpec使用
有时,Android系统控件无法满足我们的需求,因此有必要自定义View.具体方法参见官方开发文档:http://developer.android.com/guide/topics/ui/custo ...
- Android的自定义View及View的绘制流程
目标:实现Android中的自定义View,为理清楚Android中的View绘制流程“铺路”. 想法很简单:从一个简单例子着手开始编写自定义View,对ViewGroup.View类中与绘制View ...
- Android解决自定义View获取不到焦点的情况
引言: 我们在使用Android View或者SurfaceView进行图形绘制,可以绘制各种各样我们喜欢的图形,然后满怀信心的给我们的View加上onTouchEvent.onKeyDown.onK ...
- android开发:Android 中自定义View的应用
大家好我们今天的教程是在Android 教程中自定义View 的学习,对于初学着来说,他们习惯了Android 传统的页面布局方式,如下代码: <?xml version="1.0&q ...
- Android 创建自定义 View 的属性 (attrs) 时需要注意的问题
自定义 View 的属性并不难,可以参照官方的文档 https://developer.android.com/training/custom-views/create-view.html 但是需要注 ...
- android中自定义view构造函数ContentItemView(Context context, AttributeSet paramAttributeSet)的用处
自己定义一个view <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:a ...
- Android之自定义View以及画一个时钟
https://www.2cto.com/kf/201509/443112.html 概述: 当Android自带的View满足不了开发者时,自定义View就发挥了很好的作用.建立一个自定义View, ...
随机推荐
- KMP算法求解
// KMP.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using namespac ...
- Docker 第一篇--初识docker
已经多年不写博客, 看完<晓松奇谈>最后一期猛然觉醒, 决定仔细梳理下自己这几年的知识脉络. 既然决定写, 那么首先就从最近2年热门的开源项目Docker开始.Docker 这两年在国内很 ...
- Emoji选项列表
一.需要的前提文件 从网上下载Emoji的表情包,当然是png的图片,因为WPF不支持彩色的Emoji,所以,做列表的时候,需要用图片. 随着压缩包一起的还有一个Emoji.xml文件,文件的层级结构 ...
- ASP.NET Core project.json imports 是什么意思?
示例代码: "frameworks": { "netcoreapp1.0.0": { "imports" : "portable- ...
- css常用hack
原文地址:css常用hack 突然想起今天早上在CNZZ看到的统计数据,使用IE6.7的用户比例还真多,看到之后我的心都碎了.微软都放弃了为毛还有这么多人不死心? 所以说,IE下的兼容还是得做的. – ...
- iOS UITableView 与 UITableViewController
很多应用都会在界面中使用某种列表控件:用户可以选中.删除或重新排列列表中的项目.这些控件其实都是UITableView 对象,可以用来显示一组对象,例如,用户地址薄中的一组人名.项目地址. UITab ...
- UVA, 10336 Rank the Languages
难点在于:递归函数和输出: #include <iostream> #include <vector> #include <algorithm> #include ...
- mysql删除重复记录语句的方法
例如: id name value 1 a pp 2 a pp 3 b iii 4 b pp 5 b pp 6 c pp 7 c pp 8 c iii id是主键 要求得到这样的结果 id name ...
- Linux的locale、LC_ALL和LANG
如果你是一个Linux新手,并且刚刚安装了一个新的英文系统但想要设置成中文系统,肯定会接触到上面几个变量,在网上搜索了一系列解决方法,给一些变量赋一下值,再export一下,或者写到配置文件里面,然后 ...
- Linux上运行NET
今天尝试了下Ubuntu上运行NET程序,按照 https://github.com/aspnet/Home 的指引,一步一步来: 1.安装DNVM(原名KVM) Linux控制台下输入 curl - ...