您的位置:首页 > 其它

View的介绍(例子:自定义简单的走马灯)

2016-08-19 09:11 501 查看
一、View结构原理(本段摘自)
View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类。View定义了绘图的基本操作,基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。具体操作如下:
1、measure操作

     measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:

     (1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。

 
2、layout操作

     layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:

     (1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;

     (2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;

 
3、draw操作

     draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:

     (1)绘制背景;

     (2)如果要视图显示渐变框,这里会做一些准备工作;

     (3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;

     (4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;

     (5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;

     (6)绘制滚动条;

      从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。

二、自定义View用到的类

我们在重写onDraw方法的时候需要一般会用到画笔Paint类和绘制矩形区域的Rect类。

1.paint::画笔,可设置画笔的颜色,风格,效果等,如:

mPaint = new Paint();
//绘画,文字颜色和内容mPaint.setColor(mTitleTextColor);canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
2.Rect:绘制一块矩形区域,如:

mBound = new Rect();
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);  //定下文字边界

注意:这块矩形区域,需要用左上右下两个坐标点表示(left,top,right,bottom),你也可以获取一个Rect实例的Width和Height。

右下角(300,500)其实是不在这个矩形里面的,但是左上角(100,50)在,也就是说,这个矩形实际表示的区域是:(100,50,299,499)。

三、View中还有三个比较重要的方法

requestLayout

View重新调用一次layout过程。

invalidate

View重新调用一次draw过程

forceLayout

标识View在下一次重绘,需要重新调用layout过程。

四、Canvas画布
设置好画笔后就是在画布上“作画”了,可以看到onDraw方法有画布的参数,画布的方法有很多,可以画文字,图片,图形等,这里不详细介绍了,最后给个例子吧,先上图片(实际效果是滚动的走马灯,这里只能截图啦!!):



我们只需要继承View来定义我们想要的视图就好啦,上代码(若有不懂看注释):
package com.example.lanzheng;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;

import com.example.lanzheng.wheeldemo.R;

/**
* Created by lan.zheng on 2016/8/19.
*/
public class RainbowWaitingBar extends View {
private int[] mImage = {R.drawable.blue_selected,R.drawable.green_select,R.drawable.yellow_select
,R.drawable.orange_select,R.drawable.pink_select,R.drawable.purple_select};
//图片的Bitmap
Bitmap bitmap;
//压缩后
Bitmap lBitmap;
//资源
Resources lResources;
//绘制范围
private Paint mPaint;
private Matrix matrix;

private float ih = 50;    //默认size
private float iw = 300;   //默认size
private int count = 6;   //itemnum
private int rate = 50;   //默认速率为50毫秒
private int size = 20;   //默认每个Item大小为20
private float oldH;      //原始高度
private float oldW;      //原始宽度
Runnable mRunnable ;

public RainbowWaitingBar(Context context) {
super(context);
}

public RainbowWaitingBar(Context context, AttributeSet attrs) {
this(context, attrs,0);
}

public RainbowWaitingBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获得我们所定义的自定义样式属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RainbowWaitingBar);
//获取自定义的属性数量,根据不的属性来赋值
int n = array.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.RainbowWaitingBar_rate:
// 获取自定义的速率
rate = array.getInteger(R.styleable.RainbowWaitingBar_rate, rate);
break;
case R.styleable.RainbowWaitingBar_size:
// 默认自定义的item大小
size = array.getInteger(R.styleable.RainbowWaitingBar_size, size);
break;
default:
break;
}
}
array.recycle();
//初始化Paint,为绘画做准备
mPaint = new Paint();
matrix = new Matrix();
lResources = getResources();
bitmap = BitmapFactory.decodeResource(lResources,mImage[0]);  //得到原始图片的宽高
oldH = bitmap.getHeight();
oldW = bitmap.getWidth();
ih = size;  //需要的宽高
iw = size*6;
matrix.postScale(size/oldW,size/oldH);  //压缩比例
mRunnable = new Runnable() {
@Override
public void run() {
invalidate(); //重画onDraw
}
};
}

Handler mHandler = new Handler();  //handler控制速率
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.BLACK);  //黑色的画笔,下面为循环操作,每执行一次此函数位移图片
for(int i = 0;i<mImage.length;i++){
int position = (i+count) % 6 ;
bitmap = BitmapFactory.decodeResource(lResources,mImage[position]);
lBitmap = Bitmap.createBitmap(bitmap,0,0,(int)oldW,(int)oldH,matrix,true);  //压缩图片
canvas.drawBitmap(lBitmap,i*size,0,mPaint);
}
count--;  //循环6次
if(count == 0){
count = 6;
}
sleepToInvalidate();  //延时重画
}

private void sleepToInvalidate(){
mHandler.postDelayed(mRunnable,rate);  //用Handler来进行延时UI的重画
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
//设置控件大小
setMeasuredDimension((int)iw , (int)ih);
}
}
使用的时候只需要在xml引用即可:
<com.example.lanzheng.RainbowWaitingBar
android:layout_margin="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/><!--此处可以引入自定的属性-->


是不是觉得好像不能设置大小,和速率很不方便? 都是默认的效果,那我们来自定义一些属性吧,在attrs.xml中加入如下代码:
<declare-styleable name="RainbowWaitingBar">
<attr name="rate" format="integer"/>
<attr name="size" format="integer"/>
</declare-styleable>
RainbowWaitingBar代码不变,因为我已经提前写好了,最后就是使用了,我们只需要引入这两句在布局里即可:
app:size="40"

app:rate="100"

看看改变后的效果吧:整体可变化,且速率可以自己调整,想要其他的属性效果,可以继续往里面加入,这里不再多说啦~



最后说三个问题。
1.多线程的问题:
原来是使用Thread来进行延时重画,发现处理UI相关内容时可能会发现Logcat提示Only
the original thread that created a view hierarchy can touch its views这样的错误,这主要是Android的相关View和控件不是线程安全的,我们必须做独立的处理这点比J2ME麻烦一些,这里Android给 我们提供了很多方法,有关线程的,我们需要了解下J2ME中一些传统的线程创建方法,比如Runnable或直接new Thread(),大家需要了解UI线程、worker线程以及一些概念。用Handler可以很好的处理该问题(主线程或者这里说的原始线程original
thread 一般情况下是UI线程,当然UI线程并不一定是主线程,我们不能长时间的阻塞该应用,在Android平台上可能会产生类似Force close或Wait这样的对话框这里我们成为ANR),通过一个Handler对象可以很好的传递Runnable或Message。

2.Matrix():

Matrix是一个3 x 3的矩阵,他对图片的处理分为四个基本类型:

(1)、Translate————平移变换

(2)、Scale————缩放变换

(3)、Rotate————旋转变换

(4)、Skew————错切变换

上面的例子用的是缩放变换,这里只提醒一点,设置了matrix.postScale(size/oldW,size/oldH);

之后,要再次使用前先matrix.reset(),重置好后再次设置,不这样做的话,截出来的图会是旧的大小。

3.关于Bitmap的Bitmap.createBitmap():

这个方法要注意操作的长和宽,必须要小于等于图片的长和宽,不然将会抛出异常。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: