Andriod 自定义控件之建立能够复用的组合控件

前面已学习了一种自定义控件的实现,是Andriod 自定义控件之音频条,还没学习的同窗能够学习下,学习了的同窗也要去温习下,必定要本身彻底的掌握了,再继续学习,贪多嚼不烂可不是好的学习方法,咱们争取学习了一种技术就会一种技术,并且不光看了就算了,最好的方法就是看完我本身再练习下,再扩展下,在原来的基础上在添加一些东西,好比,增长一些功能实现等等。html

今天咱们打算学习下另一种自定义控件,就是建立可重复使用的组合控件,那么问题来了:python

  • 什么是可重复使用?
    就是在应用中,能够在多个地方共同使用一套代码。这样不只能减小咱们的工做量,并且还能保持应用风格的一致,这种应用最多最直接的体现就是统一风格样式的标题栏。
  • 那什么又是组合控件呢?
    组合控件,顾名思义就是多个控件组合在一块儿,相互协做共同完成某些特定的功能。

下面咱们就针对app应用中风格统一的标题栏来开始咱们的学习。android

首先,既然是一组组合的控件,那就必须有一个能够来包含这些控件的容器,咱们所接触的能够存放控件的容器不少,好比LinearLayout、RelativeLayout等等多种Layout,今天咱们就选择RelativeLayout来作咱们的容器。和之前同样,咱们先定义一个CompositeViews类来继承RelativeLayout类,并重写它的构造方法,代码以下:微信

public class CompositeViews extends RelativeLayout{

    public CompositeViews(Context context) {
        this(context,null);
    }
    public CompositeViews(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public CompositeViews(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
 }

接下来,咱们再定义三个TextView控件,分别是mLefeText,mRightText,textTitle用来显示“返回”,“搜索”以及“标题”而且使用LayoutParams规定它们在容器里面的对齐方式。请看代码:app

public class CompositeViews extends RelativeLayout{
    private TextView mLefeText;
    private TextView mRightText;
    private TextView textTitle;
    private LayoutParams leftLayoutParams;
    private LayoutParams ridhtLayoutParams;
    private LayoutParams titleLayoutParams;

    public CompositeViews(Context context) {
        this(context,null);
    }
    public CompositeViews(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public CompositeViews(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

initView(context)方法中是用来初始化三个组合控件的,请看:eclipse

private void initView(Context context) {
        mLefeText = new TextView(context);
        mRightText = new TextView(context);
        textTitle = new TextView(context);
        /*
         * 左按钮位置
         */
        leftLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        leftLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
        mLefeText.setText("返回");
        mLefeText.setTextSize(22);
        /*
         * 右按钮位置
         */
        ridhtLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        ridhtLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
        mRightText.setText("搜索");
        mRightText.setTextSize(22);
        /*
         * 中间标题位置
         */
        titleLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        titleLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
        textTitle.setText("这是一个标题");
        textTitle.setTextSize(22);
    }

ok,以上的代码已经实现了组合控件的显示和对齐方式,咱们把定义的View添加到布局文件中并在Activity加载吧ide

activity_main.xml文件布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:background="#999999"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.sanhuimusic.mycustomview.view.CompositeViews
        android:id="@+id/topBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</LinearLayout>

MainActivity:学习

public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

咱们先来运行看下结果ui

这里写图片描述

ok,已显示出来了,可是相信你们也看出来了,这上面的代码中,各个控件中的属性是都是咱们固定写死的,既然咱们是建立可服用的控件,固定写死的东西确定是不可取的,那么咱们怎么能够灵活地获取控件的属性,以致于能达到复用呢?

这就必需要接触另一种技术了,就是自定义属性。用咱们自定义的属于能够在每次使用咱们定义的控件时为其分配属性便可。下面咱们来学习下自定义属性。

自定义属性其实也是至关的简单,首先,咱们如今资源文件res下values目录下新建一个attrs.xml文件(eclipse自带,as自建),新建的attrs.xml是一个包含以下代码的文件:

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

</resources>

在resources中有各类属性供咱们使用,同窗们能够本身看下。根据咱们如今的需求,咱们选择使用declare-styleable来声明咱们的属性集,而后为其定义特有的name属性,这个name是供咱们在使用自定义属性时,经过它能够查找到里面的全部属性。请看以下代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CompositeViews">
        <attr name="titleText" format="string"/>
        <attr name="titleTextSize" format="dimension"/>
        <attr name="titleColor" format="color"/>
        <attr name="titleBackground" format="color|reference"/>

        <attr name="leftTextColor" format="color"/>
        <attr name="leftBackground" format="color|reference"/>
        <attr name="leftText" format="string"/>
        <attr name="leftTextSize" format="dimension"/>

        <attr name="rightTextColor" format="color"/>
        <attr name="rightBackground" format="color|reference"/>
        <attr name="rightText" format="string"/>
        <attr name="rightTextSize" format="dimension"/>
    </declare-styleable>

</resources>

单独拿一行属性来解析下它所表明的含义:如 :attr表明的是一个属性,它里面所包含name字段是这条属性名,经过该属性名能够获取以format的约束为真的属性值;formate是该属性的格式,分别包含string,dimension,color,reference等等,分别表明字符串,大小,颜色,引用。其余的你们能够自行学习resources所包含的属性。

好了,自定义属性咱们已学习完毕,那么该怎么使用咱们本身定义的属性呢?其实也很简单,在咱们的activity_main.xml文件中直接使用咱们定义的属性就能够了,可是在使用是以前必须在指定引用第三方控件的命名空间,在跟布局文件中添加以下一行代码:

xmlns:custom="http://schemas.android.com/apk/res-auto"

custom是咱们第三方命名空间的名字,能够任意命名,咱们在使用自定义属性时必须以它开头。请看代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:background="#999999"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.sanhuimusic.mycustomview.view.CompositeViews
        android:id="@+id/topBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"

        custom:titleText="@string/titleText"
        custom:titleColor="#000000"
        custom:titleTextSize="@dimen/titleTextSize"
        custom:titleBackground="#999999"
        custom:leftText="@string/leftText"
        custom:leftTextColor="#FFFFFF"
        custom:leftBackground="#666666"
        custom:leftTextSize="@dimen/leftTextSize"
        custom:rightText="@string/rightText"
        custom:rightTextColor="#FFFFFF"
        custom:rightBackground="#666666"
        custom:rightTextSize="@dimen/rightTextSize"
        />
 
</LinearLayout>

咱们是使用custom加上咱们自定义属性里面< attr name="titleText" format="string"/>里的name值来动态设置属性值的,如:custom:titleText="@string/titleText"。

ok,在咱们xml文件中已设定好属性值,那么该怎么显示出来呢?这个是须要经过一个类型组TypedArray来获取的,它里面包含各类从AttributeSet属性集中获取属性的方法,因此咱们修改上面的构造方法和initView(context)方法,以下所示:

private void initView(Context context, AttributeSet attrs) {
        mLefeText = new TextView(context);
        mRightText = new TextView(context);
        textTitle = new TextView(context);

        /**
         * 获取自定义属性
         */
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CompositeViews);
        String titleText = typedArray.getString(R.styleable.CompositeViews_titleText);
        float titleTextSize = typedArray.getDimension(R.styleable.CompositeViews_titleTextSize, 16);
        int titleColor = typedArray.getColor(R.styleable.CompositeViews_titleColor,0);
        Drawable titleBackground = typedArray.getDrawable(R.styleable.CompositeViews_titleBackground);

        String leftText = typedArray.getString(R.styleable.CompositeViews_leftText);
        int leftTextColor = typedArray.getColor(R.styleable.CompositeViews_leftTextColor, 0);
        float leftTextSize = typedArray.getDimension(R.styleable.CompositeViews_leftTextSize, 16);
        Drawable leftBackground = typedArray.getDrawable(R.styleable.CompositeViews_leftBackground);

        String rightText = typedArray.getString(R.styleable.CompositeViews_rightText);
        int rightTextColor = typedArray.getColor(R.styleable.CompositeViews_rightTextColor, 0);
        float rightTextSize = typedArray.getDimension(R.styleable.CompositeViews_rightTextSize, 16);
        Drawable rightBackground = typedArray.getDrawable(R.styleable.CompositeViews_rightBackground);
        typedArray.recycle();
        /*
         * 左按钮位置
         */
        leftLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        leftLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
        mLefeText.setText(leftText);
        mLefeText.setTextColor(leftTextColor);
        mLefeText.setTextSize(leftTextSize);
        mLefeText.setBackground(leftBackground);
        addView(this.mLefeText,leftLayoutParams);
         /*
         * 右按钮位置
         */
        ridhtLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        ridhtLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
        mRightText.setText(rightText);
        mRightText.setTextColor(rightTextColor);
        mRightText.setTextSize(rightTextSize);
        mRightText.setBackground(rightBackground);
        addView(mRightText,ridhtLayoutParams);
        /*
         * 中间标题位置
         */
        titleLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        titleLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
        textTitle.setText(titleText);
        textTitle.setTextSize(titleTextSize);
        textTitle.setTextColor(titleColor);
        textTitle.setBackground(titleBackground);
        addView(textTitle,titleLayoutParams);
    }

代码解释:首先经过上下文context获取到属性存放到TypedArray 中,而后经过TypedArray 里封装好的各类方法获取对应的属性值,而后再分别为咱们的控件设置属性。这样就完成了,自定义属性的使用,而且复用度高,每当须要使用标题栏是都只须要在xml中添加咱们定义的View控件,为其配置属性便可使用,节约了开发时间,提升了效率,而且还保持的app风格的一致。

好,到这里感受已经讲完了整个过程吧,其实还有一个重要的实现尚未讲。咱们的控件已经能够呈现出来了,可是怎么完成里面控件的做用呢?

这里比较常见的作法是利用回调机制来实现功能的开发,首先咱们先定义一个接口,建立两个方法,用于左右控件的点击事件。

public interface TopBarClickListener{
            void leftClickListener();
            void rightClickListener();
    }

而后在构造方法中为左右控件添加点击事件,但不实现功能,等待调用者本身实现:

private void setListener() {
        mLefeText.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mTopBarClickListener.leftClickListener();
            }
        });
        mRightText.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mTopBarClickListener.rightClickListener();
            }
        });
    }

