一、前言
<font face = 黑体>对于 Android 开发者来说,原生控件往往无法满足要求,需要开发者自定义一些控件,因此,需要去了解自定义 view 的实现原理。这样即使碰到需要自定义控件的时候,也可以游刃有余。
1.1、自定义控件的优点
- <font face = 黑体>多个地方复用;
- <font face = 黑体>统一的逻辑样式;
- <font face = 黑体>精简代码。
1.2、自定义控件的方式
- <font face = 黑体>继承已有控件;
- <font face = 黑体>继承布局;
- <font face = 黑体>继承根 View。
二、继承已有控件
<font face = 黑体>我们要自定义的控件和已有控件非常相似,那么我们就可以在已有控件的基础上,继承组件,增加属性,来实现自己想要的效果。
<font face = 黑体>案例一: 自定义提交按钮(按钮点击变色),效果如下所示:
<font face = 黑体>具体实现步骤如下所示:
<font face = 黑体>新建资源文件 R.drawable.btn_submit
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@mipmap/btn_submit_p" android:state_pressed="true"/> <item android:drawable="@mipmap/btn_submit_n" /> </selector>
<font face = 黑体>新建 SubmitButton 类继承 Button 类
public class SubmitButton extends Button { public SubmitButton(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { setBackgroundResource(R.drawable.btn_submit); setText("自定义提交按钮"); setTextSize(18); setTextColor(0xffffffff); // 白色 } }
<font face = 黑体>使用
<?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:orientation="vertical" tools:context=".MainActivity"> <Button android:id="@+id/btn_normal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="45dp" android:layout_marginTop="30dp" android:layout_marginEnd="45dp" android:text="普通Button" android:textSize="15sp" /> <com.zjgsu.customviewdemo.SubmitButton android:id="@+id/btn_custom" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="45dp" android:layout_marginTop="30dp" android:layout_marginEnd="45dp" /> </LinearLayout>
三、继承布局
<font face = 黑体>当单个控件不好实现我们需要的效果的时候,即需要有多个已有控件来实现我们的效果,这时候我们可以利用继承布局的方式来实现,可以继承线性布局或者相对布局等。
<font face = 黑体>案例二: 自定义 Item,效果如下所示:
3.1、通过调用方法设置 Item 的内容
<font face = 黑体>我们可以看到第一个 Item 上三个要素都是全的,左边的内容、右边的内容和右边的箭头,但是第二个和第三个则是只有箭头和只有内容,这个是根据我们写的方法设置的。
<font face = 黑体>具体实现步骤如下所示:
<font face = 黑体>新建单个 Item 的布局 item_layout
<?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="wrap_content" android:orientation="vertical" tools:context=".MainActivity"> <View android:layout_width="wrap_content" android:layout_height="1px" android:background="#898888" /> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/selector_item" android:clickable="true" android:focusable="true" android:padding="10dp"> <TextView android:id="@+id/tv_left" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_centerVertical="true" android:textColor="#333333" android:textSize="15sp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:gravity="center_vertical"> <TextView android:id="@+id/tv_right" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="5dp" android:textColor="#646464" android:textSize="14sp" /> <ImageView android:id="@+id/iv_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/arrow" /> </LinearLayout> </RelativeLayout> <View android:layout_width="wrap_content" android:layout_height="1px" android:background="#898888" /> </LinearLayout>
<font face = 黑体>新建 ItemView 继承自 RelativeLayout,并写一个设置内容与是否显示箭头的方法
public class ItemView extends RelativeLayout { private TextView tvLeft; private TextView tvRight; private ImageView ivArrow; public ItemView(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.item_layout, this); tvLeft = findViewById(R.id.tv_left); tvRight = findViewById(R.id.tv_right); ivArrow = findViewById(R.id.iv_arrow); } /**
*
* @param tvLeftStr 左边内容
* @param tvRightStr 右边内容
* @param isShowArrow 是否显示箭头
*/
public void setView(String tvLeftStr, String tvRightStr, boolean isShowArrow) {
if (tvLeftStr != null) {
tvLeft.setText(tvLeftStr);
}
if (tvRightStr != null) {
tvRight.setText(tvRightStr);
}
if (isShowArrow) {
ivArrow.setVisibility(VISIBLE);
} else {
ivArrow.setVisibility(GONE);
}
}
}
```
<font face = 黑体>使用
<com.zjgsu.customviewdemo.ItemView android:id="@+id/item_1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" />
item1 = findViewById(R.id.item_1); item2 = findViewById(R.id.item_2); item3 = findViewById(R.id.item_3); item1.setView("姓名", "Bob", true); item2.setView("年龄", null, true); item3.setView("学校", "中国人民大学", false);
3.2、通过自定义属性设置 Item 的内容
<font face = 黑体>自定义属性一般有以下三个步骤:
<font face = 黑体>在 attrs.xml 中添加属性;
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ItemView"> <attr name="leftText" format="string"/> <attr name="rightText" format="string"/> <attr name="showArrow" format="boolean"/> </declare-styleable> </resources>
<font face = 黑体>在自定义的控件中绑定属性;
public class ItemView extends RelativeLayout { private TextView tvLeft; private TextView tvRight; private ImageView ivArrow; public ItemView(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.item_layout, this); tvLeft = findViewById(R.id.tv_left); tvRight = findViewById(R.id.tv_right); ivArrow = findViewById(R.id.iv_arrow); // 获取自定义属性 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ItemView); String leftText = a.getString(R.styleable.ItemView_leftText); String rightText = a.getString(R.styleable.ItemView_rightText); boolean showArrow = a.getBoolean(R.styleable.ItemView_showArrow, true); setView(leftText, rightText, showArrow); a.recycle(); } /**
*
* @param tvLeftStr 左边内容
* @param tvRightStr 右边内容
* @param isShowArrow 是否显示箭头
*/
public void setView(String tvLeftStr, String tvRightStr, boolean isShowArrow) {
if (tvLeftStr != null) {
tvLeft.setText(tvLeftStr);
}
if (tvRightStr != null) {
tvRight.setText(tvRightStr);
}
if (isShowArrow) {
ivArrow.setVisibility(VISIBLE);
} else {
ivArrow.setVisibility(GONE);
}
}
}
```
<font face = 黑体>在 xml 中使用。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ItemView="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:orientation="vertical" tools:context=".ItemActivity"> <com.zjgsu.customviewdemo.ItemView android:id="@+id/item_1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" ItemView:leftText="姓名" ItemView:rightText="Bob1" ItemView:showArrow="true" /> <com.zjgsu.customviewdemo.ItemView android:id="@+id/item_2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" ItemView:leftText="年龄" /> <com.zjgsu.customviewdemo.ItemView android:id="@+id/item_3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" ItemView:leftText="学校" ItemView:rightText="中国人民大学" ItemView:showArrow="false" /> </LinearLayout>
四、继承根 View
<font face = 黑体>当我们需要实现一些特殊样式并且继承已有控件和布局都无法实现,那么我们就需要通过继承根 View 的方式自己去画了,即自定义 View。
<font face = 黑体>案例三: 自定义 View,实现折线图,效果如下所示:
<font face = 黑体>具体代码如下所示:
public class LineView extends View {
Paint linePaint;
public LineView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initPaint();
}
// 初始化画笔
private void initPaint() {
linePaint = new Paint();
linePaint.setColor(Color.BLUE);
linePaint.setStrokeWidth(3);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 原点
int xPoint = 200;
int yPoint = 400;
// y 轴
canvas.drawLine(xPoint, 400 - 300, xPoint, yPoint, linePaint);
// x 轴
canvas.drawLine(xPoint, yPoint, xPoint + 500, yPoint, linePaint);
// 画线 上一条线的终点就是要画的线的起点
canvas.drawLine(xPoint, yPoint, xPoint + 80, yPoint - 150, linePaint);
canvas.drawLine(xPoint + 80, yPoint - 150, xPoint + 160, yPoint - 100, linePaint);
canvas.drawLine(xPoint + 160, yPoint - 100, xPoint + 240, yPoint - 200, linePaint);
canvas.drawLine(xPoint + 240, yPoint - 200, xPoint + 400, yPoint - 100, linePaint);
}
}
<font face = 黑体>这里的自定义 View 案例很简单,但是 Android 里面的自定义 View 所涉及的知识还是很多的,也算是 Android 进阶的一个内容,但是这篇博客并不是讲自定义 View 的,所以这里就不展开讲了。
五、小结
<font face = 黑体>这篇博客主要讲了 Android 自定义控件的三种方式,每一种方式都给出了一个简单的案例来讲解。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。