Android自定义控件(一)
2016-01-05 10:15
656 查看
1、创建新视图的类型与希望达到的目标:
如果现有控件已经满足希望实现的基本功能,那么只需要对现有控件的外观和行为进行修改或扩展即可。通过重写事件处理程序和onDraw方法,但是仍然回调超类的方法,可以对视图进行定制,而不必重新实现它的功能。例如,定制一个TextView来显示指定位数的小数。
可以通过组合多个视图创建不可分割的、可重用的控件,从而使它可以综合使用多个相互关联的视图的功能。例如,通过组合一个TextView和一个Button来创建一个秒表定时器,当单击它的时候,就重置计数器。
当需要一个全新的界面,而通过修改或者组合现有控件不能是西安这个目标的时候,就可以创建一个全新的控件。
1.1 修改现有视图
在一个已有的控件基础上创建一个新视图,就需要创建一个扩展了原控件的新类。
1.2 创建组合控件视图
复合控件是指不可分割的、自包含的视图组(ViewGroup),其中包含了多个排列和连接在一起的子视图。
当创建复合控件时,必须对它包含的视图的布局、外观和交互进行定义。复合控件是通过扩展一个ViewGroup(通常是一个布局)来创建的。因此,要创建一个新的复合控件,首先需要选择一个最合适放置子控件的布局类,然后扩展该类。
我们来实现这样的一个布局效果图:
首先,创建组合控件布局文件view_custom_layout.xml;
要在一个空画布上创建新的控件,就需要对View或者Surface类(3D)进行扩展。View类提供一个Canvas和一系列绘制方法以及Paint类,因此,使用它可以运用位图和光栅图像创建一个可视化的界面,之后,可以重写像触摸屏或者按键按下这样的用户事件以提供交互。
例如,要实现以下效果的自定义视图:
代码实现如下:
1.5 attr属性format讲解:http://www.cnblogs.com/rayray/p/3442026.html
如果现有控件已经满足希望实现的基本功能,那么只需要对现有控件的外观和行为进行修改或扩展即可。通过重写事件处理程序和onDraw方法,但是仍然回调超类的方法,可以对视图进行定制,而不必重新实现它的功能。例如,定制一个TextView来显示指定位数的小数。
可以通过组合多个视图创建不可分割的、可重用的控件,从而使它可以综合使用多个相互关联的视图的功能。例如,通过组合一个TextView和一个Button来创建一个秒表定时器,当单击它的时候,就重置计数器。
当需要一个全新的界面,而通过修改或者组合现有控件不能是西安这个目标的时候,就可以创建一个全新的控件。
1.1 修改现有视图
在一个已有的控件基础上创建一个新视图,就需要创建一个扩展了原控件的新类。
/** * Desc: 在TextView的基础上绘制一条横线和竖线 * Author: xss * Time:2016/1/5 10:25 */ public class CustomTextView extends TextView { private Paint marginPaint; //绘制边缘 private Paint linePaint; //绘制页面背景 private int paperColor; //页面颜色值 private float margin; //边缘宽度值 public CustomTextView(Context context) { super(context); init(); } public CustomTextView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { //获得对资源表的引用 Resources mResources = getResources(); //创建将在onDraw方法中使用的画刷 marginPaint = new Paint(Paint.ANTI_ALIAS_FLAG); marginPaint.setColor(mResources.getColor(R.color.custom_textView_margin)); linePaint = new Paint(Paint.ANTI_ALIAS_FLAG); linePaint.setColor(mResources.getColor(R.color.custom_textView_lines)); //获得页面背景和边缘宽度 paperColor = mResources.getColor(R.color.custom_textView_paper); margin = mResources.getDimension(R.dimen.custom_textView_margin); } /** * 一旦绘制了页面图像后,就可以调用超类的onDraw方法,让它像往常一样绘制文本 * @param canvas */ @Override protected void onDraw(Canvas canvas) { //绘制页面的颜色 canvas.drawColor(paperColor); //绘制边缘 canvas.drawLine(0, 0, 0, getMeasuredHeight(), linePaint); //绘制左高 canvas.drawLine(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight(), linePaint); //绘制底宽 canvas.drawLine(margin, 0, margin, getMeasuredHeight(), marginPaint); //绘制margin //移动文本,让它跨过边缘 canvas.save(); canvas.translate(margin, 0); //使用TextView渲染文本 super.onDraw(canvas); canvas.restore(); } }实现效果图:
1.2 创建组合控件视图
复合控件是指不可分割的、自包含的视图组(ViewGroup),其中包含了多个排列和连接在一起的子视图。
当创建复合控件时,必须对它包含的视图的布局、外观和交互进行定义。复合控件是通过扩展一个ViewGroup(通常是一个布局)来创建的。因此,要创建一个新的复合控件,首先需要选择一个最合适放置子控件的布局类,然后扩展该类。
我们来实现这样的一个布局效果图:
首先,创建组合控件布局文件view_custom_layout.xml;
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" android:padding="8dp"> <TextView android:id="@+id/tv_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="label" /> <EditText android:id="@+id/edt_content" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="#00ff00" android:hint="Please input" android:paddingLeft="10dp" android:paddingRight="10dp" android:gravity="center_vertical" android:focusable="true" android:textCursorDrawable="@drawable/color_cursor"/> <TextView android:id="@+id/tv_extra" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="unit" /> </LinearLayout> <LinearLayout android:id="@+id/ll_span_line" android:layout_width="match_parent" android:layout_height="1px" android:background="#bbb" android:orientation="vertical"/> </merge>其次,自定义控件的一些属性,在attr.xml中定义如下:
<declare-styleable name="CustomLinearLayout"> <attr name="custom_label_text" format="string" /> <attr name="custom_label_textSize" format="dimension" /> <attr name="custom_label_textColor" format="color" /> <attr name="custom_label_padding" format="dimension" /> <attr name="custom_edt_text" format="string" /> <attr name="custom_edt_textSize" format="dimension" /> <attr name="custom_edt_textColor" format="color" /> <attr name="custom_edt_hint_text" format="string" /> <attr name="custom_edt_hint_textColor" format="color" /> <attr name="custom_edt_padding" format="dimension" /> <attr name="custom_edt_marginLeft" format="dimension" /> <attr name="custom_edt_marginRight" format="dimension" /> <attr name="custom_extra_text" format="string" /> <attr name="custom_extra_textSize" format="dimension" /> <attr name="custom_extra_textColor" format="color" /> <attr name="custom_extra_background" format="reference|color" /> <attr name="custom_line_visibility"> <enum name="VISIBLE" value="0" /> <enum name="INVISIBLE" value="1" /> <enum name="GONE" value="8" /> </attr> <attr name="custom_edt_inputType"> <!--整形定义 format="integer"--> <flag name="none" value="0x00000000" /> <flag name="text" value="0x00000001" /> <flag name="textCapCharacters" value="0x00001001" /> <flag name="textMultiLine" value="0x00020001" /> <flag name="textUri" value="0x00000011" /> <flag name="textEmailAddress" value="0x00000021" /> <flag name="textPersonName" value="0x00000061" /> <flag name="textPassword" value="0x00000081" /> <flag name="number" value="0x00000002" /> <flag name="numberDecimal" value="0x00002002" /> <flag name="numberPassword" value="0x00000012" /> <flag name="phone" value="0x00000003" /> <flag name="datetime" value="0x00000004" /> <flag name="date" value="0x00000014" /> <flag name="time" value="0x00000024" /> </attr> <attr name="custom_edt_imeOptions" > <flag name="normal" value="0x00000000" /> <flag name="actionUnspecified" value="0x00000000" /> <flag name="actionNone" value="0x00000001" /> <flag name="actionGo" value="0x00000002" /> <flag name="actionSearch" value="0x00000003" /> <flag name="actionSend" value="0x00000004" /> <flag name="actionNext" value="0x00000005" /> <flag name="act e06e ionDone" value="0x00000006" /> </attr> <attr name="custom_edt_background" format="color" /> </declare-styleable>最后,就是重写我们的布局,代码如下:
public class CustomLinearLayout extends LinearLayout { private Context context; private TextView tv_label; private EditText edt_content; private TextView tv_extra; private LinearLayout ll_span_line; private String labelText; private float labelTextSize; private int labelTextColor; private float labelPadding; private String contentText; private float contentTextSize; private int contentTextColor; private String hintText; private int hintTextColor; private float contentPadding; private float contentMarginLeft; private float contentMarginRight; private int edtInputType; private int edtImeOptions; private int edtBackground; private String extraText; private float extraTextSize; private int extraTextColor; private int extraBackground; private int lineVisibility; public CustomLinearLayout(Context context) { super(context); this.context = context; init(); } public CustomLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; obtainStyleAttributes(context, attrs, 0, 0); init(); } public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context = context; obtainStyleAttributes(context, attrs, defStyleAttr, 0); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); this.context = context; obtainStyleAttributes(context, attrs, defStyleAttr, defStyleRes); init(); } /** * 获取属性值 * @param context * @param attrs * @param defStyleAttr * @param defStyleRes */ private void obtainStyleAttributes(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomLinearLayout, defStyleAttr, defStyleRes); labelText = a.getString(R.styleable.CustomLinearLayout_custom_label_text); labelTextSize = a.getDimension(R.styleable.CustomLinearLayout_custom_label_textSize, 12f); labelTextColor = a.getColor(R.styleable.CustomLinearLayout_custom_label_textColor, Color.parseColor("#000000")); labelPadding = a.getDimension(R.styleable.CustomLinearLayout_custom_label_padding, 0f); contentText = a.getString(R.styleable.CustomLinearLayout_custom_edt_text); contentTextSize = a.getDimension(R.styleable.CustomLinearLayout_custom_edt_textSize, 12f); contentTextColor = a.getColor(R.styleable.CustomLinearLayout_custom_edt_textColor, Color.parseColor("#000000")); hintText = a.getString(R.styleable.CustomLinearLayout_custom_edt_hint_text); hintTextColor = a.getColor(R.styleable.CustomLinearLayout_custom_edt_hint_textColor, Color.parseColor("#000000")); contentPadding = a.getDimension(R.styleable.CustomLinearLayout_custom_edt_padding, 0f); contentMarginLeft = a.getDimension(R.styleable.CustomLinearLayout_custom_edt_marginLeft, 0f); contentMarginRight = a.getDimension(R.styleable.CustomLinearLayout_custom_edt_marginRight, 0f); edtInputType = a.getInt(R.styleable.CustomLinearLayout_custom_edt_inputType, EditorInfo.TYPE_NULL); //EditorInfo.TYPE_NULL edtImeOptions = a.getInt(R.styleable.CustomLinearLayout_custom_edt_imeOptions, EditorInfo.IME_ACTION_NONE); edtBackground = a.getColor(R.styleable.CustomLinearLayout_custom_edt_background, Color.parseColor("#ffffff")); extraText = a.getString(R.styleable.CustomLinearLayout_custom_extra_text); extraTextSize = a.getDimension(R.styleable.CustomLinearLayout_custom_extra_textSize, 12f); extraTextColor = a.getColor(R.styleable.CustomLinearLayout_custom_extra_textColor, Color.parseColor("#000000")); extraBackground = a.getResourceId(R.styleable.CustomLinearLayout_custom_extra_background, R.color.COLOR_000000); lineVisibility = a.getInt(R.styleable.CustomLinearLayout_custom_line_visibility, View.VISIBLE); a.recycle(); } /** * 获取控件对象 */ private void init() { setOrientation(LinearLayout.VERTICAL); setGravity(Gravity.CENTER_VERTICAL); LayoutInflater inflater = (LayoutInflater)this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.view_custom_layout, this, true); final ViewGroup viewGroup = (ViewGroup)getChildAt(0); if (viewGroup != null) { tv_label = (TextView)viewGroup.getChildAt(0); edt_content = (EditText)viewGroup.getChildAt(1); tv_extra = (TextView)viewGroup.getChildAt(2); } ll_span_line = (LinearLayout)getChildAt(1); // tv_label = (TextView)view.findViewById(R.id.tv_label); // edt_content = (EditText)view.findViewById(R.id.edt_content); // tv_extra = (TextView)view.findViewById(R.id.tv_extra); // ll_span_line = (LinearLayout)view.findViewById(R.id.ll_span_line); tv_label.setText(labelText); //setTextSize函数对应的单位本身就是sp tv_label.setTextSize(TypedValue.COMPLEX_UNIT_PX, labelTextSize); tv_label.setTextColor(labelTextColor); tv_label.setPadding((int) labelPadding, (int) labelPadding, (int) labelPadding, (int) labelPadding); showKeyBoard(); edt_content.setText(contentText); edt_content.setTextSize(TypedValue.COMPLEX_UNIT_PX, contentTextSize); edt_content.setTextColor(contentTextColor); edt_content.setHint(hintText); edt_content.setHintTextColor(hintTextColor); edt_content.setPadding((int) contentPadding, (int) contentPadding, (int) contentPadding, (int) contentPadding); LinearLayout.LayoutParams lp = (LayoutParams) edt_content.getLayoutParams(); lp.setMargins((int) contentMarginLeft, 0, (int) contentMarginRight, 0); //左上右下 edt_content.setImeOptions(edtImeOptions); setEdtInputType(); //设置InputType edt_content.setBackgroundColor(edtBackground); if (!TextUtils.isEmpty(contentText)) { edt_content.setKeyListener(DigitsKeyListener.getInstance(contentText)); } edt_content.setFocusableInTouchMode(true); tv_extra.setText(extraText); tv_extra.setTextSize(TypedValue.COMPLEX_UNIT_PX, extraTextSize); tv_extra.setTextColor(extraTextColor); tv_extra.setBackgroundColor(extraBackground); //getResources().getColor(extraBackground) if (lineVisibility == VISIBLE) { ll_span_line.setVisibility(VISIBLE); } else if (lineVisibility == INVISIBLE) { ll_span_line.setVisibility(INVISIBLE); } else if (lineVisibility == GONE) { ll_span_line.setVisibility(GONE); } } private void showKeyBoard() { if (edt_content != null) { edt_content.setFocusable(true); edt_content.setFocusableInTouchMode(true); edt_content.requestFocus(); //调用系统输入法 InputMethodManager inputMethodManager = (InputMethodManager) edt_content.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.showSoftInput(edt_content, 0); } } }1.3 创建定制的视图
要在一个空画布上创建新的控件,就需要对View或者Surface类(3D)进行扩展。View类提供一个Canvas和一系列绘制方法以及Paint类,因此,使用它可以运用位图和光栅图像创建一个可视化的界面,之后,可以重写像触摸屏或者按键按下这样的用户事件以提供交互。
例如,要实现以下效果的自定义视图:
代码实现如下:
public class CustomView extends View { private Paint mPaint; //使用代码进行创建时必需的构造函数 public CustomView(Context context) { super(context); init(); } //使用资源文件进行填充时必需的构造函数 public CustomView(Context context, AttributeSet attrs) { super(context, attrs); init(); } //使用资源文件进行填充时必需的构造函数 public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { //为提高效率,paint对象最好在构造函数中创建,因为在onDraw方法中创建的任何对象都会在屏幕刷新的时候被创建和销毁 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.WHITE); } @Override protected void onDraw(Canvas canvas) { int height = getMeasuredHeight(); int width = getMeasuredWidth(); //找出控件的中心 int px = width / 2; int py = height / 2; String text = "Hello world !"; //计算文本字符串的宽度 float textWidth = mPaint.measureText(text); //在控件中心绘制文本字符串 canvas.drawText(text, px - textWidth / 2, py, mPaint); } /** * 调整控件大小 * @param widthMeasureSpec * @param heightMeasureSpec * 对参数解码,可以计算出适合的高宽值 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredWidth = measureWidth(widthMeasureSpec); int measuredHeight = measureHeight(heightMeasureSpec); //必须调用setMeasuredDimension,否则在布局控件的时候,会造成运行时异常 setMeasuredDimension(measuredWidth, measuredHeight); } /** * 返回计算的组建的宽度 * @param measureSpec * @return */ private int measureWidth(int measureSpec) { int sepcMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); //如果不指定限制,就是默认大小 int result = 500; if (sepcMode == MeasureSpec.AT_MOST) { //计算控件在这个最大尺寸范围内的理想大小,如果控件填充了可用空间,则返回外边界 result = specSize; } else if (sepcMode == MeasureSpec.EXACTLY) { //如果控件可以放置在这个边界内,则返回该值 result = specSize; } return result; } /** * 返回计算的组建的高度 * @param measureSpec * @return */ private int measureHeight(int measureSpec) { int sepcMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); //如果不指定限制,就是默认大小 int result = 500; if (sepcMode == MeasureSpec.AT_MOST) { //计算控件在这个最大尺寸范围内的理想大小,如果控件填充了可用空间,则返回外边界 result = specSize; } else if (sepcMode == MeasureSpec.EXACTLY) { //如果控件可以放置在这个边界内,则返回该值 result = specSize; } return result; } }1.4 attrs.xml属性全解:http://blog.csdn.net/aldridge1/article/details/14005403
1.5 attr属性format讲解:http://www.cnblogs.com/rayray/p/3442026.html
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories