Android 自定义UI组合控件设计方法

来源:互联网 发布:网络知名言情女作家 编辑:程序博客网 时间:2024/05/18 20:36

1,概述

在设计Android程序的时候,为了提高编程效率和维持统一的风格,往往需要把一些UI组件组合在一起,包装成一个独立的组件单元,在使用中作为一个整体,象使用系统控件一样地使用。这样的组件单元,我们称之为自定义UI组合控件。

Android自定义UI组合控件在程序设计中具有重要意义,它可以简化程序设计难度、提高代码复用性、降低代码耦合度、提高程序模块化、降低程序的维护的难度,随着软件项目日趋庞大和功能日趋复杂,自定义控件的作用越来越突出。

Android开发环境ADT为我们提供了象按钮文本框等等的一些常用组件,通过研究源码,我们发现Android是这样实现组件定义的:

第一步:在atts.xml中定义控件属性

第二步:定义一个View的子类,实现属性参数的获取、内部子控件的定义及响应事件的定义。

自定义组件示意图:

根据这样的思路,我们也可以用同样的方法定义自己的控件,本文依照系统的设计思路,来讲解自定义组合控件的设计方法。

2,控件设计

   2.1设计控件属性

      控件属性存放在atts.xml文件中。atts.xml中以declare-styleable为单元定义了一系列属性集合,它位于res/values目录下.定义的内容格式为:

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

<resources>

    <declare-styleable name = "属性集合名">

        <attr name="属性名" format = "属性类型"/>

         ……      

    </declare-styleable>

</resources>

其中declare-styleable定义的是属性集合,attr标签定义的是具体属性名称和类型,这些属性就是用户在使用控件时需要设置的属性。

2.2定义自己的View,实现想要的控件

自定义View的基本思路是这样的:这个类继承自RelativeLayout。对应atts.xml的属性,类中要定义对应的field,同时定义控件中包含的子控件,比如Button,TextView等,另外还需要定义控件的布局属性LayoutParams,确定子控件的显示位置。

定义这个类的重点是定义它的构造函数,在这里,必须重写带AttributeSet属性的构造函数,以便获取属性。构造函数要实现的功能有:用TypeArray获取控件属性值;实例化各子控件,给各控件的属性赋值。

自定义View的设计如下所示:

package com.example.mypanel;                           //自定义包名

public class MyPanel extends RelativeLayout {                //MyPanel为自定义View

//自定义属性,与atts.xml一致

private String titleString;

 //子控件,即自定义控件中包含控件,比如按钮、文字、图片等。

private Button backBtn, exitBtn, forwardBtn;

//子控件布局属性,定义子控件在自定义控件中的位置

private LayoutParams backBtnParams, exitBtnParams, titleParams;

 

//构造函数

public MyPanel(Context context, AttributeSet attrs) {

           super(context, attrs);

           //TypeArray获取控件属性值

           TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.myPanel);

     //TypedArray中读取

           titleString = ta.getString(R.styleable.myPanel_title);

 

           //回收TypedArray

           ta.recycle();

 

           //把属性赋给具体组件

           title = new TextView(context);

           title.setText( titleString );

           title.setTextSize(titleSize );

           backBtn = new Button(context );

           backBtn.setText(  backBtnText );

           exitBtn = new Button( context );

           exitBtn.setText( exitBtnText );

          

           //定义布局属性,指定子控件的显示位置

           backBtnParams = new RelativeLayout.LayoutParams(

LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT ) ;

           backBtnParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT );

           addView( backBtn, backBtnParams);

exitBtnParams = new RelativeLayout.LayoutParams(

LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT ) ;

           exitBtnParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT );

           addView( exitBtn, exitBtnParams );                 

          

    titleParams = new RelativeLayout.LayoutParams(

LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT ) ;

    titleParams.addRule(RelativeLayout.CENTER_IN_PARENT );

    addView( title,  titleParams );

   

 }

}

 

至此,我们自定义的控件已经具备了完整的外观形态。

 

2.3在程序中调用自定义控件

  应用自定义控件需要注意三点:

其一,在XML开头处引用自定义控件的包名来定义控件命名空间,在Eclipse下格式为:

   xmlns:别名=http://schemas.android.com/apk/res/完整包名

  Android Studio下的格式为:

  xmlns:别名=http://schemas.android.com/apk/res/res-auto

 其二,在引用自定义控件时提供完整类的路径

  比如:<com.example.mypanel.MyPanel  …… />

其三,自定义属性要用别名引用

 比如:custom:title = "Hello Title"

        custom:titleSize = "15dp"

 下面来看具体实现:

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

   xmlns:custom="http://schemas.android.com/apk/res/com.example.mypanel"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context=".MainActivity" >

 

   <com.example.mypanel.MyPanel

        android:id="@+id/panel"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:background="#DFDFDF"

        android:paddingTop="2dp"

       custom:title = "Hello Title"

        custom:titleSize = "15dp"

        custom:titleColor = "#FF0E00"

        custom:backBtnText = "BACK"

        custom:exitBtnText = "EXIT"

        />

</RelativeLayout>


可见,引用自定义控件与引用系统控件方法基本类似。此时,可以运行程序查看显示效果了,如下图所示:

   

 

 

 

2.4      加入响应事件

如果在MyPanel中为按钮加入OnClick响应事件,即可直接实现控件对点击事件的响应,可是,作为一个控件,如果每次调用都更改定义控件的源程序,这违背了定义一个通用控件的初衷。控件的属性及事件的定义都应该由调用者实现。要实现这一点,需要用到接口回调机制。也就是在控件的定义中,按钮事件不直接实现具体行为,而是调用一个接口中的抽象方法,这些对调用者来说都是不可见的,调用者只要实现了该接口的抽象方法,具体的事件行为也就实现了。

