Android 自定义View(一) 介绍和一个简单TextView显示
来源:互联网 发布:lol网络正常ping高 编辑:程序博客网 时间:2024/05/22 09:00
转载注明出处:http://blog.csdn.net/lb454048898/article/details/50791101
意义
首先知道自定义View的意义,就是Android自身提供的控件已经满足不了我们的需求的时候就会进行继承View来进行自定义View的代码编写,实现需求。
需求
定义需求,是何种,例如 下拉刷新上拉更多的列表 , 看具体展示内容,可继承 GridView 或者 ListView 。
步骤
一般来说,只需要了解最基本的自定义方法即可。
下面介绍几个最普遍的方法:
OnMeasure (测量控件宽高的方法。)
OnDraw (绘制控件内容的方法)
OnLayout (此方法继承ViewGroup用的较多,用于摆放Group里子view的位置)
正题
- 定义自定义字节属性,此属性是在布局文件xml里View的属性。
- 在View的构造方法中获取属性。
- 重写onMeasure方法
- 重写OnDraw方法
这四个步骤是最常见的步骤。
来看一段ImageView的onMeasure方法。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { resolveUri(); int w; int h; // Desired aspect ratio of the view's contents (not including padding) float desiredAspect = 0.0f; // We are allowed to change the view's width boolean resizeWidth = false; // We are allowed to change the view's height boolean resizeHeight = false; final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); if (mDrawable == null) { // If no drawable, its intrinsic size is 0. mDrawableWidth = -1; mDrawableHeight = -1; w = h = 0; } else { w = mDrawableWidth; h = mDrawableHeight; if (w <= 0) w = 1; if (h <= 0) h = 1; // We are supposed to adjust view bounds to match the aspect // ratio of our drawable. See if that is possible. if (mAdjustViewBounds) { resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; desiredAspect = (float) w / (float) h; } } int pleft = mPaddingLeft; int pright = mPaddingRight; int ptop = mPaddingTop; int pbottom = mPaddingBottom; int widthSize; int heightSize; if (resizeWidth || resizeHeight) { /* If we get here, it means we want to resize to match the drawables aspect ratio, and we have the freedom to change at least one dimension. */ // Get the max possible width given our constraints widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec); // Get the max possible height given our constraints heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec); if (desiredAspect != 0.0f) { // See what our actual aspect ratio is float actualAspect = (float)(widthSize - pleft - pright) / (heightSize - ptop - pbottom); if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { boolean done = false; // Try adjusting width to be proportional to height if (resizeWidth) { int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; // Allow the width to outgrow its original estimate if height is fixed. if (!resizeHeight && !mAdjustViewBoundsCompat) { widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); } if (newWidth <= widthSize) { widthSize = newWidth; done = true; } } // Try adjusting height to be proportional to width if (!done && resizeHeight) { int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; // Allow the height to outgrow its original estimate if width is fixed. if (!resizeWidth && !mAdjustViewBoundsCompat) { heightSize = resolveAdjustedSize(newHeight, mMaxHeight, heightMeasureSpec); } if (newHeight <= heightSize) { heightSize = newHeight; } } } } } else { /* We are either don't want to preserve the drawables aspect ratio, or we are not allowed to change view dimensions. Just measure in the normal way. */ w += pleft + pright; h += ptop + pbottom; w = Math.max(w, getSuggestedMinimumWidth()); h = Math.max(h, getSuggestedMinimumHeight()); widthSize = resolveSizeAndState(w, widthMeasureSpec, 0); heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); } setMeasuredDimension(widthSize, heightSize); }
里面两种情况,判断width和height的mode是否为 MeasureSpec.EXACTLY,当width 和 height 其中一个为MeasureSpec.EXACTLY的时候进入情况一也就是if (resizeWidth || resizeHeight)
如果不是的情况下,进入情况2 else
段。最后将得到的width 和 height 值使用 默认方法 setMeasuredDimension
传入值。
自定义属性值
在values 文件夹里的 任一xml的<resources>
里加上
declare-styleable
标签。记得加上属性name
然后就可以在里面定义属于你自己的自定义属性值
example:
<attr name="customsText" format="string" /> <attr name="customsSize" format="dimension" /> <declare-styleable name="FirstView"> <attr name="customsText" /> <attr name="customsColor" format="color" /> <attr name="customsSize" /> </declare-styleable>
关于declare-styleable里的 attr标签里的属性 format可以参考
declare-styleable中format详解 里的内容;
注意事项
关于自定义View在布局xml里的应用
跟你的工具有关,
在布局xml里最外层的控件都会有这个属性值xmlns:android="http://schemas.android.com/apk/res/android"
这个属性值就是获取Android本身自带的xml属性值。xmlns:android
这个android就是属性值开头例如:android:width
等。
正常添加自己定义的属性值都是xmlns:xxxx="http://schemas.android.com/apk/res/包名
如eclipse 如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/viewdemo.cifer.com.myview" android:layout_width="match_parent" android:layout_height="match_parent"> <viewdemo.cifer.com.myview.view.FirstView android:layout_width="wrap_content" android:layout_height="wrap_content" custom:customsText="3712" android:padding="10dp" custom:customsColor="#ff0000" android:layout_centerInParent="true" custom:customsSize="40sp" /></RelativeLayout>
不过在Android Studio里这样写,会导致程序crash。需要将获取自定义属性的地址改成xmlns:custom="http://schemas.android.com/apk/res-auto"
即可,所有的自定义属性都会出来再custom:
里。
example
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <viewdemo.cifer.com.myview.view.FirstView android:layout_width="wrap_content" android:layout_height="wrap_content" custom:customsText="3712" android:padding="10dp" custom:customsColor="#ff0000" android:layout_centerInParent="true" custom:customsSize="40sp" /></RelativeLayout>
构造方法
这里没什么说的,唯一需要注意的是构造方法的前两个的引用不是super
而是this
;
public FirstView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FirstView(Context context) { this(context, null); } public FirstView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FirstView, defStyleAttr, 0);//在这里有两种获取方法,我都列出来,我自己用的第二种,可以方便控制是否有此属性,进行获取.注:构造方法只会调用一次。 int n = typedArray.getIndexCount(); for (int i = 0; i < n; i++) { int attr = typedArray.getIndex(i); switch (attr) { case R.styleable.FirstView_customsText: break; case R.styleable.FirstView_customsColor: // 默认颜色设置为黑色 break; case R.styleable.FirstView_customsSize: // 默认设置为16sp,TypeValue也可以把sp转化为px break; } } try { mTitleText = typedArray.getString(R.styleable.FirstView_customsText); mTitleTextColor = typedArray.getColor(R.styleable.FirstView_customsColor, Color.BLACK); mTitleTextSize = typedArray.getDimensionPixelSize(R.styleable.FirstView_customsSize, (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics())); } catch (Exception e) { e.printStackTrace(); } finally { typedArray.recycle(); } /** * 获得绘制文本的宽和高 */ mPaint = new Paint(); mPaint.setTextSize(mTitleTextSize); mPaint.setColor(mTitleTextColor); mBound = new Rect(); mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound); }
注意一下,在构造方法里获得的属性值是布局文件xml里,可以在View类里写入方法修改如
public void setText(String text){ this.mTitleText = text; postInvalidate();//invalidate();}
关于View类自带方法的 postInvalidate() 和 invalidate()
的区别可以参考android中Invalidate和postInvalidate的区别
重写onMeasure方法
上面亮出了ImageView的onMeasure方法,觉得很复杂,正常来说一般不过几十行代码,就是计算内容的width和height值,与系统默认的width 和 height值,进行比较,根据布局文件xml里width/height
的设置取最小值or最大值。
onMeasure获取到他的MeasureSpec的模式。具体的内容可以查看MeasureSpec介绍 的内容。
看一下简单的重写onMeasure方法
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = 0; int height = 0; /** * 设置宽度 */ int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); switch (specMode) { case MeasureSpec.EXACTLY:// 明确指定了 width = getPaddingLeft() + getPaddingRight() + specSize; break; case MeasureSpec.AT_MOST:// 一般为WARP_CONTENT width = getPaddingLeft() + getPaddingRight() + mBound.width(); break; } /** * 设置高度 */ specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec); switch (specMode) { case MeasureSpec.EXACTLY:// 明确指定了 height = getPaddingTop() + getPaddingBottom() + specSize; break; case MeasureSpec.AT_MOST:// 一般为WARP_CONTENT height = getPaddingTop() + getPaddingBottom() + mBound.height(); break; } setMeasuredDimension(width, height); }
onDraw
这里较为重要,因为界面的呈现效果,和界面美化,动画全在这里做,也是更新最频繁的方法。
简单的onDraw方法
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLACK); canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); mPaint.setColor(mTitleTextColor); canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint); }
更多方法
onFinishInflate() 当View中所有的子控件 均被映射成xml后触发onSizeChanged(int, int, int, int) 改变大小时调用。 参数分别为 width ,height , oldWidth , oldHeightonTrackballEvent(MotionEvent) 轨迹球事件onTouchEvent(MotionEvent) 触屏事件onFocusChanged(boolean, int, android.graphics.Rect) 当View获取 或失去焦点时触发 onWindowFocusChanged(boolean) 当view被附着到一个窗口时触发onDetachedFromWindow() 当view离开附着的窗口时触发,该方法和 onAttachedToWindow() 是相反的。onWindowVisibilityChanged(int) 当窗口中包含的可见的view发生变化时触发以及常见的onKeyDown(int, KeyEvent) onKeyUp(int, KeyEvent)
更多资料参考
Android.View绘制流程 来自 - WPJY
Android View绘制流程及invalidate()等方法分析 来自- qinjuning
Android应用程序窗口(Activity)的测量(Measure)、布局(Layout)和绘制(Draw)过程分析 来自 - 老罗(罗升阳)
- Android 自定义View(一) 介绍和一个简单TextView显示
- Android自定义View入门---自定义一个TextView
- android自定义View(一)之下划线TextView
- Android 自定义View (TextView)
- Android自定义View(一、初体验自定义TextView)
- Android自定义View(一、初体验自定义TextView)
- Android自定义View(一、初体验自定义TextView)
- Android自定义View(一、初体验自定义TextView)
- android自定义view解决textview显示排版的问题
- android 自定义View开发实战(五) TextView滚动显示
- Android自定义View单TextView显示多种文字样式
- Android自定义View 一<最简单的自定义View>
- android 自定义一个简单View总结
- 一个简单的Android自定义view详解
- 一个简单的Android自定义View
- Android 实现一个简单的自定义View
- android自定义TextView(一)
- 自定义view(一)自定义textview
- Python xml ElementTree 增加自动缩进(autoindent) 换行
- SQLSERVER 链接 MYSQL 的 两种方法 及 未发现数据源名称并且未指定默认驱动程序 处理办法
- Redis源码学习(一)内存管理
- JNI数据类型
- 卷积神经网络
- Android 自定义View(一) 介绍和一个简单TextView显示
- 十种常见排序算法
- 2015年大二上-数据结构-查找-1-(1)-线性表的折半查找
- rsync
- java的Switch用法简介
- JQ 加在文字后边没事 但是加在链接文件里边就不出现效果
- 安卓图片加载之使用universalimageloader加载圆形圆角图片
- 错误处理与异常抛出_Swift基础知识学习
- inotify