Android 自定义View £神魔★判官ぃ 2022-07-14 06:56 312阅读 0赞 转载请标明出处:http://blog.csdn[.NET][]/lmj623565791/article/details/24252901 很多的[Android][]入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章。先总结下自定义View的步骤: 1、自定义View的属性 2、在View的构造方法中获得我们自定义的属性 \[ 3、重写onMesure \] 4、重写onDraw 我把3用\[\]标出了,所以说3不一定是必须的,当然了大部分情况下还是需要重写的。 1、自定义View的属性,首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。 **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<?****xml** version="1.0" encoding="utf-8"**?>** 2. **<****resources****>** 3. 4. **<****attr** name="titleText" format="string" **/>** 5. **<****attr** name="titleTextColor" format="color" **/>** 6. **<****attr** name="titleTextSize" format="dimension" **/>** 7. 8. **<****declare-styleable** name="CustomTitleView"**>** 9. **<****attr** name="titleText" **/>** 10. **<****attr** name="titleTextColor" **/>** 11. **<****attr** name="titleTextSize" **/>** 12. **</****declare-styleable****>** 13. 14. **</****resources****>** 我们定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型: 一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;不清楚的可以google一把。 然后在布局中声明我们的自定义View **\[objc\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2. xmlns:tools="http://schemas.android.com/tools" 3. xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01" 4. android:layout\_width="match\_parent" 5. android:layout\_height="match\_parent" > 6. 7. <com.example.customview01.view.CustomTitleView 8. android:layout\_width="200dp" 9. android:layout\_height="100dp" 10. custom:titleText="3712" 11. custom:titleTextColor="\#ff0000" 12. custom:titleTextSize="40sp" /> 13. 14. </RelativeLayout> 一定要引入 xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"我们的命名空间,后面的包路径指的是项目的package 2、在View的构造方法中,获得我们的自定义的样式 **\[java\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. /\*\* 2. \* 文本 3. \*/ 4. **private** String mTitleText; 5. /\*\* 6. \* 文本的颜色 7. \*/ 8. **private** **int** mTitleTextColor; 9. /\*\* 10. \* 文本的大小 11. \*/ 12. **private** **int** mTitleTextSize; 13. 14. /\*\* 15. \* 绘制时控制文本绘制的范围 16. \*/ 17. **private** Rect mBound; 18. **private** Paint mPaint; 19. 20. **public** CustomTitleView(Context context, AttributeSet attrs) 21. \{ 22. **this**(context, attrs, 0); 23. \} 24. 25. **public** CustomTitleView(Context context) 26. \{ 27. **this**(context, **null**); 28. \} 29. 30. /\*\* 31. \* 获得我自定义的样式属性 32. \* 33. \* @param context 34. \* @param attrs 35. \* @param defStyle 36. \*/ 37. **public** CustomTitleView(Context context, AttributeSet attrs, **int** defStyle) 38. \{ 39. **super**(context, attrs, defStyle); 40. /\*\* 41. \* 获得我们所定义的自定义样式属性 42. \*/ 43. TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0); 44. **int** n = a.getIndexCount(); 45. **for** (**int** i = 0; i < n; i++) 46. \{ 47. **int** attr = a.getIndex(i); 48. **switch** (attr) 49. \{ 50. **case** R.styleable.CustomTitleView\_titleText: 51. mTitleText = a.getString(attr); 52. **break**; 53. **case** R.styleable.CustomTitleView\_titleTextColor: 54. // 默认颜色设置为黑色 55. mTitleTextColor = a.getColor(attr, Color.BLACK); 56. **break**; 57. **case** R.styleable.CustomTitleView\_titleTextSize: 58. // 默认设置为16sp,TypeValue也可以把sp转化为px 59. mTitleTextSize = a.getDimensionPixelSize(attr, (**int**) TypedValue.applyDimension( 60. TypedValue.COMPLEX\_UNIT\_SP, 16, getResources().getDisplayMetrics())); 61. **break**; 62. 63. \} 64. 65. \} 66. a.recycle(); 67. 68. /\*\* 69. \* 获得绘制文本的宽和高 70. \*/ 71. mPaint = **new** Paint(); 72. mPaint.setTextSize(mTitleTextSize); 73. // mPaint.setColor(mTitleTextColor); 74. mBound = **new** Rect(); 75. mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound); 76. 77. \} 我们重写了3个构造方法,默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造调用我们的三个参数的构造,我们在三个参数的构造中获得自定义属性。 3、我们重写onDraw,onMesure调用系统提供的: **\[java\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. @Override 2. **protected** **void** onMeasure(**int** widthMeasureSpec, **int** heightMeasureSpec) 3. \{ 4. **super**.onMeasure(widthMeasureSpec, heightMeasureSpec); 5. \} 6. 7. @Override 8. **protected** **void** onDraw(Canvas canvas) 9. \{ 10. mPaint.setColor(Color.YELLOW); 11. canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); 12. 13. mPaint.setColor(mTitleTextColor); 14. canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint); 15. \} 此时的效果是: ![Center][] 是不是觉得还不错,基本已经实现了自定义View。但是此时如果我们把布局文件的宽和高写成wrap\_content,会发现效果并不是我们的预期: ![Center 1][] 系统帮我们测量的高度和宽度都是MATCH\_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP\_CONTENT,或者MATCH\_PARENT系统帮我们测量的结果就是MATCH\_PARENT的长度。 所以,当设置了WRAP\_CONTENT时,我们需要自己进行测量,即重写onMesure方法”: 重写之前先了解MeasureSpec的specMode,一共三种类型: EXACTLY:一般是设置了明确的值或者是MATCH\_PARENT AT\_MOST:表示子布局限制在一个最大值内,一般为WARP\_CONTENT UNSPECIFIED:表示子布局想要多大就多大,很少使用 下面是我们重写onMeasure代码: **\[java\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. @Override 2. **protected** **void** onMeasure(**int** widthMeasureSpec, **int** heightMeasureSpec) 3. \{ 4. **int** widthMode = MeasureSpec.getMode(widthMeasureSpec); 5. **int** widthSize = MeasureSpec.getSize(widthMeasureSpec); 6. **int** heightMode = MeasureSpec.getMode(heightMeasureSpec); 7. **int** heightSize = MeasureSpec.getSize(heightMeasureSpec); 8. **int** width; 9. **int** height ; 10. **if** (widthMode == MeasureSpec.EXACTLY) 11. \{ 12. width = widthSize; 13. \} **else** 14. \{ 15. mPaint.setTextSize(mTitleTextSize); 16. mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds); 17. **float** textWidth = mBounds.width(); 18. **int** desired = (**int**) (getPaddingLeft() + textWidth + getPaddingRight()); 19. width = desired; 20. \} 21. 22. **if** (heightMode == MeasureSpec.EXACTLY) 23. \{ 24. height = heightSize; 25. \} **else** 26. \{ 27. mPaint.setTextSize(mTitleTextSize); 28. mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds); 29. **float** textHeight = mBounds.height(); 30. **int** desired = (**int**) (getPaddingTop() + textHeight + getPaddingBottom()); 31. height = desired; 32. \} 33. 34. 35. 36. setMeasuredDimension(width, height); 37. \} 现在我们修改下布局文件: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****RelativeLayout** xmlns:android="http://schemas.android.com/apk/res/android" 2. xmlns:tools="http://schemas.android.com/tools" 3. xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01" 4. android:layout\_width="match\_parent" 5. android:layout\_height="match\_parent" **>** 6. 7. **<****com.example.customview01.view.CustomTitleView** 8. android:layout\_width="wrap\_content" 9. android:layout\_height="wrap\_content" 10. custom:titleText="3712" 11. android:padding="10dp" 12. custom:titleTextColor="\#ff0000" 13. android:layout\_centerInParent="true" 14. custom:titleTextSize="40sp" **/>** 15. 16. **</****RelativeLayout****>** 现在的效果是: ![Center 2][] 完全复合我们的预期,现在我们可以对高度、宽度进行随便的设置了,基本可以满足我们的需求。 当然了,这样下来我们这个自定义View与TextView相比岂不是没什么优势,所有我们觉得给自定义View添加一个事件: 在构造中添加: **\[java\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **this**.setOnClickListener(**new** OnClickListener() 2. \{ 3. 4. @Override 5. **public** **void** onClick(View v) 6. \{ 7. mTitleText = randomText(); 8. postInvalidate(); 9. \} 10. 11. \}); **\[java\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **private** String randomText() 2. \{ 3. Random random = **new** Random(); 4. Set<Integer> set = **new** HashSet<Integer>(); 5. **while** (set.size() < 4) 6. \{ 7. **int** randomInt = random.nextInt(10); 8. set.add(randomInt); 9. \} 10. StringBuffer sb = **new** StringBuffer(); 11. **for** (Integer i : set) 12. \{ 13. sb.append("" + i); 14. \} 15. 16. **return** sb.toString(); 17. \} 下面再来运行: ![20140421151738296][] 我们添加了一个点击事件,每次让它随机生成一个4位的随机数,有兴趣的可以在onDraw中添加一点噪点,然后改写为验证码,是不是感觉很不错。 好了,各位学习的,打酱油的留个言,顶个呗~ # [源码点击此处下载][Link 1] # [.NET]: http://lib.csdn.net/base/dotnet [Android]: http://lib.csdn.net/base/android [view plain]: http://blog.csdn.net/lmj623565791/article/details/24252901/# [CODE]: https://code.csdn.net/assets/CODE_ico.png [CODE_CODE]: https://code.csdn.net/snippets/304331 [ico_fork.svg]: https://code.csdn.net/assets/ico_fork.svg [ico_fork.svg 1]: https://code.csdn.net/snippets/304331/fork [Center]: /images/20220714/54688fdf7f354e54aede0ae8ea83172d.png [Center 1]: /images/20220714/344f48f02a91412f9751c0210e12d3d2.png [Center 2]: /images/20220714/7bee1fa8d4e84a55bd390b81e73e9e07.png [20140421151738296]: /images/20220714/f6dc70f28f2e4a3eac0ac81303d69eb2.png [Link 1]: http://download.csdn.net/detail/lmj623565791/7227061
还没有评论,来说两句吧...