自定义控件相关

1.自定义控件属性的步骤

res/values目录下建立一个attrs.xml的文件增加对控件的属性的定义
<?xml version="1.0" encoding="utf-8"?>  
    <resources>  
        <declare-styleable name="MyView">  
            <attr name="textColor" format="color" />  
            <attr name="textSize" format="dimension" />  
        </declare-styleable>  
    </resources>

声名命名空间
xmlns:mytogglebtn="http://schemas.android.com/apk/res/com.example.slidebutton"

AttributeSet来完成控件类的构造函数,并在构造函数中将自定义控件类中变量与attrs.xml中的属性连接起来
public MyView(Context context,AttributeSet attrs)    
        {    
            super(context,attrs);    
            mPaint = new Paint();    
 
            TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.MyView);              
            int textColor = a.getColor(R.styleable.MyView_textColor,0XFFFFFFFF);    
            float textSize = a.getDimension(R.styleable.MyView_textSize, 36);    
 
            mPaint.setTextSize(textSize);    
            mPaint.setColor(textColor);    
 
            a.recycle();    
        }  
 
@Override  
protected void onDraw(Canvas canvas)  
 
        {      
            super.onDraw(canvas);    
            //设置填充    
            mPaint.setStyle(Style.FILL);    
 
            //画一个矩形,前俩个是矩形左上角坐标,后面俩个是右下角坐标    
            canvas.drawRect(new Rect(10, 10, 100, 100), mPaint);    
 
            mPaint.setColor(Color.BLUE);    
            //绘制文字    
            canvas.drawText(mString, 10, 110, mPaint);    
        }   

2.LayoutInflater

LayoutInflater  主要是用于加载布局,应用于需要动态添加View的时候
LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的

//获取到LayoutInflater的实例

LayoutInflater layoutInflater = LayoutInflater.from(context);

//加载布局,第一个参数就是要加载的布局id,第二个参数是指给该布局的外部再嵌套一层父布局,如果不需要就直接传null。
View buttonLayout = layoutInflater.inflate(resourceId, root);

//添加到LinearLayout中

mainLayout.addView(buttonLayout);


inflate(layoutId, null)时,布局中的宽高属性的设置无效;
inflate(R.layout.button, root,false)时,布局中的宽高属性有效;
inflate(layoutId, root, true )时,后面不需要执行addView的操作,否则报异常


Android里面的画图分为2D和3D两种: 2D是由Skia 来实现的,3D部分是由OpenGL实现的。
setContentView()方法的内部也是使用LayoutInflater来加载布局的
View通过LayoutParams类告诉其父视图它想要的大小

3.实现view的更新有两组方法


一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用

4.绘制View

一个类继承自View,那么,它也有三个构造方法,有一个参数的构造方法实在代码中new这个自定义控件时被调用;有两个参数的构造方法是在布局中使用这个自定义控件的时候调用,有三个参数的构造方法,实在使用到这个自定义控件的样式时被调用;同样,用到那个就重写那个


每一个视图的绘制过程都必须经历三个最主要的阶段onMeasure()、onLayout()和onDraw()


第一步 onMeasure 决定View的大小

measure函数的作用是为整个View树计算实际的大小, 设置每个View对象的布局大小(“窗口”大小)。
实际对应属性就是View中的mMeasuredHeight(高)和mMeasureWidth(宽)。
方法中参数widthMeasureSpec和heightMeasureSpec, 这两个值分别用于确定视图的宽度和高度的规格和大小。
MeasureSpec的值由specSize和specMode共同组成的, 其中specSize记录的是大小, specMode记录的是规格。
。EXACTLY 表示父视图希望子视图的大小应该是由specSize的值来决定的。子元素将被限定在给定的边界里而忽略它本身大小;
。AT_MOST 表示子视图最多只能是specSize中指定的大小, 开发人员应该尽可能小得去设置这个视图, 并且保证不会超过specSize。
。UNSPECIFIED 表示开发人员可以将视图按照自己的意愿设置成任意的大小, 没有任何限制。这种情况比较少见, 不太会用到。

measureChild()方法来测量相应子视图的大小

视图大小的控制是由父视图、布局文件、以及视图本身共同完成的,父视图会提供给子视图参考的大小,
而开发人员可以在XML文件中指定视图的大小,然后视图本身会对最终的大小进行拍板。

