自定義界面上繪製Text,可經過拖動控制文字大小及其位置

項目地址
imagehtml

最近項目上有個需求,須要在一塊區域中顯示文字,這塊區域能夠拖動,也能夠經過拖拽右下角來改變大小,裏面的文字大小要根據區域的大小進行自適應。剛開始以爲這個需求不難,只須要一個TextView就能實現。
後來發現雖然使用TextView能夠很容易實現拖動與縮放的功能,可是文字大小不會改變。在求助github的時候發現了AutoFitTextView控件,參考https://github.com/AndroidDeveloperLB/AutoFitTextView,可是使用的時候,每一次須要根據區域的大小從新建立一個TextView。想一想這樣對性能消耗太大,不如本身編寫一個。git

程序常量與變量

具體含義見註釋github

//事件類型, 0表明移動, 1表明縮放
    private static final int MOVEVIEW = 0;
    private static final int DRAGVIEW = 1;

    //背景畫筆
    private Paint backPaint;
    //文字畫筆
    private TextPaint textPaint;
    //背景區域
    private Rect backRect;
    //文字測量出的區域
    private Rect textMeasureRect;
    //事件類型
    private int eventType;
    //手指點擊的位置
    private int lastX, lastY;
    //背景與文字的長寬
    private int rectWidth, rectHeight, textWidth, textHeight;
    //字體大小
    private int textSize;

    private String text = "這是一個測試程序abcdefg!@#$%^&";

構造函數

由於是將文字繪製在View上,因此在構造函數裏生成了一個背景畫筆和文字畫筆,同時生成背景的矩陣。canvas

public MyDrawView(Context context, AttributeSet attrs) {
        super(context, attrs);

        backPaint = new Paint(); //設置一個筆刷大小是3的黃色的畫筆
        backPaint.setColor(Color.YELLOW);
        backPaint.setStrokeJoin(Paint.Join.ROUND);
        backPaint.setStrokeCap(Paint.Cap.ROUND);
        backPaint.setStrokeWidth(3);

        textPaint = new TextPaint();
        textPaint.setColor(Color.BLUE);
        textPaint.setTextSize(100);

        backRect = new Rect(100, 100, 400, 200);
        textMeasureRect = new Rect();
    }

Touch事件

在TouchEvent中,手指按下時,根據手指的點擊位置,判斷是拖動事件仍是縮放事件,並設置eventType。
根據eventType,在手指拖動時,執行移動或者縮放的操做。執行完以後,別忘了invalidate,從新繪製區域。ide

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getRawX();
                lastY = (int) event.getRawY();
                Log.d(TAG, "onTouchEvent: " + event.getX() + "   " + backRect.right + "   " + event.getY() + "   " +
                        backRect.bottom);
                if ((Math.abs(event.getX() - backRect.right)) < 100
                        && ((Math.abs(event.getY() - backRect.bottom) < 100))) {
                    eventType = DRAGVIEW;
                } else {
                    eventType = MOVEVIEW;
                }
                Log.d(TAG, "onTouchEvent: " + eventType);
                break;
            case MotionEvent.ACTION_MOVE:
                switch (eventType) {
                    case MOVEVIEW:
                        int dx = (int) (event.getRawX() - lastX);
                        int dy = (int) (event.getRawY() - lastY);
                        backRect.offset(dx, dy);
                        lastX = (int) event.getRawX();
                        lastY = (int) event.getRawY();
                        break;
                    case DRAGVIEW:
                        Log.d(TAG, "onTouchEvent: " + backRect.toShortString());
                        if (event.getX() > backRect.left + 100 && event.getY() > backRect.top + 100) {
                            backRect.set(backRect.left, backRect.top, (int) event.getX(), (int) event.getY());
                        }
                        break;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        invalidate(); //從新繪製區域
        return true;
    }

onDraw在界面上繪製

在onDraw時間裏,須要調用adjustTextSize函數計算文字大小,獲得文字大小後,須要進行文字繪製。在繪製的時候,經過StaticLayout對文字進行自動換行操做,將文字繪製在背景的Rect上。
參考Android自定義View使用canvas繪製文字實現居中、自動換行Android使用StaticLayout實現文本繪製自動換行函數

@Override
    protected void onDraw(Canvas canvas) {
        canvas.drawRect(backRect, backPaint);

        adjustTextSize();

        //文字自動換行
        StaticLayout layout = new StaticLayout(text, textPaint, backRect.width(), Layout.Alignment.ALIGN_NORMAL, 1.0F,
                0.0F, true);
        canvas.save();
        textPaint.setTextAlign(Paint.Align.LEFT);
        //文字的位置
        canvas.translate(backRect.left, backRect.top);
        layout.draw(canvas);
        canvas.restore();

        super.onDraw(canvas);
    }

計算文字大小

首先獲取背景的長度和寬度,最開始將文字大小設爲行高的0.9倍,而後經過TextPaint.getTextBounds,獲取文字的長寬,若是文字的面積大於背景矩陣面積*0.7(由於行與行之間有間隙,假設文字佔據了行高的0.7倍),則減少文字的大小,一直到文字大小稍小於背景面積*0.7爲止。如此即可得到合適的文字大小。性能

/**
     * 肯定文字大小
     */
    private void adjustTextSize() {
        rectWidth = backRect.right - backRect.left;
        rectHeight = backRect.bottom - backRect.top;
        textSize = (int) (rectHeight * 0.9);
        textPaint.setTextSize((float) (textSize));
        textPaint.getTextBounds(text, 0, text.length(), textMeasureRect);
        textWidth = textMeasureRect.width();
        textHeight = textMeasureRect.height();
        //0.7爲文字佔據行高的係數
        while (rectWidth * rectHeight * 0.7 < textWidth * textHeight) {
            textSize = textSize -3;
            textPaint.setTextSize((float) (textSize));
            textPaint.getTextBounds(text, 0, text.length(), textMeasureRect);
            textWidth = textMeasureRect.width();
            textHeight = textMeasureRect.height();
        }
        textPaint.getTextBounds(text, 0, text.length(), textMeasureRect);
        Log.d(TAG, "adjustTextSize: " + textMeasureRect.width() + "   " + textMeasureRect.height());
    }

後記:通過這個項目,知道了在解決問題前,應該先好好的分析解決思路。第三方控件並非萬能的,只有深入的理解了Android上控件的機理,才能準確的找到解決問題的思路。測試

相關文章
相關標籤/搜索