在毕设项目中多处用到自定义控件,一直打算总结一下自定义控件的实现方式,今天就来总结一下吧。在此之前学习了郭霖大神博客上面关于自定义View的几篇博文,感觉受益良多,本文中就参考了其中的一些内容。　总结来说,自定义控件的实现有三种方式,分别是:组合控件、自绘控件和继承控件。下面将分别对这三种方式进行介绍。　(一)组合控件　组合控件,顾名思义就是将一些小的控件组合起来形成一个新的控件,这些小的控件多是系统自带的控件。比如很多应用中普遍使用的标题栏控件,其实用的就是组合控件,那么下面将通过实现一个简单的标题栏自定义控件来说说组合控件的用法。　1、新建一个Android项目,创建自定义标题栏的布局文件title_bar.xml:　1　　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　　可见这个标题栏控件还是比较简单的,其中在左边有一个返回按钮,背景是一张事先准备好的图片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　　12　13　　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　　3、运行效果如下:　(三)继承控件　就是继承已有的控件,创建新控件,保留继承的父控件的特性,并且还可以引入新特性。下面就以支持横向滑动删除列表项的自定义ListView的实现来介绍。　1、创建删除按钮布局delete_btn.xml,这个布局是在横向滑动列表项后显示的:　1　　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　　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　　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　　4、定义适配器类CustomListViewAdapter,继承自ArrayAdapter:　1　public　class　CustomListViewAdapter　extends　ArrayAdapter　{　2　3　public　CustomListViewAdapter(Context　context,　int　textViewResourceId,　4　List　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　　6、在MainActivity中对列表做初始化、设置列表项删除按钮点击事件等处理:　1　public　class　MainActivity　extends　Activity　{　2　3　//　自定义Lv　4　private　CustomListView　mCustomLv;　5　//　自定义适配器　6　private　CustomListViewAdapter　mAdapter;　7　//　内容列表　8　private　List　contentList　=　new　ArrayList　();　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、运行效果如下: