#安卓杂记(七):自定义控件及属性获取

平日里喜欢阅读网易云阅读客户端的资讯,对订阅版块的设计甚是喜爱,就想琢磨着如何实现类似于卡片式的文件夹的样式:
网易云客户端订阅版块
显然,安卓里没有自带的这样的控件。有人一定会问,每个卡片无非就是个LinearLayout或RelativeLayout啊。的确没错。如果用RelativeLayout写的话,许是这样的罢:

<RelativeLayout 
            android:id="@+id/news01"
            android:layout_height="match_parent"
            android:layout_width="0dp"
            android:layout_marginTop="2dp"
            android:layout_marginLeft="2dp"
            android:background="@color/white"
            android:clickable="true"
            android:layout_weight="1">

            <ImageView 
                android:contentDescription="@string/news_image"
                android:src="@drawable/news01"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:layout_centerInParent="true"/>

            <TextView 
                android:layout_centerHorizontal="true"
                android:layout_alignParentBottom="true"
                android:layout_marginBottom="10dp"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:text="国内"/>

 </RelativeLayout> 

嗯,确实没错,可是十个RelativeLayout一起叠加下去,不仅代码冗余难读,而且不易维护。再者,如果我们要在这个Layout上加个Listener呢,莫非也要一个一个加?显然不成。由此,自定义控件就变得格外有意义。

一、定义xml样式

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/news_card"
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:focusable="true"
            android:clickable="true"
            android:background="@color/white">

            <ImageView 
                android:id="@+id/card_image"
                android:src="@drawable/news02"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:layout_centerInParent="true"/>

            <TextView 
                android:id="@+id/card_text"
                android:layout_centerHorizontal="true"
                android:layout_below="@id/card_image"
                android:layout_marginTop="10dp"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:text="card_name"/>

        </RelativeLayout>

我们期待的样子是这样嘀:
自定义样式

二、定义attrs.xml文件

定义自定义控件属性需要在res/values文件下定义一个attrs.xml文件,以布局文件可以调用定义属性:

<?xml version="1.0" encoding="utf-8"?>
    <resources >
        <declare-styleable name="newsCard">
            <attr name="card_id" format="integer"/>
            <attr name="text" format="string"/>
            <attr name="src" format="reference"/>
        </declare-styleable>
    </resources> 

其中,中的format有以下几种格式:
string , integer , dimension , reference , color , enum
reference指资源引用。

