文档章节

构建自定义组件

robertchao
 robertchao
发布于 2016/05/04 10:44
字数 6654
阅读 2
收藏 0


Android中,你的应用程序程序与View类组件有着一种固定的联系,例如按钮(Button)文本框(TextView),可编辑文本框(EditText),列表框(ListView),复选框(CheckBox),单选框(RadioButton),滚动条(Gallery),微调器(Spinner), 等等,还有一些比较先进的有着特殊用途的View组件,例如AutoCompleteTextViewImageSwitcher和 TextSwitcher。除此之外,种类繁多的像 线性布局(LinearLayout)框架布局(FrameLayout), 这样的布局组件(Layout)也被认为是View组件,他们是从View类派生过来的。

你的应用程序就是这些控制组件和布局组件以某种方式结合显示在屏幕上,一般来说这些组件对你来说基本够用,但是你也应该知道你是可以通过类继承创建属于自己的组件,一般可以继承像View、Layouts(布局组件)这样的组件,甚至可以是一些比较高级的控制类组件。下面我们说一下为什么要继承:

·         你可以为实现某种功能创建一个完全自定义风格的组件,例如用二维的图形创建控制组件实现声音的控制,就像电子控制一样。

·         你可以把几种组件结合形成一个新的组件,你的组件可能同时包含ComboBox(一个能输入的文本列表)和dual-pane selector control(左右两个List窗口,你可以分配窗口每一项的从属关系)等等。

·         你可以创建自己的布局组件(Layout)。SDK中的布局组件已经提供了一系列的选项让你打造属于自己的应用程序,但是高级的开发人员会发现根据现有的Layout组件开发新的Layout组件是很有必要的,甚至是完全从底层开发新的组件。

·         你可以覆盖一个现有组件的显示或功能。例如,改变EditText(可编辑文本)组件在屏幕上的显示方式(可以参考Notepad的例子,里面教你如何创建一个下划线的显示页面)。

·         你可以捕获像按键按下这样的事件,以一些通用的方法来处理这些事件(一个游戏的例子)。

为了实现某种目标你可能很有必要扩展一个已经存在的View组件,下面我们结合一些例子教你如何去做。

内容:

基本方法(The Basic Approach )

完全自定义组件(Fully Customized Components )

定制组件的例子(Customized Component Example )

组件的混合(或者控制类的混合) (Compound Components (or Compound Controls) )

修改现有组件(Tweaking an Existing Component )

小结(Go Forth and Componentize )

基本方法(The Basic Approach )

下面的一些步骤都比较概括,教你如何创建自己的组件:

1.      让你的类(Class)继承一个现有的View 类或View的子类。

2.      重载父类的一些方法:需要重载的父类方法一般以‘on’开头,如onDraw(),onMeasure()和 onKeyDown()等等。

o               这个在Activity 或则ListActivity 派生中同样适用,你需要重载一些生命周期函数和一些其他功能性的HOOK函数。

3.      使用你的继承类:一旦你的继承类创建完成,你可以在基类能够使用的地方使用你的继承类,但完成功能就是你自己编写的了。

继承类能够定义在activities里面,这样你能够方便的调用,但是这并不是必要的(或许在你的应用程序中你希望创建一个所有人都可以使用的组件)。

完全自定义组件(Fully Customized Components)

完全自定义组件的方法可以创建一些用于显示的图形组件(graphical components),也许是一个像电压表的图形计量器,或者想卡拉OK里面显示歌词的小球随着音乐滚动。无论那种方式,你也不能单纯的利用组件的结合完成,无论你怎么结合这些现有的组件。

幸运的是,你可以以你自己的要求轻松地创建完全属于自己的组件,你会发现不够用的只是你的想象力、屏幕的尺寸和处理器的性能(记住你的应用程序最后只会在那些性能低于桌面电脑的平台上面运行)。

下面简单介绍如何打造完全自定义的组件:

1.      最为通用的VIEW类的父类毫无疑问是View类,因此,最开始你要创建一个基于此类的一个子类。

2.      你可以写一个构造函数从XML文件中提取属性和参数,当然你也可以自己定义这些属性和参数(也许是图形计量器的颜色和尺寸,或者是指针的宽度和幅度等等)

3.      你可能有必要写自己的事件监听器,属性的访问和修改函数和一些组件本身的功能上的代码。

4.      如果你希望组件能够显示什么东西,你很有可能会重载 onMeasure() 函数,因而你就不得不重载 onDraw() 函数。当两个函数都用默认的,那么 onDraw()函数将不会做任何事情,并且默认的 onMeasure() 函数自动的设置了一个100x100 —的尺寸,这个尺寸可能并不是你想要的。

5.      其他有必要重载的on... 系列函数都需要重新写一次。

onDraw()和onMeasure()

onDraw()函数将会传给你一个 Canvas 对象,通过它你可以在二维图形上做任何事情,包括其他的一些标准和通用的组件、文本的格式,任何你可以想到的东西都可以通过它实现。

注意: 这里不包括三维图形如果你想使用三维的图形,你应该把你的父类由View改为SurfaceView类,并且用一个单独的线程。可以参考GLSurfaceViewActivity 的例子。

onMeasure() 函数有点棘手,因为这个函数是体现组件和容器交互的关键部分,onMeasure()应该重载,让它能够有效而准确的表现它所包含部分的测量值。这就有点复杂了,因为我们不但要考虑父类的限制(通过onMeasure()传过来的),同时我们应该知道一旦测量宽度和高度出来后,就要立即调用setMeasuredDimension() 方法。

概括的来讲,执行onMeasure()函数分为一下几个阶段:

1.      重载的onMeasure()方法会被调用,高度和宽度参数同时也会涉及到(widthMeasureSpec 和heighMeasureSpec两个参数都是整数类型),同时你应该考虑你产品的尺寸限制。这里详细的内容可以参考View.onMeasure(int, int) (这个连接内容详细的解释了整个measurement操作)。

2.      你的组件要通过onMeasure()计算得到必要的measurement长度和宽度从而来显示你的组件,它应该与规格保持一致,尽管它可以实现一些规格以外的功能(在这个例子里,父类能够选择做什么,包括剪切、滑动、提交异常或者用不同的参数又一次调用onMeasure()函数)。

3.      一旦高度和宽度计算出来之后,必须调用setMeasuredDimension(int width, int height),否则就会导致异常。

一个自定义组件的例子(A Customized Component Example)

在 API Demos 中的CustomView提供了以一个自定义组件的例子,这个自定义组件在 LabelView 类中定义。

LabelView例子涉及到了自定义组件的方方面面:

·         首先让自定义组件从View类中派生出来。

·         编写带参数的构造函数(参数可以来源于XML文件)。这里面的一些处理都已经在View父类中完成,但是任然有些Labelview使用的自定义组件特有的新的参数需要处理。

·         一些标准的Public函数,例如setText(), setTextSize(), setTextColor()

·         重载onMeasure()方法来确定组件的尺寸(注意:在LabelView中是通过一个私有函数measureWidth()来实现的)

·         重载onDraw()函数把Lable显示在提供的canvas上。

在例子中,你可以通过custom_view_1.xml看到自定义组件LabelView的用法。在XML文件中特别要注意的是android:和app:两个参数的混合运用,app:参数表示应用程序中被认为是LabelView组件的个体,这些也会作为资源在R类中定义。

组件混合技术Compound Components (or Compound Controls)

如果你不想创建一个完全自定义的组件,而是由几个现有组件的组合产生的新的组件,那么混合组件技术就更加适合。简单的来说,这样把几个现有的组件融合到一个逻辑组合里面可以封装成一个新的组件。例如,一个Combo Box组件可以看作是是一个EditText和一个带有弹出列表的Button组件的混合体。如果你点击按钮为列表选择一项,

在Android中,其实还有其他的两个View类可以做到类似的效果: SpinnerAutoCompleteTextView,,但是Combo Box作为一个例子更容易让人理解。

下面简单的介绍如何创建组合组件:

1.      一般从Layout类开始,创建一个Layout类的派生类。也许在Combo box我们会选择水平方向的LinearLayout作为父类。记住,其他的Layout类是可以嵌套到里面的,因此混合组件可以是任何组件的混合。注意,正如Activity一样,你既可以使用外部XML文件来声明你的组件,也可以嵌套在代码中。

2.      在新的混合组件的构造函数中,首先,调用所有的父类的构造函数,传入对应的参数。然后可以设置你的混合组件的其他的一些方面,在哪创建EditText组件,又在哪创建PopupList组件。注意:你同时也可以在XML文件中引入一些自己的属性和参数,这些属性和参数也可以被你的混合组件所使用。

3.      你也可以创建时间监听器去监听新组件中View类触发的事件,例如,对List选项单击事件的监听,你必须在此时间发生后更新你EditText的值。

4.      你可能创建自己的一些属性,带有访问和修改方法。例如,允许设置EditText初始值并且提供访问它的方法。

5.      在Layout的派生类中,你没有必要去重载onDraw()和onMeasure()方法,因为Layout会有比较好的默认处理。但是,如果你觉得有必要你也可以重载它。

6.      你也可能重载一些on系列函数,例如通过onKeyDown()的重载,你可以通过按某个键去选择列表中的对应的值。

