項目地址
html
最近項目上有個需求,須要在一塊區域中顯示文字,這塊區域能夠拖動,也能夠經過拖拽右下角來改變大小,裏面的文字大小要根據區域的大小進行自適應。剛開始以爲這個需求不難,只須要一個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(); }
在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時間裏,須要調用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上控件的機理,才能準確的找到解決問題的思路。測試