measure过程结束后,视图的大小就已经测量好了,接下来就是layout的过程了。正如其名字所描述的一样,这个方法是用于给视图进行布局的,也就是确定视图的位置。
layout()方法接收四个参数,分别代表着左、上、右、下的坐标,当然这个坐标是相对于当前视图的父视图而言的。

第二步 onLayout 决定View在ViewGroup中的位置

ViewGroup中的onLayout()方法竟然是一个抽象方法,这就意味着所有ViewGroup的子类都必须重写这个方法。

首先getMeasureWidth()方法在measure()过程结束后就可以获取到了,而getWidth()方法要在layout()过程结束后才能获取到。
另外,getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的,
而getWidth()方法中的值则是通过视图右边的坐标减去左边的坐标计算出来的。

第三步 onDraw

measure和layout的过程都结束后,接下来就进入到draw的过程了。


http://blog.csdn.net/guolin_blog/article/details/17357967


getChildMeasureSpec可以设置子View的内外边距。并且记录预定大小。若spec,padding均为0,则子布局为实际大小makeMeasureSpec(size,MeasureSpec.EXACTLY)得到的是sizemakeMeasureSpec(size,MeasureSpec.UNSPECIFIED)得到的是子布局的实际大


http://blog.csdn.net/coderinchina/article/details/51655896 自定义控件实现流式布局


5、layout_width和layout_height


它们其实是用于设置View在布局中的大小的。首先View必须存在于一个布局中,之后如果将layout_width设置成match_parent表示让View的宽度填充满布局,
如果设置成wrap_content表示让View的宽度刚好可以包含其内容,如果设置成具体的数值则View的宽度会变成相应的数值。
这也是为什么这两个属性叫作layout_width和layout_height,而不是width和height。

任何一个Activity中显示的界面其实主要都由两部分组成,标题栏和内容布局。

在setContentView()方法中,Android会自动在布局文件的最外层再嵌套一个FrameLayout,所以layout_width和layout_height属性才会有效果。

6、GestureDetector 手势

GestureDetector(Gesture:手势Detector:识别)
实现手势识别有两种方法,一种实现OnGestureListener接口,另一种是使用SimpleOnGestureListener类。
使用GestureDetectorCompat替换了GestureDetector
OnGestureListener有下面的几个动作:
按下(onDown): 刚刚手指接触到触摸屏的那一刹那,就是触的那一下。
抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作。
长按(onLongPress): 手指按在持续一段时间,并且没有松开。
滚动(onScroll): 手指在触摸屏上滑动。
按住(onShowPress): 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。
抬起(onSingleTapUp):手指离开触摸屏的那一刹那。

7、改变一个View的位置,有好几种方式比如


1)调用View的layout方法,设置View的布局位置
2)修改View的layoutParam参数
3)ParentView调用scrollTo/scrollBy方法改动childView的位置,不会改变一个View的getLeft,getRight,getBottom,getTop的值
4)View类中的offsetLeftAndRight和offsetTopAndBottom,可以改变上面的getLeft,getRight,getBottom,getTop的返回值
5)Scroller的众多方法中,有两个方法最主要,就是startScroll()和computeScrollOffset()

dragHelper.smoothSlideViewTo()
通过此方法可以把父布局中某一个子View移动到指定的左边,移动过程会调用ViewDragHelper.Callback中的
onViewPositionChanged()、onViewReleased()、clampViewPositionVertical等方法。
换句话说该方法就相当于模仿了人为的拖拽子View到特点坐标的过程。
可以通过此方法实现手势惯性的效果(只要手指轻轻滑动一段距离,判断用户确实想把子View滑动到目标坐标,
即使用户中途松手或者未能滑动到目标坐标,也会自动把子View滑动到目标位置)

8、左侧菜单

1、ViewPager 和 左侧菜单 滑动冲突
http://blog.sina.com.cn/s/blog_5d36a42c0101azvr.html
2、左侧菜单ViewDragHelper实现
http://blog.csdn.net/qibin0506/article/details/40986957
3、左侧菜单源码
http://download.csdn.net/detail/xinnian25/9242803

9、圆形进度条


自定义控件,继承View,建立attrs.xml定义属性,构造函数中用TypeArray将自定义控件类中变量与attrs.xml中的属性连接起来,
在onDraw方法中调用canvas.drawCircle画外面的大圆环,canvas.drawText写出百分比,canvas.drawArc画圆弧。然后在Activity中
启动线程循环设置进度

1、XML
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        xmlns:android_custom="http://schemas.android.com/apk/res/com.gl.goao"  
        android:orientation="vertical">  

    <com.zls.goldbao.views.RoundProgressBar  
                        android:id="@+id/tenderdetail_roundProgressBar"  
                        android:layout_width="match_parent"  
                        android:layout_height="80dp"  
                        android:layout_gravity="center"  
                        android_custom:roundColor="#D1D1D1"  
                        android_custom:roundProgressColor="@color/green"  
                        android_custom:textColor="@color/black"  
                        android_custom:roundWidth="10dip"  
                        android_custom:textSize="18sp" />  
2、Activity
    private int progress = 0;  
      
    new Thread(new Runnable() {  
                          
                        @Override  
                        public void run() {  
                            while(progress <= 100){  
                                progress += 3;  
                                  
                                System.out.println(progress);  
                                  
                                mRoundProgressBar1.setProgress(progress);  
                                  
                                  
                                try {  
                                    Thread.sleep(100);  
                                } catch (InterruptedException e) {  
                                    e.printStackTrace();  
                                }  
                            }  
                              
                        }  
                    }).start();  
3、源码下载地址
http://download.csdn.net/detail/xinnian25/9262621

10、图片验证码

先把验证码放入数组中,然后随机取出几个放入StringBuilder,创建Bitmap和Canvas,然后canvas.drawText把验证码写上去,设置大小边距等样式,
canvas.drawLine方法画斜线,返回这个bitmap。在activity里面调用

1、Activity
private Code codeYzm;  
iv.setImageBitmap(codeYzm.getInstance().createBitmap());
codeYzm.getInstance().getCode().toString()

2、源码下载
http://download.csdn.net/detail/xinnian25/9243051

11、Canvas


在onDraw,调用save和restore方法
save:用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。
restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
save和restore要配对使用(restore可以比save少,但不能多),如果restore调用次数比save多,会引发Error。

平移:canvas.translate(200, 100);
旋转90度:canvas.rotate(90);
向x轴移动-100:canvas.translate(-100, 0);
在x轴移动-100的基础上,向y轴移动200-图片高度:canvas.translate(-100, 200-图片高度);
旋转是针对于x和y坐标的(0,0)点的,x和y轴是会旋转的,还有一个旋转方法是canvas.rotate(90,x,y);就是绕x,y点旋转
图片永远显示在canvas中的,图片相对于canvas是没有任何变化的

12、自定义View的常用方法

onFinishInflate() 当View中所有的子控件 均被映射成xml后触发
onMeasure(int, int) 确定所有子元素的大小
onLayout(boolean, int, int, int, int) 当View分配所有的子元素的大小和位置时触发
onSizeChanged(int, int, int, int) 当view的大小发生变化时触发
onDraw(Canvas) view渲染内容的细节
onKeyDown(int, KeyEvent) 有按键按下后触发
onKeyUp(int, KeyEvent) 有按键按下后弹起时触发
onTrackballEvent(MotionEvent) 轨迹球事件
onTouchEvent(MotionEvent) 触屏事件
onFocusChanged(boolean, int, Rect) 当View获取 或失去焦点时触发
onWindowFocusChanged(boolean) 当窗口包含的view获取或失去焦点时触发
onAttachedToWindow() 当view被附着到一个窗口时触发
onDetachedFromWindow() 当view离开附着的窗口时触发,Android123提示该方法和 onAttachedToWindow() 是相反的。
onWindowVisibilityChanged(int) 当窗口中包含的可见的view发生变化时触发

13、getMeasuredHeight()与getHeight的区别

实际上在当屏幕可以包裹内容的时候,他们的值相等,只有当view超出屏幕后,才能看出他们的区别:getMeasuredHeight()是实际View的
大小,与屏幕无关,而getHeight的大小此时则是屏幕的大小。当超出屏幕后, getMeasuredHeight() 等于 getHeight()加上屏幕之外没
有显示的大小

getMeasuredHeight : 表示的是view的实际大小
getHeight: 表示的是view在屏幕上显示的大小

14、某个View组件的宽度和高度等信息


