Android 自定義View(1)

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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章