在毕设项目中多处用到自定义控件,一直打算总结一下自定义控件的实现方式,今天就来总结一下吧。在此之前学习了郭霖大神博客上面关于自定义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. Java程序猿学习的建议(转)

    第一部分:对于尚未做过Java工作的同学,包括一些在校生以及刚准备转行Java的同学. 一.Java基础 首先去找一个Java的基础教程学一下,这里可以推荐一个地址,或者你也可以参照这个地址上去找相应 ...

  2. HTML5探秘:用requestAnimationFrame优化Web动画

    本文转载自: HTML5探秘:用requestAnimationFrame优化Web动画

  3. Excel应该这么玩——7、我是预言家:绘制趋势图

    让我们先看一个场景:你是公司销售部的员工,你手里有公司最近几年的销售额相关的数据,经理希望你预测下个月的销售额.盯着一堆数据,你或许会想到画一张XY坐标图,然后将每个月份的销售额标定为一个坐标.但是下 ...

  4. linux安全运维之谁动了chattr

    安全一直是老生常谈的问题,今天我们来谈谈chattr. 如果涉及到侵权问题:请联系w18030432178@outlook.com,我会尽快删除帖子 目录 0.chattr的简介 0.0 chattr ...

  5. Supervisor重新加载配置

    Supervisor重新加载配置启动新的进程 liaojie 发布于 1年前,共有 0 条评论 一.添加好配置文件后 二.更新新的配置到supervisord supervisorctl update ...

  6. oracle表分区、表分析及oracle数据泵文件导入导出开心版

    1.先说oracle表分区是什么吧,这样吧我们来举个桃子,栗子太小,我们就不举了,我们来举个桃子. 你有500万份文件,你要把他存在磁盘上,好嘛,我们就一个文件夹,500万分文件在那儿杵着,我们想找到 ...

  7. HttpClient中转上传文件

    场景:客户端(浏览器)A---->选择文件上传---->服务器B---->中转文件---->服务器C---->返回结果---->服务器B---->客户端A 有 ...

  8. CI 框架导出文件

    CI框架目录结构: |-application (应用目录) |-system (核心目录) |-downexcel (文件存在目录) |-ZipBackDir (备份目录) |-index.php ...

  9. 你年纪轻轻怎么这么"不讲理"

    今天在公交上遇到一出"精彩的打戏",这给我的12月份带来了一丝的"惊喜",1号就能让我碰到这样的事,看来这个月会惊喜不断啊!!!! 这精彩的戏是这样的,公交到了 ...

  10. 关于AngularJs,数据绑定与自定义验证

    最近开始着手学起了Angular,抱着好奇的心情开始研究了起来.忽然发现angular可以巧妙而方便的进行数据的绑定验证啊什么的.(当然,我只是刚开始学,所有可能有更强大的功能,只是我还没有看到) 那 ...