在onCreate方法中知道某个View组件的宽度和高度等信息,而直接
调用View组件的getWidth()、getHeight()、getMeasuredWidth()、getMeasuredHeight()、getTop()、getLeft()等方法
是无法获取到真实值的,只会得到0。这是因为View组件布局要在onResume回调后完成

private void measureView(View child) {
   ViewGroup.LayoutParams p = child.getLayoutParams();
   if (p == null) {
      p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
   }

   int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
   int lpHeight = p.height;
   int childHeightSpec;
   if (lpHeight > 0) {
      childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
   } else {
      childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
   }
   child.measure(childWidthSpec, childHeightSpec);
}

15、事件分发机制

DispatchTouchEvent返回值的含义
重写dispatchTouchEvent的时候,无论你是return true,亦或是return false都会导致child接受不到事件。
return true : 告诉parent,这个事件我消费了。如果这个是down事件,那么我就会作为一个target或者说handle(事件持有者),后续的
move事件或者up事件等,都会直接分发到我这里,不继续往下分发。
return false:告诉parent,这个事件我不需要,那么会交回给parent的onTouchEvnet处理
只有return super.dispatchTouchEvent的时候才会将事件继续往下传递。

android的两大基础控件类型:View和ViewGroup  
View普通控件,没有子布局  
ViewGroup继承自View,表示可以有子控件  
View的dispatchTouchEvent、onTouchEvent  
ViewGroup的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent  
      
事件传递的顺序是先经过onTouch,再传递到onClick  
如果把onTouch方法里的返回值改成true,onClick方法不再执行了!  

onInterceptTouchEvent()
返回true所有子控件将没有机会参与Touch事件,同时把事件传递给当前的控件的onTouchEvent()处理;
返回false,则把事件交给子控件的onInterceptTouchEvent()

onTouchEvent()这个事件是从子控件回传到父控件的,返回false,则向上传递给父控件,返回true,那么touch事件都在这里处理,父控件是处理不了

关于返回值的问题,基本规则很清楚,如果return true,那么表示该方法消费了此次事件,
如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。
       
参考文章:http://blog.csdn.net/yanzi1225627/article/details/22592831  
http://blog.csdn.net/cjc_karen/article/details/51386793 站在大神肩膀上,深入理解 Android事件分发机制

16、上下拉刷新

下拉刷新主要有两种实现方式:
1. 在ListView中添加header和footer,监听ListView的滑动事件,动态设置header/footer的高度,但是这种方式只适用于ListView,RecyclerView。
2. 第二种方式则是继承ViewGroup或其子类,监听事件,通过scroll或Layout的方式移动child。如图(又分两种情况)

Layout时将header放到屏幕外面,target则填充满屏幕。这个也是SwipeRefreshLayout的实现原理(第二种,只下拉header)
header可以有两种显示方式,一种是只下拉header,另外一种则是header和target一起下拉。

对于事件分发一般有两种处理方式
1. 在onIntercept + onTouchEvnet中处理
2. 在dispatchTouchEvent中处理

继承ViewGroup(例如LinearLayout)
,用measure方法重新(ViewGroup.getChildMeasureSpec和MeasureSpec.makeMeasureSpec)获得高宽,
动态添加View(addView),头部的View放在屏幕上方,底部View放在屏幕下方,在onInterceptTouchEvent+ onTouchEvnet中监听手势

http://download.csdn.net/detail/xinnian25/8529457
https://github.com/zarics/ZrcListView
https://github.com/aliouswang/FriendRefreshView
http://blog.csdn.net/u010386612/article/details/51372696   实现支持所有View的通用的下拉刷新控件

http://www.race604.com/flyrefresh/
http://bbs.apkbus.com/article/13846


17、Paint    和   Canvas

Paint paint=new Paint();
paint.setColor(Color.RED);
paint.setAlpha(100);   //取值范围为0~255,数值越小越透明
paint.setStyle(Paint.Style.FILL);  //Style.FILL实心; Style.FILL_AND_STROKE同时实心和空心; Style.STROKE空心;
paint.setStrokeWidth((float) 10.0);  //空心线宽,浮点型数据
paint.setTextSize(20);
paint.setTypeface(Typeface.DEFAULT);//字体样式, Typeface.DEFAULT:默认字体。Typeface.DEFAULT_BOLD:加粗字体。Typeface.MONOSPACE:monospace字体。Typeface.SANS_SERIF:sans字体。Typeface.SERIF:serif字体。
paint.setTextScaleX(2); //字体比例因子,当大于1的时候表示横向拉伸,当小于1的时候表示横向压缩
paint.setARGB(透明度,红色值,绿色值,蓝色值); //设置画笔颜色和透明度
paint.setUnderlineText(true); //设置画笔的下画线
paint.setTextSkewX(1);//倾斜因子,默认为0,正数表示向左倾斜,负数表示向右倾斜
paint.setAntiAlias(true);//设置画笔为无锯齿
setShadowLayer(float radius, float dx, float dy, int shadowColor) 设置阴影
radius : 表示阴影的倾斜度  dx : 水平位移 dy : 垂直位移  shadowColor : 阴影颜色
mPaint.setShadowLayer(10,15,15,Color.RED);//设置阴影

canvas.drawColor(Color.RED); //设置背景颜色

canvas.drawLine(起始端点的X坐标, 起始端点的Y坐标,终止端点的X坐标,终止端点的Y坐标,绘制直线所使用的画笔);
canvas.drawLine(绘制端点的数组,画笔);
canvas.drawLine(绘制直线的端点数组每条直线占用4个数据,跳过的数据个数这些数据将不参与绘制过程,实际参与绘制的数据个数,绘制直线所使用的画笔);

canvas.drawPaint(绘制点的X坐标,绘制点的Y坐标,绘制直线所使用的画笔);
canvas.drawPaint(绘制端点的数组每个端点占用两个数据,绘制时所使用的画笔);
canvas.drawPaint(绘制端点的数组每个端点占用两个数据,跳过的数据个数这些数据将不参与绘制过程,实际参与绘制的数据个数,绘制时所使用的画笔);

Rect r=new Rect();
r.left=50;r.top=50;r.right=450; r.bottom=250;
canvas.drawRect(Rect对象,绘制时所使用的画笔);
canvas.drawRect(RectF对象,绘制时所使用的画笔);  //Rect的参数为int类型,而RectF的参数类型为float类型
canvas.drawRect(矩形的左边位置,矩形的上边位置,矩形的右边位置,矩形的下边位置,绘制时所使用的画笔);
canvas.drawRoundRect(RectF对象,x方向上的圆角半径,y方向上的圆角半径,绘制时所使用的画笔);  //圆角矩形

canvas.drawCircle(圆心的x坐标,圆心的y坐标,圆的半径,绘制时所使用的画笔); //圆形
canvas.drawOval(椭圆外切矩形的RectF对象,绘制时所使用的画笔); //椭圆形

Path path = new Path();                     //Path对象
path.moveTo(50, 100);                           //起始点
path.lineTo(50, 300);                           //连线到下一点
path.lineTo(100, 500);                      //连线到下一点
path.lineTo(400, 500);                      //连线到下一点
path.lineTo(300, 300);                      //连线到下一点
path.lineTo(450, 50);                           //连线到下一点
path.lineTo(200, 200);                      //连线到下一点
canvas.drawPath(包含路径信息的Path对象,绘制时所使用的画笔); //绘制任意多边形

canvas.drawArc(圆弧所在的椭圆对象,圆弧的起始角度,圆弧的角度,是否显示半径连线true表示显示圆弧与圆心的半径连线false表示不显示,绘制时所使用的画笔);  //绘制圆弧

canvas.drawText(字符串内容,显示位置的x坐标,显示位置的y坐标,绘制时所使用的画笔);
canvas.drawText(char字符数组形式,显示的起始字符位置,显示字符的个数,显示位置的x坐标,显示位置的y坐标,绘制时所使用的画笔);
canvas.drawText(字符串内容,显示的起始字符位置,显示的终止字符位置,显示位置的x坐标,显示位置的y坐标,绘制时所使用的画笔);

canvas.drawBitmap(Bitmap对象代表了图像资源,图像显示的左边位置,图像显示的上边位置,绘制时所使用的画笔);

canvas.clipRect(Rect对象用于定义裁剪区的范围);
canvas.clipRect(矩形裁剪区的左边位置可以是浮点型或者整型,矩形裁剪区的上边位置可以是浮点型或者整型,矩形裁剪区的右边位置可以是浮点型或者整型,矩形裁剪区的下边位置可以是浮点型或者整型);

