自己定义控件是一些android程序猿感觉非常难攻破的难点,起码对我来说是这种,可是我们能够在网上找一些好的博客关于自己定义控件好好拿过来学习研究下,多练,多写点也能找到感觉,把一些原理弄懂,今天就讲下自己定义组合控件,这个特别适合在标题栏或者设置界面,看以下图:

android  自己定义组合控件_xml

就很适合使用组合控件了,如今写一个玩玩:


activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:hy="http://schemas.android.com/apk/res/com.example.customcircle"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<com.example.customcircle.SettingView
android:id="@+id/sv1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
hy:title = "标题1"
hy:content = "内容1"
></com.example.customcircle.SettingView>
<com.example.customcircle.SettingView
android:id="@+id/sv2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
hy:title = "标题2"
hy:content = "内容2"
></com.example.customcircle.SettingView>
<com.example.customcircle.SettingView
android:id="@+id/sv3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
hy:title = "标题3"
hy:content = "内容3"
></com.example.customcircle.SettingView>
</LinearLayout>


SettingView.java
public class SettingView extends RelativeLayout {
private TextView title;
private TextView content;

public SettingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}

public SettingView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
/**
* 获取一个样式的属性集
* 把布局文件xml中的获取到的样式集合attrs跟自己自己定义的样式集合建立一个映射关系
*/
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SettingView);
String title = a.getString(0);
String content = a.getString(1);
//设置到界面上
setTitle(title);
setContent(content);
a.recycle();
}

public SettingView(Context context) {
super(context);
initView(context);
}

private View initView(Context context){
View view = View.inflate(context, R.layout.setting_view, this);
title = (TextView) view.findViewById(R.id.title);
content = (TextView) view.findViewById(R.id.content);
return view;
}

public void setTitle(String tilte){
title.setText(tilte);
}
public void setContent(String strContent){
content.setText(strContent);
}
public void setTextSize(int size){
content.setTextSize(size);
title.setTextSize(size);
}
}


setting_view.xml

<?

xml version="1.0" encoding="utf-8"?>

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

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:orientation="vertical" 

    android:background="#22000000"

    >

    

   <RelativeLayout 

   android:layout_width="fill_parent"

   android:layout_height="wrap_content"

   

   >

   <TextView

       android:id="@+id/title"

       android:layout_width="fill_parent"

       android:layout_height="wrap_content"

       android:textSize="20sp"

       android:layout_marginTop="5dp"

       android:layout_marginLeft="5dp"

       />

   <TextView

       android:id="@+id/content"

       android:layout_width="fill_parent"

       android:layout_height="wrap_content"

       android:textSize="16sp"

       android:layout_marginLeft="5dp"

       android:layout_below="@id/title"

       />

   <CheckBox 

       android:id="@+id/cb"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:layout_alignParentRight="true"

       android:layout_centerVertical="true"

       />

  

</RelativeLayout>

<View 

       android:layout_width="fill_parent"

       android:layout_height="1px"

       android:background="#000000"

       android:layout_below="@id/cb"

       />

</LinearLayout>

我们发如今activity_main.xml中有二个属性

hy:title = "标题1"

hy:content = "内容1"

这个时候xml文件会报错



error: Error parsing XML: unbound prefix  说没有hy这种前缀,由于我们写android:什么属性  都是由于有xmlns:android ="http://schemas.android.com/apk/res/android"


这样一个命名空间在,




那我们也自己定义一个命名空间,仿照上面的写就是了



xmlns:hy ="http://schemas.android.com/apk/res/com.example.customcircle"//res后面的是你得包名



如今xml文件有报错了:



Multiple annotations found at this line:


 - error: No resource identifier found for attribute 'content' in package


 'com.example.customcircle'


说在我们的包下没有找到conent的定义




 hy:title = "标题"


   hy:content = "内容"


是我自己定义的属性


假设要使用自己定义的属性,就要把这些属性定义出来,而为什么系统的android:属性名---就能够呢?

那是由于android sdk已经声明好了,在找到我们使用的sdk


D:\java\tools\adt-bundle-windows-x86_64-20130917\sdk\platforms\android-10\data\res\values在这个文件夹下有一个attrs.xml文件,我们就找一个平时大家都会用到的