MyPanel.java中,定义了一个接口,包含两个抽象方法:

    public interface MyPanelOnClickListener{

                 public void backBtnClick();

public void exitBtnClick();

        }

   private MyPanelOnClickListener listener;  //该接口类型的MyPanel成员属性。

按钮的OnClick方法只调用抽象类中的方法:

  public void onClick(View arg0) {

                                    // TODO Auto-generated method stub

                                    listener.backBtnClick();  

                           }

  

然后定义事件的回调方法:

public void setMyPanelOnClickListener( MyPanelOnClickListener listener){

                 this.listener = listener;

        }

它的参数是MyPanelOnClickListener类型,在调用者调用这个方法时,必须实例化这个类。也就是必须实例化接口的两个方法backBtnClick()public void exitBtnClick(),这个例化的过程就是实现事件具体行为的过程。

 

下面给出加入响应事件后的MyPanel

public class MyPanel extends RelativeLayout {

        private Button backBtn, exitBtn;

        private TextView title;    

        private String titleString;

        private int titleSize;

        private int titleTextColor;

       

        private String backBtnText;  



private String exitBtnText;

       

        private LayoutParams backBtnParams, exitBtnParams, titleParams;

 

        //回调接口

        public interface MyPanelOnClickListener{

                 public void backBtnClick();

                 public void exitBtnClick();

        }

       

        private MyPanelOnClickListener listener;

       

        //回调事件

        public void setMyPanelOnClickListener( MyPanelOnClickListener listener){

                 this.listener = listener;

        }

        public MyPanel(Context context, AttributeSet attrs) {

                 super(context, attrs);

                 // TODO Auto-generated constructor stub  

               TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.myPanel);

                

                 titleString = ta.getString(R.styleable.myPanel_title);

                 titleSize = ta.getDimensionPixelSize(R.styleable.myPanel_titleSize,10);

                 titleTextColor = ta.getColor(R.styleable.myPanel_titleColor, Color.BLUE);

                

                 backBtnText = ta.getString(R.styleable.myPanel_backBtnText);

                 exitBtnText = ta.getString(R.styleable.myPanel_exitBtnText);

                

                 ta.recycle();

                

                 //把属性赋给具体组件

                 title = new TextView(context);

                 title.setText( titleString );

                 title.setTextSize(titleSize );

                

                 backBtn = new Button(context );

                 backBtn.setText(  backBtnText );

                

                 exitBtn = new Button( context );

                 exitBtn.setText( exitBtnText );

                

                 //定义布局属性

                 backBtnParams = new RelativeLayout.LayoutParams(

 LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT ) ;

                 backBtnParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT );

                 addView( backBtn, backBtnParams );

                

                 exitBtnParams = new RelativeLayout.LayoutParams(

LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT ) ;

                 exitBtnParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT );

                 addView( exitBtn, exitBtnParams );                 

                

            titleParams = new RelativeLayout.LayoutParams(

 LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT ) ;

            titleParams.addRule(RelativeLayout.CENTER_IN_PARENT );

           addView( title,  titleParams );

           

          //按钮事件

                 backBtn.setOnClickListener(new OnClickListener(){

                           @Override

                           public void onClick(View arg0) {

                                    // TODO Auto-generated method stub

                                    listener.backBtnClick();

                           }                          

                 });

                

                 exitBtn.setOnClickListener(new OnClickListener(){

 

                           @Override

                           public void onClick(View arg0) {

                                    // TODO Auto-generated method stub

                                    listener.exitBtnClick();                                 

                           }       

                 });    

        }

}

2.5      自定义控件在程序中的应用

调用者的OnCreate方法如下所示:

        @Override

        protected void onCreate(Bundle savedInstanceState) {

                 super.onCreate(savedInstanceState);

                 setContentView(R.layout.activity_main);

                 //实例化一个MyPanel

                 MyPanel panel = (MyPanel)this.findViewById(R.id.panel);

       //MyPanel设置点击事件

                 panel.setMyPanelOnClickListener(new MyPanel.MyPanelOnClickListener(){

 

                           @Override

                           public void backBtnClick() {

                                    // TODO Auto-generated method stub

                                    Toast.makeText(MainActivity.this, "BACK BUTTON", 1000).show();

                           }

                       @Override

                           public void exitBtnClick() {

                                    // TODO Auto-generated method stub

                                    Toast.makeText(MainActivity.this, "EXIT BUTTON", 1000).show();

                                    finish();

                           }

                           });    

        }

 

在这里,首先实例化了一个MyPanel对象,其次,调用setMyPanelOnClickListener方法来设置点击事件,MyPanel.MyPanelOnClickListener的一个匿名内部类对象做为参数传递给setMyPanelOnClickListener方法。可见,调用MyPanel与调用其它系统控件的方法是一样的,这正是我们设计自定义控件要实现的目标。

 

2.6      为自定义控件设置其它方法

实际应用中的控件,应具备完整的功能。当然,也可以为我们自己设计的控件加上需要的功能。

比如,可以在MyPanel中加入一个隐藏按钮的方法

public  void hideExitBtn(){

     exitBtn. .setVisibility(View.INVISIBLE);

}

 

2.7程序间共享自定义控件

   在一个应用里设计的自定义控件,有时在别的应用里也可以用。这时,我们可以把自定义控件设计成一个单独的项目,在新建项目时,选择“将项目作为library
阅读全文
0 0
原创粉丝点击