再者,把定义好的接口暴露给调用者:

public void setOnTopBarClickListener(TopBarClickListener topBarClickListener){
        mTopBarClickListener = topBarClickListener;
    }

最后,谁调用,谁实现。这就完成了不一样界面复用控件实现不一样的功能的便利。在这里咱们只在MainActivity中打印Toast就能够了。

public class MainActivity extends AppCompatActivity {
    private CompositeViews topBar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        topBar = (CompositeViews) findViewById(R.id.topBar);
        topBar.setOnTopBarClickListener(new CompositeViews.TopBarClickListener(){
            @Override
            public void leftClickListener() {
                ToastUtil.makeText(MainActivity.this,"您点击了返回键",Toast.LENGTH_SHORT).show();
            }
            @Override
            public void rightClickListener() {
                ToastUtil.makeText(MainActivity.this,"您点击了搜索键",Toast.LENGTH_SHORT).show();
            }
        });
    }
}

OK,看看结果吧

这里写图片描述

好,已经能够实现咱们的需求了,是否是学会不少呢。

今天主要讲了android自定义View中另外一种的实现,而且还学习了自定义属性,同窗们下去好好消化下,并本身动手现实一两个例子吧,好了,今天就讲到这里,谢谢你们。

更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。

这里写图片描述

相关文章
相关标签/搜索