Android自定义复合控件实现通用标题栏

本文实例为大家分享了Android复合控件实现通用标题栏的具体代码,供大家参考,具体内容如下

效果图

估计大家应该和我一样,每次去看别人博客的时候,都喜欢一拉到底,先看看有没有效果图,符不符合自己的需求,符合咱就继续看,不符合免得浪费表情,所以效果图先上为敬

写在前面的一点儿废话

作为Android的菜鸟一枚,一直觉得能够写自定义控件是一个很炫酷的技能,最近看了徐宜生老师的群英传之后,感觉收获还是挺多的。这篇文章就主要记录的是学习自定义控件中最简单的复合控件的过程。虽然现在MD中Toolbar已经完全满足各种各样的需求,但对于我这种菜鸟来说自己动手写一个还是能学到很多东西的!

1、自定义控件的属性

既然是自定义的控件,肯定得提供属性选项,以方便实现不同的样式。提供自定义的属性是很简单的,在res资源目录下的values目录下创建一个attrs.xml的属性集定义的xml文件,在该文件中自定义各种必要的属性

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

 <declare-styleable name="NormalTopBar">
  <!--中间标题属性-->
  <attr name="titleText" format="string"/>
  <attr name="titleTextSize" format="dimension"/>
  <attr name="titleTextColor" format="color"/>
  <attr name="leftText" format="string"/>

  <!--左边按钮属性-->
  <attr name="leftTextSize" format="dimension"/>
  <attr name="leftTextColor" format="color"/>
  <attr name="leftImageSrc" format="reference"/>
  <attr name="rightText" format="string"/>

  <!--右边按钮属性-->
  <attr name="rightTextSize" format="dimension"/>
  <attr name="rightTextColor" format="color"/>
  <attr name="leftBackground" format="color"/>
  <attr name="rightBackground" format="color"/>
  <attr name="rightImageSrc" format="reference"/>
 </declare-styleable>
</resources>

既然自定义了属性,就需要在自定义控件模板中去获取这些属性的赋值,以处理得到相应的显示效果。在这里,系统提供了TypeArray类,获取到该类的实例后就可通过getString()等方法获得布局文件中设置的属性值

 private void getTypeArray(Context context, AttributeSet attrs) {
  //将attrs.xml中定义的属性存储到TypeArray中
  TypedArray typeArray=context.obtainStyledAttributes(attrs,R.styleable.NormalTopBar);

  leftText=typeArray.getString(R.styleable.NormalTopBar_leftText);
  leftTextColor=typeArray.getColor(R.styleable.NormalTopBar_leftTextColor, Color.BLACK);
  leftTextSize=typeArray.getDimension(R.styleable.NormalTopBar_leftTextSize,12);
  leftImageId=typeArray.getResourceId(R.styleable.NormalTopBar_leftImageSrc,0);
  titleText=typeArray.getString(R.styleable.NormalTopBar_titleText);
  titleTextColor=typeArray.getColor(R.styleable.NormalTopBar_titleTextColor,Color.BLACK);
  titleTextSize=typeArray.getDimension(R.styleable.NormalTopBar_titleTextSize,20);
  rightText=typeArray.getString(R.styleable.NormalTopBar_rightText);
  rightTextColor=typeArray.getColor(R.styleable.NormalTopBar_rightTextColor,Color.BLACK);
  rightTextSize=typeArray.getDimension(R.styleable.NormalTopBar_rightTextSize,12);
  rightImageId=typeArray.getResourceId(R.styleable.NormalTopBar_rightImageSrc,0);

  typeArray.recycle();//获取完所有属性后需要调用recycle来避免重新创建发生的错误

 }

参数中attrs是控件构造函数中传入的属性集参数,而R.styleable.NormalTopBar就是在attrs.xml文件中定义的该控件属性集的名字。

2、动态添加控件组合成自定义符合控件

标题栏中一般包括了左边的按钮,中间的标题,右边的按钮。在本文中,我把该控件分成了5个部分,左边有一个ImageView和一个TextView用于用户点击,中间有一个TextView用于显示标题,右边和左边一样,成对称分布,然后这些控件的父控件是RelativeLayout,方便子控件的布局。了解了有哪些控件之后,就可以初始化这些控件对象,然后分别指定合适的布局,动态添加布局中。

 private void addAllView(Context context) {
  leftTextView =new TextView(context);
  rightTextView =new TextView(context);
  titleTextView=new TextView(context);
  leftImage=new ImageView(context);
  rightImage=new ImageView(context);

  leftImage.setId(R.id.leftimageid);
  leftImage.setImageResource(leftImageId);
  //leftImage.setAdjustViewBounds(true);

  leftTextView.setText(leftText);
  leftTextView.setTextSize(leftTextSize);
  leftTextView.setTextColor(leftTextColor);

  titleTextView.setText(titleText);
  titleTextView.setTextSize(titleTextSize);
  titleTextView.setTextColor(titleTextColor);
  titleTextView.setGravity(Gravity.CENTER);//一定要设置textview内容的位置

  rightTextView.setText(rightText);
  rightTextView.setTextSize(rightTextSize);
  rightTextView.setTextColor(rightTextColor);

  rightImage.setId(R.id.rightimageid);
  rightImage.setImageResource(rightImageId);

  //为组建设置相应的布局

  if(leftImageId!=0&&leftText!=null){
   leftImageParams=new LayoutParams(dpToPx(context,35), dpToPx(context,35));
   leftImageParams.addRule(ALIGN_PARENT_LEFT,TRUE);
   leftImageParams.addRule(CENTER_VERTICAL,TRUE);
   addView(leftImage,leftImageParams);

   leftTextParams =new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
   leftTextParams.addRule(RelativeLayout.RIGHT_OF,R.id.leftimageid);
   leftTextParams.addRule(CENTER_VERTICAL,TRUE);
   leftTextView.setGravity(Gravity.LEFT);
   addView(leftTextView, leftTextParams);
  }else if(leftImageId!=0&&leftText==null){
   leftImageParams=new LayoutParams(dpToPx(context,35), dpToPx(context,35));
   leftImageParams.addRule(ALIGN_PARENT_LEFT,TRUE);
   leftImageParams.addRule(CENTER_VERTICAL,TRUE);
   addView(leftImage,leftImageParams);
  }else{
   leftTextParams =new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
   leftTextParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
   leftTextParams.addRule(CENTER_VERTICAL,TRUE);
   addView(leftTextView, leftTextParams);
  }

  titleParams=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
  titleParams.addRule(CENTER_IN_PARENT,TRUE);
  titleParams.addRule(TEXT_ALIGNMENT_CENTER);
  addView(titleTextView,titleParams);

  if(rightImageId!=0&&rightText!=null){
   rightTextParams =new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
   rightTextParams.addRule(RelativeLayout.LEFT_OF,R.id.rightimageid);
   rightTextParams.addRule(RelativeLayout.CENTER_VERTICAL,TRUE);
   rightTextView.setGravity(Gravity.RIGHT);
   addView(rightTextView, rightTextParams);
   rightImageParams=new LayoutParams(dpToPx(context,35), dpToPx(context,35));
   rightImageParams.addRule(CENTER_VERTICAL,TRUE);
   rightImageParams.addRule(ALIGN_PARENT_RIGHT,TRUE);
   addView(rightImage,rightImageParams);
  }else if(rightImageId!=0&&rightText==null){
   rightImageParams=new LayoutParams(dpToPx(context,35), dpToPx(context,35));
   rightImageParams.addRule(CENTER_VERTICAL,TRUE);
   rightImageParams.addRule(ALIGN_PARENT_RIGHT,TRUE);
   addView(rightImage,rightImageParams);
  }else{
   rightTextParams =new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
   rightTextParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
   rightTextParams.addRule(RelativeLayout.CENTER_VERTICAL,TRUE);
   addView(rightTextView, rightTextParams);
  }

 }

这一段代码首先是初始化得到各子控件的实例对象,然后将属性值赋值给对应的控件,接着利用LayoutParams类对各子空间的大小和位置进行设定,最后利用addView方法即可将这些子控件添加到控件整体布局中。

这段代码中,主要的难点在于运用LayoutParams,要注意该布局的外层viewGroup是RelativeLayout,所以在定义和初始化的时候都需要使用RelativeLayout.LayoutParams.另外LayoutParams的构造函数中的参数用于控制大小,我在设置ImageView对应的LayoutParams时,最开始把宽和高都设置为WRAP_CONTENT,但是运行后效果不理想,imageview宽度占据了一半的空间,最后决定对该控件的大小指定尺寸大小,不过要注意构造函数中的数值单位是px,所以需要先定义一个函数将dp转为px再赋值给构造函数。

这段代码的另外一个难点是,当我两侧的按钮同时有文字和图标时,对于ImageView和TextView的定位是个问题。在下面代码中

leftTextParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);

这行代码将TextView定位在了父控件的左侧,而当左侧同时有ImageView和TextView时二者肯定就会重叠,这肯定不是想要的结果。所以需要把ImageView仍定位在最左边,然后TextView定位在前者的右边,而在方法addRule()中,可以使用 addRule(RelativeLayout.RIGHT_OF,int view) 来把对应的控件定位在参数中view控件的右边,但是该参数需要的是资源ID,可问题是在上面我们是动态添加的ImageView,并没有在xml文件中定义id。我尝试了直接用imageview.getId(),但得到的结果经调试发现是-1,并不能实现想要的效果,最后一搜找到了一个方法,首先在资源目录res下的values下再新建一个ids.xml的文件,然后在文件中定义一个类型为id的item

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <item name="leftimageid" type="id"/>
 <item name="rightimageid" type="id"/>
</resources>

然后利用ImageView.setId(R.id.leftimageid) 就能给动态添加的控件赋值一个不会与其他资源id重复的id,接着就可以在布局中使用。

3、定义接口暴露给调用者

到目前位置,编写的自定义控件已经可以在xml布局文件中使用,而且也能在界面上显示出来,但是左右两侧的按钮点击事件对于不同的使用者或者不同的页面,所要完成的动作肯定是不一样的,所以得暴露一个接口给调用者自己去实现。

public interface normalTopClickListener{
  void onLeftClick(View view);
  void onRightClick(View view);
 }

然后给调用者提供一个set函数让调用者来实现该接口中的方法

 public void setTopClickListener(normalTopClickListener mListener){
  this.mClickListener =mListener;
 }

最后在控件模板中,在左右控件的点击事件里去调用接口的方法,即可得到调用者的具体实现

private void addOnClick() {

  leftTextView.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View view) {
    mClickListener.onLeftClick(view);
   }
  });

  leftImage.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View view) {
    mClickListener.onLeftClick(view);
   }
  });

  rightTextView.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View view) {
    mClickListener.onRightClick(view);
   }
  });

  rightImage.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View view) {
    mClickListener.onRightClick(view);
   }
  });

 }

github源码

结语

终于写完了第一篇博客,说句实在的,第一次写起来感觉真不简单。如果文中有任何错误或者建议,欢迎指出,不胜感激

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义状态栏颜色与应用标题栏颜色一致

    每次看IOS上的应用,应用中状态栏的颜色总能与应用标题栏颜色保持一致,用户体验很不错,对于这种效果,像我这种好奇心强的人就会去看看那安卓是否可以呢?若是在安卓4.4之前,答案是否定的,但在4.4之后,谷歌允许开发者自定义状态栏背景颜色啦,这是个不错的体验!若你手机上安装有最新版的qq,并且你的安卓SDK版本是4.4及以上,你可以看下它的效果: 实现此功能有两种方法: 1.在xml中设置主题或自定义style: Theme.Holo.Light.NoActionBar.TranslucentDec

  • Android自定义简单的顶部标题栏

    本文实例为大家分享了Android实现简单顶部标题栏的具体代码,供大家参考,具体内容如下 实现功能: 1)自定义View标题栏布局: 2)灵活的可以自己传入类型,选择所需要的控件来显示隐藏 3)相对于我之前写过的一篇,免继承,可直接在布局里使用 4)直接可以在布局控件里设置属性 老规矩,上几张效果图: 由效果图可见,这个是可以根据传入type来控制,比较灵活的 下面就来实现以下步骤,最后我会贴上源码 1.创建一个布局文件,命名,layout_titlebar,来部署我们的标题栏样式,可以自定义更

  • Android中自定义标题栏样式的两种方法

    原装的Android标题栏配色比较单调,就是黑色的一坨,现在假设你的软件需要独自添加标题栏,这样不仅美观而且可以将进度条等加进去,如何实现: 方法一.在你的那张Activity中onCreate方法中加上下面代码: requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); setContentView(R.layout.main); //软件activity的布局 getWindow().setFeatureInt(Window.FEATURE_CUS

  • Android 顶部标题栏随滑动时的渐变隐藏和渐变显示效果

    各位早上好,话不多说,先上效果图: 注意顶部:首页TextView的变化(显示和隐藏)! 首先分析下:UI状态,其是由RecyclerView添加头部组成+RecyclerView 头部添加和RecyclerView分别引用如下:具体的分装数据的过程这里就不在说明,下篇博客会更加深入的写关于 RecyclerView总添加多种不同type类型 compile 'com.bartoszlipinski.recyclerviewheader:library:1.2.1' compile 'com.a

  • Android自定义通用标题栏CustomTitleBar

    本文实例为大家分享了Android自定义通用标题栏的具体代码,供大家参考,具体内容如下/p> 1自定义一个public_titlebar.xml文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root

  • Android 全屏无标题栏的三种实现方法

    一.通过Java代码 在setContentView之前执行: requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题栏 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);//隐藏状态栏 二.调用Android自带的Theme 直接在AndroidManifest.xml中需要全屏显

  • 3种Android隐藏顶部状态栏及标题栏的方法

    本文包含3种隐藏顶部状态栏及标题栏和一种隐藏Android 4.0平板底部状态栏的方法,分享给大家供大家参考,具体内容如下 方法一 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 隐藏标题栏 requestWindowFeature(Window.FEA

  • Android组合控件自定义标题栏

    本文实例为大家分享了Android简单的自定义标题栏,供大家参考,具体内容如下 android自定义控件向来都是开发者最头疼的,但是我们要有那种迎难而上的精神. MainActivity package com.example.customview; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import a

  • Android中去掉标题栏的几种方法(三种)

    1.在java代码中 (SplashActivity继承AppCompatActivity时无效) 2.在manifest.xml中改Theme 3.先在style.xml中自定义style <?xml version="1.0" encoding="UTF-8" ?> <resources> <style name="notitle"> <item name="android:windowNo

  • Android中隐藏标题栏和状态栏的方法

    一.隐藏标题栏 复制代码 代码如下: //隐藏标题栏        this.requestWindowFeature(Window.FEATURE_NO_TITLE); 二.隐藏状态栏 复制代码 代码如下: //隐藏状态栏        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 三.去掉所有Activity界

随机推荐