前言
創(chuàng)新互聯(lián)公司專注于東河網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供東河營銷型網(wǎng)站建設(shè),東河網(wǎng)站制作、東河網(wǎng)頁設(shè)計(jì)、東河網(wǎng)站官網(wǎng)定制、成都小程序開發(fā)服務(wù),打造東河網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供東河網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
首先,我們來看看實(shí)現(xiàn)的是怎么樣的效果:
如果我們拿到這樣的UI,想到的布局應(yīng)該是用4個(gè)EditText包在橫向的LinearLayout里面,但今天要講的View,所以我們決定用一個(gè)自定義的EditText 畫出來。
學(xué)到什么?
功能需求
思路
完全重畫一個(gè)EditText,就包含了測量布局和重新繪制這兩個(gè)關(guān)鍵步驟。好了,到這里理一下整體的思路:
開始動(dòng)手
準(zhǔn)備開始了,果斷繼承一個(gè)AppCompatEditText 來初始化基本參數(shù)先:
/** * 驗(yàn)證碼輸入框,重寫EditText的繪制方法實(shí)現(xiàn)。 * @author RAE */ public class CodeEditText extends AppCompatEditText { // 驗(yàn)證碼文本顏色 private int mTextColor; // 輸入的最大長度 private int mMaxLength = 4; // 邊框?qū)挾? private int mStrokeWidth; // 邊框高度 private int mStrokeHeight; // 邊框之間的距離 private int mStrokePadding = 20; // 用矩形來保存方框的位置、大小信息 private final Rect mRect = new Rect(); // 方框的背景 private Drawable mStrokeDrawable; /** * 構(gòu)造方法 * */ public CodeEditText(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CodeEditText); int indexCount = typedArray.getIndexCount(); for (int i = 0; i < indexCount; i++) { int index = typedArray.getIndex(i); if (index == R.styleable.CodeEditText_strokeHeight) { this.mStrokeHeight = (int) typedArray.getDimension(index, 60); } else if (index == R.styleable.CodeEditText_strokeWidth) { this.mStrokeWidth = (int) typedArray.getDimension(index, 60); } else if (index == R.styleable.CodeEditText_strokePadding) { this.mStrokePadding = (int) typedArray.getDimension(index, 20); } else if (index == R.styleable.CodeEditText_strokeBackground) { this.mStrokeDrawable = typedArray.getDrawable(index); } else if (index == R.styleable.CodeEditText_strokeLength) { this.mMaxLength = typedArray.getInteger(index, 4); } } typedArray.recycle(); if (mStrokeDrawable == null) { throw new NullPointerException("stroke drawable not allowed to be null!"); } setMaxLength(mMaxLength); setLongClickable(false); // 去掉背景顏色 setBackgroundColor(Color.TRANSPARENT); // 不顯示光標(biāo) setCursorVisible(false); } @Override public boolean onTextContextMenuItem(int id) { return false; } /** * 設(shè)置最大長度 */ private void setMaxLength(int maxLength) { if (maxLength >= 0) { setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)}); } else { setFilters(new InputFilter[0]); } } }
開始測量布局
初始化完了就要開始測量布局了,計(jì)算公式為:
輸入框?qū)挾?= 邊框?qū)挾?* 數(shù)量 + 邊框間距 *(數(shù)量-1)
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 當(dāng)前輸入框的寬高信息 int width = getMeasuredWidth(); int height = getMeasuredHeight(); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); // 判斷高度是否小于推薦高度 if (height < mStrokeHeight) { height = mStrokeHeight; } // 輸入框?qū)挾?= 邊框?qū)挾?* 數(shù)量 + 邊框間距 *(數(shù)量-1) int recommendWidth = mStrokeWidth * mMaxLength + mStrokePadding * (mMaxLength - 1); // 判斷寬度是否小于推薦寬度 if (width < recommendWidth) { width = recommendWidth; } widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, widthMode); heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, heightMode); // 設(shè)置測量布局 setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); }
畫家登場
來到最重要的步驟了,重畫輸入框!來一步步看代碼注釋:
@Override protected void onDraw(Canvas canvas) { // 在畫支持設(shè)置文本顏色,把系統(tǒng)化的文本透明掉,相當(dāng)于覆蓋 mTextColor = getCurrentTextColor(); setTextColor(Color.TRANSPARENT); // 系統(tǒng)畫的方法 super.onDraw(canvas); // 重新設(shè)置文本顏色 setTextColor(mTextColor); // 重繪背景顏色 drawStrokeBackground(canvas); // 重繪文本 drawText(canvas); }
繪制背景方框
/** * 繪制方框 */ private void drawStrokeBackground(Canvas canvas) { // 下面繪制方框背景顏色 // 確定反饋位置 mRect.left = 0; mRect.top = 0; mRect.right = mStrokeWidth; mRect.bottom = mStrokeHeight; int count = canvas.getSaveCount(); // 當(dāng)前畫布保存的狀態(tài) canvas.save(); // 保存畫布 for (int i = 0; i < mMaxLength; i++) { mStrokeDrawable.setBounds(mRect); // 設(shè)置位置 mStrokeDrawable.setState(new int[]{android.R.attr.state_enabled}); // 設(shè)置圖像狀態(tài) mStrokeDrawable.draw(canvas); // 畫到畫布上 // 確定下一個(gè)方框的位置 float dx = mRect.right + mStrokePadding; // X坐標(biāo)位置 // 保存畫布 canvas.save(); // [注意細(xì)節(jié)] 移動(dòng)畫布到下一個(gè)位置 canvas.translate(dx, 0); } // [注意細(xì)節(jié)] 把畫布還原到畫反饋之前的狀態(tài),這樣就還原到最初位置了 canvas.restoreToCount(count); // 畫布?xì)w位 canvas.translate(0, 0); // 下面繪制高亮狀態(tài)的邊框 // 當(dāng)前高亮的索引 int activatedIndex = Math.max(0, getEditableText().length()); mRect.left = mStrokeWidth * activatedIndex + mStrokePadding * activatedIndex; mRect.right = mRect.left + mStrokeWidth; mStrokeDrawable.setState(new int[]{android.R.attr.state_focused}); mStrokeDrawable.setBounds(mRect); mStrokeDrawable.draw(canvas); }
一般畫布的移動(dòng)canvas.translate(x,y)會(huì)結(jié)合canvas.save();來使用。
1、調(diào)用canvas.save();保存當(dāng)前畫布的狀態(tài),用PS來解析就是按下ctrl +s鍵,然后幫你新建一個(gè)新的圖層。你之后畫的內(nèi)容不會(huì)影響到之前畫的內(nèi)容,要回到之前的狀態(tài)就調(diào)用canvas.restoreToCount(count)來還原。
2、把畫布的位置移到下一個(gè)位置canvas.translate(x,y),下圖所示,你會(huì)發(fā)現(xiàn)方框在畫布中的位置沒有發(fā)生變化而是畫布距離發(fā)生了變化。這就是畫布平移的效果了。
畫驗(yàn)證碼文字
/** * 重繪文本 */ private void drawText(Canvas canvas) { int count = canvas.getSaveCount(); canvas.translate(0, 0); int length = getEditableText().length(); for (int i = 0; i < length; i++) { String text = String.valueOf(getEditableText().charAt(i)); TextPaint textPaint = getPaint(); textPaint.setColor(mTextColor); // 獲取文本大小 textPaint.getTextBounds(text, 0, 1, mRect); // 計(jì)算(x,y) 坐標(biāo) int x = mStrokeWidth / 2 + (mStrokeWidth + mStrokePadding) * i - (mRect.centerX()); int y = canvas.getHeight() / 2 + mRect.height() / 2; canvas.drawText(text, x, y, textPaint); } canvas.restoreToCount(count); }
監(jiān)聽文本變化回調(diào)自動(dòng)完成方法
@Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter); // 當(dāng)前文本長度 int textLength = getEditableText().length(); if (textLength == mMaxLength) { hideSoftInput(); if (mOnInputFinishListener != null) { mOnInputFinishListener.onTextFinish(getEditableText().toString(), mMaxLength); } } }
查看完整的源碼
到這里你能大概理解畫布的概念了,本文完。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)創(chuàng)新互聯(lián)的支持。