总之,把Layout类作为基类有下面几个优点:

·         正如activity一样,你也可以通过XML文件去声明你的新组件,或者你也可以在代码中嵌套。

·         onDraw()函数和onMeasure()函数是没有必要重载的,两个函数已经做得很好了。

·         你可以很快的创建你的混合组件,并且可以像单一组件那样使用。

混合组件的例子(Examples of Compound Controls)

In theAPI Demos project 在API Demos工程中,有两个List类的例子——Example 4和Example6,里面的SpeechView组件是从LinearLayout类派生过来,实现显示演讲显示功能,对应的原代码是List4.java和List6.java。

调整现有组件(Tweaking an Existing Component)

在某些情况下,你可能有更简单的方法去创建你的组件。如果你应经有了一个非常类似的组件,你所要做的只是简单的从这个组件派生出你的组件,重在其中一些有必要修改的方法。通过完全自定义组件的方法你也可以同样的实现,但通过冲View派生产生新的组件,你可以简单获取一些已经存在的处理机制,这些很可能是你所想要的,而没有必要从头开始。

例如,在SDK中有一个NotePad的例子(NotePad application )。该例子演示了很多Android平台实用的细节,例如你会学到从EditView派生出能够自动换行的记事本。这还不是一个完美的例子,因为相比早期的版本来说,这些API已经感变了很多,但它确实说明了一些问题。

如果你还未查看该程序,现在你就可以在Eclipse中导入记事本例程(或仅通过提供的链接查看相应的源代码)。特别是查看NoteEditor.java 中的MyEditText的定义。

下面有几点要注意的地方:

1.      声明(The Definition)

这个类是通过下面一行代码来定义的:

publicstatic class MyEditText extends EditText

o               它是定义在NoteEditor activity类里面的,但是它是共有的(public),因此如果有必要,它可以通过NoteEditor.MyEditText从NoteEditor外面来调用。

o               它是static类(静态类),意味着不会出现所谓的通过父类访问数据的“虚态方法”, 这样就使该类成为一个可以不严重依赖NoteEditor的单独类。对于不需要从外部类访问的内联类的创建,这是一个很清晰地思路,保证所产生的类很小,并且允许它可以被其他的类方便的调用。

o               它是EditText类的扩展,它是我们选择的用来自定义的父类。当我们完成以后,新的类就可以作为一个普通的EditText来使用。

2.      类的初始化

一般来说,父类是首先调用的。进一步来说,这不是一个默认的构造函数,而是一个带参数的构造函数。因为EditText是使用从XML布局文件提取出来的参数进行创建,因此我们的构造函数也要取出参数并且将这些参数传递给父类。

3.      方法重载

在本例中,仅对onDraw()一个方法进行重载。但你可以很容易地为你的定制组件重载其他需要的方法。

对于记事本例子来说,通过重载onDraw()方法我们可以在EidtView的画布(canvas)上绘制蓝色的线条(canvas类是通过重写的onDraw()方法传递)。该函数快要结束时要调用super.onDraw()函数。父类的方法是应该调用,但是在这个例子里面,我们是在我们划好了蓝线之后调用的。

4.      使用定制组件

现在,我们已经有自己定制的组件了,但是应该怎样使用它呢?在记事本例子中,定制的组件直接在预定义的布局文件中使用,让我们看一看res/layout目录中的note_editor.xml文件。

<view xmlns:android="http://schemas.android.com/apk/res/android"

 class="com.android.notepad.NoteEditor$MyEditText"

  id="@+id/note"

 android:layout_width="fill_parent"

 android:layout_height="fill_parent"

 android:background="@android :drawable/empty"

  android:padding="10dip"

  android:scrollbars="vertical"

 android:fadingEdge="vertical" />

o               该自定义组件在XML中是作为一个一般的View类来创建的,并且是通过全路径包来描述的。注意这里内联类是通过NoteEditor$MyEditText来表示的,这是Java编程中引用内联类的标准方法。

o               在定义中的其他属性和参数将传递给定制组件的构造函数,然后才传到EditText构造函数中,因此这些参数也是你使用EditText组件的参数。注意,这里你也可以增加你自己的参数,我们将在下面讨论这个问题。

这就是你全部需要做的,诚然这是一个简单的例子。但问题的关键是:你的需求有多复杂,那么你的自定义组件就有多么复杂。

一个更为复杂的组件可能需要重载更多的on系列函数,并且还要很多特有的函数来充分实现自定义组件的功能。唯一的限制就是你的想象力和你需要组件去执行什么工作。

现在开始你的组件化之旅吧

