android View的相关知识点
来源:互联网 发布:js怎么设置cssimporant 编辑:程序博客网 时间:2024/05/29 04:57
自定义View—–利用Canvas画图
对于android开发者来说,炫酷的界面可以给APP加分。但是由于第三方的UI有时候不符合我们的需求,这时候就需要自己写View,因此对于View绘制的基础我们还是需要掌握。
自定义View构造方法
- View(Context context) 在代码中简单创建View被调用 View view = new View(this)
- View(Context context, AttributeSet attrs) 当xml中布局了自定义View后,在inflate布局时被调用
- View(Context context, AttributeSet attrs, int defStyleAttr) 和第二种构造方法类似,但多了一个defStyleAttr参数,当xml文件中有@style属性时被调用。
- View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)和第三种构造方法类似,但又多了一个参数,当xml文件中有theme属性时被调用
属性值定义的优先级:xml>style>Theme中的默认Sytle>默认Style(通过obtainStyledAttributes的第四个参数指定)>在Theme中直接指定属性值
一般来说,自定义View都是继承自View,而我们通常是在Canvas画布上来绘制图形。获取Canvas主要有两种方式
- 通过重写View.onDraw方法,View中的Canvas对象会被当做参数传递过来,我们操作这个Canvas,效果会直接反应在View中。
- 如下所示,直接创建一个新的Canvas
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(b);
这里主要讲解第一种情况下的自定义View
在画图时,有时候需要知道手机屏幕的大小,下面这种方法是获取屏幕大小的常规方法(API21)
DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics);int width = metrics.widthPixels;int height = metrics.heightPixels;
之前网上还有一些其他的获取屏幕的方法,例如通过canvas来获取等,这里就不一一列举了。但值得注意的是
WindowManager manager = getWindowManager();Display display = manager.getDefaultDisplay();int width = display.getWidth();int height = display.getHeight();
该代码中getWidth()方法和getHeight()方法已经在新的API中被划掉了,也就是不建议这样来获取屏幕尺寸。
下面是我写的一段关于自定义View的测试代码,主要就是在onDraw方法中绘制图形,共参考
package com.example.root.myapplication;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.os.Bundle;import android.support.v7.app.ActionBarActivity;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.view.View;public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);// setContentView(R.layout.activity_main); setContentView(new CustomView_1(this)); } /**inter class**/ class CustomView_1 extends View{ Paint paint ; public CustomView_1(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public CustomView_1(Context context){ super(context); paint = new Paint(); //setting the painting pen paint.setColor(Color.BLUE); paint.setStrokeJoin(Paint.Join.ROUND); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeWidth(1); } @Override protected void onDraw(Canvas canvas){ DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); int width = metrics.widthPixels; int length = metrics.heightPixels; //paint the background to yellow canvas.drawColor(Color.YELLOW); //paint a circle canvas.drawCircle(width/4, width/4, width/4, paint); //avoid area RectF rect = new RectF(width/2,0,width,width/2); //paint a arc canvas.drawArc(rect,0,90,true,paint); rect = new RectF(400,400,500,500); //paint a rect canvas.drawRoundRect(rect,15,15,paint); } }}
运行试试,Done
关于canvas中的移动translate rotate,其实也很好理解,之前用canvas画了一个时钟,在画刻度的时候用到了rotate。当我们用到了这两个方法时,其实相当于改变了作图时的参考坐标(位置和方向)
eg:
canvas.translate(100, 100); %表示画布的(0,0)点向下,向右移动了一百个像素canvas.rotate(30,0,0);%表示将画布以(0,0)点为中心向右旋转30度,之后X,Y轴的方向也跟着转变了
View以及ViewGroup部分源码剖析
View是Android 所有图形界面相关组建、界面、布局等的基类,从官方SDK中可以看到View类直接继承自Object类,而且实现了Drawable.Callback KeyEvent.Callback AccessibilityEventSource等接口。平时用到的ImageView, KeyboardView, MediaRouteButton, ProgressBar, SurfaceView, TextView, ViewGroup等都是直接继承于它,而像AbsListView, AbsSpinner, AbsoluteLayout, AdapterView,等也间接继承于它。
SDK中列出了常常见View需要用到的一些方法
Creation(创建)
- Constructors()
- onFinishInflate()
Layout(布局)
- onMeasure(int, int)
- onLayout(boolean, int, int, int, int)
- onSizeChanged(int, int, int, int)
Drawing(绘图)
- onDraw(Canvas canvas)
Event processing(事件处理)
- onKeyDown(int, KeyEvent)
- onKeyUp(int, KeyEvent)
- onTrackballEvent(MotionEvent)
- onTouchEvent(MotionEvent)
Focus(聚焦)
- onFocusChanged(boolean, int, android.graphics.Rect)
- onWindowFocusChanged(boolean)
Attaching(绑定)
- onAttachedToWindow()
- onDetachedFromWindow()
- onWindowVisibilityChanged(int)
本文重点将下面几个方法
onFinishInflate() 。当View和他的所有子View从XML中解析完成后调用,因此一般用在ViewGroup子类中,用于获取子View的引用,例如
@Override protected void onFinishInflate() { super.onFinishInflate(); int count = getChildCount(); if (count > 0) { for (int i = 0; i < count; ++i) { //getChildAt就是获取子View addHeaderView(getChildAt(i)); } removeAllViews(); } } /** * Returns the view at the specified position in the group. * * @param index the position at which to get the view from * @return the view at the specified position or null if the position * does not exist within the group */ public View getChildAt(int index) { if (index < 0 || index >= mChildrenCount) { return null; } return mChildren[index]; }
onMeasure(int, int)。 测量这个View的高和宽,其实也是设置View的宽和高度。如果子View中没有重写这个方法,那么将会在View对象中调用默认的onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //onMeasure方法实际上实在调用setMeasuredDimension()方法 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } /**final 类型,不能被覆盖**/ protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int opticalWidth = insets.left + insets.right; int opticalHeight = insets.top + insets.bottom; measuredWidth += optical ? opticalWidth : -opticalWidth; measuredHeight += optical ? opticalHeight : -opticalHeight; } setMeasuredDimensionRaw(measuredWidth, measuredHeight); } /**private 类型,不能被子类覆盖*/ private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; } /** * This is called to find out how big a view should be. The parent * supplies constraint information in the width and height parameters. * /**final 类型,不能被覆盖**/用于改变子容器中子View大小 */ public final void measure(int widthMeasureSpec, int heightMeasureSpec) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int oWidth = insets.left + insets.right; int oHeight = insets.top + insets.bottom; widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth); heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight); }
由上面的程序可以看出,onMeasure()方法最终转化成了对mMeasuredWidth、mMeasuredHeight的设置。
而子类的View测量多是通过覆盖onMeasure()方法实现对View测量。
对于像TextView这类View,重写的onMeasure()方法其实质上就是调用了View的setMeasuredDimension方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode =MeasureSpec.getMode(widthMeasureSpec); int heightMode =MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; ... setMeasuredDimension(width, height); }
而对于像ViewGroup一类(实际上是他们的实现类)的容器重写的onMeasure()方法还需调用View类中的measure()方法对容器中的子View进行操作
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Sets up mListPadding super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int childWidth = 0; int childHeight = 0; int childState = 0; ... if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) { final View child = obtainView(0, mIsScrap); //measureScrapChild方法调用了View的measure方法 measureScrapChild(child, 0, widthMeasureSpec); ... } ... setMeasuredDimension(widthSize , heightSize);}
onLayout(boolean, int, int,int, int)。onLayout是用来指定各个子View的位置,这个方法的使用主要在ViewGroup中。这个方法也和上面的方法类似,也只是子类覆盖的一个方法,真正调用的方法是Layout()。
onDraw(android.graphics.Canvas)。onDraw是用来对View进行绘图的,这个方法也和上面的方法类似,也只是子类覆盖的一个方法,真正调用的方法是draw()方法。
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas’ layers to prepare for fading
* 3. Draw view’s content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
关于View值得注意的细节
- Invalidate()和postInvalidate()
Invalidate()只能在主线程(UI线程)中被调用,postInvalidate()可以在子线程(非UI线程)中被调用。下面是源码介绍
/** * Invalidate the whole view. If the view is visible, * {@link #onDraw(android.graphics.Canvas)} will be called at some point in * the future. * <p> * This must be called from a UI thread. To call from a non-UI thread, call * {@link #postInvalidate()}. */ public void invalidate() { invalidate(true); } /** * <p>Cause an invalidate to happen on a subsequent cycle through the event loop. * Use this to invalidate the View from a non-UI thread.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @see #invalidate() * @see #postInvalidateDelayed(long) */ public void postInvalidate() { postInvalidateDelayed(0); }
请注意,在view的内容或者大小改变时,常会调用invalidate() (postInvalidate())和 requestLayout(). 这两个调用是确保稳定运行的关键。当view的某些内容发生变化的时候,需要调用invalidate来通知系统对这个view进行redraw,当某些元素变化会引起组件大小变化时,需要调用requestLayout方法。调用时若忘了这两个方法,将会导致hard-to-find bugs。
本文参考博客http://blog.csdn.net/cwcwj3069/article/details/49867747
- android View的相关知识点
- Android View相关知识点
- Android View滑动相关的基础知识点
- View相关知识点
- Android Edittext相关的知识点
- android view的相关布局
- Android View 处理相见恨晚的知识点
- 【学习笔记】View相关知识点
- android.view.View 相关
- 实习入职第四天:view旋转的相关知识点
- android 自定义View知识点
- android View知识点总结
- Android View基础知识点
- android TextView 相关的知识点 汇总
- android EditText相关的知识点汇总
- Android 实现密码键盘的相关知识点
- Android Binder相关的一些基础知识点.
- Android 软键盘相关的知识点
- this class is not key value coding-compliant for the key countryImageView-bug
- Android基础之Listview的滚动事件的学习
- 贪心&Packets
- android系列学习:tab切换,fragment中嵌套listview,listview自定义item,优化以及onclick
- 缓存页面数据(对象+图片)
- android View的相关知识点
- Ubuntu14.04安装搜狗拼音输入法开机死机
- Anaconda中安装python3.4环境
- 编程基础——第三单元 数组
- [Baby steps giant steps] BSGS EXT-BSGS
- XML与html的区别
- poj3069
- 2015年底Google停止对eclipse的adt更新,转由eclipse团体提供
- 基于区域SURF的图像匹配算法研究