android自定义View的实现方式有3种,组合控件,继承android控件和继承View。
一、组合控件
这种方式较为简单,基本实现过程是:
1.自定义类继承Linearlayout等;
2.在布局文件中使用系统控件任意组合成预定样式,在构造方法中调用inflate()方法绑定布局文件;或者直接在代码中创建(new)出各种需要的控件;
3.在需要的地方像使用系统控件一样使用它。
注意:当直接在代码中new控件时,可以只实现带1个参数的构造方法,当采用绑定layout的方法时,需要实现至少带2个参数的构造方法;
例:一个自定义标题栏。
package com.wy.customview.view;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.wy.customview.R;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* Created by wuyong on 2016/4/25.
*/
public class MyGroupView extends LinearLayout {
@Bind(R.id.ivBack)
ImageView ivBack;
@Bind(R.id.tvTitle)
TextView tvTitle;
@Bind(R.id.ivSet)
ImageView ivSet;
Context context;
public MyGroupView(Context context, AttributeSet attrs) {
super(context, attrs);
inflate(context, R.layout.layout_my_group_view, this);
ButterKnife.bind(this);
this.context=context;
}
@OnClick({R.id.ivBack, R.id.ivSet})
public void onClick(View view) {
switch (view.getId()) {
case R.id.ivBack:
( (Activity)context).finish();
//((Activity)getContext()).finish();
break;
case R.id.ivSet:
// do something```
break;
}
}
public void setTvTitle(String title) {
this.tvTitle.setText(title); ;
}
}
layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@color/colorPrimary"
>
<ImageView
android:id="@+id/ivBack"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:src="@mipmap/back"
android:layout_marginLeft="10dp"
android:layout_centerVertical="true"
/>
<TextView
android:id="@+id/tvTitle"
android:text="我的标题"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:layout_centerInParent="true"
android:textColor="#ffffff"
/>
<ImageView
android:id="@+id/ivSet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/set"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
/>
</RelativeLayout>
调用它的Activity:
package com.wy.customview.activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.wy.customview.R;
import com.wy.customview.view.MyGroupView;
import butterknife.Bind;
import butterknife.ButterKnife;
public class MyGroupActivity extends AppCompatActivity {
@Bind(R.id.mvGroup)
MyGroupView mvGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_group);
ButterKnife.bind(this);
mvGroup.setTvTitle("这是组合控件");
}
}
layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.wy.customview.view.MyGroupView
android:id="@+id/mvGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.wy.customview.view.MyGroupView>
</RelativeLayout>
自定义MyGroupView,继承LinearLayout,实现带2个参数的构造方法,绑定layout。里面包含一个返回的箭头,一个用于显示标题的TextView和一个常见的Set图标。在MyGroupView中监听图标的点击事件,给TextView设置set方法,然后在activity中调用即可。在activity中取得MyGroupView的实例,调用set方法即可任意设置标题。还可以根据项目需要,给自定义控件添加各种方法。
二、继承android控件
只需要继承一个现有的控件,在此基础上增加自己需要的功能即可。
例:一个自定义ListView,支持滑动弹出删除按钮,点击删除item。
MyListView:
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ListView;
import android.widget.PopupWindow;
import com.wy.customview.R;
/**
* Created by wuyong on 2016/4/25.
*/
public class MyListView extends ListView {
private int xDown;//手指按下X坐标
private int yDown;//手指按下Y坐标
private int xMove;//手指x方向移动距离
private int yMove;//手指y方向移动距离
private int minMove;//手指移动最小距离
private boolean isSliding;
private LayoutInflater inflater;
private PopupWindow popupWindow;
private int popHeight;
private int popWidth;
private int position;
private View mView;//触摸的当前View
private Button button;
private DeleteListener deleteListener;
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context){
inflater=LayoutInflater.from(context);
minMove= ViewConfiguration.get(context).getScaledTouchSlop();
View view=this.inflater.inflate(R.layout.layout_delete,null);
button= (Button) view.findViewById(R.id.btnDelete);
popupWindow=new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.getContentView().measure(0,0);
// popHeight=popupWindow.getContentView().getHeight();//错误
// popWidth=popupWindow.getContentView().getWidth();//错误
popHeight=popupWindow.getContentView().getMeasuredHeight();
popWidth=popupWindow.getContentView().getMeasuredWidth();
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (deleteListener==null) return;
deleteListener.onDeleteList(position);
popupWindow.dismiss();
}
});
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x= (int) ev.getX();
int y= (int) ev.getY();
int action=ev.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
if (popupWindow.isShowing()){
popupWindow.dismiss();
return false;
}
xDown=x;
yDown=y;
position=pointToPosition(xDown, yDown);
mView=getChildAt(position-getFirstVisiblePosition());
break;
case MotionEvent.ACTION_MOVE:
xMove=x;
yMove=y;
int absX=xMove-xDown;
int absY=yMove-yDown;
//判断是否在向左滑动
if (xMove<xDown&& Math.abs(absX)>minMove&&yMove<yDown&&Math.abs(absY)<minMove){
isSliding=true;
}
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action=ev.getAction();
if (isSliding){
switch (action){
case MotionEvent.ACTION_MOVE:
int [] location=new int[2];
mView.getLocationOnScreen(location);
popupWindow.setAnimationStyle(R.style.popwindow_delete_btn);
popupWindow.showAtLocation(mView, Gravity.LEFT|Gravity.TOP,
location[0]+mView.getWidth(),
location[1]+mView.getHeight()/2-popHeight/2);
break;
case MotionEvent.ACTION_UP:
isSliding=false;
break;
}
return true;
}
return super.onTouchEvent(ev);
}
public void setOnDeleteListListener(DeleteListener deleteListener){
this.deleteListener=deleteListener;
}
public interface DeleteListener{
void onDeleteList(int position);
}
}
popupwindow中只含有一个button,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/btnDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/holo_red_dark"
android:textColor="@android:color/white"
android:drawableLeft="@mipmap/ic_delete"
android:text="删除"
/>
</LinearLayout>
接着在Activity中调用它:
public class MyListViewActivity extends AppCompatActivity {
@Bind(R.id.myListView)
MyListView myListView;
private ArrayAdapter<String> adapter;
List<String> list=new ArrayList<String>(Arrays.asList("a", "b", "c", "d", "e", "f",
"g", "h", "i", "j", "k","l","m","n","o","p","q"));
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_list_view);
ButterKnife.bind(this);
adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,list);
myListView.setAdapter(adapter);
myListView.setOnDeleteListListener(new MyListView.DeleteListener() {
@Override
public void onDeleteList(int position) {
list.remove(position);
adapter.notifyDataSetChanged();
}
});
}
}
layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.wy.customview.view.MyListView
android:id="@+id/myListView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</RelativeLayout>
三、继承View的自绘控件
主要是实现其中的3个方法,onMeasure(),onLayout(),onDraw();
例:一个点击控件自动生成5位乱序数字的View。
public class MyText extends View {
private Paint paint;
private Rect rect;
private String text;
public MyText(Context context) {
super(context);
init();
}
public MyText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
paint=new Paint();
rect=new Rect();
text=randomText();
paint.getTextBounds(text,0,text.length(),rect);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
text=randomText();
postInvalidate();
}
});
}
private String randomText(){
Random random=new Random();
Set<Integer> set=new HashSet<Integer>();
/* for (int i=0;i<5;i++){
int a=random.nextInt(10);
set.add(a);
}*/
while (set.size()<5){
int a=random.nextInt(10);
set.add(a);
}
StringBuffer buffer=new StringBuffer();
for (Integer i:set){
buffer.append(String.valueOf(i));
}
return buffer.toString();
}
/**
* 用于设置控件宽高
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 用于设置控件位置
* @param changed
* @param left
* @param top
* @param right
* @param bottom
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
/**
* 绘制
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.YELLOW);
canvas.drawRect(0,0,getWidth(),getHeight(),paint);
paint.setColor(Color.WHITE);
paint.setTextSize(30);
canvas.drawText(text,getWidth()/2-rect.width()/2,getHeight()/2+rect.height()/2,paint);
}
}
在layout布局中使用:我直接插入到之前的例子中:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.wy.customview.view.MyGroupView
android:id="@+id/mvGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.wy.customview.view.MyGroupView>
<com.wy.customview.view.MyText
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_below="@+id/mvGroup"
android:layout_marginTop="20dp"
android:layout_centerHorizontal="true"
/>
</RelativeLayout>
运行APP,成功实现自定义View。