Android小白进阶(一)–组合控件-最简单的自定义控件形式
✍ Author by NamCooper
一、系列简介
- 1、准备整理一些自己一路走过来学习到的、日常开发中比较常用的东西。
- 2、看别人都写了那么多博客,好羡慕。
- 3、本人并不会多么高深的东西,所以写一些入门的基础知识而已,如果你是大神,请轻轻地鄙视我 。
- 4、本系列都以均以实际代码演示为主,不会涉及多少专业的名词术语。小白文,仅此而已。
二、本文内容
- 自定义控件中最容易学、最容易理解的组合控件,本文中会详细解释如何使用这种最简单的自定义控件形式。
三、概念介绍
- 1、**自定义控件:**移动端开发的主要目的是展示和交互,那么我们在学习之初就会接触到很多种类的控件,例如Button、TextView、ImageView、ListView等等我们称之为Android原生控件的东西。
而在实际的开发中,这些原生控件固然很好用,但是不够灵活,往往不能实现我们所需要的特殊效果,这个时候我们就需要根据自己的实际需要开发出一个合适的控件来用,也就是自定控件。
自定义控件整体上可以分为三大类,由实现的难易程度排序为:组合控件、继承ViewGroup控件、继承View控件 。
有的大神可能会把组合控件归为继承ViewGroup的控件,当然这没有错,不过对于一个刚入门的小白来说,组合控件这种基本形式的自定义控件非常容易理解,所以在这里我单独列出来,作为开胃菜。 - 2、组合控件: 自定义控件的最简单实现,熟练掌握组合控件,完全可以完成至少50%的自定义控件开发工作。所以,虽然这种形式比较low,但非常实用!
四、实现演练
1、先看下图,这是QQ的设置界面,也是绝大多数app会使用到界面
在实际开发中,如果我们拿到了这样一个界面设计,新手肯定会选择整体布局采用LinearLayout(vertical),每个子条目采用一个LinearLayout(horizontal),在子LinearLayout中使用TextView、imageView横向排列来进行实现。稍微熟练一点儿的开发者,会将每一个子条目抽取为一个布局文件,使用include来优化布局。
但是!上面两种方法无论哪一种,都会导致在Avtivity中要做非常多个findViewById,然后对每个原生控件一一赋值从而展示出效果,low而且麻烦!
2、组合控件实现(废话尽量不说,直接根据步骤上代码)
①、首先按照QQ设置界面的条目写一个布局setting_item.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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="10dp"
android:paddingTop="10dp">
<TextView
android:id="@+id/tv_left"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_weight="1"
android:text="我爱玩Dota"
android:textColor="#323232"
android:textSize="14sp"/>
<TextView
android:id="@+id/tv_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:text="修改昵称"
android:textSize="12sp"/>
<ImageView
android:id="@+id/iv_img"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginRight="20dp"
android:scaleType="fitCenter"
android:src="@mipmap/right"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#000"/>
</LinearLayout>
预览图
②、创建一个自定义View,命名为SettingItemView,继承FrameLayout。里面定义了一个设置数据的方法setData,也比较简单,有入门经验就可以看得懂。
public class SettingItemView extends FrameLayout {
private View view;
private TextView textLeft;
private TextView textRight;
private ImageView img;
public SettingItemView(@NonNull Context context) {
this(context, null);
}
public SettingItemView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
}
public SettingItemView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
view = View.inflate(context, R.layout.setting_item, null);
initView();
this.addView(view);
}
/**
* 查找控件
*/
private void initView() {
textLeft = (TextView) view.findViewById(R.id.tv_left);
textRight = (TextView) view.findViewById(R.id.tv_right);
img = (ImageView) view.findViewById(R.id.iv_img);
}
/**
* 设置数据
*
* @param leftTxt 左边显示的文本
* @param rightTxt 右边显示的文本
* @param imgResId 右边的图标的资源id
*/
public void setData(String leftTxt, String rightTxt, int imgResId) {
textLeft.setText(leftTxt);
textRight.setText(rightTxt);
img.setImageResource(imgResId);
}
③、在布局文件中使用SettingItemView。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ccc"
android:orientation="vertical"
tools:context="com.namcooper.widgetdemos.activity.SettingItemActivity">
<com.namcooper.widgetdemos.widget.SettingItemView
android:id="@+id/siv_item1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.namcooper.widgetdemos.widget.SettingItemView
android:id="@+id/siv_item2"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.namcooper.widgetdemos.widget.SettingItemView
android:id="@+id/siv_item3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"/>
<com.namcooper.widgetdemos.widget.SettingItemView
android:id="@+id/siv_item4"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
④、运行结果。
<img src=“http://img.ksban.cn/SettingItemView运行结果1.png” width = “532” height = "778"align=center/>
⑤、设置数据。
public class SettingItemActivity extends AppCompatActivity {
private SettingItemView item1;
private SettingItemView item2;
private SettingItemView item3;
private SettingItemView item4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setting_item);
initView();
initData();
}
private void initView() {
item1 = (SettingItemView) findViewById(R.id.siv_item1);
item2 = (SettingItemView) findViewById(R.id.siv_item2);
item3 = (SettingItemView) findViewById(R.id.siv_item3);
item4 = (SettingItemView) findViewById(R.id.siv_item4);
}
private void initData() {
item1.setData("旋涡鸣人","螺旋丸",R.mipmap.ic_launcher);
item2.setData("波风水门","瞬身之术",R.mipmap.right);
item3.setData("宇智波鼬","月读",R.mipmap.ic_launcher_round);
item4.setData("宇智波佐助","天照",R.mipmap.right);
}
}
⑥、运行结果。
至此,应该说这个控件已经基本实现了,但是经常查看第三方开源控件的同学都会发现一个现象,自定义控件的属性不仅可以通过代码设置,也可以通过xml文件中添加自定义属性来实现。下面就说一说针对上面这个简单控件,如何声明自定义属性。**
3、实现自定义属性
(这里只针对这个简单控件定义几个简单属性,需要学习自定义属性,请参考Android 深入理解Android中的自定义属性)。
①、新增命名空间和属性:在项目values目录下的attrs.xml中(attrs.xml不存在的话就手动创建),增加如下代码。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--命名空间的名字-->
<declare-styleable name="SettingItemView">
<!--左侧文本-->
<attr name="LeftText" format="string"/>
<!--右侧文本-->
<attr name="RightText" format="string"/>
<!--图标-->
<attr name="Img" format="reference"/>
</declare-styleable>
</resources>
②、布局文件中使用新增的属性。
注意在布局文件的根标签中要声明命名空间!
xmlns:SettingItemView="http://schemas.android.com/apk/res-auto"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:SettingItemView="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ccc"
android:orientation="vertical"
tools:context="com.namcooper.widgetdemos.activity.SettingItemActivity">
<com.namcooper.widgetdemos.widget.SettingItemView
android:id="@+id/siv_item1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
SettingItemView:Img="@mipmap/right"
SettingItemView:LeftText="纲手"
SettingItemView:RightText="大蛞蝓"/>
<com.namcooper.widgetdemos.widget.SettingItemView
android:id="@+id/siv_item2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
SettingItemView:Img="@mipmap/ic_launcher_round"
SettingItemView:LeftText="自来也"
SettingItemView:RightText="蛤蟆吉"/>
<com.namcooper.widgetdemos.widget.SettingItemView
android:id="@+id/siv_item3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
SettingItemView:Img="@mipmap/ic_launcher"
SettingItemView:LeftText="大蛇丸"
SettingItemView:RightText="万蛇"/>
<com.namcooper.widgetdemos.widget.SettingItemView
android:id="@+id/siv_item4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
SettingItemView:Img="@mipmap/right"
SettingItemView:LeftText="猿飞日斩"
SettingItemView:RightText="猿猴王·猿魔"/>
</LinearLayout>
③、修改SettingItemView的实现代码。
public SettingItemView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
view = View.inflate(context, R.layout.setting_item, null);
initView();
initXmlData(context, attrs);
this.addView(view);
}
/**
* 加载自定义属性
*
* @param attrs
*/
private void initXmlData(Context context, AttributeSet attrs) {
//获取xml文件中的额自定义属性集
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SettingItemView);
//获取属性值
int imgId = array.getResourceId(R.styleable.SettingItemView_Img, R.mipmap.right);
String left_txt = array.getString(R.styleable.SettingItemView_LeftText);
String right_txt = array.getString(R.styleable.SettingItemView_RightText);
//给控件赋值
textLeft.setText(left_txt);
textRight.setText(right_txt);
img.setImageResource(imgId);
}
④、将原activity中的代码注释掉,然后直接运行。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setting_item);
//initView();
//initData();
}
结果如下:
五、总结一下
- 组合控件的实现步骤:
1、按照要实现的结果创建一个布局xml文件,里面使用原生控件实现控件的框架。
2、创建一个View继承FrameLayout,将上一步创建的xml加载进来,然后通过fingViewById找到其中的所有子控件,并创建给这些子控件赋值的方法。
3、增加自定义属性,使组合控件中的子控件属性都可以通过xml文件直接声明。 - 以上就是自定义控件之组合控件的简单实现了,如果你真的能熟练的写这样的控件,那么恭喜你,起码50%的自定义控件以及布局代码优化你都可以做了!
- 本文仅供学习交流,不对之处欢迎指正。