Android實現(xiàn)梯形TextView效果
效果圖:

自定義代碼實現(xiàn)邏輯:
public class LadderTextView extends android.support.v7.widget.AppCompatTextView {
private static final String TAG = "LadderView";
private Path linePath;
private Paint paint, textPaint;
private int width, height;
private float strokeWidth = 2;
private Region mRegion;
private String textContent;
private int lineOffset = 0;//劃線的偏移量
private int textOffset = 0;//文本的偏移量
private float offsetScale = 1;//梯高與(梯頂與梯底)之差的比例(梯底比梯頂長)
private boolean isLeft = true;//分為左和右兩種斜角梯形模式
private boolean isSelected = false;//是否是選定
private int selectedColor = Color.BLACK;
public LadderTextView(Context context) {
super(context);
init();
}
public LadderTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initAttributes(context, attrs);
init();
}
public LadderTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttributes(context, attrs);
init();
}
private void initAttributes(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LadderTextView);
textContent = typedArray.getString(R.styleable.LadderTextView_textContent);
offsetScale = typedArray.getFloat(R.styleable.LadderTextView_offsetScale, 0.5f);
isLeft = typedArray.getBoolean(R.styleable.LadderTextView_isLeft, true);
isSelected = typedArray.getBoolean(R.styleable.LadderTextView_isSelected, true);
selectedColor = typedArray.getColor(R.styleable.LadderTextView_selectedColor, Color.GREEN);
strokeWidth = typedArray.getDimension(R.styleable.LadderTextView_strokeWidth, 1);
typedArray.recycle();
}
private void init() {
Log.v(TAG, "init");
mRegion = new Region();
paint = new Paint();
textPaint = new Paint();
linePath = new Path();
paint.setAntiAlias(true);
paint.setStrokeWidth(dp2px(getContext(), strokeWidth));
paint.setColor(selectedColor);
paint.setStyle(isSelected ? Paint.Style.FILL_AND_STROKE : Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
textPaint.setAntiAlias(true);
textPaint.setTextSize(getTextSize());//傳遞TextSize(px)
textPaint.setColor(isSelected ? Color.WHITE : selectedColor);
setText("");//去除掉原有的Text內(nèi)容
lineOffset = dp2px(getContext(), strokeWidth) / 2;
textOffset = (int) (getTextSize() / 2) + getBaseline() * 2;
Log.v(TAG, "lineOffset textOffset ->" + lineOffset + " " + textOffset);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
width = getWidth();
height = getHeight();
Log.v(TAG, "width height->" + width + " " + height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.v(TAG, "onDraw");
if (isLeft) {
linePath.moveTo(0 + lineOffset, 0 + lineOffset);
linePath.lineTo(width, 0 + lineOffset);
linePath.lineTo((int) (width - offsetScale * height), height - lineOffset);
linePath.lineTo(0 + lineOffset, height - lineOffset);
linePath.close();
setTextAlignment(TEXT_ALIGNMENT_TEXT_START);
canvas.drawPath(linePath, paint);
canvas.drawText(textContent == null ? "" : textContent,
getPaddingStart() + lineOffset,
height / 2 + textOffset, textPaint);
} else {
linePath.moveTo(0 + lineOffset + offsetScale * height, 0 + lineOffset);
linePath.lineTo(width - lineOffset, 0 + lineOffset);
linePath.lineTo(width - lineOffset, height - lineOffset);
linePath.lineTo(0, height - lineOffset);
linePath.close();
setTextAlignment(TEXT_ALIGNMENT_TEXT_END);
canvas.drawPath(linePath, paint);
canvas.drawText(textContent == null ? "" : textContent,
getWidth() - lineOffset - getPaddingEnd() - getDrawTextWidth(textPaint, textContent),
height / 2 + textOffset, textPaint);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (!isInRegion(event)) {//點擊的點的位置不在范圍內(nèi)則不響應(yīng)
return false;
}
}
return super.dispatchTouchEvent(event);
}
/**
* 判斷點擊的位置是否在要求的范圍內(nèi)
* @param event
* @return
*/
public boolean isInRegion(MotionEvent event) {
RectF rectF = new RectF();
linePath.computeBounds(rectF, true);
mRegion.setPath(linePath, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
return mRegion.contains((int) event.getX(), (int) event.getY());
}
/**
* 獲取要畫的字符串的寬度
*
* @param paint
* @param textContent
* @return
*/
private int getDrawTextWidth(Paint paint, String textContent) {
float totalWidth = 0f;
if (textContent != null && textContent.length() > 0) {
int len = textContent.length();
float[] widths = new float[len];
paint.getTextWidths(textContent, widths);
for (int j = 0; j < len; j++) {
totalWidth += widths[j];
}
}
return (int) Math.ceil(totalWidth);
}
/**
* @param dpValue (DisplayMetrics類中屬性density)
* @return
*/
private int dp2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public void setTextContent(String textContent) {
this.textContent = textContent;
invalidate();
}
public void setMSelected(boolean isSelected) {
textPaint.setColor(isSelected ? Color.WHITE : selectedColor);
paint.setStyle(isSelected ? Paint.Style.FILL_AND_STROKE : Paint.Style.STROKE);
this.isSelected = isSelected;
invalidate();
}
@Override
public boolean isSelected() {
return isSelected;
}
}
要點分析
1.背景與文本內(nèi)容的繪制
計算好四個點的位置連線,TextView默認(rèn)的文本內(nèi)容則設(shè)置為空字符串,采用drawText的方式來實現(xiàn)文本的顯示。需要注意的是計算文本的字號長度大小、顏色以及位于整個view的位置、偏移量等。
2.梯形范圍內(nèi)外的點擊事件處理
依照于設(shè)計,梯形內(nèi)的點擊才有響應(yīng),則要計算點擊的位置是否在梯形內(nèi),然后通過dispatchTouchEvent來做事件的分發(fā)。
/**
* 判斷點擊的位置是否在要求的范圍內(nèi)
* @param event
* @return
*/
public boolean isInRegion(MotionEvent event) {
RectF rectF = new RectF();
linePath.computeBounds(rectF, true);
mRegion.setPath(linePath, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
return mRegion.contains((int) event.getX(), (int) event.getY());
}
3.其它自定義屬性
依據(jù)于需求來定,注意invalidate。
最后,完整代碼
https://github.com/ganshenml/LadderTextView
以上就是Android實現(xiàn)梯形TextView效果的詳細(xì)內(nèi)容,更多關(guān)于Android 梯形TextView的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android利用FlexboxLayout輕松實現(xiàn)流動布局
flexbox是屬于CSS的一種布局方案,可以簡單、完整、響應(yīng)式的實現(xiàn)各種頁面布局。谷歌將其引入以提高復(fù)雜布局的能力。下面這篇文章主要給大家介紹了在Android中利用FlexboxLayout輕松實現(xiàn)流動布局的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-04-04
android6.0權(quán)限動態(tài)申請框架permissiondispatcher的方法
下面小編就為大家分享一篇android6.0權(quán)限動態(tài)申請框架permissiondispatcher的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01
DialogFragment運(yùn)行原理及使用方法詳解
這篇文章主要介紹了DialogFragment運(yùn)行原理及使用方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10
通過OpenGL ES混合模式縮放視頻緩沖區(qū)來適應(yīng)顯示尺寸
當(dāng)開發(fā)基于軟件模式的游戲時,通過縮放視頻緩沖區(qū)來適應(yīng)顯示尺寸是最棘手的問題之一;作為開發(fā)人員,我們必須嘗試在性能與顯示質(zhì)量之間找到最佳平衡點2012-12-12
Androd 勇闖高階性能優(yōu)化之布局優(yōu)化篇
Android性能優(yōu)化方面也有很多文章了,這里就做一個總結(jié),從原理到方法,工具等做一個簡單的了解,從而可以慢慢地改變編碼風(fēng)格,從而提高性能2021-10-10
Android WebView攔截iframe標(biāo)簽內(nèi)部跳轉(zhuǎn)教程
這篇文章主要介紹了Android WebView攔截iframe標(biāo)簽內(nèi)部跳轉(zhuǎn)教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
Android Studio 視頻播放失敗 start called in state1 異常怎么解決
很多朋友問小編在使用MediaPlayer播放音頻時報出 E/MediaPlayerNative: start called in state 1, mPlayer(0x0)問題,該如何處理呢,今天小編給大家?guī)砹薃ndroid Studio 視頻播放失敗 start called in state1 異常問題,需要的朋友可以參考下2020-03-03
Android多線程+單線程+斷點續(xù)傳+進(jìn)度條顯示下載功能
這篇文章主要介紹了Android多線程+單線程+斷點續(xù)傳+進(jìn)度條顯示下載功能,需要的朋友可以參考下2017-06-06

