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-自动换行的标签容器)
最近一段时间比较忙,都没有时间更新博客,今天公司的事情忙完得空,继续为我的自定义控件系列博客添砖加瓦.本篇博客讲解的是标签自动换行的布局容器,正好前一阵子有个项目中需要,想了想没什么难度就自己弄了 ...
随机推荐
- UWP学习开发笔记记录(开篇)
零零散散开发微软移动2年多了,基本上从未记录或写过任何笔记.所以打算写一些自己的心得和技术的分享,大家一起来共同探讨.虽然现在UWP的工作几乎没有了,但是我感觉大家都是在观望,再看接下来微软的动作,所 ...
- iOS开发:http中的get和post请求
什么是 HTTP ? 超文本传输协议(HTTP)的设计目的是保证客户端与服务器之间的通信. HTTP 的工作方式是客户端与服务器之间的请求-应答协议. web 浏览器可能是客户端,而计算机上的网络应用 ...
- 夺命雷公狗-----React---18--value和defaultValue的区别
<!DOCTYPE> <html> <head> <meta charset="utf-8"> <title></ ...
- linux安全运维之谁动了chattr
安全一直是老生常谈的问题,今天我们来谈谈chattr. 如果涉及到侵权问题:请联系w18030432178@outlook.com,我会尽快删除帖子 目录 0.chattr的简介 0.0 chattr ...
- 利用 Serial Over Lan(SOL)搭建 XEN 的调试信息输出环境
如有转载,请注明出处与本文连接,谢谢! 修改XEN的源码实现额外的功能,需要有一个调试环境来得到XEN的调试信息(有关源码编译并安装 XEN 请阅读我以前的博文:在CentOS下源码安装 Xen并搭建 ...
- 【笔记】jquery js获取浏览器滑动条距离顶部距离的写法
jq写法 $(window).scroll(function(){ var $top = $(document).scrollTop(); }); /*注:$(window).scroll(funct ...
- WCF、Net remoting、Web service概念及区别
Windows通信基础(Windows Communication Foundation,WCF)是基于Windows平台下开发和部署服务的软件开发包(Software Development Kit ...
- Python企业级开发之一:基础
Python企业级开发相关内容.这里涉及到Python开发过程中的问题以及解决办法.还提供新的开发思路. 脚本开发的一些共同的问题.如:1.对OO的支持不完善,2.问题定位方式给出的信息过于晦涩,3. ...
- Android存储
Android的四种数据存储方式: 1.SharedPrefrences 2.SQLite 3.Content Provider 4.File SharedPrefrences: 1.是一种轻型的数据 ...
- <script>中的代码
<script type="text/javascript"> $(document).ready(function () { $.formValidator.init ...