如你所见,Android提供了一种精巧而又强大的组件模型,让你尽可能的完成你的工作。从简单的组件调整到组件混合,甚至完全自定义组件,灵活的运用这些技术,你应该可以得到一个完全符合你外观要求的的Android程序

不多说,主要是Canvas, Paint, Path


[java]  view plain copy
  1. public class CustomView1 extends View {  
  2.   
  3.     private Paint mPaint;  
  4.     // private static final String mText = "drawText";  
  5.     private String mText = "drawText";  
  6.   
  7.     public CustomView1(Context context, AttributeSet attrs) {  
  8.         super(context, attrs);  
  9.     }  
  10.   
  11.     @Override  
  12.     protected void onDraw(Canvas canvas) {  
  13.         super.onDraw(canvas);  
  14.         mPaint = new Paint();  
  15.         mPaint.setColor(Color.BLUE);  
  16.         // FILL填充, STROKE描边,FILL_AND_STROKE填充和描边  
  17.         mPaint.setStyle(Style.FILL);  
  18.         canvas.drawRect(new Rect(1010100100), mPaint);// 画一个矩形  
  19.   
  20.         mPaint.setColor(Color.GREEN);  
  21.         mPaint.setTextSize(35.0f);  
  22.         canvas.drawText(mText, 1060, mPaint);  
  23.     }  
  24.   
  25. }  



布局


[java]  view plain copy
  1. <pre name="code" class="java"><?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7. <ethan.customview1.CustomView1    
  8.     android:layout_width="wrap_content"   
  9.     android:layout_height="wrap_content"   
  10.     />  
  11. </LinearLayout></pre><pre name="code" class="java"></pre>  
分类: Android 2012-03-03 14:44  3329人阅读  评论(6)  收藏  举报

书接上回 

在xml里建立属性,然后java代码里用typedArray获得这些属性,得到属性后,利用属性做一些事.例:得到xml里的color,赋给paint.

1.在res/values/下新建attrs.xml


[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <declare-styleable name="CustomView2">  
  4.         <attr name="textColor" format="color" />  
  5.         <attr name="textSize" format="dimension" />  
  6.     </declare-styleable>  
  7. </resources>  
  8. <!-- name="CustomView1"控件名称 得到TypedArray时用 -->  
  9. <!-- name="textColor" 对应test:textColor -->  
  10. <!-- format="color" 对应构造方法里a.getColor(R.styleable.CustomView2_textColor, 0xFFFFFFFF); -->  


format详解可参照 http://blog.csdn.net/ethan_xue/article/details/7315064
2.主要看构造函数



[java]  view plain copy
  1. public class CustomView2 extends View {  
  2.   
  3.     private Paint mPaint2;  
  4.     private String mText = "drawText";  
  5.   
  6.     public CustomView2(Context context, AttributeSet attrs) {  
  7.         super(context, attrs);  
  8.         mPaint2 = new Paint();  
  9.         // TypedArray是存放资源的array,1.通过上下文得到这个数组,attrs是构造函数传进来的,对应attrs.xml  
  10.         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView2);  
  11.         // 获得xml里定义的属性,格式为 名称_属性名 后面是默认值  
  12.         int textColor = a.getColor(R.styleable.CustomView2_textColor, 0xFFFFFFFF);  
  13.         float textSize = a.getDimension(R.styleable.CustomView2_textSize, 35);  
  14.         mPaint2.setColor(textColor);  
  15.         mPaint2.setTextSize(textSize);  
  16.         // 为了保持以后使用该属性一致性,返回一个绑定资源结束的信号给资源  
  17.         a.recycle();  
  18.     }  
  19.   
  20.     @Override  
  21.     protected void onDraw(Canvas canvas) {  
  22.         super.onDraw(canvas);  
  23.   
  24.         mPaint2.setStyle(Style.FILL);  
  25.         canvas.drawText(mText, 1060, mPaint2);  
  26.     }  
  27.   
  28. }  
3.布局



[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!-- xmlns:test="http://schemas.android.com/apk/res/ethan.customview1" 包名 -->  
  3. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     xmlns:test="http://schemas.android.com/apk/res/ethan.customview1"   
  5.     android:orientation="vertical"  
  6.     android:layout_width="fill_parent"  
  7.     android:layout_height="fill_parent"  
  8.     >  
  9. <ethan.customview1.CustomView2    
  10.     android:layout_width="wrap_content"   
  11.     android:layout_height="wrap_content"   
  12.     test:textColor="#f00"  
  13.     test:textSize="20sp"  
  14.     />  
  15. </LinearLayout>  
4.效果图


1. reference:参考某一资源ID。

    (1)属性定义:

            <declare-styleable name = "名称">

                   <attr name = "background" format = "reference" />

            </declare-styleable>

    (2)属性使用:

             <ImageView

                     android:layout_width = "42dip"
                     android:layout_height = "42dip"
                     android:background = "@drawable/图片ID"

                     />

2. color:颜色值。

    (1)属性定义:

            <declare-styleable name = "名称">

                   <attr name = "textColor" format = "color" />

            </declare-styleable>

    (2)属性使用:

            <TextView

                     android:layout_width = "42dip"
                     android:layout_height = "42dip"
                     android:textColor = "#00FF00"

                     />

3. boolean:布尔值。

    (1)属性定义:

            <declare-styleable name = "名称">

                   <attr name = "focusable" format = "boolean" />

            </declare-styleable>

    (2)属性使用:

            <Button

                    android:layout_width = "42dip"
                    android:layout_height = "42dip"

                    android:focusable = "true"

                    />

4. dimension:尺寸值。

    (1)属性定义:

            <declare-styleable name = "名称">

                   <attr name = "layout_width" format = "dimension" />

            </declare-styleable>

    (2)属性使用:

            <Button

                    android:layout_width = "42dip"
                    android:layout_height = "42dip"

                    />

5. float:浮点值。

    (1)属性定义:

            <declare-styleable name = "AlphaAnimation">

                   <attr name = "fromAlpha" format = "float" />
                   <attr name = "toAlpha" format = "float" />

            </declare-styleable>

    (2)属性使用:

            <alpha
                   android:fromAlpha = "1.0"
                   android:toAlpha = "0.7"

                   />

6. integer:整型值。

    (1)属性定义:

            <declare-styleable name = "AnimatedRotateDrawable">

                   <attr name = "visible" />
                   <attr name = "frameDuration" format="integer" />
                   <attr name = "framesCount" format="integer" />
                   <attr name = "pivotX" />
                   <attr name = "pivotY" />
                   <attr name = "drawable" />

            </declare-styleable>

    (2)属性使用:

            <animated-rotate

                   xmlns:android = "http://schemas.android.com/apk/res/android"  
                   android:drawable = "@drawable/图片ID"  
                   android:pivotX = "50%"  
                   android:pivotY = "50%"  
                   android:framesCount = "12"  
                   android:frameDuration = "100"

                   />

7. string:字符串。

    (1)属性定义:

            <declare-styleable name = "MapView">
                   <attr name = "apiKey" format = "string" />
            </declare-styleable>

    (2)属性使用:

            <com.google.android.maps.MapView
                    android:layout_width = "fill_parent"
                    android:layout_height = "fill_parent"
                    android:apiKey = "0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"

                    />

8. fraction:百分数。

    (1)属性定义:

            <declare-styleable name="RotateDrawable">
                   <attr name = "visible" />
                   <attr name = "fromDegrees" format = "float" />
                   <attr name = "toDegrees" format = "float" />
                   <attr name = "pivotX" format = "fraction" />
                   <attr name = "pivotY" format = "fraction" />
                   <attr name = "drawable" />
            </declare-styleable>

    (2)属性使用:

            <rotate

                   xmlns:android = "http://schemas.android.com/apk/res/android
               android:interpolator = "@anim/动画ID"

                   android:fromDegrees = "0" 
               android:toDegrees = "360"

                   android:pivotX = "200%"

                   android:pivotY = "300%
               android:duration = "5000"

                   android:repeatMode = "restart"

                   android:repeatCount = "infinite"

                   />

9. enum:枚举值。

    (1)属性定义:

            <declare-styleable name="名称">
                   <attr name="orientation">
                          <enum name="horizontal" value="0" />
                          <enum name="vertical" value="1" />
                   </attr>            

            </declare-styleable>

    (2)属性使用:

            <LinearLayout

                    xmlns:android = "http://schemas.android.com/apk/res/android"
                    android:orientation = "vertical"
                    android:layout_width = "fill_parent"
                    android:layout_height = "fill_parent"
                    >
            </LinearLayout>

10. flag:位或运算。

     (1)属性定义:

             <declare-styleable name="名称">
                    <attr name="windowSoftInputMode">
                            <flag name = "stateUnspecified" value = "0" />
                            <flag name = "stateUnchanged" value = "1" />
                            <flag name = "stateHidden" value = "2" />
                            <flag name = "stateAlwaysHidden" value = "3" />
                            <flag name = "stateVisible" value = "4" />
                            <flag name = "stateAlwaysVisible" value = "5" />
                            <flag name = "adjustUnspecified" value = "0x00" />
                            <flag name = "adjustResize" value = "0x10" />
                            <flag name = "adjustPan" value = "0x20" />
                            <flag name = "adjustNothing" value = "0x30" />
                     </attr>         

             </declare-styleable>

     (2)属性使用:

            <activity

                   android:name = ".StyleAndThemeActivity"
                   android:label = "@string/app_name"
                   android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
                   <intent-filter>
                          <action android:name = "android.intent.action.MAIN" />
                          <category android:name = "android.intent.category.LAUNCHER" />
                   </intent-filter>
             </activity>

     注意:

     属性定义时可以指定多种类型值。

    (1)属性定义:

            <declare-styleable name = "名称">

                   <attr name = "background" format = "reference|color" />

            </declare-styleable>

    (2)属性使用:

             <ImageView

                     android:layout_width = "42dip"
                     android:layout_height = "42dip"
                     android:background = "@drawable/图片ID|#00FF00"

                     />
分类: Android 2012-03-03 15:50  2489人阅读  评论(4)  收藏  举报
onFinishInflate() 当View中所有的子控件 均被映射成xml后触发

onMeasure(int, int) 确定所有子元素的大小

onLayout(boolean, int, int, int, int) 当View分配所有的子元素的大小和位置时触发

onSizeChanged(int, int, int, int) 当view的大小发生变化时触发

onDraw(Canvas) view渲染内容的细节

onKeyDown(int, KeyEvent) 有按键按下后触发

onKeyUp(int, KeyEvent) 有按键按下后弹起时触发

onTrackballEvent(MotionEvent) 轨迹球事件

onTouchEvent(MotionEvent) 触屏事件

onFocusChanged(boolean, int, Rect) 当View获取 或失去焦点时触发 

onWindowFocusChanged(boolean) 当窗口包含的view获取或失去焦点时触发

onAttachedToWindow() 当view被附着到一个窗口时触发

onDetachedFromWindow() 当view离开附着的窗口时触发,该方法和  onAttachedToWindow() 是相反的。

onWindowVisibilityChanged(int) 当窗口中包含的可见的view发生变化时触发

下图是从电子书提取的资料,与上面所讲类似

目标:实现textview和ImageButton组合,可以通过Xml设置自定义控件的属性。 

      通过代码或者通过xml设置自定义控件的属性

1.控件布局:以Linearlayout为根布局,一个TextView,一个ImageButton。 
  
Xml代码
[html]  view plain copy
  1. < ?xml version="1.0" encoding="utf-8"?>    
  2.     < LinearLayout xmlns:Android="http://schemas.android.com/apk/res/android"    
  3.     android:layout_width="fill_parent" android:layout_height="fill_parent"    
  4.     android:gravity="center_vertical">    
  5.     < TextView android:layout_height="wrap_content" android:id="@+id/text1"    
  6.     android:layout_width="wrap_content">< /TextView>    
  7.     < ImageButton android:layout_width="wrap_content"    
  8.     android:layout_height="wrap_content" android:id="@+id/btn1">< /ImageButton>    
  9.     < /LinearLayout>  
2.自定义控件代码,从LinearLayout继承: 
  
Java代码
[java]  view plain copy
  1. public class ImageBtnWithText extends LinearLayout {    
  2.      }    
  3.       public ImageBtnWithText(Context context) {    
  4.       this(context, null);    
  5.       }    
  6.          
  7.       public ImageBtnWithText(Context context, AttributeSet attrs) {    
  8.       super(context, attrs);    
  9.        //在构造函数中将Xml中定义的布局解析出来。   
  10.       LayoutInflater.from(context).inflate(R.layout.imagebtn_with_text, thistrue);  
  11.         }    
  12.       }  

 
