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。