Android自定义View入门及实战

前言

在Android应用开发过程中,固定的一些控件和属性可能满足不了开发的需求,所以在一些特殊情况下,我们需要自定义控件与属性,同样,这也是面试中面试官问的几率比较高的问题,也是由初级工程师通向中高级工程师必备的。

实现步骤

  1. 继承View类或其子类
  2. 复写view中的一些函数
  3. 为自定义View类增加属性(两种方式)
  4. 绘制控件(导入布局)
  5. 响应用户事件
  6. 定义回调函数(根据自己需求来选择)

哪些方法需要被重写

  • onDraw()
    view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的绘制。对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的(但必须实现dispatchDraw()函数,告诉子view绘制自己)。
  • onLayout()
    主要是为viewGroup类型布局子视图用的,在View中这个函数为空函数。
  • onMeasure()
    用于计算视图大小(即长和宽)的方式,并通过setMeasuredDimension (width, height) 保存计算结果。
  • onTouchEvent()
    定义触屏事件来响应用户操作。

还有一些可以重写的方法

1.onKeyDown() 当按下某个键盘时
2.onKeyUp() 当松开某个键盘时
3.onTrackballEvent() 当发生轨迹球事件时
4.onSizeChange() 当该组件的大小被改变时
5.onFinishInflate() 回调方法,当应用从XML加载该组件并用它构建界面之后调用的方法 6.onWindowFocusChanged(boolean) 当该组件得到、失去焦点时
7.onAttachedToWindow() 当把该组件放入到某个窗口时
8.onDetachedFromWindow() 当把该组件从某个窗口上分离时触发的方法
9.onWindowVisibilityChanged(int) 当包含该组件的窗口的可见性发生改变时触发的方法

View的绘制过程

View的绘制过程

自定义控件的三种方式

1.继承已有的控件 当要实现的控件和已有的控件在很多方面比较类似, 通过对已有控件的扩展来满足要求。
2.继承一个布局文件 一般用于自定义组合控件,在构造函数中通过inflater和addView()方法加载自定义控件的布局文件形成图形界面(不需要onDraw方法)。
3.继承view 通过onDraw方法来绘制出组件界面。

自定义属性的两种方法

  • 在布局文件中直接加入属性,在构造函数中去获得。

布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
     <com.lee.demo.initView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" 
         inittext="@string/hello_world"
         />
</RelativeLayout>

在构造方法中

public initView(Context context, AttributeSet attrs) {        
    super(context, attrs);        
    // TODO Auto-generated constructor stub
        int textId = attrs.getAttributeResourceValue(null, "inittext", 0);
        String text = context.getResources().getText(textId).toString();
}
  • 在res/values/ 下建立一个attrs.xml 来声明自定义view的属性。

可以定义的属性有:

<declare-styleable name = "名称"> 
//参考某一资源ID (name可以随便命名)
<attr name = "background" format = "reference" /> 
//颜色值 
<attr name = "textColor" format = "color" /> 
//布尔值
<attr name = "focusable" format = "boolean" />
 //尺寸值 
<attr name = "layout_width" format = "dimension" /> 
//浮点值 
<attr name = "fromAlpha" format = "float" /> 
//整型值 
<attr name = "frameDuration" format="integer" /> 
//字符串 
<attr name = "text" format = "string" /> 
//百分数 
<attr name = "pivotX" format = "fraction" /> 
//枚举值 
<attr name="orientation"> <enum name="horizontal" value="0" /> 
<enum name="vertical" value="1" /> 
</attr> 
//位或运算 
<attr name="windowSoftInputMode"> 
<flag name = "stateUnspecified" value = "0" /> 
<flag name = "stateUnchanged" value = "1" /> 
</attr> 
//多类型
<attr name = "background" format = "reference|color" /> 
</declare-styleable>
  • attrs.xml进行属性声明
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="myView">
        <attr name="text" format="string"/>
        <attr name="textColor" format="color"/>
    </declare-styleable>
</resources>
  • 添加到布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:myview="http://schemas.android.com/apk/com.example.demo"
    >
     <com.lee.demo.initView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" 
         myview:text = "test"
         myview:textColor ="#ff0000"
         />
</RelativeLayout>
public initView(Context context, AttributeSet attrs) {        
        super(context, attrs);        
        // TODO Auto-generated constructor stub
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView); 
        String text = a.getString(R.styleable.myView_text); 
        int textColor = a.getColor(R.styleable.myView_textColor, Color.WHITE); 
        a.recycle();
    }

或者:

    public initView(Context context, AttributeSet attrs) {        
        super(context, attrs);        
        // TODO Auto-generated constructor stub
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView); 
        int n = a.getIndexCount();        
        for(int i=0;i<n;i++){            
            int attr = a.getIndex(i);            
            switch (attr) {            
            case R.styleable.myView_text:                
                break;            
            case R.styleable.myView_textColor:                
                break;
            }
        }
       a.recycle();
    }

自定义随手指移动的小球来学习自定义View

0.gif

实现上面的效果我们大致需要分成这几步:
1.在res/values/ 下建立一个attrs.xml 来声明自定义view的属性
2.一个继承View并复写部分函数的自定义view的类
3.一个展示自定义view 的容器界面

1.自定义view命名为cicleview,它有一个属性值,格式为color:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="cicleview">
        <attr name="TextColor" format="color"/>
    </declare-styleable>        
</resources>

2.在构造函数获取获得view的属性配置和复写onDraw和onTouchEvent函数实现绘制界面和用户事件响应。

public class cicleview extends View{    
    //定义画笔和初始位置
    Paint p = new Paint();    
    public float currentX = 50;    
    public float currentY = 50;    
    public int textColor;    
    public cicleview(Context context, AttributeSet attrs) {        
        super(context, attrs);        
        //获取资源文件里面的属性,由于这里只有一个属性值,不用遍历数组,直接通过R文件拿出color值
        //把属性放在资源文件里,方便设置和复用
        TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.myView);
        textColor = array.getColor(R.styleable.myView_TextColor,Color.BLACK);
        array.recycle();
    }    
    @Override
    protected void onDraw(Canvas canvas) {        
        super.onDraw(canvas);        
        //画一个蓝色的圆形
        p.setColor(Color.BLUE);
        canvas.drawCircle(currentX,currentY,30,p);        
        //设置文字和颜色,这里的颜色是资源文件values里面的值
        p.setColor(textColor);
        canvas.drawText("BY finch",currentX-30,currentY+50,p);
    }   
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        currentX = event.getX();
        currentY = event.getY();
        invalidate();//重新绘制图形
        return true;
    }
}

这里通过不断的更新当前位置坐标和重新绘制图形实现效果,要注意的是使用TypedArray后一定要记得recycle(). 否则会对下次调用产生影响。

3.把cicleview加入到activity_main.xml布局里面

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:myview="http://schemas.android.com/apk/res-auto"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="finch.scu.cn.myview.MainActivity">

    <com.lee.demo.cicleview
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        myview:TextColor="#ff0000"
        />
</RelativeLayout>

结束!!!

全体起立,奏国歌.行注目礼!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容