3.在主界面布局xml中使用自定义控件: 
  
Xml代码 
[html]  view plain copy
  1. < com.demo.widget2.ImageBtnWithText    
  2.    android:id="@+id/widget"    
  3.    android:layout_width="fill_parent"    
  4.    android:layout_height="fill_parent" />  

即使用完整的自定义控件类路径:com.demo.widget2.ImageBtnWithText定义一个元素。 
  运行可以看到控件已经能够被加载到界面上。 
4.给按钮设置图片和文本 
  如果图片是固定不变的,那么直接在控件布局中设置ImageButton的src属性即可。 
  4.1通过Java代码设置,在控件代码中提供函数接口: 
  
Java代码
[java]  view plain copy
  1. public void setButtonImageResource(int resId) {    
  2.    mBtn.setImageResource(resId);    
  3.    }    
  4.       
  5.    public void setTextViewText(String text) {    
  6.    mTv.setText(text);    
  7.    }  

然后再在主界面的onCreate()通过函数调用设置即可。 
  4.2通过Xml设置属性 
  4.2.1首先定义Xml可以设置的属性集合,在values下创建attrs.xml,文件名可随意,一般都叫attrs.xml 
  
Xml代码
[html]  view plain copy
  1. < ?xml version="1.0" encoding="utf-8"?>    
  2.   < resources>    
  3.    < declare-styleable name="ImageBtnWithText">    
  4.    < attr name="android:text"/>    
  5.    < attr name="android:src"/>    
  6.    < /declare-styleable>    
  7.    < /resources>  

