前面两篇博客,把View绘制的方法说了一下,但是,我们只在onDraw里面做文章,控件都是直接传入一个Context,还不能在布局文件里使用自定义View。这一篇博客,就不再讲绘制,在我们原先的基础上,真正实现一个可用的View。

方案:

1、在attrs.xml文件中添加View的自定义属性。

2、继承一个已有的View,比如Button,这样就不要考虑onMeasure方法的内容,因为安卓自带的View肯定已经把它写好了。

3、继承一个View,我们自己设计,实现View的全部功能。

自定义属性

首先,学会自定义属性,两个步骤1、配置attrs.xml文件,写入我们自定义属性;2、在View中引用我们的自定义属性。

配置attrs.xml文件

在res/values包下新建attrs.xml文件,配置好自定义控件名称、自定义属性名称。

我自定义了一个View,名叫RectView,自定义两个属性,rect_hight和rect_width,然后配置文件如下:

format的可选参数类型:

“reference” //引用

“color” //颜色

“boolean” //布尔值

“dimension” //尺寸值

“float” //浮点值

“integer” //整型值

“string” //字符串

“fraction” //百分数,比如200%

使用自定义属性

在自定义View中获取自定义属性:

//获取TypedArray对象

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RectView);

//获取自定义属性

int mWidth = typedArray.getDimensionPixelSize(R.styleable.RectView_rect_width, 300);

//回收TypedArray

typedArray.recycle();

或者使用for循环遍历

int n = typedArray.getIndexCount();

for (int i = 0; i < n; i++) {

switch (typedArray.getIndex(i)){

case R.styleable.RectView_rect_width:

int mWidth = typedArray.getDimensionPixelSize(R.styleable.RectView_rect_width, 300);

break;

}

}

Demo

使用上面给出的attrs.xml文件,在布局文件中使用我们的自定义View,在使用自定义属性的时候,注意在布局中写这一行:xmlns:app=”http://schemas.android.com/apk/res-auto”,在Android Studio下输入App回车即可。

布局文件:

xmlns:app="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:layout_width="200dp"

android:layout_height="200dp"

app:rect_width="150dp"

app:rect_hight="150dp"

android:background="@android:color/holo_blue_light"/>

自定义View代码:

/**

* 在布局文件中使用自定义View

* Created by ChenSS on 2016/11/24.

*/

public class RectView extends View {

private int mWidth, mHeight;

private Paint mPaint;

public RectView(Context context) {

this(context, null);

}

public RectView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public RectView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

//获取所需的控件参数

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RectView);

mWidth = typedArray.getDimensionPixelSize(R.styleable.RectView_rect_width, 300);

mHeight = typedArray.getDimensionPixelSize(R.styleable.RectView_rect_hight, 500);

mPaint = new Paint();

mPaint.setAntiAlias(true);

//注意回收

typedArray.recycle();

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

canvas.drawRoundRect(30, 30, mWidth, mHeight, 40, 20, mPaint);

}

}

实现的效果:

简单地实现了一个自定义控件,绘制一个圆角矩形,从效果图可以看出,layout_width和layout_height决定了View的大小(蓝色区域),而我们自定义的rect_width和rect_hight用于绘制我们自己的图形(黑色区域)。

自定义View方式一:继承已有的View

继承Button,直接拷贝Button的所有构造函数;

注意:调用3个参数的构造函数时,第三个参数使用:R.attr.buttonStyle,这样使用wrap_content的时候,显示的大小和默认Button相同;

绘制思路:使用Xfermode,先绘制圆角矩形,然后设置PorterDuff.Mode,再绘制位图,将位图中圆角矩形的部分显示出来;

也可以使用Shape的子类绘制圆角矩形。

/**

* 自定义圆角矩形ImageView

* Created by ChenSS on 2016/11/24.

*/

public class RectView extends Button {

private Paint mPaint;

private Bitmap mBitmap;

private PorterDuffXfermode xfermode;

//边框

private static final int RECT_BORDER=5;

//圆角

private static final int RECT_RADIO=10;

public RectView(Context context) {

this(context, null);

}

public RectView(Context context, AttributeSet attrs) {

this(context, attrs, R.attr.buttonStyle);

}

public RectView(Context context, AttributeSet attrs, int defStyleAttr) {

this(context, attrs, defStyleAttr, 0);

}

public RectView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {

super(context, attrs, defStyleAttr, defStyleRes);

xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);

mBitmap = ViewHelper.findBitmapById(context, R.mipmap.view_shape);

mPaint = new Paint();

mPaint.setAntiAlias(true);

mPaint.setColor(Color.GRAY);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//位图改变大小

mBitmap=ViewHelper.zoomImage(mBitmap,getMeasuredWidth(),getMeasuredHeight());

//描边

canvas.drawRoundRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(),

RECT_BORDER+RECT_RADIO, RECT_BORDER+RECT_RADIO, mPaint);

