Android自定义View的三种实现方式
在毕设项目中多处用到自定义控件,一直打算总结一下自定义控件的实现方式,今天就来总结一下吧。在此之前学习了郭霖大神博客上面关于自定义View的几篇博文,感觉受益良多,本文中就参考了其中的一些内容。
总结来说,自定义控件的实现有三种方式,分别是:组合控件、自绘控件和继承控件。下面将分别对这三种方式进行介绍。
(一)组合控件
组合控件,顾名思义就是将一些小的控件组合起来形成一个新的控件,这些小的控件多是系统自带的控件。比如很多应用中普遍使用的标题栏控件,其实用的就是组合控件,那么下面将通过实现一个简单的标题栏自定义控件来说说组合控件的用法。
1、新建一个Android项目,创建自定义标题栏的布局文件title_bar.xml:

1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="wrap_content"
5 android:background="#0000ff" >
6
7 <Button
8 android:id="@+id/left_btn"
9 android:layout_width="wrap_content"
10 android:layout_height="wrap_content"
11 android:layout_centerVertical="true"
12 android:layout_margin="5dp"
13 android:background="@drawable/back1_64" />
14
15 <TextView
16 android:id="@+id/title_tv"
17 android:layout_width="wrap_content"
18 android:layout_height="wrap_content"
19 android:layout_centerInParent="true"
20 android:text="这是标题"
21 android:textColor="#ffffff"
22 android:textSize="20sp" />
23
24 </RelativeLayout>

可见这个标题栏控件还是比较简单的,其中在左边有一个返回按钮,背景是一张事先准备好的图片back1_64.png,标题栏中间是标题文字。
2、创建一个类TitleView,继承自RelativeLayout:

1 public class TitleView extends RelativeLayout {
2
3 // 返回按钮控件
4 private Button mLeftBtn;
5 // 标题Tv
6 private TextView mTitleTv;
7
8 public TitleView(Context context, AttributeSet attrs) {
9 super(context, attrs);
10
11 // 加载布局
12 LayoutInflater.from(context).inflate(R.layout.title_bar, this);
13
14 // 获取控件
15 mLeftBtn = (Button) findViewById(R.id.left_btn);
16 mTitleTv = (TextView) findViewById(R.id.title_tv);
17
18 }
19
20 // 为左侧返回按钮添加自定义点击事件
21 public void setLeftButtonListener(OnClickListener listener) {
22 mLeftBtn.setOnClickListener(listener);
23 }
24
25 // 设置标题的方法
26 public void setTitleText(String title) {
27 mTitleTv.setText(title);
28 }
29 }

在TitleView中主要是为自定义的标题栏加载了布局,为返回按钮添加事件监听方法,并提供了设置标题文本的方法。
3、在activity_main.xml中引入自定义的标题栏:

1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:id="@+id/main_layout"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="vertical" >
6
7 <com.example.test.TitleView
8 android:id="@+id/title_bar"
9 android:layout_width="match_parent"
10 android:layout_height="wrap_content" >
11 </com.example.test.TitleView>
12
13 </LinearLayout>

4、在MainActivity中获取自定义的标题栏,并且为返回按钮添加自定义点击事件:

1 private TitleView mTitleBar;
2 mTitleBar = (TitleView) findViewById(R.id.title_bar);
3
4 mTitleBar.setLeftButtonListener(new OnClickListener() {
5
6 @Override
7 public void onClick(View v) {
8 Toast.makeText(MainActivity.this, "点击了返回按钮", Toast.LENGTH_SHORT)
9 .show();
10 finish();
11 }
12 });

5、运行效果如下:
这样就用组合的方式实现了自定义标题栏,其实经过更多的组合还可以创建出功能更为复杂的自定义控件,比如自定义搜索栏等。
(二)自绘控件
自绘控件的内容都是自己绘制出来的,在View的onDraw方法中完成绘制。下面就实现一个简单的计数器,每点击它一次,计数值就加1并显示出来。
1、创建CounterView类,继承自View,实现OnClickListener接口:

1 public class CounterView extends View implements OnClickListener {
2
3 // 定义画笔
4 private Paint mPaint;
5 // 用于获取文字的宽和高
6 private Rect mBounds;
7 // 计数值,每点击一次本控件,其值增加1
8 private int mCount;
9
10 public CounterView(Context context, AttributeSet attrs) {
11 super(context, attrs);
12
13 // 初始化画笔、Rect
14 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
15 mBounds = new Rect();
16 // 本控件的点击事件
17 setOnClickListener(this);
18 }
19
20 @Override
21 protected void onDraw(Canvas canvas) {
22 super.onDraw(canvas);
23
24 mPaint.setColor(Color.BLUE);
25 // 绘制一个填充色为蓝色的矩形
26 canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
27
28 mPaint.setColor(Color.YELLOW);
29 mPaint.setTextSize(50);
30 String text = String.valueOf(mCount);
31 // 获取文字的宽和高
32 mPaint.getTextBounds(text, 0, text.length(), mBounds);
33 float textWidth = mBounds.width();
34 float textHeight = mBounds.height();
35
36 // 绘制字符串
37 canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2
38 + textHeight / 2, mPaint);
39 }
40
41 @Override
42 public void onClick(View v) {
43 mCount ++;
44
45 // 重绘
46 invalidate();
47 }
48
49 }

2、在activity_main.xml中引入该自定义布局:

1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:id="@+id/main_layout"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="vertical" >
6
7 <com.example.test.CounterView
8 android:id="@+id/counter_view"
9 android:layout_width="100dp"
10 android:layout_height="100dp"
11 android:layout_gravity="center_horizontal|top"
12 android:layout_margin="20dp" />
13
14 </LinearLayout>

3、运行效果如下:
(三)继承控件
就是继承已有的控件,创建新控件,保留继承的父控件的特性,并且还可以引入新特性。下面就以支持横向滑动删除列表项的自定义ListView的实现来介绍。
1、创建删除按钮布局delete_btn.xml,这个布局是在横向滑动列表项后显示的:

1 <?xml version="1.0" encoding="utf-8"?>
2 <Button xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="wrap_content"
4 android:layout_height="wrap_content"
5 android:background="#FF0000"
6 android:padding="5dp"
7 android:text="删除"
8 android:textColor="#FFFFFF"
9 android:textSize="16sp" >
10
11 </Button>

2、创建CustomListView类,继承自ListView,并实现了OnTouchListener和OnGestureListener接口:

1 public class CustomListView extends ListView implements OnTouchListener,
2 OnGestureListener {
3
4 // 手势动作探测器
5 private GestureDetector mGestureDetector;
6
7 // 删除事件监听器
8 public interface OnDeleteListener {
9 void onDelete(int index);
10 }
11
12 private OnDeleteListener mOnDeleteListener;
13
14 // 删除按钮
15 private View mDeleteBtn;
16
17 // 列表项布局
18 private ViewGroup mItemLayout;
19
20 // 选择的列表项
21 private int mSelectedItem;
22
23 // 当前删除按钮是否显示出来了
24 private boolean isDeleteShown;
25
26 public CustomListView(Context context, AttributeSet attrs) {
27 super(context, attrs);
28
29 // 创建手势监听器对象
30 mGestureDetector = new GestureDetector(getContext(), this);
31
32 // 监听onTouch事件
33 setOnTouchListener(this);
34 }
35
36 // 设置删除监听事件
37 public void setOnDeleteListener(OnDeleteListener listener) {
38 mOnDeleteListener = listener;
39 }
40
41 // 触摸监听事件
42 @Override
43 public boolean onTouch(View v, MotionEvent event) {
44 if (isDeleteShown) {
45 hideDelete();
46 return false;
47 } else {
48 return mGestureDetector.onTouchEvent(event);
49 }
50 }
51
52 @Override
53 public boolean onDown(MotionEvent e) {
54 if (!isDeleteShown) {
55 mSelectedItem = pointToPosition((int) e.getX(), (int) e.getY());
56 }
57 return false;
58 }
59
60 @Override
61 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
62 float velocityY) {
63 // 如果当前删除按钮没有显示出来,并且x方向滑动的速度大于y方向的滑动速度
64 if (!isDeleteShown && Math.abs(velocityX) > Math.abs(velocityY)) {
65 mDeleteBtn = LayoutInflater.from(getContext()).inflate(
66 R.layout.delete_btn, null);
67
68 mDeleteBtn.setOnClickListener(new OnClickListener() {
69
70 @Override
71 public void onClick(View v) {
72 mItemLayout.removeView(mDeleteBtn);
73 mDeleteBtn = null;
74 isDeleteShown = false;
75 mOnDeleteListener.onDelete(mSelectedItem);
76 }
77 });
78
79 mItemLayout = (ViewGroup) getChildAt(mSelectedItem
80 - getFirstVisiblePosition());
81
82 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
83 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
84 params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
85 params.addRule(RelativeLayout.CENTER_VERTICAL);
86
87 mItemLayout.addView(mDeleteBtn, params);
88 isDeleteShown = true;
89 }
90
91 return false;
92 }
93
94 // 隐藏删除按钮
95 public void hideDelete() {
96 mItemLayout.removeView(mDeleteBtn);
97 mDeleteBtn = null;
98 isDeleteShown = false;
99 }
100
101 public boolean isDeleteShown() {
102 return isDeleteShown;
103 }
104
105 /**
106 * 后面几个方法本例中没有用到
107 */
108 @Override
109 public void onShowPress(MotionEvent e) {
110
111 }
112
113 @Override
114 public boolean onSingleTapUp(MotionEvent e) {
115 return false;
116 }
117
118 @Override
119 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
120 float distanceY) {
121 return false;
122 }
123
124 @Override
125 public void onLongPress(MotionEvent e) {
126
127 }
128
129 }

3、定义列表项布局custom_listview_item.xml,它的结构很简单,只包含了一个TextView:

1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:descendantFocusability="blocksDescendants" >
6
7 <TextView
8 android:id="@+id/content_tv"
9 android:layout_width="wrap_content"
10 android:layout_height="wrap_content"
11 android:layout_centerVertical="true"
12 android:layout_margin="30dp"
13 android:gravity="center_vertical|left" />
14
15 </RelativeLayout>

4、定义适配器类CustomListViewAdapter,继承自ArrayAdapter<String>:

1 public class CustomListViewAdapter extends ArrayAdapter<String> {
2
3 public CustomListViewAdapter(Context context, int textViewResourceId,
4 List<String> objects) {
5 super(context, textViewResourceId, objects);
6 }
7
8 @Override
9 public View getView(int position, View convertView, ViewGroup parent) {
10 View view;
11
12 if (convertView == null) {
13 view = LayoutInflater.from(getContext()).inflate(
14 R.layout.custom_listview_item, null);
15 } else {
16 view = convertView;
17 }
18
19 TextView contentTv = (TextView) view.findViewById(R.id.content_tv);
20 contentTv.setText(getItem(position));
21
22 return view;
23 }
24
25 }

5、在activity_main.xml中引入自定义的ListView:

1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:id="@+id/main_layout"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="vertical" >
6
7 <com.example.test.CustomListView
8 android:id="@+id/custom_lv"
9 android:layout_width="match_parent"
10 android:layout_height="wrap_content" />
11
12 </LinearLayout>

6、在MainActivity中对列表做初始化、设置列表项删除按钮点击事件等处理:

1 public class MainActivity extends Activity {
2
3 // 自定义Lv
4 private CustomListView mCustomLv;
5 // 自定义适配器
6 private CustomListViewAdapter mAdapter;
7 // 内容列表
8 private List<String> contentList = new ArrayList<String>();
9
10 @Override
11 protected void onCreate(Bundle savedInstanceState) {
12 super.onCreate(savedInstanceState);
13 requestWindowFeature(Window.FEATURE_NO_TITLE);
14 setContentView(R.layout.activity_main);
15
16 initContentList();
17
18 mCustomLv = (CustomListView) findViewById(R.id.custom_lv);
19 mCustomLv.setOnDeleteListener(new OnDeleteListener() {
20
21 @Override
22 public void onDelete(int index) {
23 contentList.remove(index);
24 mAdapter.notifyDataSetChanged();
25 }
26 });
27
28 mAdapter = new CustomListViewAdapter(this, 0, contentList);
29 mCustomLv.setAdapter(mAdapter);
30 }
31
32 // 初始化内容列表
33 private void initContentList() {
34 for (int i = 0; i < 20; i++) {
35 contentList.add("内容项" + i);
36 }
37 }
38
39 @Override
40 public void onBackPressed() {
41 if (mCustomLv.isDeleteShown()) {
42 mCustomLv.hideDelete();
43 return;
44 }
45 super.onBackPressed();
46 }
47
48 }