属性集合名字:ImageBtnWithText,自己可根据实际来定义; 
  集合中包含的属性列表,每行一个属性。 
  4.2.2修改自定义控件实现代码,以获取xml中定义的属性 
  
Java代码
[java]  view plain copy
  1. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ImageBtnWithText);    
  2.   CharSequence text = a.getText(R.styleable.ImageBtnWithText_android_text);    
  3.   if(text != null) mTv.setText(text);    
  4.   Drawable drawable = a.getDrawable(R.styleable.ImageBtnWithText_android_src);    
  5.   if(drawable != null) mBtn.setImageDrawable(drawable);    
  6.   a.recycle();  

首先通过context.obtainStyledAttributes获得所有属性数组; 
  然后通过TypedArray类的getXXXX()系列接口获得相应的值。 
  4.2.3在主界面布局中设置自定义控件的属 
  android:text="ABC" android:src="@drawable/icon 
  4.3自定义名称属性: 
  在4.2中使用的属性名是Android系统命名空间的,都以android开头,比如android:text等。 
实际开发中会自定义一些属性名,这些属性名仍然定义在4.2.1提到的attrs.xml中: 
  4.3.1定义属性名 
  
Xml代码 
[html]  view plain copy
  1. < attr name="appendText" format="string"/>  

和直接使用系统的attr不同的是要增加一个format属性,说明此属性是什么格式的。format可选项可参见注1 
  4.3.2使用自定义属性 
  
Xml代码
[html]  view plain copy
  1. < ?xml version="1.0" encoding="utf-8"?>    
  2.    < LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
  3.    xmlns:myspace="http://schemas.android.com/apk/res/com.demo.customwidget"    
  4.    android:orientation="vertical" android:layout_width="fill_parent"    
  5.    android:layout_height="fill_parent">    
  6.    < com.demo.widget2.ImageBtnWithText    
  7.    android:text="ABC" android:src="@drawable/icon" android:id="@+id/widget"    
  8.    android:layout_width="fill_parent" android:layout_gravity="center"    
  9.    android:layout_height="fill_parent" myspace:appendText="123456">    
  10.    < /com.demo.widget2.ImageBtnWithText>    
  11.    < /LinearLayout>  
效果图

三种得到LinearInflater的方法

a. LayoutInflater inflater = getLayoutInflater();



b. LayoutInflater localinflater =
  (LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE); 


c. LayoutInflater inflater = LayoutInflater.from(context);



onDraw 方法绘图,invalidate刷新界面。

效果图:

点击一下换颜色

    

onDraw画完图后,给控件设置点击事件 ,将参数传到控件里,然后invalidate刷新

1.onDraw画图,并增加changeColor方法


[java]  view plain copy
  1. public class CusView3 extends View {  
  2.   
  3.     private int color = 0;  
  4.   
  5.     public CusView3(Context context, AttributeSet attrs) {  
  6.         super(context, attrs);  
  7.     }  
  8.   
  9.     @Override  
  10.     protected void onDraw(Canvas canvas) {  
  11.         super.onDraw(canvas);  
  12.         Paint mPaint = new Paint();  
  13.         if (color > 2) {  
  14.             color = 0;  
  15.         }  
  16.         switch (color) {  
  17.         case 0:  
  18.             mPaint.setColor(Color.GREEN);  
  19.             break;  
  20.         case 1:  
  21.             mPaint.setColor(Color.RED);  
  22.             break;  
  23.         case 2:  
  24.             mPaint.setColor(Color.BLUE);  
  25.             break;  
  26.   
  27.         default:  
  28.             break;  
  29.         }  
  30.         mPaint.setStyle(Style.FILL);  
  31.         mPaint.setTextSize(35.0f);  
  32.         canvas.drawText("点击我刷新"1060, mPaint);  
  33.     }  
  34.   
  35.     public void changeColor() { //为了让外面调用  
  36.         color++;  
  37.     }  
  38. }  
2.布局



[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7.     <xue.test.CusView3  
  8.         android:id="@+id/cusview3"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         >  
  12.     </xue.test.CusView3>  
  13. </LinearLayout>  


3.画图后 给控件设置点击事件 ,将参数传到控件里,然后invalidate刷新



[java]  view plain copy
  1. public class TestCustomViewActivity extends Activity {  
  2.     @Override  
  3.     public void onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.main);  
  6.         view3 = (CusView3) findViewById(R.id.cusview3);  
  7.         // 点击事件  
  8.         view3.setOnClickListener(new View.OnClickListener() {  
  9.   
  10.             @Override  
  11.             public void onClick(View v) {  
  12.                 Message message = new Message();  
  13.                 message.what = 1;  
  14.                 myHandler.sendMessage(message);  
  15.             }  
  16.         });  
  17.     }  
  18.   
  19.     Handler myHandler = new Handler() {  
  20.         // 接收到消息后处理  
  21.         public void handleMessage(Message msg) {  
  22.   
  23.             switch (msg.what) {  
  24.             case 1:  
  25.                 // 调用方法  
  26.                 view3.changeColor();  
  27.                 // 刷新方法  
  28.                 view3.invalidate();  
  29.                 break;  
  30.             }  
  31.             super.handleMessage(msg);  
  32.         }  
  33.     };  
  34.     private CusView3 view3;  
  35.   
  36. }  


至于自定义控件占整屏的问题,可能需要用layoutparams


源码下载: http://download.csdn.net/detail/ethan_xue/4152203

扫描二维码,一起学习


© 著作权归作者所有

共有 人打赏支持
robertchao
粉丝 3
博文 55
码字总数 30285
作品 0
海淀
Android工程师
私信 提问
Web组件构建框架--Polymer

在2013年的Google I/O大会上,Google发布了Polymer,它是一个使用Web组件构建Web应用的类库,同时也使用了为Web构建可重用组件的新的HTML 5标准。Polymer为大部分Web组件技术提供了polyfills...

匿名
2013/06/03
18.6K
1
phonycode/wuss-weapp

Wuss Weapp 一款高质量,组件齐全,高自定义的微信小程序 UI 组件库 文档 https://phonycode.github.io/wuss-weapp 扫码体验 使用微信扫一扫体验小程序组件示例 演示图片 快速上手 在开始使用...

phonycode
2018/10/14
0
0
如何利用阿里视频云开源组件,快速自定义你的H5播放器?

阿里云播放器SDK(ApsaraVideo for Player SDK)是阿里视频云端到云到端服务的重要一环,除了支持点播和直播的基础播放功能外,还深度融合视频云业务,支持视频的加密播放、安全下载、首屏秒...

樰篱
2018/08/20
0
0
Firefox 支持 Web 组件技术,简化复杂网站开发

周一的时候,Mozilla 在新版 Firefox 中启用了“Web 组件”技术,旨在让网站的构建变得更加轻松。对于普通用户来说,这一技术层面的变化不会给我们造成任何影响。但是对于复杂网站的构建者,...

达尔文
2018/05/31
2.7K
5
基于可视化复杂SQL拼接插件 QueryBuilder

本文演示地址为:https://www.oschina.net/p/QueryBuilder 引言 基于Web的自定义查询系统在国内一直是一个短板,目前国内普遍的自定义查询均为一个简单的查询组件的罗列,达不到真正的实现动...

码上中国博客
2015/07/17
0
34

没有更多内容

加载失败,请刷新页面

加载更多

第11章 多线程

程序、进程、线程 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 **进程(process)**是程序的一次执行过程或是正在运行的一个程序。动...

流小文
10分钟前
0
0
SpringBoot引入第三方jar包或本地jar包的处理方式

在开发过程中有时会用到maven仓库里没有的jar包或者本地的jar包,这时没办法通过pom直接引入,那么该怎么解决呢 一般有两种方法 - 第一种是将本地jar包安装在本地maven库 - 第二种是将本地j...

独钓渔
今天
2
0
五、MyBatis缓存

一、MyBatis缓存介绍 缓存的使用可以明显的加快访问数据速度,提升程序处理性能,生活和工作中,使用缓存的地方很多。在开发过程中,从前端-->后端-->数据库等都涉及到缓存。MyBatis作为数据...

yangjianzhou
今天
2
0
最近研究如何加速UI界面开发,有点感觉了

最近在开发JFinal学院的JBolt开发平台,后端没啥说的,做各种极简使用的封装,开发者上手直接使用。 JBolt开发平台包含常用的用户、角色、权限、字典、全局配置、缓存、增删改查完整模块、电...

山东-小木
今天
3
0
《月亮与六便士》的读后感作文3000字

《月亮与六便士》的读后感作文3000字: 看完英国作家威廉.萨默塞特.毛姆所著《月亮与六便士》(李继宏译),第一疑问就是全书即没提到“月亮”,也没提到“六便士”。那这书名又与内容有什么...

原创小博客
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部