//离屏缓存

int sc = canvas.saveLayer(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), null, Canvas.ALL_SAVE_FLAG);

//图片遮罩

canvas.drawRoundRect(RECT_BORDER, RECT_BORDER,

mBitmap.getWidth()-RECT_BORDER, mBitmap.getHeight()-RECT_BORDER,

RECT_RADIO, RECT_RADIO, mPaint);

mPaint.setXfermode(xfermode);

canvas.drawBitmap(mBitmap,RECT_BORDER, RECT_BORDER, mPaint);

mPaint.setXfermode(null);

// 还原画布

canvas.restoreToCount(sc);

}

}

采用这种方式自定义View,实现起来就十分简单,直接在onDraw中调用getMeasuredWidth()、getMeasuredHeight()就可以了,不用我们多考虑什么。

自定义View方式二:继承View

如果是继承View,就要重新考虑一下View的大小了,在Xml布局中,会有3种情况:match_parent、wrap_content或者指定一个确切的大小,区分这些情况,我们就要考虑MeasureSpec的Mode了,Mode一共三种类型:

EXACTLY:一般是设置了明确的值,或者是MATCH_PARENT

AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT

UNSPECIFIED:表示子布局想要多大就多大,如果你想绘制Listview这样的东西,就考虑使用吧

对上面的代码做一些修改,把继承Button改成继承View,如果不重写onMeasure方法,你会发现match_parent、wrap_content效果是一样的,下面代码简单地重写了一下onMeasure方法。

/**

* 继承View实现自定义控件

* Created by ChenSS on 2016/11/24.

*/

public class RectView extends View {

private Paint mPaint;

private Bitmap mBitmap;

private PorterDuffXfermode xfermode;

//边框

private static final int RECT_BORDER = 5;

//圆角

private static final int RECT_RADIO = 10;

//默认宽度

private static final int DEFAULT_WIDTH = 600;

//默认高度

private static final int DEFAULT_HEIGHT = 200;

public RectView(Context context) {

this(context, null);

}

public RectView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public RectView(Context context, AttributeSet attrs, int defStyleAttr) {

this(context, attrs, defStyleAttr, 0);

}

public RectView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {

super(context, attrs, defStyleAttr, defStyleRes);

xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);

mBitmap = ViewHelper.findBitmapById(context, R.mipmap.view_shape);

mPaint = new Paint();

mPaint.setAntiAlias(true);

mPaint.setColor(Color.GRAY);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//位图改变大小

mBitmap = ViewHelper.zoomImage(mBitmap, getMeasuredWidth(), getMeasuredHeight());

//描边

canvas.drawRoundRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(),

RECT_BORDER + RECT_RADIO, RECT_BORDER + RECT_RADIO, mPaint);

//离屏缓存

int sc = canvas.saveLayer(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), null, Canvas.ALL_SAVE_FLAG);

//图片遮罩

canvas.drawRoundRect(RECT_BORDER, RECT_BORDER,

mBitmap.getWidth() - RECT_BORDER, mBitmap.getHeight() - RECT_BORDER,

RECT_RADIO, RECT_RADIO, mPaint);

mPaint.setXfermode(xfermode);

canvas.drawBitmap(mBitmap, RECT_BORDER, RECT_BORDER, mPaint);

mPaint.setXfermode(null);

// 还原画布

canvas.restoreToCount(sc);

}

@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);

int width;

int height;

if (widthMode == MeasureSpec.EXACTLY) {

width = widthSize;

} else {

width = DEFAULT_WIDTH;

}

if (heightMode == MeasureSpec.EXACTLY) {

height=heightSize;

} else {

height = DEFAULT_HEIGHT;

}

setMeasuredDimension(width, height);

}

}

自定义View进阶:Java代码修改View的显示

