本文章参考了部分Android 自定义View (一)并结合我自己的完善和思考后得出
界面部分一直是我比较薄弱的地方,所以为了填补我的这份弱点,在自我学习的过程中,自定义View的熟悉与熟练,就成了我的必经之路,那么这次我就开始一步一步了解自定义View
那么,自定义View需要做哪些事情呢?
一、继承一个View
这种方式很多人都熟悉,继承Layout,继承系统的组件如TextView等,最基础的就是继承一个View,例如
public class MyView extends View{ public MyView(Context context, AttributeSet attrs) { super(context, attrs); } }这样就有了一个最简单的自定义View,当然这个View现在还不能显示,还需要下面的第二步第三步等步骤才能显示,
但是,现在的这个View是只能显示一片白的,什么都没有的
在我们使用的时候,当然会希望有我们自己要显示的内容,这时候就会需要我们知道几个方法
View框架中大量的on函数基本上都应用到了Template模式(模板模式),掌握这一模式对于理解这些框架大有裨益(找时间去了解一下)
注:View在ViewGroup中被分配的位置大小如何,这由onLayout()决定
参数说明:参数changed表示view有新的尺寸或位置;参数left表示相对于父view的Left位置;参数top表示相对于父view的Top位置;参数right表示相对于父view的Right位置;参数bottom表示相对于父view的Bottom位置
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); }
注:View本身大小多少,这由onMeasure()决定
widthMeasureSpec, heightMeasureSpec这两个参数,这两个参数由ViewGroup中的layout_width,layout_height和padding以及View自身的layout_margin共同决定。权值weight也是尤其需要考虑的因素,有它的存在情况可能会稍微复杂点
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
注:绘制View,onDraw()定义了如何绘制这个View
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); }
二、布局中声明View
就像系统提供的组件一样,我们的自定义组件也要这样
在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:myview="http://schemas.android.com/apk/res/com.example.testview" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.customview01.view.CustomTitleView android:layout_width="200dp" android:layout_height="100dp" myview:titleText="3712" myview:titleTextColor="#ff0000" myview:titleTextSize="40sp" /> </RelativeLayout>
这张图我是从网上直接拷过来做了一下注解
在实际使用中,自定义属性的配置其实用的不算频繁,很多人直接是写在View里面直接写在内部的
三、自定义属性的获取
其实,到了上面两步配置好后,已经可以直接运行显示自定义View了,这一步的话是用于上图中用绿色标注出来的
自定义属性的配置的,写的完善的话,就跟TextView这种组件一样使用了,那么,要类似于这种组件的属性配置的话,需要做哪些事情呢
1、自定义View的属性,首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="titleText" format="string" /> <attr name="titleTextColor" format="color" /> <attr name="titleTextSize" format="dimension" /> <declare-styleable name="CustomTitleView"> <attr name="titleText" /> <attr name="titleTextColor" /> <attr name="titleTextSize" /> </declare-styleable> </resources>我定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:
一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;
然后就是在布局文件中的使用了
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:myview="http://schemas.android.com/apk/res/com.example.testview" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <com.example.testview.MyView android:layout_width="wrap_content" android:layout_height="wrap_content" myview:titleText="我是文字内容" myview:titleTextColor="#ff0000" myview:titleTextSize="40sp" /> </LinearLayout>
在布局文件写好之后,就是在代码中显示了,我这里就直接贴出我的测试代码了
public class MyView extends View{ /** * 文本 */ private String mTitleText; /** * 文本的颜色 */ private int mTitleTextColor; /** * 文本的大小 */ private float mTitleTextSize; /** * 绘制时控制文本绘制的范围 */ private Rect mBound; private Rect mBound2; private Paint mPaint; private Paint mPaint2; public MyView(Context context) { this(context,null); } public MyView(Context context, AttributeSet attrs) { this(context, attrs,0); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initMyViewData(context, attrs); } private void initMyViewData(Context context,AttributeSet attrs){ //这里获取到我们在attrs.xml设置的declare-styleable TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView); //然后通过declare-styleable获取到之前定义在里面的几个属性 mTitleText = array.getString(R.styleable.MyView_titleText); mTitleTextColor = array.getColor(R.styleable.MyView_titleTextColor, Color.BLACK); mTitleTextSize = array.getDimension(R.styleable.MyView_titleTextSize, 36); array.recycle(); //一定要调用,否则这次的设定会对下次的使用造成影响 /** * 获得绘制文本的宽和高 */ mPaint = new Paint(); mPaint2 = new Paint(); mPaint.setTextSize(mTitleTextSize); mPaint2.setTextSize(mTitleTextSize); // mPaint.setColor(mTitleTextColor); mBound = new Rect(); mBound2 = new Rect(); //这句话我自己的理解是在mBound的矩形内,适配mTitleText第一个字符到最后一个字符的范围,这里是我第二和第三个参数填的0和字符串长度所以是第一个字符到最后一个字符的宽度范围 mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound); Log.i("main", "结果:" + mBound.left + ",右边:" + mBound.right + ",上面:" + mBound.top + ",下面:" + mBound.bottom); mPaint2.getTextBounds("我是第二行吗", 0, 6, mBound2); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { // TODO Auto-generated method stub super.onLayout(changed, left, top, right, bottom); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); Log.i("main", "开始画画"); mPaint.setColor(Color.YELLOW); //在画布的x、y、right、bottom范围内画,画笔为mPaint canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); //设置画笔颜色 mPaint.setColor(mTitleTextColor); // canvas.drawText(text, x, y, paint),第一个参数是我们需要绘制的文本,第三个参数是我们的画笔, // 这两个不用多说,主要是第二和第三个参数的含义,这两个参数在不同的情况下的值还是不一样的,x默认是这个字符串的左边在屏幕的位置, // 如果设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心,y是指定这个字符baseline在屏幕上的位置 canvas.drawText(mTitleText, 0, mBound2.height(), mPaint); canvas.drawText("我是第二行吗", getWidth() / 2 - mBound2.width() / 2, getHeight() / 2 + mBound2.height() / 2, mPaint2); } }
那么第一篇就到这里,这里主要是对环境方面的配置以及方法的初步功能识别,具体的细化研究就等以后几篇深入研究了