7、运行效果如下:
Refer:http://blog.csdn.net/guolin_blog/article/details/17357967
Android自定义View的三种实现方式的更多相关文章
- 【朝花夕拾】Android自定义View篇之(四)自定义View的三种实现方式及自定义属性使用介绍
前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10979161.html],谢谢! 尽管Android系统提供了不少控件,但是有很多酷炫效果仍然 ...
- Android自定义View(三、深入解析控件测量onMeasure)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51490283 本文出自:[openXu的博客] 目录: onMeasure什么时候会被调用 ...
- Android 自定义 view(三)—— onDraw 方法理解
前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...
- Android 自定义 View 圆形进度条总结
Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...
- Android自定义View(LimitScrollerView-仿天猫广告栏上下滚动效果)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53303872 本文出自:[openXu的博客] 1分析 2定义组合控件布局 3继承最外层控件 ...
- android自定义view之---组合view
最近工作比较轻松,没有什么事情干,于是进入高产模式(呃....高产似xx). 应该很多童鞋对自定义view这个东西比较抵触,可能是听网上说view比较难吧,其实自定义view并没有很难 自定义view ...
- Android 自定义 view(四)—— onMeasure 方法理解
前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...
- Android自定义View(RollWeekView-炫酷的星期日期选择控件)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53420889 本文出自:[openXu的博客] 目录: 1分析 2定义控件布局 3定义Cus ...
- Android自定义View(LineBreakLayout-自动换行的标签容器)
最近一段时间比较忙,都没有时间更新博客,今天公司的事情忙完得空,继续为我的自定义控件系列博客添砖加瓦.本篇博客讲解的是标签自动换行的布局容器,正好前一阵子有个项目中需要,想了想没什么难度就自己弄了 ...
随机推荐
- 商品条形码(JBarcode)Java版(二)
下午开了一个下午的会议,其实开会我听不进去,因为今天妖都特别冷,下班在公司等待小媳妇一个钟头,然后带着她去吃饭,吃完饭回到家.她做运动,我就开始慢慢整理我自己的小博客. ——题记 先说一下,写这篇文章 ...
- WPF中Popup的几个问题
要用popup控件来解决一些问题.就此带来了一批问题. 问题一. 在popup外任意位置点击时要能关闭popup,这个本来简单,只要加上StaysOpen=false就可以了.但我的popup中有个O ...
- Qt qml treeview 树控件
qml并没有提供树控件,只能自己写了.model仍然用ListModel对象,弄成层级的就行.delegate必须用loader动态的增加子控件,如此而已. [先看效果] [下载] http://do ...
- C语言 06 指针
int *p; //第一个*是象征意义. p = &a; 等价于 int *p = &a; //第二个*是不正确的 *p = &a; //第三个*是访问指针变量指向的存储空间. ...
- mssql数据库添加,修改,删除字段
通用式: alter table [表名] add [字段名] 字段属性 default 缺省值 default 是可选参数 增加字段: alter table [表名] add 字段名 smalli ...
- ClientScript.RegisterStartupScript 不起作用
asp.net webform 使用 ClientScript.RegisterStartupScript 不起作用 form 加上 runat="server",ok
- wex5 实战 wex5与js的组件关系与执行顺序(父子与先后)
初学wex5,先理理让人容易混淆的三个概念: 一 基本概念: 1 wex5组件,顾名思义,在编辑窗口右侧的组件集合里的,都是wex5基于开源自创的组件,并封装了一套自已的方法.目的是为了方便.相关方法 ...
- CSS 设置背景透明度,不影响子元素
由于 opacity 属性能被子元素继承,使用它设置父元素背景透明度时也会影响子元素. 解决方法: 1> 使用 RGBA Example .classname { /* RGBa, 透明度0.6 ...
- java中的队列
转载自:http://blog.csdn.net/guijava/article/details/3784658 在java5中新增加了java.util.Queue接口,用以支持队列的常见操作.Qu ...
- HDU 2732:Leapin' Lizards(最大流)
http://acm.hdu.edu.cn/showproblem.php?pid=2732 题意:给出两个地图,蜥蜴从一个柱子跳跃到另外一个地方,那么这个柱子就可能会坍塌,第一个地图是柱子可以容忍跳 ...