有时候,我们会有这样的需求,当我们点击图片的时候,会有一个新的图片替换掉旧的,其中的关键方法是postInvalidate(),在调用postInvalidate()之前,我们需要对参数进行一些设置,调用postInvalidate()之后,onDraw中的内容会重新执行一遍。自定义View的其它方法请参考:自定义View常用方法汇总

Demo:

/**

* 继承View实现自定义控件

* Created by ChenSS on 2016/11/24.

*/

public class RectView extends View {

private Paint mPaint;

private Bitmap mBitmap;

private PorterDuffXfermode xfermode;

private Context mContext;

//边框

private static final int RECT_BORDER = 5;

//圆角

private static final int RECT_RADIO = 10;

//默认宽度

private static final int DEFAULT_WIDTH = 600;

//默认高度

private static final int DEFAULT_HEIGHT = 200;

public RectView(Context context) {

this(context, null);

}

public RectView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public RectView(Context context, AttributeSet attrs, int defStyleAttr) {

this(context, attrs, defStyleAttr, 0);

}

public RectView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {

super(context, attrs, defStyleAttr, defStyleRes);

mContext=context;

xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);

mBitmap = ViewHelper.findBitmapById(context, R.mipmap.view_shape);

mPaint = new Paint();

mPaint.setAntiAlias(true);

mPaint.setColor(Color.GRAY);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//位图改变大小

mBitmap = ViewHelper.zoomImage(mBitmap, getMeasuredWidth(), getMeasuredHeight());

//描边

canvas.drawRoundRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(),

RECT_BORDER + RECT_RADIO, RECT_BORDER + RECT_RADIO, mPaint);

//离屏缓存

int sc = canvas.saveLayer(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), null, Canvas.ALL_SAVE_FLAG);

//图片遮罩

canvas.drawRoundRect(RECT_BORDER, RECT_BORDER,

mBitmap.getWidth() - RECT_BORDER, mBitmap.getHeight() - RECT_BORDER,

RECT_RADIO, RECT_RADIO, mPaint);

mPaint.setXfermode(xfermode);

canvas.drawBitmap(mBitmap, RECT_BORDER, RECT_BORDER, mPaint);

mPaint.setXfermode(null);

// 还原画布

canvas.restoreToCount(sc);

}

@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);

int width;

int height;

if (widthMode == MeasureSpec.EXACTLY) {

width = widthSize;

} else {

width = DEFAULT_WIDTH;

}

if (heightMode == MeasureSpec.EXACTLY) {

height=heightSize;

} else {

height = DEFAULT_HEIGHT;

}

setMeasuredDimension(width, height);

}

/**

* 修改图片

*@param drawableId 图片资源ID

*/

public void setBitmap(int drawableId){

mBitmap.recycle();

mBitmap = ViewHelper.findBitmapById(mContext, drawableId);

if(mBitmap.isRecycled())

return;

postInvalidate();

}

}

上面的代码中,我们添加了一个setBitmap方法,这个方法用于获取本地的图片Id,然后修改自定义View中的mBitmap对象,然后调用postInvalidate()方法进行View的重新绘制。

在Activity中修改View的显示

public class RectActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_rect);

final RectView rectView= (RectView) findViewById(R.id.rect_id);

rectView.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

//替换背景

rectView.setBitmap(R.mipmap.view_robot);

}

});

}

}

点击前:

点击后:

