国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義View實現仿GitHub的提交活躍表格

2019-12-12 03:59:49
字體:
來源:轉載
供稿:網友

說明

本文可能需要一些基礎知識點,如Canvas,Paint,Path,Rect等類的基本使用,建議不熟悉的同學可以學習GcsSloop安卓自定義View教程目錄,會幫助很大。

github.jpg

上圖就是github的提交表格,直觀來看可以分為幾個部分進行繪制:

(1)各個月份的小方格子,并且色彩根據提交次數變化,由淺到深
(2)右下邊的顏色標志,我們右對齊就可以了
(3)左邊的星期,原圖是從周日畫到周六,我們從周一畫到周日
(4)上面的月份,我們只畫出1-12月
(5)點擊時候彈出當天的提交情況,由一個小三角和圓角矩形組成

需要解決的計算問題:

(1)生成任意一年的所有天,包含年月日周,提交次數,色塊顏色,坐標
(1)一年中所有的小方格子坐標
(2)右下邊顏色標志坐標
(3)左邊星期坐標
(4)上面月份坐標
(5)點擊彈出的提示框和文字坐標

生成某年所有天數

每天的信息我們需要封裝成一個類,代碼如下:

/** * Created by Administrator on 2017/1/13. * 封裝每天的屬性,方便在繪制的時候進行計算 */public class Day implements Serializable{ /**年**/ public int year; /**月**/ public int month; /**日**/ public int date; /**周幾**/ public int week; /**貢獻次數,默認0**/ public int contribution = 0; /**默認顏色,根據提交次數改變**/ public int colour = 0xFFEEEEEE; /**方格坐標,左上點,右下點,確定矩形范圍**/ public float startX; public float startY; public float endX; public float endY; @Override public String toString() {  //這里直接在彈出框中顯示  return ""+year+"年"+month+"月"+date+"日周"+week+","+contribution+"次"; }}

要想先繪制表格,需要計算出所有的天,這里計算一年中所有的天,我們通過從當年1月1日算起,到12月31日,因為星期是連續的,所以我們需要我們提供某年的1月1日是周幾,比如2016年1月1日是周5,這里必要的參數是2016和周5,那么我們用一個類來實現該方法,代碼如下:

public class DateFactory { /**平年map,對應月份和天數**/ private static HashMap<Integer,Integer> monthMap = new LinkedHashMap<>(12); /**閏年map,對應月份和天數**/ private static HashMap<Integer,Integer> leapMonthMap = new LinkedHashMap<>(12); static {  //初始化map,只有2月份不同  monthMap.put(1,31);leapMonthMap.put(1,31);  monthMap.put(2,28);leapMonthMap.put(2,29);  monthMap.put(3,31);leapMonthMap.put(3,31);  monthMap.put(4,30);leapMonthMap.put(4,30);  monthMap.put(5,31);leapMonthMap.put(5,31);  monthMap.put(6,30);leapMonthMap.put(6,30);  monthMap.put(7,31);leapMonthMap.put(7,31);  monthMap.put(8,31);leapMonthMap.put(8,31);  monthMap.put(9,30);leapMonthMap.put(9,30);  monthMap.put(10,31);leapMonthMap.put(10,31);  monthMap.put(11,30);leapMonthMap.put(11,30);  monthMap.put(12,31);leapMonthMap.put(12,31); } /**  * 輸入年份和1月1日是周幾  * 閏年為366天,平年為365天  * @param year 年份  * @param weekday 該年1月1日為周幾  * @return 該年1月1日到12月31日所有的天數  */ public static List<Day> getDays(int year, int weekday) {  List<Day> days = new ArrayList<>();  boolean isLeapYear = isLeapYear(year);  int dayNum = isLeapYear ? 366 : 365;  Day day;  int lastWeekday = weekday;  for (int i = 1; i <= dayNum; i++) {   day = new Day();   day.year = year;   //計算當天為周幾,如果大于7就重置1   day.week = lastWeekday<= 7 ? lastWeekday : 1;   //計算當天為幾月幾號   int[] monthAndDay = getMonthAndDay(isLeapYear, i);   day.month = monthAndDay[0];   day.date = monthAndDay[1];   //記錄下昨天是周幾并+1   lastWeekday = day.week;   lastWeekday++;   days.add(day);  }  checkDays(days);  return days; } /**  * 獲取月和日  * @param isLeapYear 是否閏年  * @param currentDay 當前天數  * @return 包含月和天的數組  */ public static int[] getMonthAndDay(boolean isLeapYear,int currentDay) {  HashMap<Integer,Integer> maps = isLeapYear?leapMonthMap:monthMap;  Set<Map.Entry<Integer,Integer>> set = maps.entrySet();  int count = 0;  Map.Entry<Integer, Integer> month = null;  for (Map.Entry<Integer, Integer> entry : set) {   count+=entry.getValue();   if (currentDay<=count){    month = entry;    break;   }  }  if (month == null){   throw new IllegalStateException("未找到所在的月份");  }  int day = month.getValue()-(count-currentDay);  return new int[]{month.getKey(),day}; } /**  * 判斷是閏年還是平年  * @param year 年份  * @return true 為閏年  */ public static boolean isLeapYear(int year) {  return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; } /**  * 檢測生成的天數是否正常  * @param days  */ private static void checkDays(List<Day> days) {  if (days == null) {   throw new IllegalArgumentException("天數為空");  }  if (days.size() != 365 && days.size() != 366) {   throw new IllegalArgumentException("天數異常:" + days.size());  } } public static void main(String[] args){  //test  List<Day> days = DateFactory.getDays(2016, 5);  for (int i = 0; i < days.size(); i++) {   System.out.println(days.get(i).toString());  } }}

具體的計算邏輯可以看看代碼,不是很難,這樣我們就能得到某年的所有天。

繪制天數格子

因為該view比較長,所以需要橫屏顯示,方便起見,這里我們也不再進行view的測量計算,也不再進行自定義屬性,只關注其核心邏輯即可。

首先我們需要將需要的成員變量定義出來:

 /**灰色方格的默認顏色**/ private final static int DEFAULT_BOX_COLOUR = 0xFFEEEEEE; /**提交次數顏色值**/ private final static int[] COLOUR_LEVEL =   new int[]{0xFF1E6823, 0xFF44A340, 0xFF8CC665, 0xFFD6E685, DEFAULT_BOX_COLOUR}; /**星期**/ private String[] weeks = new String[]{"Mon", "Wed", "Fri", "Sun"}; /**月份**/ private String[] months =   new String[]{"Jan", "Feb", "Mar", "Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; /**默認的padding,繪制的時候不貼邊畫**/ private int padding = 24; /**小方格的默認邊長**/ private int boxSide = 8; /**小方格間的默認間隔**/ private int boxInterval = 2; /**所有周的列數**/ private int column = 0; private List<Day> mDays;//一年中所有的天 private Paint boxPaint;//方格畫筆 private Paint textPaint;//文字畫筆 private Paint infoPaint;//彈出框畫筆 private Paint.FontMetrics metrics;//測量文字 private float downX;//按下的點的X坐標 private float downY;//按下的點的Y坐標 private Day clickDay;//按下所對應的天

這些提取的變量是慢慢增加的,在自定義的時候一下想不全的時候可以先寫,等用到某些變量的時候就提取出來。
然后我們初始化一下數據:

public GitHubContributionView(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  initView(); } public void initView() {  mDays = DateFactory.getDays(2016, 5);  //方格畫筆  boxPaint = new Paint();  boxPaint.setStyle(Paint.Style.FILL);  boxPaint.setStrokeWidth(2);  boxPaint.setColor(DEFAULT_BOX_COLOUR);  boxPaint.setAntiAlias(true);  //文字畫筆  textPaint = new Paint();  textPaint.setStyle(Paint.Style.FILL);  textPaint.setColor(Color.GRAY);  textPaint.setTextSize(12);  textPaint.setAntiAlias(true);  //彈出的方格信息畫筆  infoPaint = new Paint();  infoPaint.setStyle(Paint.Style.FILL);  infoPaint.setColor(0xCC888888);  infoPaint.setTextSize(12);  infoPaint.setAntiAlias(true);  //將默認值轉換px  padding = UI.dp2px(getContext(), padding);  boxSide = UI.dp2px(getContext(), boxSide);  metrics = textPaint.getFontMetrics(); }

這里我們以2016年來舉例,mDays就是獲取2016年的所有天的集合(參數可以當作自定義屬性提取出來),相關的Paint也已經初始化好了,接下來就需要在onDraw方法里畫,先畫所有的方格子和月份標志:

 /**  * 畫出1-12月方格小塊和上面的月份  * @param canvas 畫布  */ private void drawBox(Canvas canvas) {  //方格的左上右下坐標  float startX, startY, endX, endY;  //起始月份為1月  int month = 1;  for (int i = 0; i < mDays.size(); i++) {   Day day = mDays.get(i);   if (i == 0){    //畫1月的文本標記,坐標應該是x=padding,y=padding-boxSide/2(間隙),y坐標在表格上面一點    canvas.drawText(months[0],padding,padding-boxSide/2,textPaint);   }   if (day.week == 1 && i != 0) {    //如果當天是周1,那么說明增加了一列    column++;    //如果列首的月份有變化,那么說明需要畫月份    if (day.month>month){     month = day.month;     //月份文本的坐標計算,x坐標在變化,而y坐標都是一樣的,boxSide/2(間隙)     canvas.drawText(months[month-1],padding+column*(boxSide+boxInterval),padding-boxSide/2,textPaint);    }   }   //計算方格坐標點,x坐標隨列數的增多而增加,y坐標隨行數的增多而變化   startX = padding + column * (boxSide + boxInterval);   startY = padding + (day.week - 1) * (boxSide + boxInterval);   endX = startX + boxSide;   endY = startY + boxSide;   //將該方格的坐標保存下來,這樣可以在點擊方格的時候計算彈框的坐標   day.startX = startX;   day.startY = startY;   day.endX = endX;   day.endY = endY;   //給畫筆設置當前天的顏色   boxPaint.setColor(day.colour);   canvas.drawRect(startX, startY, endX, endY, boxPaint);  }  boxPaint.setColor(DEFAULT_BOX_COLOUR);//恢復默認顏色 }

這里主要是注意下行數列數的變化和月份坐標的計算,格子畫好了。

繪制星期文本

我們再畫左邊的星期文本:

/**  * 畫左側的星期  * @param canvas 畫布  */ private void drawWeek(Canvas canvas) {  //文字是左對齊,所以找出最長的字  float textLength = 0;  for (String week : weeks) {   float tempLength = textPaint.measureText(week);   if (textLength < tempLength) {    textLength = tempLength;   }  }  //依次畫出星期文本,坐標點x=padding-文本長度-文本和方格的間隙,y坐標隨行數變化  canvas.drawText(weeks[0], padding - textLength - 2, padding + boxSide - metrics.descent, textPaint);  canvas.drawText(weeks[1], padding - textLength - 2, padding + 3 * (boxSide + boxInterval) - metrics.descent, textPaint);  canvas.drawText(weeks[2], padding - textLength - 2, padding + 5 * (boxSide + boxInterval) - metrics.descent, textPaint);  canvas.drawText(weeks[3], padding - textLength - 2, padding + 7 * (boxSide + boxInterval) - metrics.descent, textPaint); }

繪制顏色深淺標志

然后根據表格的高度再畫出右下邊的顏色深淺標志:

/**  * 畫出右下角的顏色深淺標志,因為是右對齊的所以需要從右往左畫  * @param canvas 畫布  */ private void drawTag(Canvas canvas) {  //首先計算出兩個文本的長度  float moreLength = textPaint.measureText("More");  float lessLength = textPaint.measureText("Less");  //畫 More 文本,x坐標=padding+(列數+1)*(方格邊長+方格間隙)-一個方格間隙-文本長度  float moreX = padding + (column + 1) * (boxSide + boxInterval) - boxInterval - moreLength;  //y坐標=padding+(方格行數+1,和表格底部有些距離)*(方格邊長+方格間隙)+字體的ascent高度  float moreY = padding + 8 * (boxSide + boxInterval) + Math.abs(metrics.ascent);  canvas.drawText("More", moreX, moreY, textPaint);  //畫深淺色塊,坐標根據上面的More依次計算就可以了  float interval = boxSide - 2;//文字和色塊間的距離  float leftX = moreX - interval - boxSide;  float topY = moreY - boxSide;  float rightX = moreX - interval;  float bottomY = moreY;//色塊的Y坐標是一樣的  for (int i = 0; i < COLOUR_LEVEL.length; i++) {   boxPaint.setColor(COLOUR_LEVEL[i]);   canvas.drawRect(leftX - i * (boxSide + boxInterval), topY, rightX - i * (boxSide + boxInterval), bottomY, boxPaint);  }  //最后畫 Less 文本,原理同上  canvas.drawText("Less", leftX - 4 * (boxSide + boxInterval) - interval - lessLength, moreY, textPaint); }

這樣整個表格主體繪制完成。

處理點擊事件

接下來要處理點擊事件,判斷點擊的坐標如果在方格內,那么彈出對于的文本框,先處理點擊事件:

 @Override public boolean onTouchEvent(MotionEvent event) {  //獲取ACTION_DOWN的坐標,用來判斷點在哪天,并彈出?  if (MotionEvent.ACTION_DOWN == event.getAction()) {   downX = event.getX();   downY = event.getY();   findClickBox();  }  //這里因為我們只是記錄坐標點,不對事件進行攔截所以默認返回  return super.onTouchEvent(event); }

判斷是否在方格內:

 /**  * 判斷是否點擊在方格內  */ private void findClickBox() {  for (Day day : mDays) {   //檢測點擊的坐標如果在方格內,則彈出信息提示   if (downX >= day.startX && downX <= day.endX && downY >= day.startY && downY <= day.endY) {    clickDay = day;//紀錄點擊的哪天    break;   }  }  //點擊完要刷新,這樣每次點擊不同的方格,彈窗就可以在相應的位置顯示  refreshView(); } /**  * 點擊彈出文字提示  */ private void refreshView() {  invalidate(); }

繪制彈出文本框

然后看看彈出文本框的繪制:

/**  * 畫方格上的文字彈框  * @param canvas 畫布  */ private void drawPopupInfo(Canvas canvas) {  if (clickDay != null) {//點擊的天不為null時候才畫   //先根據方格來畫出一個小三角形,坐標就是方格的中間   Path infoPath = new Path();   //先從方格中心   infoPath.moveTo(clickDay.startX + boxSide / 2, clickDay.startY + boxSide / 2);   //然后是方格的左上點   infoPath.lineTo(clickDay.startX, clickDay.startY);   //然后是方格的右上點   infoPath.lineTo(clickDay.endX, clickDay.startY);   //畫出三角   canvas.drawPath(infoPath,infoPaint);   //畫三角上的圓角矩形   textPaint.setColor(Color.WHITE);   //得到當天的文本信息   String popupInfo = clickDay.toString();   System.out.println(popupInfo);   //計算文本的高度和長度用以確定矩形的大小   float infoHeight = metrics.descent - metrics.ascent;   float infoLength = textPaint.measureText(popupInfo);   Log.e("height",infoHeight+"");   Log.e("length",infoLength+"");   //矩形左上點應該是x=當前天的x+邊長/2-(文本長度/2+文本和框的間隙)   float leftX = (clickDay.startX + boxSide / 2 ) - (infoLength / 2 + boxSide);   //矩形左上點應該是y=當前天的y+邊長/2-(文本高度+上下文本和框的間隙)   float topY = clickDay.startY-(infoHeight+2*boxSide);   //矩形的右下點應該是x=leftX+文本長度+文字兩邊和矩形的間距   float rightX = leftX+infoLength+2*boxSide;   //矩形的右下點應該是y=當前天的y   float bottomY = clickDay.startY;   System.out.println(""+leftX+"/"+topY+"/"+rightX+"/"+bottomY);   RectF rectF = new RectF(leftX, topY, rightX, bottomY);   canvas.drawRoundRect(rectF,4,4,infoPaint);   //繪制文字,x=leftX+文字和矩形間距,y=topY+文字和矩形上面間距+文字頂到基線高度   canvas.drawText(popupInfo,leftX+boxSide,topY+boxSide+Math.abs(metrics.ascent),textPaint);   clickDay = null;//重新置空,保證點擊方格外信息消失   textPaint.setColor(Color.GRAY);//恢復畫筆顏色  } }

這樣主體邏輯完成,但需要開放設置某天提交次數的方法:

/**  * 設置某天的次數  * @param year 年  * @param month 月  * @param day 日  * @param contribution 次數  */ public void setData(int year,int month,int day,int contribution){  //先找到是第幾天,為了方便不做參數檢測了  for (Day d : mDays) {   if (d.year == year && d.month == month && d.date == day){    d.contribution = contribution;    d.colour = getColour(contribution);    break;   }  }  refreshView(); } /**  * 根據提交次數來獲取顏色值  * @param contribution 提交的次數  * @return 顏色值  */ private int getColour(int contribution){  int colour = 0;  if (contribution <= 0){   colour = COLOUR_LEVEL[4];  }  if (contribution == 1){   colour = COLOUR_LEVEL[3];  }  if (contribution == 2){   colour = COLOUR_LEVEL[2];  }  if (contribution == 3){   colour = COLOUR_LEVEL[1];  }  if (contribution >= 4){   colour = COLOUR_LEVEL[0];  }  return colour; }

好了,所有邏輯完成,主要涉及到一些計算,完整代碼:

/** * Created by Administrator on 2017/1/13. * 仿GitHub的提交活躍表 * 橫屏使用 */public class GitHubContributionView extends View { /**灰色方格的默認顏色**/ private final static int DEFAULT_BOX_COLOUR = 0xFFEEEEEE; /**提交次數顏色值**/ private final static int[] COLOUR_LEVEL =   new int[]{0xFF1E6823, 0xFF44A340, 0xFF8CC665, 0xFFD6E685, DEFAULT_BOX_COLOUR}; /**星期**/ private String[] weeks = new String[]{"Mon", "Wed", "Fri", "Sun"}; /**月份**/ private String[] months =   new String[]{"Jan", "Feb", "Mar", "Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; /**默認的padding,繪制的時候不貼邊畫**/ private int padding = 24; /**小方格的默認邊長**/ private int boxSide = 8; /**小方格間的默認間隔**/ private int boxInterval = 2; /**所有周的列數**/ private int column = 0; private List<Day> mDays;//一年中所有的天 private Paint boxPaint;//方格畫筆 private Paint textPaint;//文字畫筆 private Paint infoPaint;//彈出框畫筆 private Paint.FontMetrics metrics;//測量文字 private float downX;//按下的點的X坐標 private float downY;//按下的點的Y坐標 private Day clickDay;//按下所對應的天 public GitHubContributionView(Context context) {  this(context, null); } public GitHubContributionView(Context context, AttributeSet attrs) {  this(context, attrs, 0); } public GitHubContributionView(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  initView(); } public void initView() {  mDays = DateFactory.getDays(2016, 5);  //方格畫筆  boxPaint = new Paint();  boxPaint.setStyle(Paint.Style.FILL);  boxPaint.setStrokeWidth(2);  boxPaint.setColor(DEFAULT_BOX_COLOUR);  boxPaint.setAntiAlias(true);  //文字畫筆  textPaint = new Paint();  textPaint.setStyle(Paint.Style.FILL);  textPaint.setColor(Color.GRAY);  textPaint.setTextSize(12);  textPaint.setAntiAlias(true);  //彈出的方格信息畫筆  infoPaint = new Paint();  infoPaint.setStyle(Paint.Style.FILL);  infoPaint.setColor(0xCC888888);  infoPaint.setTextSize(12);  infoPaint.setAntiAlias(true);  //將默認值轉換px  padding = UI.dp2px(getContext(), padding);  boxSide = UI.dp2px(getContext(), boxSide);  metrics = textPaint.getFontMetrics(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {  super.onSizeChanged(w, h, oldw, oldh); } @Override protected void onDraw(Canvas canvas) {  super.onDraw(canvas);  column = 0;  canvas.save();  drawBox(canvas);  drawWeek(canvas);  drawTag(canvas);  drawPopupInfo(canvas);  canvas.restore(); } /**  * 畫出1-12月方格小塊和上面的月份  * @param canvas 畫布  */ private void drawBox(Canvas canvas) {  //方格的左上右下坐標  float startX, startY, endX, endY;  //起始月份為1月  int month = 1;  for (int i = 0; i < mDays.size(); i++) {   Day day = mDays.get(i);   if (i == 0){    //畫1月的文本標記,坐標應該是x=padding,y=padding-boxSide/2(間隙),y坐標在表格上面一點    canvas.drawText(months[0],padding,padding-boxSide/2,textPaint);   }   if (day.week == 1 && i != 0) {    //如果當天是周1,那么說明增加了一列    column++;    //如果列首的月份有變化,那么說明需要畫月份    if (day.month>month){     month = day.month;     //月份文本的坐標計算,x坐標在變化,而y坐標都是一樣的,boxSide/2(間隙)     canvas.drawText(months[month-1],padding+column*(boxSide+boxInterval),padding-boxSide/2,textPaint);    }   }   //計算方格坐標點,x坐標一致隨列數的增多而增加,y坐標隨行數的增多而變化   startX = padding + column * (boxSide + boxInterval);   startY = padding + (day.week - 1) * (boxSide + boxInterval);   endX = startX + boxSide;   endY = startY + boxSide;   //將該方格的坐標保存下來,這樣可以在點擊方格的時候計算彈框的坐標   day.startX = startX;   day.startY = startY;   day.endX = endX;   day.endY = endY;   //給畫筆設置當前天的顏色   boxPaint.setColor(day.colour);   canvas.drawRect(startX, startY, endX, endY, boxPaint);  }  boxPaint.setColor(DEFAULT_BOX_COLOUR);//恢復默認顏色 } /**  * 畫左側的星期  * @param canvas 畫布  */ private void drawWeek(Canvas canvas) {  //文字是左對齊,所以找出最長的字  float textLength = 0;  for (String week : weeks) {   float tempLength = textPaint.measureText(week);   if (textLength < tempLength) {    textLength = tempLength;   }  }  //依次畫出星期文本,坐標點x=padding-文本長度-文本和方格的間隙,y坐標隨行數變化  canvas.drawText(weeks[0], padding - textLength - 2, padding + boxSide - metrics.descent, textPaint);  canvas.drawText(weeks[1], padding - textLength - 2, padding + 3 * (boxSide + boxInterval) - metrics.descent, textPaint);  canvas.drawText(weeks[2], padding - textLength - 2, padding + 5 * (boxSide + boxInterval) - metrics.descent, textPaint);  canvas.drawText(weeks[3], padding - textLength - 2, padding + 7 * (boxSide + boxInterval) - metrics.descent, textPaint); } /**  * 畫出右下角的顏色深淺標志,因為是右對齊的所以需要從右往左畫  * @param canvas 畫布  */ private void drawTag(Canvas canvas) {  //首先計算出兩個文本的長度  float moreLength = textPaint.measureText("More");  float lessLength = textPaint.measureText("Less");  //畫 More 文本,x坐標=padding+(列數+1)*(方格邊長+方格間隙)-一個方格間隙-文本長度  float moreX = padding + (column + 1) * (boxSide + boxInterval) - boxInterval - moreLength;  //y坐標=padding+(方格行數+1,和表格底部有些距離)*(方格邊長+方格間隙)+字體的ascent高度  float moreY = padding + 8 * (boxSide + boxInterval) + Math.abs(metrics.ascent);  canvas.drawText("More", moreX, moreY, textPaint);  //畫深淺色塊,坐標根據上面的More依次計算就可以了  float interval = boxSide - 2;//文字和色塊間的距離  float leftX = moreX - interval - boxSide;  float topY = moreY - boxSide;  float rightX = moreX - interval;  float bottomY = moreY;//色塊的Y坐標是一樣的  for (int i = 0; i < COLOUR_LEVEL.length; i++) {   boxPaint.setColor(COLOUR_LEVEL[i]);   canvas.drawRect(leftX - i * (boxSide + boxInterval), topY, rightX - i * (boxSide + boxInterval), bottomY, boxPaint);  }  //最后畫 Less 文本,原理同上  canvas.drawText("Less", leftX - 4 * (boxSide + boxInterval) - interval - lessLength, moreY, textPaint); } @Override public boolean onTouchEvent(MotionEvent event) {  //獲取點擊時候的坐標,用來判斷點在哪天,并彈出?  if (MotionEvent.ACTION_DOWN == event.getAction()) {   downX = event.getX();   downY = event.getY();   findClickBox();  }  return super.onTouchEvent(event); } /**  * 判斷是否點擊在方格內  */ private void findClickBox() {  for (Day day : mDays) {   //檢測點擊的坐標如果在方格內,則彈出信息提示   if (downX >= day.startX && downX <= day.endX && downY >= day.startY && downY <= day.endY) {    clickDay = day;//紀錄點擊的哪天    break;   }  }  //點擊完要刷新,這樣每次點擊不同的方格,彈窗就可以在相應的位置顯示  refreshView(); } /**  * 點擊彈出文字提示  */ private void refreshView() {  invalidate(); } /**  * 畫方格上的文字彈框  * @param canvas 畫布  */ private void drawPopupInfo(Canvas canvas) {  if (clickDay != null) {   //先根據方格來畫出一個小三角形,坐標就是方格的中間   Path infoPath = new Path();   //先從方格中心   infoPath.moveTo(clickDay.startX + boxSide / 2, clickDay.startY + boxSide / 2);   //然后是方格的左上點   infoPath.lineTo(clickDay.startX, clickDay.startY);   //然后是方格的右上點   infoPath.lineTo(clickDay.endX, clickDay.startY);   //畫出三角   canvas.drawPath(infoPath,infoPaint);   //畫三角上的圓角矩形   textPaint.setColor(Color.WHITE);   //得到當天的文本信息   String popupInfo = clickDay.toString();   System.out.println(popupInfo);   //計算文本的高度和長度用以確定矩形的大小   float infoHeight = metrics.descent - metrics.ascent;   float infoLength = textPaint.measureText(popupInfo);   Log.e("height",infoHeight+"");   Log.e("length",infoLength+"");   //矩形左上點應該是x=當前天的x+邊長/2-(文本長度/2+文本和框的間隙)   float leftX = (clickDay.startX + boxSide / 2 ) - (infoLength / 2 + boxSide);   //矩形左上點應該是y=當前天的y+邊長/2-(文本高度+上下文本和框的間隙)   float topY = clickDay.startY-(infoHeight+2*boxSide);   //矩形的右下點應該是x=leftX+文本長度+文字兩邊和矩形的間距   float rightX = leftX+infoLength+2*boxSide;   //矩形的右下點應該是y=當前天的y   float bottomY = clickDay.startY;   System.out.println(""+leftX+"/"+topY+"/"+rightX+"/"+bottomY);   RectF rectF = new RectF(leftX, topY, rightX, bottomY);   canvas.drawRoundRect(rectF,4,4,infoPaint);   //繪制文字,x=leftX+文字和矩形間距,y=topY+文字和矩形上面間距+文字頂到基線高度   canvas.drawText(popupInfo,leftX+boxSide,topY+boxSide+Math.abs(metrics.ascent),textPaint);   clickDay = null;//重新置空,保證點擊方格外信息消失   textPaint.setColor(Color.GRAY);//恢復畫筆顏色  } } /**  * 設置某天的次數  * @param year 年  * @param month 月  * @param day 日  * @param contribution 次數  */ public void setData(int year,int month,int day,int contribution){  //先找到是第幾天,為了方便不做參數檢測了  for (Day d : mDays) {   if (d.year == year && d.month == month && d.date == day){    d.contribution = contribution;    d.colour = getColour(contribution);    break;   }  }  refreshView(); } /**  * 根據提交次數來獲取顏色值  * @param contribution 提交的次數  * @return 顏色值  */ private int getColour(int contribution){  int colour = 0;  if (contribution <= 0){   colour = COLOUR_LEVEL[4];  }  if (contribution == 1){   colour = COLOUR_LEVEL[3];  }  if (contribution == 2){   colour = COLOUR_LEVEL[2];  }  if (contribution == 3){   colour = COLOUR_LEVEL[1];  }  if (contribution >= 4){   colour = COLOUR_LEVEL[0];  }  return colour; }}

這樣弄個布局測試下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" > <com.franky.custom.view.GitHubContributionView  android:id="@+id/cc_chart"  android:layout_width="match_parent"  android:layout_height="match_parent"  /></LinearLayout>

隨機弄些數據:

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  GitHubContributionView github = (GitHubContributionView) findViewById(R.id.cc_chart);  github.setData(2016,12,9,2);  github.setData(2016,11,9,1);  github.setData(2016,10,5,10);  github.setData(2016,8,9,3);  github.setData(2016,4,20,2);  github.setData(2016,12,13,3);  github.setData(2016,12,14,3);  github.setData(2016,2,15,4); }}

效果

gif沒有錄好,看看圖片效果:

效果.png

查看源碼

以上所述是小編給大家介紹的Android自定義View實現仿GitHub的提交活躍表格,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 桐梓县| 九江县| 石屏县| 旺苍县| 营口市| 沈丘县| 渝中区| 咸丰县| 康乐县| 江川县| 康马县| 淮安市| 江孜县| 高阳县| 灌南县| 牡丹江市| 西城区| 连江县| 随州市| 巴中市| 浮梁县| 响水县| 米脂县| 墨脱县| 海南省| 德惠市| 门头沟区| 林芝县| 阿克陶县| 武义县| 襄城县| 如皋市| 德州市| 张家川| 涟源市| 蓬安县| 华宁县| 红桥区| 津市市| 曲阜市| 那坡县|