Android自定义view学习笔记01
来源:互联网 发布:php分页详解 编辑:程序博客网 时间:2024/06/04 19:01
Android自定义view学习笔记01
昨天看博客的时候看到鸿洋老师的博客里面有关于自定义view的学习教程。一直深感所掌握的东西太少太杂,按照他的Android 自定义View (一)所讲内容,代码实践。根据实际情况稍作修改,并且补充一些在代码过程中知识点,做此笔记。
相关代码
//CustomView01.javapackage mmrx.com.myuserdefinedview.textview;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.util.AttributeSet;import android.util.Log;import android.util.TypedValue;import android.view.View;import java.util.HashSet;import java.util.Random;import java.util.Set;import mmrx.com.myuserdefinedview.R;/** * Created by mmrx on 2015/4/1. */public class CustomView01 extends View{ //文本内容 private String mTitleText; //文本的颜色 private int mTitleColor; //文本大小 private int mTitleTextSize; //文本时候的文字显示范围 private Rect mBound; private Paint mPaint; //在code中会调用这个构造函数 public CustomView01(Context context){ this(context,null); } //在xml中定义会调用这个构造函数 public CustomView01(Context context,AttributeSet set){ this(context,set,R.attr.CustomViewSytle); } //这个构造函数不是系统调用的,是需要显示调用的 //获得自定义的样式属性 public CustomView01(Context context,AttributeSet set,int defStyle){ super(context,set,defStyle); //获得自定义样式的属性 /*set是属性集合,第二个参数attrs是自定义view的属性类型数组,第三个是默认的style 是当前Theme中指向style的一个引用,第四个也是指向一个style的引用,只有第三个参数为0或者不为0但是 Theme中没有为difStyleAttr属性赋值时起作用*/ TypedArray ta = context.obtainStyledAttributes(set, R.styleable.CustomView, defStyle,R.style.CustomizeStyle); //获取属性值有两种方法,一是显示的取用,二是顺序遍历TypedArray数组 /*显示的取用 * String str = ta.getString(R.styleable.CustomView_titleText); * 顺序遍历数组 * */ int n = ta.getIndexCount(); for(int i=0;i<n;i++){ int attr = ta.getIndex(i); switch (attr){ case R.styleable.CustomView_titleText: mTitleText = ta.getString(attr); break; case R.styleable.CustomView_titleColor: //获得颜色,默认为黑色 mTitleColor = ta.getColor(attr, Color.BLACK); break; case R.styleable.CustomView_titleSize: // 默认设置为16sp,TypeValue也可以把sp转化为px mTitleTextSize = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16,getResources().getDisplayMetrics())); break; }//end switch }//end for /*在TypedArray后调用recycle主要是为了缓存。当recycle被调用后 这就说明这个对象从现在可以被重用了。TypedArray 内部持有部分数组 它们缓存在Resources类中的静态字段中,这样就不用每次使用前都需要分配内存*/ ta.recycle(); mPaint = new Paint(); mPaint.setTextSize(mTitleTextSize); mBound = new Rect(); //返回包围整个字符串的最小的一个rect区域 mPaint.getTextBounds(mTitleText,0,mTitleText.length(),mBound); this.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mTitleText = randomText(); /*刷新该界面 * invalidate()是用来刷新View的,必须是在UI线程中进行工作。 * invalidate()得在UI线程中被调动,在工作者线程中可以通过Handler来通知UI线程进行界面更新。 *而postInvalidate()在工作者线程中被调用 * */ postInvalidate(); //当前后Text长度不同时,显示会有些问题,调用该方法来通知parentView,调用该view //的onMeasure重绘 requestLayout(); } }); } 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) {// super.onMeasure(widthMeasureSpec, heightMeasureSpec); //当明确在xml中设置了宽度和高度时候,显示的就是设置的数值,但是当设置为 //wrap_content或者是match_parent时,由于没有重写onMeasure方法,系统帮我们测量的 //就是match_parent的数据 Log.i("----onMeasure----","onMeasure被调用"); //获得在xml中设置的模式 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMod = MeasureSpec.getMode(heightMeasureSpec); //获得具体的高宽数值 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width,height; //当宽度模式是具体的数值的时候,就用具体数值 if(widthMode == MeasureSpec.EXACTLY){ width = widthSize; }else{ //获取包围字符的最小的矩形 mPaint.setTextSize(mTitleTextSize); mPaint.getTextBounds(mTitleText,0,mTitleText.length(),mBound); //获取矩形的宽度 float textWidth = mBound.width(); //将矩形的宽度加上左右padding值,取整 int desired = (int)(getPaddingLeft()+textWidth+getPaddingRight()); width = desired; } //高度设置同理 if(heightMod == MeasureSpec.EXACTLY){ height = heightSize; }else{ //获取包围字符的最小的矩形 mPaint.setTextSize(mTitleTextSize); mPaint.getTextBounds(mTitleText,0,mTitleText.length(),mBound); //获取矩形的宽度 float textHeight = mBound.height(); //将矩形的宽度加上左右padding值,取整 int desired = (int)(getPaddingBottom()+textHeight+getPaddingTop()); height = desired; } //将计算好的高度宽度放入 setMeasuredDimension(width,height); } @Override protected void onDraw(Canvas canvas) { mPaint.setColor(Color.YELLOW); //参数为左上右下的坐标 canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint); //设置字体的颜色 mPaint.setColor(mTitleColor); //绘制文字,第二三个参数是文字绘制的坐标 xy,收到画笔里文字对齐方式的影响 canvas.drawText(mTitleText,getWidth()/2-mBound.width()/2, getHeight()/2+mBound.height()/2,mPaint); }}
xml文件
<!-- activity_main.xml--><!-- xmlns:customview01="http://schemas.android.com/apk/res/mmrx.com.myuserdefinedview.textview" 当是这个的时候,在Android studio下,会报错,大概意思是 在Gradle项目中,在最终的apk里,包名可能会改变,所以不应该使用硬编码的方式来设置nameSpace 应该使用xmlns:customview01="http://schemas.android.com/apk/res-auto",在编译阶段会自动 寻找实际的包--><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:customview01="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <mmrx.com.myuserdefinedview.textview.CustomView01 android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp"/></RelativeLayout>
<!-- attrs.xml--><?xml version="1.0" encoding="utf-8"?><resources> <!-- 自定义属性--> <attr name="titleText" format="string"/> <attr name="titleColor" format="color"/> <attr name="titleSize" format="dimension"/> <attr name="CustomViewSytle" format="reference"/> <declare-styleable name="CustomView"> <attr name="titleText" /> <attr name="titleColor" /> <attr name="titleSize" /> </declare-styleable></resources>
<!-- style.xml--><resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="CustomViewSytle">@style/CustomizeStyle</item> </style> <style name="CustomizeStyle"> <item name="titleText">123456</item> <item name="titleColor">#ff0000</item> <item name="titleSize">14dp</item> </style></resources>
知识点补充
在看到
public TypedArray obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
这个方法,有些不理解这几个参数的意思。来源与Android中自定义样式与View的构造函数中第三个参数defStyle的意义,这篇博文语言很通俗易懂,非常棒!
通过这个函数来获得`TypeArray`,里面包含属性值。 - set是属性值的集合; - attrs是在xml声明的属性值集合,通过`R.styleable.xxxx`来获得,个人理解就是通过这个属性来知道,xml文件中可能会自定义的一些属性的名称、类型情况; - 第三个属性defStyleAttr是一个引用,是在Theme中设置的引用,在代码中就是位于`attrs.xml`文件中的`<attr name="CustomViewSytle" format="reference"/>`这句话。而在文件`style.xml`的`<Theme>`标签中,设置了这个引用的值为`<item name="CustomViewSytle">@style/CustomizeStyle</item>`。如果在当在layout xml中和style中都没有为View指定属性时,会从Theme中这个attribute指向的Style中查找相应的属性值。如果这个参数传入0表示不向Theme中搜索默认值; - 第四个参数,这个也是指向一个Style的资源ID,但是仅在`defStyleAttr`为0或`defStyleAttr`不为0但`<Theme>`中没有为`defStyleAttr`属性赋值时起作用。
在构造函数中,使用完TypedArray为啥要调用
recycle
方法?为什么需要在TypedArray后调用recycle
在实际代码中,我在布局文件中使用自定义控件,设置的显示字符串和点击后变化的字符串数量不一样,导致控件显示方面出现了偏差。而使用
postInvalidate()
方法并不能使得onMeasure()
方法被调用,解决方案是再调用requestLayout()
方法,通知parentView重绘这个viewAndroid笔记:invalidate()和postInvalidate() 的区别及使用
Android view中的requestLayout和invalidate方法
其他补充
首先感谢@Xuyu_ 同学的补充!
由于版本的原因,存在不能向下兼容的问题(下面是我的sdk版本)。
compileSdkVersion 21
minSdkVersion 14
targetSdkVersion 21
问题描述
在资源文件styles.xml
里这句<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
报错,
解决方案:
换成parent="android:Theme.Light"
之类的并把最小版本改大点就好了,解决网址:网址
特别推荐
博文是Android中自定义样式与View的构造函数中第三个参数defStyle的意义
相关博文
Android自定义View学习笔记02
Android自定义View学习笔记03
Android自定义view学习笔记04
另外,源码同步到gitHub
- Android自定义view学习笔记01
- Android自定义view学习笔记01
- 【Android学习笔记】自定义View
- Android自定义view学习笔记
- Android学习笔记-自定义view
- Android总结笔记01:自定义View学习(一)
- android 学习笔记(1) ExpandableListActivity 自定义view
- android学习笔记3:自定义view
- Android自定义view学习笔记02
- Android自定义View学习笔记03
- Android自定义View学习笔记04
- Android自定义view学习笔记02
- Android自定义View学习笔记03
- Android自定义View学习笔记04
- android学习笔记-自定义View的属性
- Android学习笔记-自定义view保存状态
- android自定义view学习笔记1
- 自定义view学习笔记
- 5. 在新版本的 PHP 中使用旧的 PHP 代码
- MITK Renderer和RenderWindow类
- drivers_day03
- 彻底学通string.Format以及IFormattable,IFormatProvider,ICustomFormatter(2)
- struts2文件下载的中文名解决方案
- Android自定义view学习笔记01
- syscall_add
- PHP通过文件存储来实现缓存
- 缩放shader 的编写-shader学习1
- springMVC上传文件的两种方式
- 让Solr返回JSON数据
- 数据挖掘回顾一:分类算法之 kNN 算法
- 对第三方框架AFNetworking网络请求的使用浅析
- 字符串反序操作