安卓java其他类改变view_安卓自定义控件(三)实现自定义View相关推荐

  1. Android自定义控件面试题,自定义View面试总结

    本着针对面试,不负责任的态度,写下<面试总结>系列.本系列记录面试过程中各个知识点,而不是入门系列,如果有不懂的自行学习. 自定义View三种方式,组合现有控件,继承现有控件,继承View ...

  2. 2020年安卓各大应用市场份额占比分析,Android自定义View详解

    谷歌Android和苹果iOS作为主流的智能手机操作系统,引领者手机APP的发展潮流.乔布斯的苹果追求完美,围绕iPhone建立了自己的独立王国,AppStore作为官方唯一的下载渠道,越狱渠道越来越 ...

  3. 安卓系统开发要学linux!算法题+JVM+自定义View,实战解析

    开头 笼统来说,中年程序员容易被淘汰的原因其实不外乎三点. 1.输出能力已到顶点.这个人奋斗十来年了,依旧碌碌无为,很明显这人的天花板就这样了,说白了,天赋就这样. 2.适应能力越来越差.年纪大,有家 ...

  4. java 雷达图_Android雷达图(蜘蛛网图),自定义view之雷达图,正五边雷达图,分数图...

    最近业务要做分数雷达图,到网上找了很大,原理都差不多,但是要适用自己的业务,还需要微调.下面是我微调后的效果图 雷达图蜘蛛网.png 原理很简单 1,确定雷达图中心点的坐标 2,用正三角sin,反三角 ...

  5. Java class类文件和类加载器详解以及代码优化

    JVM就是Java虚拟机,它是Java程序运行的载体. 计算机只识别0和1.Java是⾼级语⾔.⾼级语⾔编写的程序要想被计算机执⾏,需要变成⼆进制形式的本地机器码.能直接变成机器码的语义是C++,它的 ...

  6. 安卓自定义View的状态保存与恢复

    安卓自定义View的状态保存与恢复 我们在开发某些安卓应用(如安卓小游戏)时,可能会用到自定义View,这时候往往需要保存自定义View的状态信息,以便在遇到某些情况(如由于系统内存资源紧张被系统杀死 ...

  7. 安卓面试之自定义View流程及原理

    引言 自定义View的流程及知识点比较多,所以直接整理了学的博客内容,多阅读整理学习 1.安卓自定义View流程原理 郭霖大神博客篇: Android LayoutInflater原理分析,带你一步步 ...

  8. Retrofit:类型安全的REST客户端for 安卓Java

    Retrofit:类型安全的REST客户端for 安卓&Java 2014年5月5日 星期一 21:11 官网:  http://square.github.io/retrofit/ GitH ...

  9. 安卓可上下滑动改变数值的折线图(基于hellochart)

    需求 接手的公司的一个项目,有一个需求是折线图可以通过上下滑动改变数值.原先的大佬自己从头写的,也能实现功能.大佬后来也把思路和代码分享出来了.大家有兴趣的可以看一下. 手把手教你写一个可以上下滑动点 ...

  10. 若依APP 一个非常火的java框架做个APP,若依Flutter安卓IOS桌面前端,若依安卓APP、若依苹果IOS APP、若依Linux APP、若依 MAC APP、若依 Windows APP

    在若依官网看了介绍,框架使用了最新技术栈.前后端分离.完全响应式布局.代码生成器......,似乎已经完美..... 若依这个框架在java非常火,后端和前端的技术非常先进,发现缺少一个原生APP,经 ...

最新文章

  1. 微信小程序使用阿里巴巴iconfont字体图标
  2. SAP SD 基础知识之定价中的条件技术(Condition Technique in Pricing)
  3. vue项目中使用echarts实现词云
  4. mysql范围条件_MySQL 索引及优化实战(一)
  5. CTF(Pwn) Rop + ret2libc 题型 常规解法思路 (初级)
  6. docker开启远程访问_Ubuntu安装Jupyter notebook——开启远程访问
  7. 彻底掌握机器学习的6个主流模型,是什么水平?
  8. 判断标签是否出界,重新设置样式
  9. ie8 ajaxSubmit 上传文件提示下载
  10. 【转载保存】webCollector使用教程
  11. POJ 6048 泰国佛塔 【dfs搜索】【暴力大比拼】【北大ACM/ICPC竞赛训练】
  12. python aiohttp百万并发
  13. linux怎样解压bin文件,linux下解压bin文件
  14. 三维空间数据建模——Smart3D的安装
  15. K线技术指标实现详解—MA
  16. 各个编程语言都有哪些「黑点」?
  17. pe结构分析之手工修复导入表
  18. 【案例教程】基于RWEQ模型的土壤风蚀模数估算及其变化归因分析实践技术
  19. 用计算机亩换算成平方,平方换算亩计算器(农村土地面积计算公式)
  20. Sublime Text介绍

热门文章

  1. 线性代数【二】:矩阵的概念与计算
  2. cartographer探秘第四章之代码解析(二) --- 传感器数据处理过程
  3. 基于windows fiber的协程(coroutine)实现
  4. 【视频】超级账本HyperLedger:Fabric源码走读(一):项目构建与代码结构
  5. CSS深入理解之border
  6. PowerDesigner16工具学习笔记-建立CDM
  7. Win10技巧:如何确定电脑是否适用Hyper-V虚拟机?
  8. 粒子场优化(Particle Field Optimization,PFO)
  9. 任意半径中值滤波(扩展至百分比滤波器)O(1)时间复杂度算法的原理、实现及效果。
  10. 魔方机器人之下位机编程----串口接收数据并解析