Android自定義view繪制表格的方法
本文實(shí)例為大家分享了Android自定義view繪制表格的具體代碼,供大家參考,具體內(nèi)容如下
先上效果圖

平時(shí)很少有這樣的表格需求,不過(guò)第一想法就是自定義view繪制表格,事實(shí)上我確實(shí)是用的canvas來(lái)繪制的,整個(gè)過(guò)程看似復(fù)雜,實(shí)為簡(jiǎn)單,計(jì)算好各個(gè)點(diǎn)的坐標(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ū)詳情門(mén)票信息表格
?*
?* @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 = "市場(chǎng)價(jià)";
? ? 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");
? ? //三個(gè)有顏色的矩形區(qū)域
? ? private RectF topRect, bottomRect, highLightRect;
? ? private static final int radius = PixelUtil.dp2px(10);
? ? //圓角矩形的Path
? ? private Path pathRoundRect;
? ? //頂部矩形的四個(gè)圓角
? ? private static final float[] radiusTop = {radius, radius, radius, radius, 0f, 0f, 0f, 0f};
? ? //底部矩形的四個(gè)圓角
? ? 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};
? ? //門(mén)票信息文字的高度
? ? private int ticketInfoTextH;
? ? //門(mén)票信息內(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()) {
? ? ? ? ? ? //畫(huà)頂部矩形
? ? ? ? ? ? pathRoundRect.addRoundRect(topRect, radiusTop, Path.Direction.CW);
? ? ? ? ? ? canvas.drawPath(pathRoundRect, pLine);
? ? ? ? ? ? //畫(huà)底部矩形
? ? ? ? ? ? pathRoundRect.reset();
? ? ? ? ? ? pathRoundRect.addRoundRect(bottomRect, radiusBottom, Path.Direction.CW);
? ? ? ? ? ? pLine.setColor(tableBgColorBottom);
? ? ? ? } else {//無(wú)門(mén)票說(shuō)明則只畫(huà)上部分表格
? ? ? ? ? ? pathRoundRect.addRoundRect(topRect, radiusAll, Path.Direction.CW);
? ? ? ? }
? ? ? ? canvas.drawPath(pathRoundRect, pLine);
? ? ? ? //畫(huà)高亮部分矩形
? ? ? ? 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());
? ? ? ? //計(jì)算baseline
? ? ? ? float baseline = hLine2y / 2 + getTextDis();
? ? ? ? //市場(chǎng)價(jià) 黑色大字
? ? ? ? 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);
? ? ? ? //第三行 畫(huà)價(jià)格、隨行人數(shù)
? ? ? ? if (ticketInfo != null) {
? ? ? ? ? ? pText.setTextSize(redTextSize);
? ? ? ? ? ? pText.setColor(textColorBlack);
? ? ? ? ? ? baseline = hLine2y + (topRect.bottom - hLine2y) / 2 + getTextDis();
? ? ? ? ? ? //市場(chǎng)價(jià)
? ? ? ? ? ? canvas.drawText(limitTextLength(ticketInfo.price), textX1, baseline, pText);
? ? ? ? ? ? //出行總?cè)藬?shù)
? ? ? ? ? ? canvas.drawText(limitTextLength(ticketInfo.discounts_num), textX5, baseline, pText);
? ? ? ? ? ? //持卡者、同行者價(jià)格
? ? ? ? ? ? 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);
? ? ? ? ? ? //底部門(mén)票說(shuō)明
? ? ? ? ? ? if (hasTicketInfo() && ticketTextStaticLayout != null) {
? ? ? ? ? ? ? ? canvas.save();
? ? ? ? ? ? ? ? //設(shè)定文字開(kāi)始繪制的坐標(biāo),該坐標(biāo)對(duì)應(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)建新對(duì)象來(lái)獲取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的字體大小等屬性時(shí),更新文字居中距離
? ? ?*
? ? ?* @return 文字居中的y坐標(biāo)
? ? ?*/
? ? private float getTextDis() {
? ? ? ? Paint.FontMetrics fontMetrics = pText.getFontMetrics();
? ? ? ? return (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
? ? }
? ? /**
? ? * ticketInfo為后臺(tái)返回的數(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);
? ? }
}可以看到無(wú)非就是定義一些顏色啊、坐標(biāo)啊這些,在onLayout的時(shí)候計(jì)算出對(duì)應(yīng)的值,然后draw即可,主要是思路要清晰,我這里是從上而下,從左到右的思路繪制。
這個(gè)view也有個(gè)弊端,就是大小及樣式固定,里面的文字均為單行展示沒(méi)處理自動(dòng)換行,不過(guò)滿足需求就好了啊,不要那么折騰,不然頭發(fā)又要變少了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android Studio報(bào):“Attribute application@theme or @ icon ”問(wèn)題的解
這篇文章主要給大家介紹了關(guān)于Android Studio報(bào):“Attribute application@theme or @ icon ”問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
Android編程實(shí)現(xiàn)獲取所有傳感器數(shù)據(jù)的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)獲取所有傳感器數(shù)據(jù)的方法,涉及Android針對(duì)傳感器Sensor相關(guān)操作技巧,需要的朋友可以參考下2017-06-06
Android Insets相關(guān)知識(shí)總結(jié)
這篇文章主要介紹了Android Insets相關(guān)知識(shí)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03
Android開(kāi)發(fā)中類加載器DexClassLoader的簡(jiǎn)單使用講解
這篇文章主要介紹了Android開(kāi)發(fā)中類加載器DexClassLoader的簡(jiǎn)單使用講解,DexClassLoader可以看作是一個(gè)特殊的Java中的ClassLoader,需要的朋友可以參考下2016-04-04
Android實(shí)現(xiàn)瘋狂連連看游戲之加載界面圖片和實(shí)現(xiàn)游戲Activity(四)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)瘋狂連連看游戲之加載界面圖片和實(shí)現(xiàn)游戲Activity,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Android應(yīng)用的Material設(shè)計(jì)中圖片的相關(guān)處理指南
這篇文章主要介紹了Android應(yīng)用的Material設(shè)計(jì)中圖片的相關(guān)處理指南,除了介紹新的方法外文中還給出了一些設(shè)計(jì)標(biāo)準(zhǔn)樣例僅供參考,需要的朋友可以參考下2016-04-04
Android 開(kāi)發(fā)手機(jī)(三星)拍照應(yīng)用照片旋轉(zhuǎn)問(wèn)題解決辦法
這篇文章主要介紹了Android 開(kāi)發(fā)手機(jī)(三星)拍照應(yīng)用照片旋轉(zhuǎn)問(wèn)題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04