<declare-styleable name="TextAppearance">

        <!-- Text color. -->

        <attr name="textColor" />

        <!-- Size of the text. Recommended dimension type for text is "sp" for scaled-pixels (example: 15sp). -->

        <attr name="textSize" />

        <!-- Style (bold, italic, bolditalic) for the text. -->

        <attr name="textStyle" />

        <!-- Typeface (normal, sans, serif, monospace) for the text. -->

        <attr name="typeface" />

        <!-- Color of the text selection highlight. -->

        <attr name="textColorHighlight" />

        <!-- Color of the hint text. -->

        <attr name="textColorHint" />

        <!-- Color of the links. -->

        <attr name="textColorLink" />

    </declare-styleable>


这就是TextView中的属性,所以我们也要模仿他创建一个attrs文件在value文件夹下



 <declare-styleable name="TextAppearance">





declare-styleable  声明的意思 name表示 声明的控件名称


我们的  <declare-styleable name="SettingView">


     </declare-styleable >


当保存的时候就会在你R.java文件里生成了一个内部类



 public static final class attr {//属性集


    }


然后声明属性



<attr name="title" format="string"></ attr>


<attr name="content" format="string" ></attr>


name表示属性的名称 format表示属性的类型




然后我们保存代码 就没错误提示了


然后再看R.java文件


 public static final class attr {


        /** <p>Must be a string value, using '\\;' to escape characters such as '\\n' or '\\uxxxx' for a unicode character.


<p>This may also be a reference to a resource (in the form


"<code>@[ <i>package</i> :]<i>type </i>:<i> name</i></code>") or


theme attribute (in the form


"<code>?[ <i>package</i> :][<i>type </i>:]<i> name</i></code>")


containing a value of this type.


         */


        public static final int content=0x7f010001;


        /** <p>Must be a string value, using '\\;' to escape characters such as '\\n' or '\\uxxxx' for a unicode character.


<p>This may also be a reference to a resource (in the form


"<code>@[ <i>package</i> :]<i>type </i>:<i> name</i></code>") or


theme attribute (in the form


"<code>?[ <i>package</i> :][<i>type </i>:]<i> name</i></code>")


containing a value of this type.


         */


        public static final int title=0x7f010000;


    }


这个时候我们执行下我们的项目,发现这自己定义的属性并没有起作用,那意思就是说我们要用代码的方式让它显示出来




AttributeSet类表示属性的集合 在我们定义的属性


比方:



 android:id= "@+id/sv1"


  android:layout_width="fill_parent"


  android:layout_height="wrap_content"


  hy:title = "标题"


  hy:content = "内容"


相当于把这些属性封装成了一个类,这真是我们java强大之处,面向对象编程思想




然在构造函数中做这个操作





public SettingView(Context context, AttributeSet attrs) {


          super(context, attrs);


         initView(context);


          /**


          * 获取一个样式的属性集


          * 把布局文件 xml中的获取到的样式集合 attrs跟自己自己定义的样式集合建立一个映射关系


          */


         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SettingView );


         String title = a.getString(0);


         String content = a.getString(1);


          //设置到界面上


         setTitle(title);


         setContent(content);


         a.recycle();


    }


这样就算成功了 


执行效果:

android  自己定义组合控件_组合控件_02

总结下自己定义组合控件的步骤:

1. 写一个类 继承  ViewGroup LinearLayout  RelativeLayout



2. 假设在布局文件中面定义view对象 使用这个view对象的两个參数的构造方法.



3. 定义这个自己定义控件里面要显示的内容

View.inflate(context, R.layout.ui_setting_view, this);



4. 加入自己定义控件的属性. 

    定义自己定义属性的命名空间



5. 声明自己定义的属性

    <declare-styleable name="SettingView">

        <attr name="title" format="string" />

        <attr name="desc_on" format="string" />

        <attr name="desc_off" format="string" />

    </declare-styleable>

   观察R文件 生成 attr内部类 生成styleable  数组 全部的attrs



6. 在xml布局文件中面配置  自己定义的属性.



7. 在自己定义view对象的构造方法里面 获取AttributeSet 

跟我们自己定义的属性建立映射关系



8. 在自己定义组合控件里面 设置 布局文件的数据, 扩展点击事件.



9. 在布局文件使用自己定义的view对象.