一般写自定义控件是为了增加控件的属性或者做一些复杂的控件避免每次都写一些麻烦的布局,自定义view也不一定很复杂,可以很简单的实现。
1、view其实可以分自绘控件、组合控件、以及继承控件这三种
2、先简单介绍一下view绘制的三个方法
1、measure操作
measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:
(1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
2、layout操作
layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:
(1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;
(2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;
3、draw操作
draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:
(1)绘制背景;
(2)如果要视图显示渐变框,这里会做一些准备工作;
(3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;
(4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;
(5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;
(6)绘制滚动条;
3、这里实现自绘控件,主要用到onMeasure()和onDraw()方法,实现过程是先实现onMeasure()和onDraw()方法,在onMeasure中获取到控件宽高
然后通过onDraw进行绘制,实现比较简单,这里是点击一次屏幕加一。
package com.example.apple.autobutton.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by apple on 17/3/18.
*/
public class CounterView extends View {
private Paint mPaint;
private int height;
private int width;
private int Count = 1;
private Rect mBounds;
public CounterView(Context context) {
this(context, null);
}
public CounterView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CounterView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mBounds = new Rect();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
height = heightMeasureSpec;//获取控件宽高
width = widthMeasureSpec;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.BLACK);
canvas.drawRect(0, 0, width, height, mPaint);
mPaint.setColor(Color.RED);
mPaint.setTextSize(30);
String text = String.valueOf(Count);
mPaint.getTextBounds(text, 0, text.length(), mBounds);
float textWidth = mBounds.width();
float textHeight = mBounds.height();
canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2 + textHeight / 2, mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Count++;
invalidate();//重绘视图
return super.onTouchEvent(event);
}
}
4、第二种是
组合控件的使用,主要是将几个不同的控件组合起来使用,这里只是使用了button和textView实现简单的购物按钮
代码布局是
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:background="@drawable/btn_add_sub_view_selector"
android:orientation="horizontal">
<Button
android:id="@+id/btn_sub"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="@drawable/btn_number_view_selector"
android:gravity="center"
android:text=""
android:textSize="20sp" />
<TextView
android:id="@+id/tv_value"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center"
android:text="1" />
<Button
android:id="@+id/btn_add"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="@drawable/btn_number_view_selector"
android:gravity="center"
android:text=""
android:textSize="20sp" />
</LinearLayout>
自定义view中代码实现:其实也比较简单,先加载布局文件,初始化三个控件然后添加监听事件,还加了一个接口回调,可以回调到activity进行处理
package com.example.apple.autobutton.view;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v7.widget.TintTypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.example.apple.autobutton.R;
/**
* Created by apple on 17/3/17.
*/
public class NumberAddSubView extends LinearLayout implements View.OnClickListener {
private Button btn_sub;
private TextView tv_value;
private Button btn_add;
private int value = 1;
private int minValue = 1;
private int maxValue = 10;
private OnNumberClickLister listener;
public int getValue() {
String valueStr = tv_value.getText().toString().trim();
if (!TextUtils.isEmpty(valueStr)) {
value = Integer.valueOf(valueStr);
}
return value;
}
public void setValue(int value) {
this.value = value;
tv_value.setText(value + "");
}
public int getMinValue() {
return minValue;
}
public void setMinValue(int minValue) {
this.minValue = minValue;
}
public int getMaxValue() {
return maxValue;
}
public void setMaxValue(int maxValue) {
this.maxValue = maxValue;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public NumberAddSubView(Context context) {
this(context, null);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public NumberAddSubView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public NumberAddSubView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
View.inflate(context, R.layout.number_view, this);
btn_sub = (Button) findViewById(R.id.btn_sub);
tv_value = (TextView) findViewById(R.id.tv_value);
btn_add = (Button) findViewById(R.id.btn_add);
getValue();
//设置点击事件
btn_sub.setOnClickListener(this);
btn_add.setOnClickListener(this);
if (attrs != null) {
TintTypedArray typedArray = TintTypedArray.obtainStyledAttributes(context, attrs, R.styleable.NumberAddSubView);
//设置值
int value = typedArray.getInt(R.styleable.NumberAddSubView_value, 0);
if (value > 0){
setValue(value);
}
//设置最小值
int minValue = typedArray.getInt(R.styleable.NumberAddSubView_minValue, 0);
if (value > 0){
setMinValue(minValue);
}
//设置最大值
int maxValue = typedArray.getInt(R.styleable.NumberAddSubView_maxValue, 0);
if (value > 0){
setMaxValue(maxValue);
}
//设置背景
Drawable numberAddSubBackground = typedArray.getDrawable(R.styleable.NumberAddSubView_NumberAddSubBackground);
if (numberAddSubBackground!=null){
setBackground(numberAddSubBackground);
}
//加号背景
Drawable numberAddBackground = typedArray.getDrawable(R.styleable.NumberAddSubView_NumberAddBackground);
if (numberAddBackground!=null){
btn_add.setBackground(numberAddBackground);
}
//减号背景
Drawable numberSubBackground = typedArray.getDrawable(R.styleable.NumberAddSubView_NumberSubBackground);
if (numberSubBackground!=null){
btn_sub.setBackground(numberSubBackground);
}
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_sub://减
subNumber();
if (listener != null) {
listener.onButtonSub(v, value);
}
break;
case R.id.btn_add://加
addNumber();
if (listener != null) {
listener.onButtonAdd(v, value);
}
break;
}
}
private void addNumber() {
if (value < maxValue) {
value++;
}
setValue(value);
}
private void subNumber() {
if (value > minValue) {
value--;
}
setValue(value);
}
/**
* 监听数字增加减少控件
*/
public interface OnNumberClickLister {
/**
* 当减少按钮被点击的时候回调
*
* @param view
* @param value
*/
void onButtonSub(View view, int value);
/**
* 当增加按钮被点击的时候回调
*
* @param view
* @param value
*/
void onButtonAdd(View view, int value);
}
/**
* 监听数字按钮
*
* @param listener
*/
public void setOnNumberClickLister(OnNumberClickLister listener) {
this.listener = listener;
}
}
5、
继承控件的使用,如名字所说,就是继承一个控件,然后可以在控件里面增加方法,下面简单实现,就修改控件大小。
package com.example.apple.autobutton.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ImageView;
/**
* Created by apple on 17/3/18.
*
*继承自定义view
*/
public class AccedeView extends ImageView {
public AccedeView(Context context) {
this(context, null);
}
public AccedeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AccedeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 设置视频宽高
* @param imageViewWidth
* @param imageViewHeight
*/
public void setImageViewSize(int imageViewWidth,int imageViewHeight){
ViewGroup.LayoutParams params = getLayoutParams();
params.width = imageViewWidth;
params.height = imageViewHeight;
setLayoutParams(params);
//requestLayout();
}
}
6、自定义控件引用布局代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:atguigu="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.apple.autobutton.MainActivity">
<com.example.apple.autobutton.view.NumberAddSubView
android:id="@+id/number_add_sub_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
atguigu:NumberAddSubBackground="@drawable/btn_add_sub_view_selector"
atguigu:NumberSubBackground="@drawable/btn_sub_selector"
atguigu:NumberAddBackground="@drawable/btn_add_selector"
atguigu:maxValue="20"
atguigu:minValue="1"
atguigu:value="1">
</com.example.apple.autobutton.view.NumberAddSubView>
<com.example.apple.autobutton.view.CounterView
android:id="@+id/counter_view"
android:layout_width="100dp"
android:layout_height="100dp" />
<com.example.apple.autobutton.view.AccedeView
android:id="@+id/accede"
android:background="@drawable/add"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
7、activity代码
package com.example.apple.autobutton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import com.example.apple.autobutton.view.AccedeView;
import com.example.apple.autobutton.view.CounterView;
import com.example.apple.autobutton.view.NumberAddSubView;
public class MainActivity extends AppCompatActivity {
private NumberAddSubView number_add_sub_view;
private CounterView counter_view;
private AccedeView accede;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
counter_view = (CounterView)findViewById(R.id.counter_view);
accede = (AccedeView)findViewById(R.id.accede);
accede.setImageViewSize(200,200);
number_add_sub_view = (NumberAddSubView)findViewById(R.id.number_add_sub_view);
number_add_sub_view.setOnNumberClickLister(new NumberAddSubView.OnNumberClickLister() {
@Override
public void onButtonSub(View view, int value) {
}
@Override
public void onButtonAdd(View view, int value) {
}
});
}
}
最后实现效果图