Android中View自定义组合控件的基本编写方法

Android中View自定义组合控件的基本编写方法,第1张

概述有很多情况下,我们只要运用好Android给我提供好的控件,经过布局巧妙的结合在一起,就是一个新的控件,我称之为“自定义组合控件”。

有很多情况下,我们只要运用好AndroID给我提供好的控件,经过布局巧妙的结合在一起,就是一个新的控件,我称之为“自定义组合控件”。

那么,这种自定义组合控件在什么情况下用呢?或者大家在做项目时候会发现,某些布局会被重复的利用,同一个布局的XML代码块会被重复的复制黏贴多次,这样会造成代码结构混乱不说,代码量也会增大,各种控件都需要在Java代码中被申明和处理相应的逻辑,工作量着实不小,所以,必须要找到一个合理的“偷懒”的方式,开动脑经去怎么简化以上说的不必要的麻烦。下面看一张图,就一个简单的布局,我们就此图来实现一个简单的自定义组合控件。

从上面的图来分析,我们可以看到,这个布局里面是没有“全新”的控件的,用的都是AndroID系统原生的控件。熟悉AndroID界面布局的人,肯定觉得这种布局真是小Case,太简单了,分分钟就可以写完。于是下面就是某一个条目的布局代码:

<!--?xml version=1.0 enCoding=utf-8?--><relativelayout androID:background="@drawable/selector_blue" androID:ID="@+ID/rl_show_address" androID:layout_height="60dip" androID:layout_wIDth="match_parent" xmlns:androID="http://schemas.androID.com/apk/res/androID">   <textvIEw androID:ID="@+ID/tv_Title" androID:layout_height="wrap_content" androID:layout_marginleft="5dip" androID:layout_margintop="1dip" androID:layout_wIDth="wrap_content" androID:text="这是标题" androID:textcolor="#000000" androID:textsize="20sp">   <textvIEw androID:ID="@+ID/tv_desc" androID:layout_below="@ID/tv_Title" androID:layout_height="wrap_content" androID:layout_marginleft="6dip" androID:layout_margintop="1dip" androID:layout_wIDth="wrap_content" androID:text="这是描述内容" androID:textcolor="#99ff0000" androID:textsize="14sp">   <checkBox androID:clickable="false" androID:focusable="false" androID:ID="@+ID/cb_status" androID:layout_alignparentright="true" androID:layout_centervertical="true" androID:layout_height="wrap_content" androID:layout_wIDth="wrap_content">     <!-- 加一条分割线 -->  <vIEw androID:background="#000000/" androID:layout_alignbottom="@ID/cb_status" androID:layout_alignparentbottom="true" androID:layout_height="0.2dip" androID:layout_margintop="7dip" androID:layout_wIDth="match_parent"> </vIEw></checkBox></textvIEw></textvIEw></relativelayout>

可以看到,这种布局确实相当的简单。但是,这时候产品经理告诉你,需求改了,我们需要在这个界面再加一个这样的条目,于是你觉得,小意思,Ctrl+C,Ctrl+V,轻松搞定,然后改一下控件的ID,在Java代码中findvIEwbyID(ID),加一段逻辑代码,搞完收工。没想到这时候产品又来了,需求改了,这里需要加10个这样的布局,于是你...诚然,这时候再Ctrl+C,Ctrl+V是不合适的,工作量就显得很大了,即使你不嫌麻烦的话,照样做了,你料不到产品会再来,那个给我删掉几个,那个再加上几个,是不是要疯了。
 

也许,我们可以相出一个偷懒的方法来呢。通过分析上面的布局,可以发现,布局上每一个子条目是不变的,布局完全一样,唯一在变化的是,红色的TextVIEw上的文本随着CheckBox的状态再改变着,而这种变化,我们是否可以想办法抽取到某个方法中呢,答案是肯定能的。我们可以将这种子条目的布局一次性封装到一个java类中,每次调用这个控件的时候,事先设定各种属性数据即可,这里涉及到了自定义属性了。分析一下这个属性集该怎么定义,从上面的图片可以看出,控件上需要设置的内容分别是,上面TextVIEw的标题,还有下面TextVIEw的描述信息,且描述信息是根据CheckBox的状态发生改变的,所以这两种状态(true或false)都需要被定义到属性集里去,于是属性集就有了。

在工程下的res/values目录下,新建attrs.xml文件,定义如下属性集:

<!--?xml version=1.0 enCoding=utf-8?--><resources>   <declare-styleable name="combinationVIEw">    </attr>    </attr>    </attr>  </declare-styleable> </resources>

定义好了属性集了,接下来我们就需要定义一个java类,来渲染这段布局,解析这个属性集,并且对象提供修改控件状态的方法,已达到复用的效果。问题来了,我们定义的这个java类需要继承哪个类呢?在这里,我们不必考虑VIEw了,因为这里不是全新自定义控件,不需要onMessure和onDraw去测量去画一个视图。那么VIEwGroup呢?我们也不必用这个类,因为这里的布局是给定好的,不需要使用onLayout给子控件设置显示的位置。那么,该继承什么呢?我们可以想象一下VIEwGroup的子类是不是可以呢?实现自定义控件的除了继承VIEw和VIEwGroup之外,还可以直接继承AndroID已有的控件进行修改,这个用面向对象的思想,应该不难想象吧。由于,该布局文件用的相对布局relativeLayout,我们想当然可以自定义java类去继承这个relativeLayout,relativeLayout里提供一些参数和方法方便我们去实现子控件的布局。但是,我们这里直接在子控件布局已经写好了,不需要使用relativeLayout提供的参数和方法来布局了。所以,导致了,即使不去继承relativeLayout,而改成linearLayout,FrameLayout...也是可以的,只要这个布局类是VIEwGroup的子类就行。以下是这个自定义组合控件的实现代码:

