在毕设项目中多处用到自定义控件,一直打算总结一下自定义控件的实现方式,今天就来总结一下吧。在此之前学习了郭霖大神博客上面关于自定义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的三种实现方式的更多相关文章

  1. 【朝花夕拾】Android自定义View篇之(四)自定义View的三种实现方式及自定义属性使用介绍

    前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10979161.html],谢谢! 尽管Android系统提供了不少控件,但是有很多酷炫效果仍然 ...

  2. Android自定义View(三、深入解析控件测量onMeasure)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51490283 本文出自:[openXu的博客] 目录: onMeasure什么时候会被调用 ...

  3. Android 自定义 view(三)—— onDraw 方法理解

    前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...

  4. Android 自定义 View 圆形进度条总结

    Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...

  5. Android自定义View(LimitScrollerView-仿天猫广告栏上下滚动效果)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53303872 本文出自:[openXu的博客] 1分析 2定义组合控件布局 3继承最外层控件 ...

  6. android自定义view之---组合view

    最近工作比较轻松,没有什么事情干,于是进入高产模式(呃....高产似xx). 应该很多童鞋对自定义view这个东西比较抵触,可能是听网上说view比较难吧,其实自定义view并没有很难 自定义view ...

  7. Android 自定义 view(四)—— onMeasure 方法理解

    前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...

  8. Android自定义View(RollWeekView-炫酷的星期日期选择控件)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53420889 本文出自:[openXu的博客] 目录: 1分析 2定义控件布局 3定义Cus ...

  9. Android自定义View(LineBreakLayout-自动换行的标签容器)

      最近一段时间比较忙,都没有时间更新博客,今天公司的事情忙完得空,继续为我的自定义控件系列博客添砖加瓦.本篇博客讲解的是标签自动换行的布局容器,正好前一阵子有个项目中需要,想了想没什么难度就自己弄了 ...

随机推荐

  1. 网络适配器输入的IP地址 已经分配给另一个适配器

    解决步骤:1.开始-运行-cmd2. set devmgr_show_nonpresent_devices=123.set devmgr_show_nonpresent_devices=14.star ...

  2. 在虚拟机中配置FastDFS+Nginx模块

    先上部署图 提示一下, ip 192.168.72.138 上面部署了两个group, 分别为 group1和g2. 另外, 同组之内的 port 要保持一致. 一.安装准备 1. #每台机器都添加两 ...

  3. paper 116:自然图像抠图/视频抠像技术梳理(image matting, video matting)

    1. Bayesian Matting, Chuang, CVPR 2001.http://grail.cs.washington.edu/projects/digital-matting/paper ...

  4. WebIM 聊天 Demo

    最近 2 个月用业余时间写了一个 IM ,动手之前想了很多,包括前期设计.语言.数据库等,经过了一番思想斗争,最终前台用 Vue.js 展示,Server 使用 node ,数据库使用 MongoDB ...

  5. Node判断文件是否链接

    通过fs.stat(), fs.lstate() and fs.fstate()都可以得到fs.Stats对象, 其中stats.isSymbolicLink()方法用来判断是否链接, 然而一直都不成 ...

  6. APICloud开发App总结(一)

    apiCloud app 开发是最近一两年刚刚兴起的一种混合开发方式.常用的模块以原生方式开发好,然后用js进行粘合.组织,完成整个的app的逻辑.这种开发方式极大的提高了软件模块的复用率,加快了ap ...

  7. Bitnami Redmine插件记录

    1.bitnami安装时自带了开发环境,如ruby.rails.devkit. 为了版本兼容,应使用bitnami的命令行. 2.通过use_redmine启动命令行:运行Bitnami\redmin ...

  8. pm2.5计算和单位换算

    1.pm2.5和pm10的计算  PM10a=PM10+PM25a PM25a=PM25+BC+OC+SOA1+SOA2+SOA3+SOA4+SOA5+SOA6+ANA+ASO4+ANO3+ACL+A ...

  9. C语言----------链表的简单操作

    #include <stdio.h> #include <malloc.h> typedef struct node{ //定义节点类型 char data; //数据域 st ...

  10. LabVIEW类方法浏览器-Class Method Browser

    随着LabVIEW的类编程应用增多,当打开较多的VI进行编辑时候,添加该类对应的VI方法到程序后背板上操作显得繁琐(需要在Project浏览器或类浏览器或库浏览器中找到该类的方法VI,然后再拖到程序背 ...