一.前言
安卓提供了许多易用的,功能强大的View对象,如TextView,ImageView等等。但是这些View不一定能满足我们的需要,我们需要自定义View。下面我们以两部分来说明,包括自定义View和自定义属性。下面我们来看下
1.关于Activity,View和布局文件的说明
首先我们在编写安卓程序常常碰到的的三个文件,两个Java文件和一个XML文件,下面我一张图来说明
其中一个布局文件对应一个View控件,当然我们也可以在一个布局文件中放别的View控件,但是这个View控件也是对应一个布局的,(这么说可能有问题,如果有错可以指正)。当我们创建View作为一个容器来包装View,然后我们在Activity中获取这些View对象,通过获取View中的值来进行逻辑判断,从而去设置View的值来完成你的业务逻辑。好的接下来让我们创建自定义View.
二.自定义View
1.创建一个布局文件
这个布局标识这个View的样式,这样我们就能把View的样子勾勒出来。下面我们就让我们创建一个RelativeLayout布局文件来定义我们的样式。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:padding="10dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textColor="#000"
android:textSize="22sp"
/>
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_title"
android:paddingTop="5dp"
android:text=""
android:textColor="#a000"
/>
<CheckBox
android:id="@+id/cb_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
/>
<View
android:layout_width="match_parent"
android:layout_height="0.2dp"
android:background="#a000"
android:layout_alignParentBottom="true"
/>
</RelativeLayout>
在这个布局文件中有两个TextView和一个CheckBox,以及一个View表示一条线 ,其中CheckBox中的android:focusable="false"
,android:clickable="false", android:focusableInTouchMode="false"表示屏蔽CheckBox的点击事件,这样就可以不用处理CheckBox的点击事件了。TextView中的text值为空,因为我们会用自定义属性来代替,这个布局的样式就是我们这个自定义View的样式了。
2.创建自定义View
接下来我们需要一个容器来承装这个布局文件,如下
@TargetApi(21)
public class SettingItemView extends RelativeLayout{
private final static String NAMESPACE="http://schemas.android.com/apk/res-auto";
private TextView tv_title;
private TextView tv_desc;
private CheckBox checkBox;
private String title;
private String desc_Off;
private String desc_On;
public SettingItemView(Context context) {
super(context);
initView();
}
public SettingItemView(Context context, AttributeSet attrs) {
super(context, attrs);
title = attrs.getAttributeValue(NAMESPACE, "title");
desc_Off = attrs.getAttributeValue(NAMESPACE, "desc_Off");
desc_On = attrs.getAttributeValue(NAMESPACE, "desc_On");
//System.out.println("title:"+title);
initView();
}
public SettingItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
public SettingItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView();
}
/**
* 初始化布局
*/
private void initView(){
//将自定义好的布局文件设置给当前的SettingItemView
View setting_item = View.inflate(getContext(), R.layout.view_setting_item,this);
checkBox = setting_item.findViewById(R.id.cb_status);
tv_title = setting_item.findViewById(R.id.tv_title);
tv_desc = setting_item.findViewById(R.id.tv_desc);
setTv_title(title);
}
public void setTv_title(String title){
tv_title.setText(title);
}
public void setTv_desc(String desc){
tv_desc.setText(desc);
}
public boolean isChecked() {
return checkBox.isChecked();
}
public void setIsChecked(boolean flag) {
this.checkBox.setChecked(flag);
//设置状态文本描述
if(flag){
setTv_desc(desc_On);
}else{
setTv_desc(desc_Off);
}
}
}
我们的View就是一个Java的Class文件,他继承了RelativeLayout这个Class,是的RelativeLayout就是安卓自己的View,用来承装他自己的布局文件,好的,现在我们要自定义View。就需要更改这个RelativeLayout,那就是新建一个类来继承他,从而改写安卓自定义的View,看看我们干了什么?首先我们在initView中来将我们自定义的布局和自定义的View关联起来,看这个代码 View setting_item = View.inflate(getContext(), R.layout.view_setting_item,this);就是我刚刚说的关联起来,然后我们可以在setting_item 获取之前在布局文件中定义的一些安卓的view对象,我们在所有构造函数都初始化了这个View,所以我们可以保证View可以被加载,同时,我们定义了一些set方法来给这些view对象设置内容,这样我们的自定义View好了,现在我们想在别的布局中使用这个View,怎么用能?如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:itheima="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="itcast.com.itcastsafe.activity.SettingActivity"
android:orientation="vertical"
>
<TextView
style="@style/TitleStyle"
android:text="设置中心"
android:id="@+id/textView"/>
<itcast.com.itcastsafe.activity.view.SettingItemView
android:id="@+id/sv_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
itheima:title="自动更新设置"
itheima:desc_Off="自动更新设置已经关闭"
itheima:desc_On="自动更新设置已经开启"
/>
</LinearLayout>
我们新建一个布局文件,使用自定义布局需要使用自定义View的全类名,好的这样就是使用了,这里我们看到xmlns:itheima="http://schemas.android.com/apk/res-auto这个命名空间和itheima:title="自动更新设置"这个属性,这是做什么能?这个就是下来我们要说的自定义属性
二.自定义属性
1.创建自定义属性文件
我们在values文件夹中创建自定义资源文件attrs.xml,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SettingItemView">
<attr name="title" format="string"></attr>
<attr name="desc_On" format="string"></attr>
<attr name="desc_Off" format="string"></attr>
</declare-styleable>
</resources>
我们定义了一个样式,名字叫做SettingItemView,里面包含三个属性分别是title,desc_On,desc_Off,好的,下一步,我们来定位这个资源文件
2.命名空间和自定义属性使用
我们定义资源文件,但是如何使用这个资源文件?,看之前我们的布局文件中有个xmlns:itheima="http://schemas.android.com/apk/res-auto"这个东西,早期的在安卓中需要你手动指定包名,现在在idea中,使用res-auto就能自动指定包名了,好的这样就能使用自定义属性了。itheima:title="自动更新设置",itheima:desc_Off="自动更新设置已经关闭",itheima:desc_On="自动更新设置已经开启" 这些就是自定义属性的使用方法,下面我们看如何在Activity中使用这些自定义的View。
三.在Activity中使用自定义View
使用自定义View和使用普通的View其实都差不多,因为自定义View就是修改了原本安卓的View。好了,看下代码吧
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setting);
final SettingItemView settingItemView = findViewById(R.id.sv_update);
sharedPreferences = getSharedPreferences("config",MODE_PRIVATE);
// settingItemView.setTv_title("自动更新设置");
if(sharedPreferences.getBoolean("auto_update",true)){
// settingItemView.setTv_desc("自动更新设置已经开启");
settingItemView.setIsChecked(true);
}else{
//settingItemView.setTv_desc("自动更新设置已经关闭");
settingItemView.setIsChecked(false);
}
settingItemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//判断是否选中
if(settingItemView.isChecked()){
settingItemView.setIsChecked(false);
//settingItemView.setTv_desc("自动更新设置已经关闭");
sharedPreferences.edit().putBoolean("auto_update",false).commit();
}else{
settingItemView.setIsChecked(true);
//settingItemView.setTv_desc("自动更新设置已经开启");
sharedPreferences.edit().putBoolean("auto_update",true).commit();
}
}
});
}
一样的使用findViewById来通过id来加载自定义的View,然后通过View中的方法来设置值,其实你注意到了吗,我们把通过View的set方法来给自定义VIew中的View来赋值,因为我们已经把这些View组成一个自定义的View了,我们不可以通过绕过自定义View来跟这些View赋值,这也告诉我们自定义View的一些场景的运用。
四.总结
如果你需要一个布局,而且这个布局在别的地方也要被使用,且这个布局颇为麻烦,为了解决这个问题,你可以使用自定义View来包裹这些简单View,让他成为一个整体,这样在下次使用该布局时,就不用那么麻烦的创建了。