Android自定義控件

前言

Android 項目中避免不了會使用自定義控件,主要能夠避免代碼的冗余,使用起來也很靈活,而且也方便后期移植入其他項目。自定義控件也是面試中經(jīng)常問到的東西,寫過挺多自定義控件但是一直沒有系統(tǒng)的總結(jié)過,今天再重新系統(tǒng)的學(xué)習(xí)一下。

自定義控件主要分為三類,自定義組合控件,自定義繪制控件,自定義繼承控件。

1.自定義組合控件是項目中經(jīng)常會用到,像標題欄,復(fù)用率較高的布局。

2.自定義繪制控件是面試中經(jīng)常會問到的,如果需求復(fù)雜,繪制的流程也會難度加大(后期會詳細介紹自定義控件的繪制)

3.自定義繼承控件主要是針對Android 原生的控件進行擴展,像自定義listView 。

下面我們來詳細說一下這三種自定義控件

一.自定義組合控件

組合控件的意思就是,我們并不需要自己去繪制視圖上顯示的內(nèi)容,而只是用系統(tǒng)原生的控件就好了,但我們可以將幾個系統(tǒng)原生的控件組合到一起,這樣創(chuàng)建出的控件就被稱為組合控件。

場景:一般一個項目中所有頁面的標題欄大體都差不多,所以我們沒有必要去每個頁面都重寫寫布局,這個時候我們就可以用自定義組合控件

1.第一步:新建一個title.xml布局文件,代碼如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/lay"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="@color/white">

  <!--返回鍵-->
 <ImageView
 android:id="@+id/title_icon"
 android:layout_width="wrap_content"
 android:layout_height="30dp"
 android:layout_centerVertical="true"
 android:paddingLeft="15dp"
 android:src="@mipmap/black_back" />

 <!--關(guān)閉按鈕-->
 <TextView
 android:id="@+id/tv_close"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerVertical="true"
 android:paddingLeft="15dp"
 android:text="關(guān)閉"
 android:textColor="#333333"
 android:textSize="15sp"
 android:visibility="gone" />
 <!--標題-->
 <TextView
 android:id="@+id/title_name"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerHorizontal="true"
 android:layout_marginBottom="13dp"
 android:layout_marginTop="13dp"
 android:ellipsize="end"
 android:maxLength="13"
 android:singleLine="true"
 android:textColor="#010101"
 android:textSize="17sp" />

 <!--右側(cè)提示字和圖標-->
 <LinearLayout
 android:id="@+id/ll_right"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignParentRight="true"
 android:layout_centerVertical="true"
 android:gravity="center_vertical"
 android:orientation="horizontal">
  <!--圖標-->
 <ImageView
 android:id="@+id/iv_right"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginRight="3dp" />
 <!--提示文字-->
 <TextView
 android:id="@+id/tv_right"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginRight="15dp"
 android:textColor="#333333"
 android:textSize="15sp" />     
 </LinearLayout>
<View
 android:layout_width="match_parent"
 android:layout_height="1dp"
 android:layout_below="@+id/title_name"
 android:background="@color/line_d7" />
</RelativeLayout>

UI效果如圖

image.png

2.第二步:接下來我們來創(chuàng)建一個View 集成RelativeLayout 如

public class WhitePublicTitleView extends RelativeLayout {
    View mView;
    ImageView title_icon;
    TextView title_name;
    RelativeLayout lay;
    ImageView iv_right;
    TextView tv_right;
    LinearLayout ll_right;
    TextView tv_close;

    public WhitePublicTitleView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public WhitePublicTitleView(Context context) {
        super(context);
    }

    @Override
 protected void onFinishInflate() {
        super.onFinishInflate();
        mView = LayoutInflater.from(getContext()).inflate(R.layout.public_title_view_white, this);
        initView();
    }

    public void setTitleBg() {
        lay.setBackgroundColor(Color.parseColor("#de3031"));
    }

    public void initView() {
        title_icon = (ImageView) mView.findViewById(R.id.title_icon);
        title_name = (TextView) mView.findViewById(R.id.title_name);
        lay = (RelativeLayout) mView.findViewById(R.id.lay);
        iv_right = (ImageView) mView.findViewById(R.id.iv_right);
        tv_right = (TextView) mView.findViewById(R.id.tv_right);
        tv_close = (TextView) mView.findViewById(R.id.tv_close);

        ll_right = (LinearLayout) mView.findViewById(R.id.ll_right);

    }

    public void setBackListener(OnClickListener clickListener) {
        title_icon.setOnClickListener(clickListener);
        tv_close.setOnClickListener(clickListener);
    }

    public void setBackState(int state) {
        title_icon.setVisibility(state);
    }

    public void setTitleNam(String name) {
        title_name.setText(name);
    }

    public void setRight(String state, int icon) {
        tv_right.setText(state);
        iv_right.setImageResource(icon);
    }

    public void setRightListener(OnClickListener clickListener) {
        ll_right.setOnClickListener(clickListener);
    }

    //展示文字關(guān)閉按鈕
 public void showTextClose() {
        tv_close.setVisibility(View.VISIBLE);
        title_icon.setVisibility(View.GONE);
    }
}

3.第三步:我們使用自定義組合控件

布局文件引入

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@color/f2"
 android:divider="@drawable/divider"
 android:orientation="vertical">

    <!--標題-->
 <com.jyjt.ydyl.Widget.WhitePublicTitleView
 android:id="@+id/title_collect"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" />

</LinearLayout>
代碼初始化具體數(shù)據(jù)
 
title_collect= (ImageView) mView.findViewById(R.id.title_collect);
title_collect.setTitleNam("我的收藏");
title_collect.setBackListener(new View.OnClickListener() {
    @Override
 public void onClick(View v) {
        SwitchActivityManager.exitActivity(MyCollectActivity.this);
    }
});

總結(jié):自定義控件在創(chuàng)建和使用起來都很簡單。

二.自定義繪制控件

自定義繪制控件的意思就是,這個View上所展現(xiàn)的內(nèi)容全部都是我們自己繪制出來的。繪制的代碼是寫在onDraw()方法中的

場景:自定義一個計數(shù)器View,這個View可以響應(yīng)用戶的點擊事件,并自動記錄一共點擊了多少次。新建一個CounterView繼承自View,代碼如下所示:

1.第一步:創(chuàng)建一個類集成View 進行繪制

public class CounterView extends View implements OnClickListener {

    private Paint mPaint;

    private Rect mBounds;

    private int mCount;

    public CounterView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBounds = new Rect();
        setOnClickListener(this);
    }

    @Override
 protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
        mPaint.setColor(Color.YELLOW);
        mPaint.setTextSize(30);
        String text = String.valueOf(mCount);
        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 void onClick(View v) {
        mCount++;
        invalidate();
    }

}  

可以看到,首先我們在onClick()方法調(diào)用調(diào)用invalidate()方法會導(dǎo)致視圖進行重繪,因此onDraw()方法在稍后就將會得到調(diào)用。

主要的邏輯當(dāng)然就是寫在onDraw()方法里首先是將Paint畫筆設(shè)置為藍色,然后調(diào)用Canvas的drawRect()方法繪制了一個矩形背景,接著將畫筆設(shè)置為黃色,繪制當(dāng)前的計數(shù),注意這里先是調(diào)用了getTextBounds()方法來獲取到文字的寬度和高度,然后調(diào)用了drawText()方法去進行繪制。

2.第二步:使用自定義繪制控件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<com.example.customview.CounterView
 android:layout_width="100dp"
 android:layout_height="100dp"
 android:layout_centerInParent="true" />

</RelativeLayout>  

效果如圖:


image.png

可以看到,這里我們將CounterView放入了一個RelativeLayout中,然后可以像使用普通控件來給CounterView指定各種屬性,比如通過layout_width和layout_height來指定CounterView的寬高,通過android:layout_centerInParent來指定它在布局里居中顯示。只不過需要注意,自定義的View在使用的時候一定要寫出完整的包名,不然系統(tǒng)將無法找到這個View。

三.自定義繼承控件

繼承控件,我們并不需要自己重頭去實現(xiàn)一個控件,只需要去繼承一個現(xiàn)有的控件,然后在這個控件上增加一些新的功能,就可以形成一個自定義的控件了。這種自定義控件的特點就是不僅能夠按照我們的需求加入相應(yīng)的功能,還可以保留原生控件的所有功能。

場景:我們做一個簡單的自定義一個diaolog ,點擊確定按鈕消失,(主要集成具體的原生控件)

第一步:創(chuàng)建一個布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/confirm_layout"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="@drawable/background_corners_white"
 android:orientation="vertical">

    <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_gravity="center_horizontal"
 android:layout_margin="5dip"
 android:background="@color/white"
 android:gravity="center"
 android:orientation="vertical">

        <ImageView

 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginBottom="18dp"
 android:layout_marginTop="24dp"
 android:src="@mipmap/ic_build_dialog" />
    </LinearLayout>

    <View
 android:layout_width="match_parent"
 android:layout_height="0.5dip"
 android:background="@color/line" />

 

 <Button
 android:id="@+id/ok_btn"
 android:layout_width="0dp"
 android:layout_height="match_parent"
 android:layout_weight="1"
 android:background="@drawable/commenttext"
 android:gravity="center"
 android:text="知道了"
 android:textColor="@color/de30"
 android:textSize="16sp" />
  

</LinearLayout>

第二部:創(chuàng)建一個類繼承Dialog

public class BuildingDialog extends Dialog {
    public View mView;
    LayoutInflater inflater;
    Button ok_btn;
    public Context mContext;
    DialogCallBack mDialogCallBack;

    public BuildingDialog(Context context) {
        this(context, R.style.dialog, null);
        this.mContext = context;
    }

    public BuildingDialog(Context context, int themeResId, String content) {
        super(context, themeResId);
        this.mContext = context;
    }

    @Override
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        inflater = LayoutInflater.from(mContext);
        mView = inflater.inflate(R.layout.dialog_build, null);
        setContentView(mView);
        initView();
        ok_btn.setOnClickListener(new View.OnClickListener() {
            @Override
 public void onClick(View v) {
                dismiss();
      // mDialogCallBack.clickokBtn();
 }
        });
    }

    public void initView() {
        ok_btn = (Button) mView.findViewById(R.id.ok_btn);
    }

    public void setDialogCallBack(DialogCallBack dialogCallBack) {
        this.mDialogCallBack = dialogCallBack;
    }

    public interface DialogCallBack {
        void clickokBtn();
    }
}

第三步:使用自定義繼承控件

BuildingDialog mBuildingDialog= new BuildingDialog(mContext);
mBuildingDialog.show(); //展示
mBuildingDialog.setDialogCallBack(this); //回調(diào)監(jiān)聽

四.自定義屬性

使用自定義控件的時候有時會創(chuàng)建自己的控件的屬性,下面我們來講講自定義屬性怎么用

主要步驟

1.定義declare-styleable,添加attr

2.使用TypedArray獲取自定義屬性

3.設(shè)置到View上

第一步:定義declare-styleable,添加attr

第二步:稍后更新

第三步:稍后更新

總結(jié):

雖然每個例子都很簡單,但是萬變不離其宗,復(fù)雜的View也是由這些簡單的原理堆積出來的。后期會寫深入了解View系列的文章,感謝大家有耐心看到最后。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,922評論 25 709
  • 相關(guān)文章 Android View體系(一)視圖坐標系A(chǔ)ndroid View體系(二)實現(xiàn)View滑動的六種方法...
    劉望舒閱讀 1,336評論 0 19
  • 一、自定義組合控件介紹 開發(fā)中,為了使用的方便,經(jīng)常把一些控件組合成一個控件,那樣就成為了我們的自定義組合控件,嚴...
    小楠總閱讀 918評論 0 4
  • 感恩金剛智慧種子法則,讓重慶的煌華的項目轉(zhuǎn)折,種子開花,期待結(jié)出果實。感恩同事的直言相告,讓我有了更好的成長,感恩...
    日精進_a07d閱讀 86評論 0 2
  • “戴著這個就感覺不到時間了” bg: 注意力輔助器最開始是為了長時間的太空航行設(shè)計的。 舊時代科幻片中常見的休眠技...
    現(xiàn)代魔法書閱讀 247評論 0 0

友情鏈接更多精彩內(nèi)容