自定义View的界面和行为

来源:互联网 发布:各省地方政府债务数据 编辑:程序博客网 时间:2024/05/16 13:58


Vew对象是所有控件和布局对象的基类, 呈现给用户的界面基本上就一棵view树, 要实现自定义的view对象, 可以继承已有的控件对象,如TextView, Button等,在对其中的一些方法进行重写来覆盖原先控件对象的行为和界面,也可以完全从View对象继承,然后重写VIew对象的一些重要方法,如onMeasure(), onDraw(),(这些on-like方法基本是对象对系统中特定事件所做出的反应, 如onTouchEvent,当用户点击一个控件时,系统就会回调该控件的onTouchEvent方法来通知对象用户点击了它,然后我们就可以在这个方法实现一些逻辑来对用户的行为做出反应, 这也是基于事件驱动/面向对象编程的一个特点,每一个对象有自己的属性(对象的描述)和行为(对外部事件做出的反应)), 系统在进行界面绘制的时候就会调用自定义控件的onMeasure和 onDraw等方法来完成界面的显示, 参考ApiDemo中的CustomView的实现,可以看出自定义View的几个步骤:

1> 定义一个继承View的类,这个类可以做为一个Activity中的一个内部类来实现(google推荐的做法), 也可以单独实现一个类,这样可以从xml布局文件中定义自定义的控件,不需要用代码是实例化一个对象。

2>控件重要方法/行为的重写, 有几个必需要实现的方法:

    1.对象的构造函数, 一般要实现两个构造函数, 一个用于代码实例化对象,一个用于从xml布局文件中来构造特定属性的对象,通用的做法是在xml布局文件中定义控件对象,这样方便控件的布局。

    2. onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法, 系统在绘制对象时,首先得确定对象在屏幕上占用多大的范围,因此在这个方法中,必须得确定好控件的尺寸然后通过一个特定的函数接口(setMeasuredDimension(width, height))去通知系统有关该控件的尺寸信息。系统传递进来的两个参数是一个约束条件,控件到底占据多大的尺寸由这两个参数决定, 每一个参数其实一个MeasureSpec对象,该对象包含了Measure's Mode和Size两个属性:

Mode                                           CutsomView's size

UNSPECIFIED            系统对对象的size没进行约束,可以任意设置

EXACTLY                系统对对象的size已经定死了,只能为MeasureSpec对象中指定的size

AT_MOST                系统对对象的最大size进行了约束,即该对象的size不能超过MeasureSpec对象中指定的size

eg:

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(measureWidth(widthMeasureSpec),                measureHeight(heightMeasureSpec));    }    /**     * Determines the width of this view     * @param measureSpec A measureSpec packed into an int     * @return The width of the view, honoring constraints from measureSpec     */    private int measureWidth(int measureSpec) {        int result = 0;        int specMode = MeasureSpec.getMode(measureSpec);   //获取Mode.        int specSize = MeasureSpec.getSize(measureSpec);   //约束的Size.        if (specMode == MeasureSpec.EXACTLY) {            // We were told how big to be            result = specSize;        } else {            // Measure the text            result = (int) mTextPaint.measureText(mText) + getPaddingLeft()                    + getPaddingRight();            if (specMode == MeasureSpec.AT_MOST) {                // Respect AT_MOST value if that was what is called for by measureSpec                result = Math.min(result, specSize);            }        }        return result;    }    /**     * Determines the height of this view     * @param measureSpec A measureSpec packed into an int     * @return The height of the view, honoring constraints from measureSpec     */    private int measureHeight(int measureSpec) {        int result = 0;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        mAscent = (int) mTextPaint.ascent();        if (specMode == MeasureSpec.EXACTLY) {            // We were told how big to be            result = specSize;        } else {            // Measure the text (beware: ascent is a negative number)            result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop()                    + getPaddingBottom();            if (specMode == MeasureSpec.AT_MOST) {                // Respect AT_MOST value if that was what is called for by measureSpec                result = Math.min(result, specSize);            }        }        return result;    }

    3. void onDraw(Canvas canvas) , 确定好控件的尺寸之后,当系统开始绘图时,会调用View对象的onDraw方法,系统会传进来一个Canvas(画布)对象, 这个对象封装了一些draw-like的方法,由于这些方法需要一个Paint对象的参数,因此在自定义View的内部实现, 一般还得有个Paint对象, 这个对象顾名思义,画笔的意思,用于控制绘制过程中参数的设定,如颜色,大小 ,风格等(The Paint class holds the style and color information about how to draw geometries, text and bitmaps.)

Paint对象常用方法

setStrokeWidth(float width) //设置画笔的宽度(一般应用于几何图形)

setTextSize(float textSize) //绘制文字时的宽度

。。。

Canvas对象常用方法

Parameters
ptsArray of points to draw [x0 y0 x1 y1 x2 y2 ...]offsetNumber of values to skip before starting to draw.countThe number of values to process, after skipping offset of them. Since one point uses two values, the number of "points" that are drawn is really (count >> 1).paintThe paint used to draw the points

drawPoint(float x, float y, Paint paint) //画点


drawPoints(float[] pts, int offset, int count, Paint paint) 

drawLine(float startX, float startY, float stopX, float stopY, Paint paint) //画线

drawCircle(float cx, float cy, float radius, Paint paint) //画圆


3> 通过上述的步骤实现了一个自定义控件后, 就可以拿来使用了,一种是通过代码来实现,可以在代码中new一个对象,然后通过Layout对象的addView

方法进行显示,但是这种方法有点复杂,没有xml布局文件那样直观和方便。因此还是用通用的做法来进行View的布局吧。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.android.apis

//绿色的是属性命名空间的名字,红色的是包名,蓝色的。。。
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
  

//自定义控件的定义得加完整的包名和类名  
    <com.example.android.apis.view.LabelView   

           android:background="@drawable/red"

           android:layout_width="match_parent"

            android:layout_height="wrap_content" 

 //app开头的属性为自定义的属性,一般在对象的构造函数中进行解析
           
app:text="Red"/>                   


    
    <com.example.android.apis.view.LabelView
            android:background="@drawable/blue"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" 
            app:text="Blue" app:textSize="20dp"/>
    
    <com.example.android.apis.view.LabelView
            android:background="@drawable/green"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" 
            app:text="Green" app:textColor="#ffffffff" />


</LinearLayout>
自定义属性的定义: 在工程res/values/目录下创建一个attrs.xml文件,在该文件中自定义属性的写法:
 <resources>
 <declare-styleable name="LabelView">        //定义一个属性集
        <attr name="text" format="string" />  //定义一个属性
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
    </declare-styleable>
</resources>
自定义属性的解析: 
    public LabelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initLabelView();
TypedArray a = context.obtainStyledAttributes(attrs,
        R.styleable.LabelView);         //获取属性集合

        CharSequence s = a.getString(R.styleable.LabelView_text);  //获取LabelView对象的text属性值
        if (s != null) {
            setText(s.toString());
        }
// Retrieve the color(s) to be used for this view and apply them.
        // Note, if you only care about supporting a single color, that you
        // can instead call a.getColor() and pass that to setTextColor().
        setTextColor(a.getColor(R.styleable.LabelView_textColor, 0xFF000000));
       int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0);    
//获取LabelView对象的textSize属性值
         if (textSize > 0) {
            setTextSize(textSize);
        }
a.recycle(); //资源回收
    }






	
				
		
原创粉丝点击