平日里喜欢阅读网易云阅读客户端的资讯,对订阅版块的设计甚是喜爱,就想琢磨着如何实现类似于卡片式的文件夹的样式:
显然,安卓里没有自带的这样的控件。有人一定会问,每个卡片无非就是个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调用各种属性了。
当然,为了简便,我没有在示例中添加点击事件。来看看最终效果吧: