定义自定义控件
2016-07-16 08:16
302 查看
[TOC]
安卓的ui元素全部都基于view或者是viewgroup。在一些app中我imenxuyao自定义view来满足我们的需求,这意味着对于现有的view的一些延伸创造view的子类以创造更加复杂的view。
自定义自己的view视图意味着扩展view或者一个存在的子类,然后能够重写view的某些行为例如
- Drawing: 控制市局上view的渲染,通过重写
- Interaction: 控制用户和view的交互方式通过控制
- Measurement: 控制view的内容区域通过重写
- Attributes: 自定义vie的XML的属性,然后使用TypedArray控制相关的行为
- Persistence: 存储或者获得相关的状态以避免失去状态,方法:
下面我们将自定义一个试图展示不同的图形。
1. 建立一个
2. 在布局文件中添加我们定义的view
3. 定义了两个属性
4. 新建
5.提取视图的相关属性赋值给成员变量,使用
6绘制一个形状
整体的代码:
下面我们重写
然后就是对于在主视图中加入按钮点击后,弹出Toast,
安卓的ui元素全部都基于view或者是viewgroup。在一些app中我imenxuyao自定义view来满足我们的需求,这意味着对于现有的view的一些延伸创造view的子类以创造更加复杂的view。
自定义自己的view视图意味着扩展view或者一个存在的子类,然后能够重写view的某些行为例如
onDraw和
onToutchEvent然后在你的活动中使用。
创建完全自定义的组件
自定义组件我们主要着重于5个方面:- Drawing: 控制市局上view的渲染,通过重写
onDraw方法
- Interaction: 控制用户和view的交互方式通过控制
onTouchEvent和手势
- Measurement: 控制view的内容区域通过重写
onMeasure()方法
- Attributes: 自定义vie的XML的属性,然后使用TypedArray控制相关的行为
- Persistence: 存储或者获得相关的状态以避免失去状态,方法:
onSavedInstanceState和
onRestoreInstanceState
下面我们将自定义一个试图展示不同的图形。
定义view类
为了实现一个可切换的图形选择器我们定义一个ShapeSelectorView继承
View视图。
1. 建立一个
ShapeSelectorView类继承View类,并写明构造方法
2. 在布局文件中添加我们定义的view
3. 定义了两个属性
app:shapeColor和
app:displayShapeName
4. 新建
values/attr.xml文件定义视图的两个属性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ShapeSelectorView"> <attr name="shapeColor" format="color" /> <attr name="displayShapeName" format="boolean" /> </declare-styleable> </resources>
5.提取视图的相关属性赋值给成员变量,使用
TypedArray和
obtainStyledAttributes在AttributeSet上
public class ShapeSelectorView extends View { private int shapeColor; private boolean displayShapeName; public ShapeSelectorView(Context context, AttributeSet attrs) { super(context, attrs); setupAttributes(attrs); } private void setupAttributes(AttributeSet attrs) { // Obtain a typed array of attributes TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ShapeSelectorView, 0, 0); // Extract custom attributes into member variables try { shapeColor = a.getColor(R.styleable.ShapeSelectorView_shapeColor, Color.BLACK); displayShapeName = a.getBoolean(R.styleable.ShapeSelectorView_displayShapeName, false); } finally { // TypedArray objects are shared and must be recycled. a.recycle(); } } }
6绘制一个形状
public class ShapeSelectorView extends View { // ... private int shapeWidth = 100; private int shapeHeight = 100; private int textXOffset = 0; private int textYOffset = 30; private Paint paintShape; // ... public ShapeSelectorView(Context context, AttributeSet attrs) { super(context, attrs); setupAttributes(attrs); setupPaint(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawRect(0, 0, shapeWidth, shapeHeight, paintShape); if (displayShapeName) { canvas.drawText("Square", shapeWidth + textXOffset, shapeHeight + textXOffset, paintShape); } } private void setupPaint() { paintShape = new Paint(); paintShape.setStyle(Style.FILL); paintShape.setColor(shapeColor); paintShape.setTextSize(30); } }
整体的代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/tvPrompt" > <com.codepath.shapeselector.ShapeSelectorView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/shapeSelector" app:shapeColor="#7f0000" app:displayShapeName="true"/> </RelativeLayout>
public class ShapeSelectorView extends View { private int shapeColor; private boolean displayShapeName; private int shapeWidth = 100; private int shapeHeight = 100; private int textXOffset = 0; private int textYOffset = 30; private Paint paintShape; public boolean isDisplayShapeName() { return displayShapeName; } //状态改变时需要重新绘制 public void setDisplayShapeName(boolean displayShapeName) { this.displayShapeName = displayShapeName; invalidate();//状态改变时,使view无效? requestLayout();//当布局改变时调用 } public int getShapeColor() { return shapeColor; } public void setShapeColor(int shapeColor) { this.shapeColor = shapeColor; invalidate(); requestLayout(); } //创建一个构造方法,包含父构造方法和属性集合和笔画的设置 public ShapeSelectorView(Context context, AttributeSet attrs) { super(context, attrs); //设置属性 setupAttributes(attrs); setupPaint(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //参数分别为左上右下 canvas.drawRect(0, 0, shapeWidth, shapeHeight, paintShape); if (displayShapeName) { canvas.drawText("Square", textXOffset, shapeHeight + textYOffset, paintShape); } } private void setupPaint() { paintShape = new Paint(); paintShape.setStyle(Paint.Style.FILL); paintShape.setColor(shapeColor); paintShape.setTextSize(30); } //参数为一些属性的集合,可以与xml文件连接起来的属性 private void setupAttributes(AttributeSet attrs) { //获得属性类型数组 TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ShapeSelectorView, 0, 0); try { //提取属性作为成员变量 shapeColor = a.getColor(R.styleable.ShapeSelectorView_shapeColor, Color.BLACK); displayShapeName = a.getBoolean(R.styleable.ShapeSelectorView_displayShapeName, false); } finally { a.recycle();//回收typedArray供以后使用 } } }
计算大小
//测量view和它的内容来确定高度和宽度 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //给名称定义额外的间距 int textPadding = 10; int contentWidth = shapeWidth; //根据测量的最小值和测量规格确定宽度:内容宽度 + 左边距+右边距 int minw = contentWidth + getPaddingLeft() + getPaddingRight(); int w = resolveSizeAndState(minw, widthMeasureSpec, 0); //让view的获得足够的高度 int minh = shapeHeight + getPaddingTop() + getPaddingBottom(); if (displayShapeName) { //如果展示名称的话要加上文字高度 minh += textYOffset + textPadding; } int h = resolveSizeAndState(minh, heightMeasureSpec, 0); //调用此方法确定测量的宽度和高度 //可以使用getMeasuredWidth和getMeasuredHeight获得值 setMeasuredDimension(w, h); }
onMeasure方法决定了基于内容的视图的高度和宽度,要记住计算包括了view的内间距和内容的大小,而且这个方法必须带哦用
setMeasuredDimension。
MeasureSpec包含了父布局对子布局的限制,
resolveSizeAndState通过两方面进行比较返回一个恰当的值。
切换视图
我们想要每次按钮被点击的时候就会使图形被改变,因此需要onTouchEvent方法处理点击事件,每次点击都会改变下标
public class ShapeSelectorView extends View { // ... private String[] shapeValues = { "square", "circle", "triangle" }; private int currentShapeIndex = 0; // Change the currentShapeIndex whenever the shape is clicked @Override public boolean onTouchEvent(MotionEvent event) { boolean result = super.onTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_DOWN) { currentShapeIndex = (++currentShapeIndex) % shapeValues.length; postInvalidate(); return true; } return result; } }
下面我们重写
onDraw方法:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); String shapeSelected = shapeValues[currentShapeIndex]; if (shapeSelected.equals("square")) { canvas.drawRect(0, 0, shapeWidth, shapeHeight, paintShape); textXOffset = 0; } else if (shapeSelected.equals("circle")) { canvas.drawCircle(shapeWidth / 2, shapeHeight / 2, shapeWidth / 2, paintShape); textXOffset = 12; } else if (shapeSelected.equals("triangle")) { canvas.drawPath(getTrianglePath(), paintShape); textXOffset = 0; } if (displayShapeName) { canvas.drawText(shapeSelected, 0 + textXOffset, shapeHeight + textYOffset, paintShape); } } protected Path getTrianglePath() { Point p1 = new Point(0, shapeHeight), p2 = null, p3 = null; p2 = new Point(p1.x + shapeWidth, p1.y); p3 = new Point(p1.x + (shapeWidth / 2), p1.y - shapeHeight);//原点为屏幕左上角 Path path = new Path(); path.moveTo(p1.x, p1.y); path.lineTo(p2.x, p2.y); path.lineTo(p3.x, p3.y); return path; } // ... }
然后就是对于在主视图中加入按钮点击后,弹出Toast,
保存view相应状态
@Override protected Parcelable onSaveInstanceState() { //创建Bundle对象 Bundle bundle = new Bundle(); //存储基本view的状态 bundle.putParcelable("instanceState", super.onSaveInstanceState()); //存储自定义view的状态 bundle.putInt("currentShapeIndex", this.currentShapeIndex); //如果有的话还应该存储其他的状态 return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { //检测我们是否保存了状态 if (state instanceof Bundle) { Bundle bundle = (Bundle) state; this.currentShapeIndex = bundle.getInt("currentShapeIndex"); state = bundle.getParcelable("instanceState"); } super.onRestoreInstanceState(state); }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories