没想到现在在大学开发APP这么挣钱,作为一名大学生的我为了有一天能够存到足够的钱去留学重操旧业,不过我快一年多没学Android了,没想到忘记得这么快,果然还是太菜了我哈哈哈哈,废话不多说,开始今天得内容。现在开发APP如果没有一个美观得UI已经不能够算得上优秀得APP了。所以重操就业得我打算先从基础开始复习。首先就是自定义View。
(1)继承View类创建自定义View
public class MyView extends View {
public MyView(Contextcontext) {
super(context);
}
public MyView(Contextcontext, @Nullable AttributeSet attrs) {
super(context,attrs);
}
public MyView(Contextcontext, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
这里我们创建了类MyView并实现了其三个构造函数,我们自定义View时最主要的就是实现onMeasure和onDraw,首先onMeasure函数主要用于测量view的宽和高的尺寸,为什么要测量宽高尺寸呢?毕竟我们可以在XML总设置width和height。这是一个非常复杂的问题,这里我会按照自己的理解来归纳一遍。
@Override
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width =MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(widthMeasureSpec );
int height = MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
这是onMeasure的一个一般的实现,我们来看看这个onMeasure,这个函数在什么时候会被调用呢?答案是ViewGroup给 子View分配空间的时候。相当于ViewGroup询问子View需要多大的空间,这时ViewGroup会调用子view的onMeasure方法并传入widthMeasureSpec和heightMeasureSpec两个参数。这两个参数中包含的mode是关键所在,我们看看下面代码
int width =MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(widthMeasureSpec);
这里的意思是调用MeasureSpec.getMode()方法获得widthMeasureSpec的模式(mode),如果等于UNSPECIFIED(即是未定义),就将width设置为100,否则就将其保留原widthMeasureSpec中的size。这里的否则是其他两种情况,就是MeasureSpec.AT_MOST(最大)和MeasureSpec.EXACTLY(固定值)。
那怎么确定三种mode的选择呢?
当父控件对子控件不加任何束缚,子元素可以得到任意想要的大小时,mode为UNSPECIFIED,这里是指size未指定,这种情况很少,只有当ViewGroup在height或者width上具有无限延伸的空间并且子View的width或height为wrap_content或者match_parent时才会出现,例如我们使用ScrollView时,无论子view的height设置为多大,都能滚动显示,这种时候就会出现传入的widthMeasureSpec和heightMeasureSpec的mode为UNSPECIFIED的情况。
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.drw.myapplication.MainActivity">
<com.drw.myapplication.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000"/>
</ScrollView>
这里的MyView的onMeasure就是上面我写的一般实现,这里的ScrollView的height和width能无限延伸,我们使用MyView并设置width和height为wrap_content时就会触发
int width =MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(widthMeasureSpec);
int height =MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(heightMeasureSpec);
使height和width变为100,最终运行结果会是下面左图,如果将ScrollView改为其他不能延伸的ViewGroup如linearLayout时运行结果就会如下右图
当ViewGroup已经知道为子View有个确定值时,例如子View的width和height为固定值,或子View的height(width)为match_parent并且ViewGroup的height(width)不具有无限延伸属性时,mode为EXACTLY,如下代码
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.drw.myapplication.MainActivity">
<com.drw.myapplication.MyView
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#000" />
</LinearLayout>
当ViewGroup为子View指定最大参考尺寸,希望子View的尺寸不要超过这个尺寸时mode为AT_MOST,一般是子View的布局参数采用wrap_content的时候。而ViewGroup对应的布局参数上不具有延伸属性。如下代码
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.drw.myapplication.MainActivity">
<com.drw.myapplication.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000" />
</LinearLayout>
这时LinearLayout会把剩余最大空间作为传入参数的size,在本例中时全屏,运行结果如下
好了,到这里就把onMeasure的基本知识点讲完了,现在我们来看看onDraw函数,在onMeasure中我们已经知道了自定义View的尺寸了,那么onDraw自然而然就是我们要实现的视觉效果了,例如是什么形状,什么颜色等等。onDraw函数传入参数是一个canvas即画布,显然就是在这个画布上画出我们要实现的效果。这里我们画园
@Override
protected voidonDraw(Canvas canvas) {
super.onDraw(canvas);
int radius = getMeasuredWidth() / 2;
int centerX = getLeft() + radius;//得到圆形的圆心x坐标
//每个view都可以通过调用getLeft(),getRight()得到View的左边的坐标和view的右边的坐标,同理getTop和getBottom也一样
int centerY = getTop() + radius;//得到圆形的圆心x坐标
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawCircle(centerX, centerY, radius, paint);
}
运行结果如下,width和height设置为100dp
下面po上全代码
<?xml version="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.drw.myapplication.MainActivity">
<com.drw.myapplication.MyView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#000"/>
</LinearLayout>
package *************;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by DRW on2018/2/8.
*/
public class MyView extends View {
public MyView(Contextcontext) {
super(context);
}
public MyView(Contextcontext, @Nullable AttributeSet attrs) {
super(context,attrs);
}
public MyView(Contextcontext, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context,attrs, defStyleAttr);
}
@Override
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width =MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(widthMeasureSpec);
int height =MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.UNSPECIFIED?100:MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
protected voidonDraw(Canvas canvas) {
super.onDraw(canvas);
int radius =getMeasuredWidth() / 2;
int centerX =getLeft() + radius;
int centerY =getTop() + radius;
Paint paint = newPaint();
paint.setColor(Color.RED);
canvas.drawCircle(centerX,centerY, radius, paint);
}
}
我是菜鸟,多多指教(DRW)