手机安全卫士共有 9 个功能,我们先来看看设置界面长什么样:
大家已经发现了,第一反应就是用 ListView 实现,但是,我们仔细一看,每一个 Item 并不完全一样,况且 Item 数量也不多,所以,我们可以用自定义(组合)控件来实现,把每一个 Item 抽象出来,做成控件,之所以叫组合控件,因为这个控件中用到了很多系统控件,我们只是修改了它们的布局而已。
关于项目相关文章,请访问:
项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe
先按照上图中的自动更新设置为模板进行实现,包括四个控件:两个 TextView,一个 CheckBox,一条横线。其中横线的实现思路很有意思,它也是一个 TextView,只是设置了它的高度为 1 个像素,然后这是背景为黑色即可。好了,废话不多说,开始写代码,首先创建布局文件:item_setting.xml,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dp">
<TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="自动更新设置" android:textColor="@android:color/black"/>
<TextView android:id="@+id/tv_desc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:text="自动更新已开启" android:layout_below="@id/tv_title" android:textColor="#b000"/>
<CheckBox android:id="@+id/cb_check" android:layout_width="wrap_content" android:layout_height="wrap_content" android:clickable="false" android:focusableInTouchMode="false" android:focusable="false" android:layout_alignParentRight="true" android:layout_centerVertical="true"/>
<TextView android:layout_width="match_parent" android:layout_height="1px" android:background="@android:color/black" android:layout_marginTop="5dp" android:layout_below="@id/tv_desc"/>
</RelativeLayout>
这就是自动更新设置的 UI 实现了,注意,该布局中屏蔽了 CheckBox 的点击事件,并且不让它获取焦点。接下来就是创建自定义组件,且使用该 UI 作为界面。创建类:SettingItemView,继承自 RelativeLayout,代码如下:
/** * Created by XWdoor on 2016/3/1 001 16:19. * 博客:http://blog.csdn.net/xwdoor */
public class SettingItemView extends RelativeLayout {
private TextView tvTitle;
private TextView tvDesc;
private CheckBox cbCheck;
public SettingItemView(Context context) {
super(context);
initView();
}
public SettingItemView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public SettingItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView(){
View view = View.inflate(getContext(), R.layout.item_setting,null);
tvTitle = (TextView) view.findViewById(R.id.tv_title);
tvDesc = (TextView) view.findViewById(R.id.tv_desc);
cbCheck = (CheckBox) view.findViewById(R.id.cb_check);
addView(view);
}
/** 设置标题 */
public void setTitle(String title){
tvTitle.setText(title);
}
/** 设置描述 */
public void setDesc(String desc){
tvDesc.setText(desc);
}
/** 是否开启 */
public boolean isChecked(){
return cbCheck.isChecked();
}
/** 设置开启状态 */
public void setChecked(boolean checked){
cbCheck.setChecked(checked);
}
}
SettingItemView 使用了先前定义的布局,并对外公开 4 个接口,分别是:设置标题、设置描述、获取开启状态、设置开启状态。接下来就是使用它了,先是创建 SettingActivity,及其布局文件:activity_setting.xml,在布局文件中,使用刚才自定义的控件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<TextView android:id="@+id/tv_title" style="@style/TitleBar" android:text="设置"/>
<!-- 自定义控件 -->
<net.xwdoor.mobilesafe.view.SettingItemView android:id="@+id/siv_update" android:layout_width="match_parent" android:layout_height="wrap_content"/>
</LinearLayout>
至此,自定义控件就弄好了,然后我们就可以像其他普通控件一样使用它了,以下是 SettingActivity 中的代码:
@Override
protected void initViews(Bundle savedInstanceState) {
setContentView(R.layout.activity_setting);
final SettingItemView sivUpdate = (SettingItemView) findViewById(R.id.siv_update);
sivUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sivUpdate.setChecked(!sivUpdate.isChecked());
if(sivUpdate.isChecked()){
//若选中,开启自动更新设置
sivUpdate.setDesc("自动更新已开启");
}else {
//关闭自动更新
sivUpdate.setDesc("自动更新已关闭");
}
}
});
}
运行结果如图:
聪明的你已经发现了,每次点击时,我们都要设置它的描述,sivUpdate.setDesc("自动更新已开启");
是已开启,sivUpdate.setDesc("自动更新已关闭");
是已关闭,一个 Item 还好,但是如果有很多个的话,管理起来就比较麻烦了,而且文字常量显示什么的属于 UI 的数据,应该在设计 UI 的时候就设置好的,所以,我们最好将这几个状态,以及标题,设置为 SettingItemView 的属性,也就是我们的自定义属性。
在 res->values
文件夹中创建资源文件:attr.xml,因为我使用的是 Android Studio,不存在该文件,所以手动创建,然后写自定义属性,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SettingItemView">
<attr name="stitle" format="string" />
<attr name="desc_on" format="string" />
<attr name="desc_off" format="string" />
</declare-styleable>
</resources>
其中 name 是自定义的,format 是该属性的值类型。好了,很简单吧,接下来使用它了,首先在布局文件的根节点中,为自定义属性声明一个命名空间:xmlns:xwdoor="http://schemas.android.com/apk/res-auto"
, 其中,xwdoor 是自定义部分,这样,系统就可以找到我们的自定义属性文件的位置,然后就可以直接用了,代码如下:
<!-- 自定义控件 -->
<net.xwdoor.mobilesafe.view.SettingItemView android:id="@+id/siv_update" android:layout_width="match_parent" android:layout_height="wrap_content" xwdoor:stitle="自动更新设置" xwdoor:desc_on="自动更新已开启" xwdoor:desc_off="自动更新已关闭"/>
当然,仅仅是这样,我们是看不到效果的,我们还需要在自定义控件 SettingItemView 中做一些修改才行,代码如下:
/** * Created by XWdoor on 2016/3/1 001 16:19. * 博客:http://blog.csdn.net/xwdoor */
public class SettingItemView extends RelativeLayout {
public static final String NAMESPACE_XWDOOR = "http://schemas.android.com/apk/res-auto";
private String mDescOff;
private String mDescOn;
private TextView tvTitle;
private TextView tvDesc;
private CheckBox cbCheck;
public SettingItemView(Context context) {
super(context);
initView();
}
public SettingItemView(Context context, AttributeSet attrs) {
super(context, attrs);
int attrCount = attrs.getAttributeCount();
//获取stitle属性的值
String title = attrs.getAttributeValue(NAMESPACE_XWDOOR, "stitle");
//获取关闭设置时的描述
mDescOn = attrs.getAttributeValue(NAMESPACE_XWDOOR, "desc_on");
//获取开启设置时的描述
mDescOff = attrs.getAttributeValue(NAMESPACE_XWDOOR, "desc_off");
initView();
setTitle(title);
setDesc(mDescOff);
}
public SettingItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
/** 设置开启状态 */
public void setChecked(boolean checked){
cbCheck.setChecked(checked);
if(checked){
setDesc(mDescOn);
}else {
setDesc(mDescOff);
}
}
}
好了,到此,我们的自定义组合控件、自定义属性就完成了,本文主要是关于 UI 设计与实现,没有多大难度,主要是知识点的整合与熟悉,且整个项目基本如此,都是熟悉 Android 开发的特性、熟悉代码风格、掌握知识点。
关于项目相关文章,请访问:
项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe