Android自定義控件的一般方法

前言

在開發(fā)AndroidUI時(shí),如果想要做出自己想要的特定的外觀效果,就不可能只依賴Android原生組件,雖然說隨著Android的版本更迭其原生組件的美觀度有了很大的提升,但是對(duì)于一個(gè)完整的App來說,它們很難與我們想要的整體效果保持一致,所以這個(gè)時(shí)候我們就需要自定義組件來實(shí)現(xiàn)特定的效果。

本文將介紹實(shí)現(xiàn)基本的自定義組件的方法,并完成一個(gè)自定義的EditText做為示例。

自定義組件基本步驟

  1. 繼承View的子類

在AndroidUI中,所有的布局、組件等界面元素都是繼承自View類,這個(gè)類定義了一個(gè)界面元素的標(biāo)準(zhǔn)行為,包括確定位置,確定尺寸,繪制外觀樣式等,當(dāng)我們自定義組件時(shí),就需要重寫onMeasure,onLayout,onDraw方法。當(dāng)我們需要的組件與原生組件的功能相似時(shí),我們可以直接繼承自具體的View的子類,如ImageButton,當(dāng)我們需要完全重新定義一個(gè)組件時(shí),需要做的工作就比較多,這時(shí)就需要繼承View或ViewGroup。本文暫時(shí)只討論前者。

  1. 為自定義View添加屬性

這里的屬性是指配置UI組件時(shí)的屬性,我們?cè)谧远x組件時(shí),原生View的屬性往往不能滿足我們的配置需求,我們可能需要一些更多樣化或更精確的控制,這時(shí)我們就需要為View添加屬性,這樣我們定義的組件就會(huì)變的通用。

  1. 編寫代碼覆蓋View中的繪制方法

即覆蓋View中的onMeasure,onLayout,onDraw等方法。

  1. 自定義事件響應(yīng)方法和部分回調(diào)方法

onFinishInflate() 回調(diào)方法,當(dāng)應(yīng)用從XML加載該組件并用它構(gòu)建界面之后調(diào)用的方法
onMeasure() 檢測(cè)View組件及其子組件的大小
onLayout() 當(dāng)該組件需要分配其子組件的位置、大小時(shí)
onSizeChange() 當(dāng)該組件的大小被改變時(shí)
onDraw() 當(dāng)組件將要繪制它的內(nèi)容時(shí)
onKeyDown 當(dāng)按下某個(gè)鍵盤時(shí)
onKeyUp 當(dāng)松開某個(gè)鍵盤時(shí)
onTouchEvent 當(dāng)發(fā)生觸屏事件時(shí)
onWindowFocusChanged(boolean) 當(dāng)該組件得到、失去焦點(diǎn)時(shí)
onAtrrachedToWindow() 當(dāng)把該組件放入到某個(gè)窗口時(shí)
onDetachedFromWindow() 當(dāng)把該組件從某個(gè)窗口上分離時(shí)觸發(fā)的方法
onWindowVisibilityChanged(int): 當(dāng)包含該組件的窗口的可見性發(fā)生改變時(shí)觸發(fā)的方法

自定義Edittext示例

本例的目的不在于創(chuàng)建一個(gè)十分美觀的組件,主要是為了通過一個(gè)簡(jiǎn)單的示例說明上述內(nèi)容的具體實(shí)現(xiàn)方法,幫助大家理解自定義組件的基本原理和方法。明白了基本的自定義組件實(shí)現(xiàn)方法,創(chuàng)建出炫酷的組件還會(huì)遠(yuǎn)嗎?

  1. 右下角字?jǐn)?shù)統(tǒng)計(jì)

由于要在下劃線下顯示字?jǐn)?shù)統(tǒng)計(jì),所以下劃線的繪制位置要修改到字的上端。另外,由于字?jǐn)?shù)統(tǒng)計(jì)的存在,還需要增加EditText的bottom padding,把字顯示在多出來的這塊padding上。

  1. 自定義屬性

設(shè)置了一個(gè)自定義屬性lineColor,用于設(shè)置下劃線顏色,可以在xml布局文件中直接進(jìn)行設(shè)置。

  1. 效果圖
帶有字?jǐn)?shù)統(tǒng)計(jì)的EditText
  1. 示例代碼

MyEditText.java

public class MyEditText extends EditText {
    // 畫下劃線的畫筆
    private Paint paint;
    // 繪制計(jì)數(shù)的畫筆
    private TextPaint textPaint;
    // 下劃線的開始y坐標(biāo)
    private int lineY;
    // 下劃線的顏色
    private int lineColor;
    // 當(dāng)前輸入的字符數(shù)
    private int count;
    // 用來獲取計(jì)數(shù)結(jié)果的字符串
    private StringBuffer countString;
    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.MyEditText);
        lineColor = array.getColor(R.styleable.MyEditText_lineColor, Color.BLUE);
        array.recycle();
        countString = new StringBuffer();
        super.setHintTextColor(Color.LTGRAY);
        // 初始化下劃線畫筆
        paint = new Paint();
        paint.setColor(lineColor);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(2);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setStrokeCap(Paint.Cap.ROUND);

        // 初始化字符計(jì)數(shù)的畫筆
        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(40);
        textPaint.setColor(Color.LTGRAY);
        super.setPadding(getPaddingLeft(),getPaddingTop(),getPaddingRight(),getPaddingBottom()+70);
        addListener();
    }

    private void addListener() {
        addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {}

            @Override
            public void afterTextChanged(Editable s) {
                countString.delete(0, countString.length());
                 count = s.length();
                countString.append(count);
                countString.append("字");
            }
        });
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 下劃線的高度
        lineY = getScrollY()+getHeight()-getPaddingBottom()+5;
        // 繪制下劃線
        canvas.drawRect(getScrollX(), lineY, getScrollX()+getWidth()-getPaddingRight(), lineY, paint);
        // 繪制右下角的計(jì)數(shù)
        canvas.drawText(countString.toString(), getScrollX()+getWidth()-getPaddingRight() - textPaint.measureText(countString.toString()), lineY + 60, textPaint);
        super.onDraw(canvas);
    }
}

res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyEditText">
        <attr name="lineColor" format="color"/>
    </declare-styleable>
</resources>

activity_main.xml

<?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:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="se.sx14.myedittext.MainActivity">

    <se.sx14.myedittext.MyEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:lineColor="#18b4ed"
        android:hint="請(qǐng)輸入內(nèi)容"
        android:background="@null"
        />
</LinearLayout>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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