三、布局文件中使用控件属性

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.example.courseexamplev2"
    android:id="@+id/news_layout"
    android:layout_margin="10dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

   <LinearLayout 
        android:baselineAligned="false"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/background_blue"
        android:orientation="horizontal">

       <com.example.courseexamplev2.NewsCard
           android:id="@+id/card01"
           android:layout_width="0dp"
           android:layout_marginTop="2dp"
           android:layout_marginLeft="2dp"
           android:layout_height="match_parent"
           android:layout_weight="1"
           app:card_id="1"
           app:text="国内"
           app:src="@drawable/news01">

       </com.example.courseexamplev2.NewsCard>

        <com.example.courseexamplev2.NewsCard
           android:id="@+id/card02"
           android:layout_width="0dp"
           android:layout_marginTop="2dp"
           android:layout_marginLeft="2dp"
           android:layout_height="match_parent"
           android:layout_weight="1"
           app:card_id="2"
           app:text="国际"
           app:src="@drawable/news02">  
       </com.example.courseexamplev2.NewsCard>

        <com.example.courseexamplev2.NewsCard
           android:id="@+id/card03"
           android:layout_width="0dp"
           android:layout_marginTop="2dp"
           android:layout_marginLeft="2dp"
           android:layout_marginRight="2dp"
           android:layout_height="match_parent"
           android:layout_weight="1"
           app:card_id="3"
           app:text="社会"
           app:src="@drawable/news03"> 
       </com.example.courseexamplev2.NewsCard>

    </LinearLayout>

    <LinearLayout 
        android:baselineAligned="false"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/background_blue"
        android:orientation="horizontal">

        <com.example.courseexamplev2.NewsCard
           android:id="@+id/card04"
           android:layout_width="0dp"
           android:layout_marginTop="2dp"
           android:layout_marginLeft="2dp"
           android:layout_height="match_parent"
           android:layout_weight="1"
           app:card_id="4"
           app:text="财经"
           app:src="@drawable/news04">  
       </com.example.courseexamplev2.NewsCard>

        <com.example.courseexamplev2.NewsCard
           android:id="@+id/card05"
           android:layout_width="0dp"
           android:layout_marginTop="2dp"
           android:layout_marginLeft="2dp"
           android:layout_height="match_parent"
           android:layout_weight="1"
           app:card_id="5"
           app:text="体育"
           app:src="@drawable/news05">  
       </com.example.courseexamplev2.NewsCard>

        <com.example.courseexamplev2.NewsCard
           android:id="@+id/card06"
           android:layout_width="0dp"
           android:layout_marginTop="2dp"
           android:layout_marginLeft="2dp"
           android:layout_marginRight="2dp"
           android:layout_height="match_parent"
           android:layout_weight="1"
           app:card_id="6"
           app:text="科技"
           app:src="@drawable/news06">  
       </com.example.courseexamplev2.NewsCard>

    </LinearLayout>

    <LinearLayout 
        android:baselineAligned="false"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/background_blue"
        android:orientation="horizontal">

        <com.example.courseexamplev2.NewsCard
           android:id="@+id/card07"
           android:layout_width="0dp"
           android:layout_marginTop="2dp"
           android:layout_marginLeft="2dp"
           android:layout_height="match_parent"
           android:layout_weight="1"
           app:card_id="7"
           app:text="播客"
           app:src="@drawable/news07">
        </com.example.courseexamplev2.NewsCard>

        <com.example.courseexamplev2.NewsCard
           android:id="@+id/card08"
           android:layout_width="0dp"
           android:layout_marginTop="2dp"
           android:layout_marginLeft="2dp"
           android:layout_height="match_parent"
           android:layout_weight="1"
           app:card_id="8"
           app:text="股市"
           app:src="@drawable/news08">
        </com.example.courseexamplev2.NewsCard>

         <RelativeLayout
             android:id="@+id/news09"
             android:layout_width="0dp"
             android:layout_height="match_parent"
             android:layout_marginTop="2dp"
             android:layout_marginLeft="2dp"
             android:layout_marginRight="2dp"
             android:background="@color/white"
             android:layout_weight="1" >

            <ImageView 
                android:src="@drawable/icon_add"
                android:contentDescription="@string/news_image"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:layout_centerInParent="true"/>

        </RelativeLayout>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/background_blue"
        android:baselineAligned="false"
        android:orientation="horizontal" >

        <RelativeLayout
            android:id="@+id/news10"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginTop="2dp"
            android:layout_marginLeft="2dp"
            android:layout_marginBottom="2dp"
            android:background="@color/white"
            android:layout_weight="1" >

            <ImageView 
                android:src="@drawable/icon_add"
                android:contentDescription="@string/news_image"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:layout_centerInParent="true"/>
        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/news11"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginTop="2dp"
            android:layout_marginLeft="2dp"
            android:layout_marginBottom="2dp"
            android:background="@color/white"
            android:layout_weight="1" >

            <ImageView 
                android:src="@drawable/icon_add"
                android:contentDescription="@string/news_image"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:layout_centerInParent="true"/>
        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/news12"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="2dp"
            android:background="@color/white"
            android:layout_weight="1" >

            <ImageView 
                android:src="@drawable/icon_add"
                android:contentDescription="@string/news_image"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
  android:layout_centerInParent="true"/>
        </RelativeLayout>

    </LinearLayout>

</LinearLayout>

注意,
必须在表头注明命名空间:xmlns:app=http://schemas.android.com/apk/res/com.example.courseexamplev2

四、自定义控件的类

package com.example.courseexamplev2;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class NewsCard extends RelativeLayout{ 
//  private RelativeLayout layout;
    private ImageView cardView;
    private TextView cardText;
    private String text;
    private Drawable src;
    int cardId;

    public NewsCard(Context context) {
        super(context);
    }

    public NewsCard(Context context, AttributeSet attrs) {  
        super(context, attrs);  
  //在构造函数中将Xml中定义的布局解析出来。
        LayoutInflater.from(context).inflate(R.layout.news_card, this, true);

        cardView = (ImageView)findViewById(R.id.card_image);
        cardText = (TextView)findViewById(R.id.card_text);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.newsCard);  

        text = a.getString(R.styleable.newsCard_text);
            if(text != null)
                cardText.setText(text);

        src = a.getDrawable(R.styleable.newsCard_src);
            if(src != null)
                cardView.setImageDrawable(src);

        cardId = a.getInt(R.styleable.newsCard_card_id, 0);

        a.recycle();

   }  

    public void setImageResource(int resId){
        cardView.setImageResource(resId);
    }
    public void setTextViewText(String text){
        cardText.setText(text);
    }

 }

在自定义控件的构造方法中,利用:

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

来获取自定义控件的属性集合,这样就可以利用a调用各种属性了。
当然,为了简便,我没有在示例中添加点击事件。来看看最终效果吧:
最终效果

posted @ 2015-04-21 22:20  Lamba  阅读(353)  评论(0编辑  收藏  举报