package com.example.combinationvIEw; import androID.content.Context;import androID.util.AttributeSet;import androID.vIEw.VIEw;import androID.Widget.CheckBox;import androID.Widget.relativeLayout;import androID.Widget.TextVIEw; public class CombinationVIEw extends relativeLayout {   private TextVIEw tv_Title;  private TextVIEw tv_desc;  private CheckBox cb_status;  // 命名空间,在引用这个自定义组件的时候,需要用到  private String namespace = http://schemas.androID.com/apk/res/com.example.combinationvIEw;  // 标题  private String Title;  // 被选中的描述  private String desc_on;  // 未被选中的描述  private String desc_off;   public CombinationVIEw(Context context,AttributeSet attrs) {    super(context,attrs);    // 将自定义组合控件的布局渲染成VIEw    VIEw vIEw = VIEw.inflate(context,R.layout.layout_combinationvIEw,this);    tv_Title = (TextVIEw) vIEw.findVIEwByID(R.ID.tv_Title);    tv_desc = (TextVIEw) vIEw.findVIEwByID(R.ID.tv_desc);    cb_status = (CheckBox) vIEw.findVIEwByID(R.ID.cb_status);     Title = attrs.getAttributeValue(namespace,Title);    desc_on = attrs.getAttributeValue(namespace,desc_on);    desc_off = attrs.getAttributeValue(namespace,desc_off);    System.out.println(Title + : + desc_on + : + desc_off);    // 初始化到子控件    if (Title != null) {      tv_Title.setText(Title);    }    if (desc_off != null) {      tv_desc.setText(desc_off);    }  }   /**   * 判断是否被选中   *    * @return   */  public boolean isChecked() {    return cb_status.isChecked();  }   /**   * 设置选中的状态   *    * @param isChecked   */  public voID setChecked(boolean isChecked) {    cb_status.setChecked(isChecked);    if (isChecked) {      tv_desc.setText(desc_on);    } else {      tv_desc.setText(desc_off);    }  } }

代码很简单,首先继承relativeLayout,复写其构造方法,在构造方法中先渲染布局的视图,然后读取属性集的属性,将默认显示的属性显示到布局上的子控件上即可。另外,还要对外提供一个判断状态的方法isChecked()来判断该控件是否被选中了,提供一个设置状态的方法setChecked(boolean),用来改变状态。PS:为了验证我上面的一段话,读者可以将继承relativeLayout,改为继承linearLayout或者继承FrameLayout,运行试试看,也是可以实现的。
 

下面是引用这个自定义组合控件的方法,首先需要在Activity的布局文件中定义出来:

<linearlayout androID:layout_height="match_parent" androID:layout_wIDth="match_parent" androID:orIEntation="vertical" xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:example="http://schemas.androID.com/apk/res/com.example.combinationvIEw">   <com.example.combinationvIEw.combinationvIEw androID:ID="@+ID/cv_first" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent" example:desc_off="我是未被选中的描述1" example:desc_on="我是被选中的描述1" example:title="我是标题1">  </com.example.combinationvIEw.combinationvIEw>   <com.example.combinationvIEw.combinationvIEw androID:ID="@+ID/cv_second" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent" example:desc_off="我是未被选中的描述2" example:desc_on="我是被选中的描述2" example:title="我是标题2">  </com.example.combinationvIEw.combinationvIEw>   <com.example.combinationvIEw.combinationvIEw androID:ID="@+ID/cv_third" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent" example:desc_off="我是未被选中的描述3" example:desc_on="我是被选中的描述3" example:title="我是标题3">  </com.example.combinationvIEw.combinationvIEw>   <com.example.combinationvIEw.combinationvIEw androID:ID="@+ID/cv_fourth" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent" example:desc_off="我是未被选中的描述4" example:desc_on="我是被选中的描述4" example:title="我是标题4">  </com.example.combinationvIEw.combinationvIEw> </linearlayout>

首先在上面定义了四个自定义组合控件,大家可以看到,代码精简多了不是?!需要注意的地方:这里引用了自定义的属性集,所以在布局节点上必须要加上命名空间

xmlns:example=http://schemas.androID.com/apk/res/com.example.combinationvIEw

其中,example是命名空间的名称,是任意取的,但是必须在控件中引用属性的名称一致,不然会报错。后面的一串是标明属性集的路径,前半部分是固定的,最后一个“/”后面的内容必须是工程的包名,否则报错。
 

下面是Activity里面的业务逻辑代码,没什么好说的

package com.example.combinationvIEw; import androID.os.Bundle;import androID.vIEw.VIEw;import androID.vIEw.VIEw.OnClickListener;import androID.app.Activity; public class MainActivity extends Activity implements OnClickListener {   private CombinationVIEw cv_first;  private CombinationVIEw cv_second;  private CombinationVIEw cv_third;  private CombinationVIEw cv_fourth;   @OverrIDe  protected voID onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentVIEw(R.layout.activity_main);    cv_first = (CombinationVIEw) findVIEwByID(R.ID.cv_first);    cv_second = (CombinationVIEw) findVIEwByID(R.ID.cv_second);    cv_third = (CombinationVIEw) findVIEwByID(R.ID.cv_third);    cv_fourth = (CombinationVIEw) findVIEwByID(R.ID.cv_fourth);    cv_first.setonClickListener(this);    cv_second.setonClickListener(this);    cv_third.setonClickListener(this);    cv_fourth.setonClickListener(this);  }   @OverrIDe  public voID onClick(VIEw v) {    switch (v.getID()) {    case R.ID.cv_first:      if (cv_first.isChecked()) {        cv_first.setChecked(false);      } else {        cv_first.setChecked(true);      }      break;    case R.ID.cv_second:      if (cv_second.isChecked()) {        cv_second.setChecked(false);      } else {        cv_second.setChecked(true);      }      break;    case R.ID.cv_third:      if (cv_third.isChecked()) {        cv_third.setChecked(false);      } else {        cv_third.setChecked(true);      }      break;    case R.ID.cv_fourth:      if (cv_fourth.isChecked()) {        cv_fourth.setChecked(false);      } else {        cv_fourth.setChecked(true);      }      break;    default:      break;    }  } }

好了,关于自定义组合控件就讲完了,非常简单,但是比较常用。以后在项目用到时,想想实现步骤,自定义一种的组合的控件,用起来确实比较方便,比单纯的复制黏贴不仅高大上,而且提高代码的复用性,简化了代码的结构和减少了代码量。

下面再来看这样的一个完整的实例,比较简单,直接上代码了:

package com.xiong.demo1;  import androID.app.Activity; import androID.os.Bundle; import androID.vIEw.VIEw; public class MainActivity extends Activity {    @OverrIDe   protected voID onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentVIEw(R.layout.main_activity);     TitlebarVIEw TitlebarVIEw = (TitlebarVIEw) findVIEwByID(R.ID.tbar_test);     TitlebarVIEw.getTextVIEwRigth().setVisibility(VIEw.GONE);     TitlebarVIEw.setTitlebarChangerliseter(new ITitleOnchangelister() {       @OverrIDe       public voID setleftOnClickLister() {         finish();       }        @OverrIDe       public voID setRigthOnClickLister() {        }     });   }  } 
<?xml version="1.0" enCoding="utf-8"?> <linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID"        xmlns:xionglh="http://schemas.androID.com/apk/res-auto"        androID:layout_wIDth="match_parent"        androID:layout_height="match_parent">    <com.xiong.demo1.TitlebarVIEw     androID:ID="@+ID/tbar_test"     androID:layout_wIDth="match_parent"     androID:layout_height="45dp"     xionglh:Titlebar_center_text="首页"     xionglh:Titlebar_center_textcolor="@androID:color/black"     xionglh:Titlebar_center_text_size="18sp"     xionglh:Titlebar_left_bg="@mipmap/left_back"     xionglh:Titlebar_right_text="安全中心"     xionglh:Titlebar_right_text_size="12sp"/> </linearLayout> 
<?xml version="1.0" enCoding="utf-8"?> <resources>   <declare-styleable name="Titlebar">      <attr name="Titlebar_center_text_size" format="dimension"/>     <attr name="Titlebar_center_text" format="string"/>     <attr name="Titlebar_center_textcolor" format="color"/>      <attr name="Titlebar_left_bg" format="reference"/>      <attr name="Titlebar_right_text_size" format="dimension"/>     <attr name="Titlebar_right_text" format="string"/>     <attr name="Titlebar_right_textcolor" format="color"/>    </declare-styleable>  </resources> 
package com.xiong.demo1;  import androID.content.Context; import androID.content.res.TypedArray; import androID.graphics.color; import androID.util.AttributeSet; import androID.util.TypedValue; import androID.vIEw.VIEw; import androID.vIEw.VIEwGroup; import androID.Widget.ImageVIEw; import androID.Widget.relativeLayout; import androID.Widget.TextVIEw;  public class TitlebarVIEw extends relativeLayout {    private ITitleOnchangelister mITitleOnchangelister;    private ImageVIEw mimgleft;   private TextVIEw mTxtCenter,mTxtRigth;    private float mTitleCenterTextSize;//标题字体大小   private String mTitleCenterText;//标题文字   private int mTitleCenterTextcolor;//标题颜色    private int mleftBg;//左边返回按钮    private float mTitleRigthTextSize;//标题字体大小   private String mTitleRigthText;//标题文字   private int mTitleRigthcolor;//标题颜色    public TitlebarVIEw(Context context,AttributeSet attrs) {     super(context,attrs);     int defualtSize = (int) TypedValue.applyDimension(         TypedValue.COMPLEX_UNIT_SP,16,getResources().getdisplayMetrics());     TypedArray typedArray = getContext().obtainStyledAttributes(attrs,R.styleable.Titlebar);     mTitleCenterTextSize = typedArray.getDimension(R.styleable.Titlebar_Titlebar_center_text_size,defualtSize);     mTitleCenterText = typedArray.getString(R.styleable.Titlebar_Titlebar_center_text);     mTitleCenterTextcolor = typedArray.getcolor(R.styleable.Titlebar_Titlebar_center_textcolor,color.RED);     mleftBg = typedArray.getResourceID(R.styleable.Titlebar_Titlebar_left_bg,R.mipmap.left_back);     mTitleRigthTextSize = typedArray.getDimension(R.styleable.Titlebar_Titlebar_right_text_size,defualtSize);     mTitleRigthText = typedArray.getString(R.styleable.Titlebar_Titlebar_right_text);     mTitleRigthcolor = typedArray.getcolor(R.styleable.Titlebar_Titlebar_right_textcolor,color.RED);     typedArray.recycle();     initVIEw();   }    private voID initVIEw() {     mTxtCenter = new TextVIEw(getContext());     mTxtCenter.setText(mTitleCenterText);     mTxtCenter.setTextSize(TypedValue.COMPLEX_UNIT_PX,mTitleCenterTextSize);     mTxtCenter.setTextcolor(mTitleCenterTextcolor);     LayoutParams centerParams = new LayoutParams(VIEwGroup.LayoutParams.WRAP_CONTENT,VIEwGroup.LayoutParams.WRAP_CONTENT);     centerParams.addRule(relativeLayout.CENTER_IN_PARENT,TRUE);     addVIEw(mTxtCenter,centerParams);     mTxtRigth = new TextVIEw(getContext());     mTxtRigth.setText(mTitleRigthText);     mTxtRigth.setTextSize(TypedValue.COMPLEX_UNIT_PX,mTitleRigthTextSize);     mTxtRigth.setTextcolor(mTitleRigthcolor);     LayoutParams rigthParams = new LayoutParams(VIEwGroup.LayoutParams.WRAP_CONTENT,VIEwGroup.LayoutParams.WRAP_CONTENT);     rigthParams.addRule(relativeLayout.AliGN_PARENT_RIGHT,TRUE);     rigthParams.addRule(relativeLayout.CENTER_VERTICAL,TRUE);      addVIEw(mTxtRigth,rigthParams);     mimgleft = new ImageVIEw(getContext());     mimgleft.setimageResource(mleftBg);     LayoutParams leftParams = new LayoutParams(VIEwGroup.LayoutParams.WRAP_CONTENT,VIEwGroup.LayoutParams.WRAP_CONTENT);     leftParams.setmargins(6,0);     leftParams.addRule(relativeLayout.CENTER_VERTICAL,TRUE);     addVIEw(mimgleft,leftParams);     VIEw vIEw = new VIEw(getContext());     vIEw.setMinimumWIDth(1);     vIEw.setBackgroundcolor(getResources().getcolor(R.color.gray_767676));     LayoutParams vIEwParams = new LayoutParams(VIEwGroup.LayoutParams.WRAP_CONTENT,1);     vIEwParams.addRule(relativeLayout.AliGN_PARENT_BottOM);     addVIEw(vIEw,vIEwParams);     mimgleft.setonClickListener(new OnClickListener() {       @OverrIDe       public voID onClick(VIEw v) {         mITitleOnchangelister.setleftOnClickLister();       }     });      mTxtRigth.setonClickListener(new OnClickListener() {       @OverrIDe       public voID onClick(VIEw v) {         mITitleOnchangelister.setRigthOnClickLister();       }     });    }    public voID setTitlebarChangerliseter(ITitleOnchangelister iTitleOnchangelister) {     this.mITitleOnchangelister = iTitleOnchangelister;   }     public ImageVIEw getLeftimage() {     return mimgleft;   }    public TextVIEw getTextVIEwCenter() {     return mTxtCenter;   }    public TextVIEw getTextVIEwRigth() {     return mTxtRigth;   } } package com.xiong.demo1; public interface ITitleOnchangelister {     voID setleftOnClickLister();   voID setRigthOnClickLister();  } 
总结

以上是内存溢出为你收集整理的Android中View自定义组合控件的基本编写方法全部内容,希望文章能够帮你解决Android中View自定义组合控件的基本编写方法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1141460.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存