Android自定義view繪制表格的方法
本文實例為大家分享了Android自定義view繪制表格的具體代碼,供大家參考,具體內(nèi)容如下
先上效果圖
平時很少有這樣的表格需求,不過第一想法就是自定義view繪制表格,事實上我確實是用的canvas來繪制的,整個過程看似復(fù)雜,實為簡單,計算好各個點的坐標(biāo)后事情就完成一半了。不廢話show code
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.graphics.Typeface; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import androidx.annotation.Nullable; import com.robot.common.entity.ScenicDetailInfo; import com.robot.common.utils.PixelUtil; /** ?* 景區(qū)詳情門票信息表格 ?* ?* @author ly ?* date 2019/8/12 10:24 ?*/ public class TicketInfoView extends View { ? ? private Paint pLine, pText; ? ? //表格寬高 ? ? private int w, h; ? ? //每一行的高度 ? ? private int rowH; ? ? //表格線的寬度 ? ? private float tableLineW; ? ? //豎線x坐標(biāo) ? ? private float vLine1x, vLine2x, vLine3x, vLine4x; ? ? //橫線y坐標(biāo) ? ? private float hLine1y, hLine2y; ? ? //每一列文字的x坐標(biāo)(單列內(nèi)居中) ? ? private float textX1, textX2, textX3, textX4, textX5, textXEnd; ? ? private static final String text1 = "市場價"; ? ? private static final String text2 = "單人出行"; ? ? private static final String text3 = "多人出行"; ? ? private static final String text4 = "持卡者"; ? ? private static final String text5 = "同行者"; ? ? private static final String text6 = "出行總?cè)藬?shù)"; ? ? private ScenicDetailInfo.TicketInfo ticketInfo; ? ? private int tableLineColor = Color.parseColor("#FFB6B4C8"); ? ? private int textColorBlack = Color.parseColor("#FF232627"); ? ? private int textColorGray = Color.parseColor("#FF65657e"); ? ? private int textColorRed = Color.parseColor("#FFfa496a"); ? ? private int blackTextSize = PixelUtil.sp2px(14); ? ? private int grayTextSize = PixelUtil.sp2px(12); ? ? private int redTextSize = PixelUtil.sp2px(13); ? ? //表格上半部分顏色 ? ? private int tableBgColorTop = Color.parseColor("#FFF6F5FF"); ? ? //表格下半部分顏色 ? ? private int tableBgColorBottom = Color.parseColor("#FFE9EAFF"); ? ? //表格高亮部分顏色 ? ? private int tableBgColorHighLight = Color.parseColor("#FF6066DD"); ? ? //三個有顏色的矩形區(qū)域 ? ? private RectF topRect, bottomRect, highLightRect; ? ? private static final int radius = PixelUtil.dp2px(10); ? ? //圓角矩形的Path ? ? private Path pathRoundRect; ? ? //頂部矩形的四個圓角 ? ? private static final float[] radiusTop = {radius, radius, radius, radius, 0f, 0f, 0f, 0f}; ? ? //底部矩形的四個圓角 ? ? private static final float[] radiusBottom = {0, 0, 0, 0, radius, radius, radius, radius}; ? ? private static final float[] radiusAll = {radius, radius, radius, radius, radius, radius, radius, radius}; ? ? //門票信息文字的高度 ? ? private int ticketInfoTextH; ? ? //門票信息內(nèi)間距 ? ? private final int ticketInfoTextPadding = PixelUtil.dp2px(5); ? ? private StaticLayout ticketTextStaticLayout; ? ? private TextPaint ticketTextPaint; ? ? private int ticketTextH; ? ? public TicketInfoView(Context context) { ? ? ? ? this(context, null); ? ? } ? ? public TicketInfoView(Context context, @Nullable AttributeSet attrs) { ? ? ? ? this(context, attrs, 0); ? ? } ? ? public TicketInfoView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { ? ? ? ? super(context, attrs, defStyleAttr); ? ? ? ? tableLineW = PixelUtil.dp2px(1.0f); ? ? ? ? pLine = new Paint(); ? ? ? ? pLine.setAntiAlias(true); ? ? ? ? pLine.setStrokeWidth(tableLineW); ? ? ? ? pText = new Paint(); ? ? ? ? pText.setAntiAlias(true); ? ? ? ? pText.setTextAlign(Paint.Align.CENTER); ? ? ? ? pathRoundRect = new Path(); ? ? ? ? topRect = new RectF(); ? ? ? ? bottomRect = new RectF(); ? ? ? ? highLightRect = new RectF(); ? ? } ? ? @Override ? ? protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ? ? ? ? super.onMeasure(widthMeasureSpec, heightMeasureSpec); ? ? ? ? w = MeasureSpec.getSize(widthMeasureSpec); ? ? ? ? rowH = (int) (w * 0.134f); ? ? ? ? computeH(); ? ? ? ? setMeasuredDimension(w, h); ? ? } ? ? @Override ? ? protected void onLayout(boolean changed, int left, int top, int right, int bottom) { ? ? ? ? super.onLayout(changed, left, top, right, bottom); ? ? ? ? vLine1x = 1 / 5f * w; ? ? ? ? vLine2x = 2 / 5f * w; ? ? ? ? vLine3x = 3 / 5f * w; ? ? ? ? vLine4x = 4 / 5f * w; ? ? ? ? hLine1y = rowH; ? ? ? ? hLine2y = rowH * 2; ? ? ? ? textX1 = vLine1x / 2; ? ? ? ? textX2 = vLine1x + (vLine2x - vLine1x) / 2; ? ? ? ? textX3 = vLine2x + (vLine3x - vLine2x) / 2; ? ? ? ? textX4 = vLine3x + (vLine4x - vLine3x) / 2; ? ? ? ? textX5 = vLine4x + (w - vLine4x) / 2; ? ? ? ? textXEnd = w / 2f; ? ? ? ? topRect.right = w; ? ? ? ? topRect.bottom = rowH * 3; ? ? ? ? bottomRect.top = topRect.bottom; ? ? ? ? bottomRect.right = w; ? ? ? ? bottomRect.bottom = topRect.bottom + ticketTextH; ? ? ? ? highLightRect.left = vLine2x; ? ? ? ? highLightRect.top = hLine1y; ? ? ? ? highLightRect.right = vLine3x; ? ? ? ? highLightRect.bottom = topRect.bottom; ? ? } ? ? @Override ? ? protected void onDraw(Canvas canvas) { ? ? ? ? super.onDraw(canvas); ? ? ? ? pathRoundRect.reset(); ? ? ? ? pLine.setStyle(Paint.Style.FILL); ? ? ? ? pLine.setColor(tableBgColorTop); ? ? ? ? if (hasTicketInfo()) { ? ? ? ? ? ? //畫頂部矩形 ? ? ? ? ? ? pathRoundRect.addRoundRect(topRect, radiusTop, Path.Direction.CW); ? ? ? ? ? ? canvas.drawPath(pathRoundRect, pLine); ? ? ? ? ? ? //畫底部矩形 ? ? ? ? ? ? pathRoundRect.reset(); ? ? ? ? ? ? pathRoundRect.addRoundRect(bottomRect, radiusBottom, Path.Direction.CW); ? ? ? ? ? ? pLine.setColor(tableBgColorBottom); ? ? ? ? } else {//無門票說明則只畫上部分表格 ? ? ? ? ? ? pathRoundRect.addRoundRect(topRect, radiusAll, Path.Direction.CW); ? ? ? ? } ? ? ? ? canvas.drawPath(pathRoundRect, pLine); ? ? ? ? //畫高亮部分矩形 ? ? ? ? pLine.setColor(tableBgColorHighLight); ? ? ? ? canvas.drawRect(highLightRect, pLine); ? ? ? ? //四根豎線 ? ? ? ? pLine.setColor(tableLineColor); ? ? ? ? pLine.setStrokeWidth(tableLineW / 2); ? ? ? ? canvas.drawLine(vLine1x, 0, vLine1x, topRect.bottom, pLine); ? ? ? ? canvas.drawLine(vLine2x, 0, vLine2x, topRect.bottom, pLine); ? ? ? ? canvas.drawLine(vLine3x, hLine1y, vLine3x, topRect.bottom, pLine); ? ? ? ? canvas.drawLine(vLine4x, hLine1y, vLine4x, topRect.bottom, pLine); ? ? ? ? //兩根橫線 ? ? ? ? canvas.drawLine(vLine1x, hLine1y, w, hLine1y, pLine); ? ? ? ? canvas.drawLine(0, hLine2y, w, hLine2y, pLine); ? ? ? ? pText.setColor(textColorBlack); ? ? ? ? pText.setTextSize(blackTextSize); ? ? ? ? pText.setTypeface(getTypeface()); ? ? ? ? //計算baseline ? ? ? ? float baseline = hLine2y / 2 + getTextDis(); ? ? ? ? //市場價 黑色大字 ? ? ? ? canvas.drawText(text1, textX1, baseline, pText); ? ? ? ? //第一行黑色大字 ? ? ? ? baseline = hLine1y / 2 + getTextDis(); ? ? ? ? canvas.drawText(text2, textX2, baseline, pText); ? ? ? ? canvas.drawText(text3, vLine2x + (w - vLine2x) / 2, baseline, pText); ? ? ? ? //第二行小字 ? ? ? ? baseline = hLine1y + (hLine2y - hLine1y) / 2 + getTextDis(); ? ? ? ? pText.setTextSize(grayTextSize); ? ? ? ? canvas.drawText(text4, textX2, baseline, pText); ? ? ? ? canvas.drawText(text5, textX4, baseline, pText); ? ? ? ? canvas.drawText(text6, textX5, baseline, pText); ? ? ? ? pText.setColor(Color.WHITE); ? ? ? ? canvas.drawText(text4, textX3, baseline, pText); ? ? ? ? //第三行 畫價格、隨行人數(shù) ? ? ? ? if (ticketInfo != null) { ? ? ? ? ? ? pText.setTextSize(redTextSize); ? ? ? ? ? ? pText.setColor(textColorBlack); ? ? ? ? ? ? baseline = hLine2y + (topRect.bottom - hLine2y) / 2 + getTextDis(); ? ? ? ? ? ? //市場價 ? ? ? ? ? ? canvas.drawText(limitTextLength(ticketInfo.price), textX1, baseline, pText); ? ? ? ? ? ? //出行總?cè)藬?shù) ? ? ? ? ? ? canvas.drawText(limitTextLength(ticketInfo.discounts_num), textX5, baseline, pText); ? ? ? ? ? ? //持卡者、同行者價格 ? ? ? ? ? ? pText.setColor(Color.WHITE); ? ? ? ? ? ? canvas.drawText(limitTextLength(ticketInfo.discounts_member), textX3, baseline, pText); ? ? ? ? ? ? pText.setColor(textColorRed); ? ? ? ? ? ? canvas.drawText(limitTextLength(ticketInfo.single), textX2, baseline, pText); ? ? ? ? ? ? canvas.drawText(limitTextLength(ticketInfo.discounts), textX4, baseline, pText); ? ? ? ? ? ? //底部門票說明 ? ? ? ? ? ? if (hasTicketInfo() && ticketTextStaticLayout != null) { ? ? ? ? ? ? ? ? canvas.save(); ? ? ? ? ? ? ? ? //設(shè)定文字開始繪制的坐標(biāo),該坐標(biāo)對應(yīng)文字的left,top ? ? ? ? ? ? ? ? canvas.translate(ticketInfoTextPadding, topRect.bottom + ticketInfoTextPadding + (bottomRect.bottom - bottomRect.top - ticketInfoTextH - 2 * ticketInfoTextPadding) / 2); ? ? ? ? ? ? ? ? ticketTextStaticLayout.draw(canvas); ? ? ? ? ? ? ? ? canvas.restore(); ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? private void computeH() { ? ? ? ? if (hasTicketInfo() && w > 2 * ticketInfoTextPadding) { ? ? ? ? ? ? if (ticketTextPaint == null) { ? ? ? ? ? ? ? ? ticketTextPaint = new TextPaint(); ? ? ? ? ? ? ? ? ticketTextPaint.setColor(textColorGray); ? ? ? ? ? ? ? ? ticketTextPaint.setTextSize(redTextSize); ? ? ? ? ? ? ? ? ticketTextPaint.setAntiAlias(true); ? ? ? ? ? ? } ? ? ? ? ? ? //此處每次都創(chuàng)建新對象來獲取ticket_info最新值 ? ? ? ? ? ? ticketTextStaticLayout = new StaticLayout(ticketInfo.ticket_info, ticketTextPaint, w - 2 * ticketInfoTextPadding, Layout.Alignment.ALIGN_CENTER, 1.1F, 1.1F, true); ? ? ? ? ? ? ticketInfoTextH = ticketTextStaticLayout.getHeight(); ? ? ? ? ? ? h = (int) (0.4 * w) + ticketTextH; ? ? ? ? } else { ? ? ? ? ? ? h = (int) (0.4 * w); ? ? ? ? } ? ? ? ? ticketTextH = Math.max((ticketInfoTextH + ticketInfoTextPadding * 2), rowH); ? ? ? ? h = hasTicketInfo() ? (int) (0.4 * w) + ticketTextH : (int) (0.4 * w); ? ? } ? ? private Typeface getTypeface() { ? ? ? ? Typeface roboto = Typeface.create("sans-serif-medium", Typeface.NORMAL); ? ? ? ? if (roboto == null || roboto.getStyle() == Typeface.NORMAL) ? ? ? ? ? ? roboto = Typeface.DEFAULT_BOLD; ? ? ? ? return roboto; ? ? } ? ? /** ? ? ?* 設(shè)置pText的字體大小等屬性時,更新文字居中距離 ? ? ?* ? ? ?* @return 文字居中的y坐標(biāo) ? ? ?*/ ? ? private float getTextDis() { ? ? ? ? Paint.FontMetrics fontMetrics = pText.getFontMetrics(); ? ? ? ? return (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; ? ? } ? ? /** ? ? * ticketInfo為后臺返回的數(shù)據(jù)模型,此處不再貼出 ? ? */ ? ? public void setTicketInfo(ScenicDetailInfo.TicketInfo ticketInfo) { ? ? ? ? this.ticketInfo = ticketInfo; ? ? ? ? if (ticketInfo != null) { ? ? ? ? ? ? computeH(); ? ? ? ? ? ? //重新layout,確定view的繪制區(qū)域 ? ? ? ? ? ? requestLayout(); ? ? ? ? } else { ? ? ? ? ? ? invalidate(); ? ? ? ? } ? ? } ? ? private String limitTextLength(String src) { ? ? ? ? if (!TextUtils.isEmpty(src) && src.length() > 5) ? ? ? ? ? ? src = src.substring(0, 5) + "..."; ? ? ? ? return src; ? ? } ? ? private boolean hasTicketInfo() { ? ? ? ? return ticketInfo != null && !TextUtils.isEmpty(ticketInfo.ticket_info); ? ? } }
可以看到無非就是定義一些顏色啊、坐標(biāo)啊這些,在onLayout的時候計算出對應(yīng)的值,然后draw即可,主要是思路要清晰,我這里是從上而下,從左到右的思路繪制。
這個view也有個弊端,就是大小及樣式固定,里面的文字均為單行展示沒處理自動換行,不過滿足需求就好了啊,不要那么折騰,不然頭發(fā)又要變少了。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用adb命令向Android模擬器中導(dǎo)入通訊錄聯(lián)系人的方法
這篇文章主要介紹了使用adb命令向Android模擬器中導(dǎo)入通訊錄聯(lián)系人的方法,實例分析了導(dǎo)入通訊錄存儲文件的技巧,需要的朋友可以參考下2015-01-01Android開發(fā)使用UncaughtExceptionHandler捕獲全局異常
本文主要介紹在Android開發(fā)中使用UncaughtExceptionHandler捕獲全局異常,需要的朋友可以參考下。2016-06-06Android Fragment 和 FragmentManager 的代碼分析
這篇文章主要介紹了Android Fragment 和 FragmentManager 的代碼分析,非常不錯,具有參考借鑒價值,需要的的朋友參考下吧2017-01-01Android編程實現(xiàn)仿QQ發(fā)表說說,上傳照片及彈出框效果【附demo源碼下載】
這篇文章主要介紹了Android編程實現(xiàn)仿QQ發(fā)表說說,上傳照片及彈出框效果,涉及Android動畫特效的相關(guān)實現(xiàn)技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2017-01-01