/*
  该方法用于锁定画布,这种方法主要用于锁定画布中的某一个或几个对象,对锁定对象操作的场合。
  使用save方法锁定画布并完成操作之后,需要使用restore方法解除锁定。
 */
save();

/*
该方法用于旋转画布,通过旋转画布,可以将画布上绘制的对象旋转。在使用这个方法的时候,将会把画布上的所有对象都旋转。
为了只对某一个对象进行旋转,则可以通过save方法锁定画布,然后执行旋转操作,最后通过restore方法解锁,此后再绘制其他图形。
 */
rotate(旋转的角度正数为顺时针方向负数为逆时针方向,旋转点的x坐标,旋转点的y坐标);

18、自定义控件包括三种方式:

1.继承控件,即继承谷歌提供的原生控件,在此基础上提供一些原生控件不具备的功能,这种方法不需要自己支持wrap_content,padding等。
2.组合控件:即组合多个原生控件来达到某些单个原生控件原本不具备的功能,这个在日常开发中应该是使用的比较多的,如基本上每个App都存在一个标题栏,而这些标题栏是可以被复用的,此时我们可以将其抽象出来组合为一个控件,这样在需要标题栏的地方直接使用该控件。
3.自绘控件:即直接继承自View类,然后自己重写onDraw方法,某些情况下可能需要重写onMeasure方法。主要用于显示不规则的效果

19、Scroller

Scroller是一个专门用于处理滚动效果的工具类,可能在大多数情况下,我们直接使用Scroller的场景并不多,但是很多大家所熟知的控件在内部都是使用Scroller来实现的,如ViewPager、ListView等

scrollBy()方法是让View相对于当前的位置滚动某段距离,而
scrollTo()方法则是让View相对于初始的位置滚动某段距离

Scroller的基本用法其实还是比较简单的,主要可以分为以下几个步骤:
1. 创建Scroller的实例
2. 调用startScroll()方法来初始化滚动数据并刷新界面
3. 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑

http://blog.csdn.net/guolin_blog/article/details/48719871


20、自定义View的注意事项

1. 让View支持wrap_content
这是因为直接继承View或ViewGroup的控件,如果不在onMeasure中处理wrap_content,那么外界在布局中使用wrap_content时就无法达到预期效果
2. 让View支持padding
直接继承View的控件,如果不再draw方法中处理padding,那么这个属性是无法起作用的。
直接继承ViewGroup的控件需要在onMeasure和onLayout中考虑padding和子元素的margin对其造成的影响,不然将导致pading和子元素的margin失效
3. 不要在View中使用Handler
这是因为View内部本身就提供了post系列方法,完全可以替代Handler的作用。除非你很明确要用Handler来发送消息。
4. View中如果有线程和动画,及时停止
如果有线程和动画需要停止的时候,onDetachedFromWindow就恶意做到。这是因为当包含此View的Activity退出或者当前View被remove时,View的onDetachedFromWindow方法就会被调用。相对的,当包含此View的Activity启动时onAttachedToWindow会被调用。同时,View不可见时,我们也需要停止线程和动画,如果不及时停止,可能会导致内存泄漏。
5. 如果有滑动嵌套时,当然要处理好滑动冲突的问题。

http://blog.csdn.net/jasonpeak/article/details/51729192  自定义View全解


21、源码分析

http://blog.csdn.net/lfdfhl/article/details/51347818    自定义View系列教程02--onMeasure源码详尽分析

http://blog.csdn.net/iispring/article/details/51314039  自定义View、ViewGroup理论基础详解


22、View 的几个构造函数

public CustomView(Context context)
—>Java代码直接new一个CustomView实例的时候,会调用这个只有一个参数的构造函数;
public CustomView(Context context, AttributeSet attrs)
—>在默认的XML布局文件中创建的时候调用这个有两个参数的构造函数。AttributeSet类型的参数负责把XML布局文件中所自定义的属性通过AttributeSet带入到View内;
public CustomView(Context context, AttributeSet attrs, int defStyleAttr)
—>构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或者Activity所用的Theme中的默认Style,且只有在明确调用的时候才会调用
public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
—>该构造函数是在API21的时候才添加上的


23、其它效果

http://blog.csdn.net/u013836857/article/details/70885446  垂直排列文字

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值