Android自定義View(CustomCalendar-定製日曆控制元件)
目錄:
應專案需求,需要做一個日曆控制元件,效果圖如下:
接到需求後,沒有立即查詢是否有相關開源日曆控制元件可用、系統日曆控制元件是否能滿足 ,第一反應就是這個控制元件該怎麼畫?誰叫咱自定義控制元件技術牛逼呢O(∩_∩)O哈哈~(開個玩笑,不要這樣子嚴肅→廣告時間:要想達到哥的高度,請認真學習自定義控制元件系列部落格喔)。言歸正傳,如圖,上部分是自定義日曆的效果圖,下面是系統自帶CalendarView
的效果,這兩個控制元件的相關功能需求元素圖上都有標註。系統自帶的日曆控制元件能夠左右滑動切換月份,效果還是挺酷的(這是我在自定義控制元件完畢之後才發現的),結果就後悔了,這麼酷幹嘛還要自定義啊?
自定義當然不是為了裝逼吶,請認真看需求,我們需要在日期下面顯示任務完成情況,當日被切換之後需要標註為灰色圓圈背景,這些都是自帶日曆控制元件不可達到的,當然,我們也可以繼承系統CalendarView
然後試著修改;那為什麼不選擇開源的日曆控制元件呢?如果我們不會自定義控制元件或者時間很緊,開源的當然是首選,誰叫我閒的慌,開源的控制元件也會有些問題,有的可能不夠完善很多bug,比較完善的可能內容太多,如果要抽取有用的內容還是需要花一定時間,如果整個庫工程都弄過來會造成大量的程式碼冗餘。另外一點很重要,任務情況需要從伺服器上獲取,當切換日期之後,日曆控制元件下方要顯示那天的任務詳情(可能需要請求資料),這麼多問題如果去修改開源庫工程工作量不一定比自定義小。綜上,還是自定義更適合我,整個日曆控制元件500多行程式碼就搞定(包括註釋、介面、各種變數),後面如果專案需求有變動,改動起來也是so easy! 當然,日常開發中,自帶控制元件能搞定的儘量就用系統自帶的。下面我們一起看看這個控制元件是怎樣實現的。
1、分析
怎樣自定義這個日曆控制元件呢?可能我們第一反應是GridView
+組合控制元件的方式,GridView
用來展示下面日期部分,這種方式實現起來相對比較容易,但是核心的內容(獲取某月的天數、具體展示在什麼位置)還是得自己做,這種方式程式碼量也不少,另外這樣做也會加大系統效能開銷,GridView
中同時顯示30來個item,item裡面還巢狀子控制元件,之前我們講控制元件填充、測量時講過儘量減少佈局的巢狀,這樣會造成過多的遍歷,一不小心又裝逼了,現在的手機那麼牛逼,這麼點工作量跟我談什麼效能?
第二種就是通過自定義View
完全繪製出來,只需要一個類搞定。其實繪製很簡單,拿到一個畫筆(Paint
如果要繪製出這個控制元件,我們首先要得到某個月的所有天數(從1號開始….)、1號是星期幾(從什麼位置開始展示),有了這兩個資料,我們就能得到第一行從哪裡開始繪製,能繪製多少天,最後一行能繪製多少天,其他中間的都是繪製7天;接下來需要繪製當前日期和被選中日期的背景,其實就是在繪製日期時先判斷下日期是不是當前日期,如果是就給他先畫一個背景,被選擇的也是一樣。我們先看看獲取日期的演算法:
/**設定月份*/
private void setMonth(String Month){
//設定的月份(2017年01月)
month = str2Date(Month);
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
//獲取今天是多少號
currentDay = calendar.get(Calendar.DAY_OF_MONTH);
Date cM = str2Date(getMonthStr(new Date()));
//判斷是否為當月
if(cM.getTime() == month.getTime()){
isCurrentMonth = true;
selectDay = currentDay;//當月預設選中當前日
}else{
isCurrentMonth = false;
selectDay = 0;
}
Log.d(TAG, "設定月份:"+month+" 今天"+currentDay+"號, 是否為當前月:"+isCurrentMonth);
calendar.setTime(month);
dayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
//第一行1號顯示在什麼位置(星期幾)
firstIndex = calendar.get(Calendar.DAY_OF_WEEK)-1;
lineNum = 1;
//第一行能展示的天數
firstLineNum = 7-firstIndex;
lastLineNum = 0;
int shengyu = dayOfMonth - firstLineNum;
while (shengyu>7){
lineNum ++;
shengyu-=7;
}
if(shengyu>0){
lineNum ++;
lastLineNum = shengyu;
}
Log.i(TAG, getMonthStr(month)+"一共有"+dayOfMonth+"天,第一天的索引是:"+firstIndex+" 有"+lineNum+
"行,第一行"+firstLineNum+"個,最後一行"+lastLineNum+"個");
}
2、自定義屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomCalendar">
<!--這四個顏色分別是月份、星期、日期、任務的背景色,只是方便除錯測量時使用,正式試用時可配置透明色-->
<attr name="mBgMonth" format="color" />
<attr name="mBgWeek" format="color" />
<attr name="mBgDay" format="color" />
<attr name="mBgPre" format="color" />
<attr name="mTextColorMonth" format="color" /> <!--標題字型顏色-->
<attr name="mTextColorWeek" format="color" /> <!--星期字型顏色-->
<attr name="mTextColorDay" format="color" /> <!--日期字型顏色-->
<attr name="mTextColorPreFinish" format="color" /> <!--任務次數字體顏色-->
<attr name="mTextColorPreUnFinish" format="color" />
<attr name="mSelectTextColor" format="color" /> <!--選中日期字型顏色-->
<attr name="mSelectBg" format="color" /> <!--選中日期背景-->
<attr name="mCurrentBg" format="color" /> <!--當天日期背景-->
<attr name="mCurrentBgStrokeWidth" format="dimension" /> <!--當天日期背景虛線寬度-->
<attr name="mCurrentBgDashPath" format="reference" /> <!--當天日期背景虛線陣列-->
<attr name="mTextSizeMonth" format="dimension" /> <!--標題字型大小-->
<attr name="mTextSizeWeek" format="dimension" /> <!--星期字型大小-->
<attr name="mTextSizeDay" format="dimension" /> <!--日期字型大小-->
<attr name="mTextSizePre" format="dimension" /> <!--任務次數字體大小-->
<attr name="mMonthRowL" format="reference" /> <!--月份箭頭-->
<attr name="mMonthRowR" format="reference" /> <!--月份箭頭-->
<attr name="mMonthRowSpac" format="dimension" />
<attr name="mSelectRadius" format="dimension" /> <!--選中日期背景半徑-->
<attr name="mMonthSpac" format="dimension" /> <!--標題月份上下間隔-->
<attr name="mLineSpac" format="dimension" /> <!--日期行間距-->
<attr name="mTextSpac" format="dimension" /> <!--日期和任務次數字體上下間距-->
</declare-styleable>
</resources>
3、onMeasure()
得到需要繪製的資料之後,接下來就是重寫onMeasure()
方法了,這個控制元件需要多寬多高?寬度直接填充父窗體即可,總高度=月份高度+星期高度+日期高度,相應的資料在上面的演算法中都得到了,請看下面分析圖:
程式碼:
/**計算相關常量,構造方法中呼叫*/
private void initCompute(){
mPaint = new Paint();
bgPaint = new Paint();
mPaint.setAntiAlias(true); //抗鋸齒
bgPaint.setAntiAlias(true); //抗鋸齒
map = new HashMap<>();
//標題高度
mPaint.setTextSize(mTextSizeMonth);
titleHeight = FontUtil.getFontHeight(mPaint) + 2 * mMonthSpac;
//星期高度
mPaint.setTextSize(mTextSizeWeek);
weekHeight = FontUtil.getFontHeight(mPaint);
//日期高度
mPaint.setTextSize(mTextSizeDay);
dayHeight = FontUtil.getFontHeight(mPaint);
//次數字體高度
mPaint.setTextSize(mTextSizePre);
preHeight = FontUtil.getFontHeight(mPaint);
//每行高度 = 行間距 + 日期字型高度 + 字間距 + 次數字體高度
oneHeight = mLineSpac + dayHeight + mTextSpac + preHeight;
//預設當前月份
String cDateStr = getMonthStr(new Date());
// cDateStr = "2015年08月";
setMonth(cDateStr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//寬度 = 填充父窗體
int widthSize = MeasureSpec.getSize(widthMeasureSpec); //獲取寬的尺寸
columnWidth = widthSize / 7;
//高度 = 標題高度 + 星期高度 + 日期行數*每行高度
float height = titleHeight + weekHeight + (lineNum * oneHeight);
Log.v(TAG, "標題高度:"+titleHeight+" 星期高度:"+weekHeight+" 每行高度:"+oneHeight+
" 行數:"+ lineNum + " \n控制元件高度:"+height);
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
(int)height);
}
4、onDraw()
@Override
protected void onDraw(Canvas canvas) {
drawMonth(canvas);
drawWeek(canvas);
drawDayAndPre(canvas);
}
①、繪製月份
private void drawMonth(Canvas canvas){
//背景
bgPaint.setColor(mBgMonth);
RectF rect = new RectF(0, 0, getWidth(), titleHeight);
canvas.drawRect(rect, bgPaint);
//繪製月份
mPaint.setTextSize(mTextSizeMonth);
mPaint.setColor(mTextColorMonth);
float textLen = FontUtil.getFontlength(mPaint, getMonthStr(month));
float textStart = (getWidth() - textLen)/ 2;
canvas.drawText(getMonthStr(month), textStart,
mMonthSpac+FontUtil.getFontLeading(mPaint), mPaint);
/*繪製左右箭頭*/
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mMonthRowL);
int h = bitmap.getHeight();
rowWidth = bitmap.getWidth();
//float left, float top
rowLStart = (int)(textStart-2*mMonthRowSpac-rowWidth);
canvas.drawBitmap(bitmap, rowLStart+mMonthRowSpac , (titleHeight - h)/2, new Paint());
bitmap = BitmapFactory.decodeResource(getResources(), mMonthRowR);
rowRStart = (int)(textStart+textLen);
canvas.drawBitmap(bitmap, rowRStart+mMonthRowSpac, (titleHeight - h)/2, new Paint());
}
②、繪製星期
private String[] WEEK_STR = new String[]{"Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat", };
private void drawWeek(Canvas canvas){
//背景
bgPaint.setColor(mBgWeek);
RectF rect = new RectF(0, titleHeight, getWidth(), titleHeight + weekHeight);
canvas.drawRect(rect, bgPaint);
//繪製星期:七天
mPaint.setTextSize(mTextSizeWeek);
mPaint.setColor(mTextColorWeek);
for(int i = 0; i < WEEK_STR.length; i++){
int len = (int)FontUtil.getFontlength(mPaint, WEEK_STR[i]);
int x = i * columnWidth + (columnWidth - len)/2;
canvas.drawText(WEEK_STR[i], x, titleHeight + FontUtil.getFontLeading(mPaint), mPaint);
}
}
③、繪製日期及任務
private void drawDayAndPre(Canvas canvas){
//某行開始繪製的Y座標,第一行開始的座標為標題高度+星期部分高度
float top = titleHeight+weekHeight;
//行
for(int line = 0; line < lineNum; line++){
if(line == 0){
//第一行
drawDayAndPre(canvas, top, firstLineNum, 0, firstIndex);
}else if(line == lineNum-1){
//最後一行
top += oneHeight;
drawDayAndPre(canvas, top, lastLineNum, firstLineNum+(line-1)*7, 0);
}else{
//滿行
top += oneHeight;
drawDayAndPre(canvas, top, 7, firstLineNum+(line-1)*7, 0);
}
}
}
/**
* 繪製某一行的日期
* @param canvas
* @param top 頂部座標
* @param count 此行需要繪製的日期數量(不一定都是7天)
* @param overDay 已經繪製過的日期,從overDay+1開始繪製
* @param startIndex 此行第一個日期的星期索引
*/
private void drawDayAndPre(Canvas canvas, float top,
int count, int overDay, int startIndex){
// Log.e(TAG, "總共"+dayOfMonth+"天 有"+lineNum+"行"+ " 已經畫了"+overDay+"天,下面繪製:"+count+"天");
//背景
float topPre = top + mLineSpac + dayHeight;
bgPaint.setColor(mBgDay);
RectF rect = new RectF(0, top, getWidth(), topPre);
canvas.drawRect(rect, bgPaint);
bgPaint.setColor(mBgPre);
rect = new RectF(0, topPre, getWidth(), topPre + mTextSpac + dayHeight);
canvas.drawRect(rect, bgPaint);
mPaint.setTextSize(mTextSizeDay);
float dayTextLeading = FontUtil.getFontLeading(mPaint);
mPaint.setTextSize(mTextSizePre);
float preTextLeading = FontUtil.getFontLeading(mPaint);
// Log.v(TAG, "當前日期:"+currentDay+" 選擇日期:"+selectDay+" 是否為當前月:"+isCurrentMonth);
for(int i = 0; i<count; i++){
int left = (startIndex + i)*columnWidth;
int day = (overDay+i+1);
mPaint.setTextSize(mTextSizeDay);
//如果是當前月,當天日期需要做處理
if(isCurrentMonth && currentDay == day){
mPaint.setColor(mTextColorDay);
bgPaint.setColor(mCurrentBg);
bgPaint.setStyle(Paint.Style.STROKE); //空心
PathEffect effect = new DashPathEffect(mCurrentBgDashPath, 1);
bgPaint.setPathEffect(effect); //設定畫筆曲線間隔
bgPaint.setStrokeWidth(mCurrentBgStrokeWidth); //畫筆寬度
//繪製空心圓背景
canvas.drawCircle(left+columnWidth/2, top + mLineSpac +dayHeight/2,
mSelectRadius-mCurrentBgStrokeWidth, bgPaint);
}
//繪製完後將畫筆還原,避免髒筆
bgPaint.setPathEffect(null);
bgPaint.setStrokeWidth(0);
bgPaint.setStyle(Paint.Style.FILL);
//選中的日期,如果是本月,選中日期正好是當天日期,下面的背景會覆蓋上面繪製的虛線背景
if(selectDay == day){
//選中的日期字型白色,橙色背景
mPaint.setColor(mSelectTextColor);
bgPaint.setColor(mSelectBg);
//繪製橙色圓背景,引數一是中心點的x軸,引數二是中心點的y軸,引數三是半徑,引數四是paint物件;
canvas.drawCircle(left+columnWidth/2, top + mLineSpac +dayHeight/2, mSelectRadius, bgPaint);
}else{
mPaint.setColor(mTextColorDay);
}
int len = (int)FontUtil.getFontlength(mPaint, day+"");
int x = left + (columnWidth - len)/2;
canvas.drawText(day+"", x, top + mLineSpac + dayTextLeading, mPaint);
//繪製次數
mPaint.setTextSize(mTextSizePre);
MainActivity.DayFinish finish = map.get(day);
String preStr = "0/0";
if(finish!=null){
//區分完成未完成
if(finish.finish >= finish.all) {
mPaint.setColor(mTextColorPreFinish);
}else{
mPaint.setColor(mTextColorPreUnFinish);
}
preStr = finish.finish+"/"+finish.all;
}else{
mPaint.setColor(mTextColorPreUnFinish);
}
len = (int)FontUtil.getFontlength(mPaint, preStr);
x = left + (columnWidth - len)/2;
canvas.drawText(preStr, x, topPre + mTextSpac + preTextLeading, mPaint);
}
}
這部分完成之後,我們自定義日曆的繪製工作就over了,下面我們看看效果圖:
5、事件處理
事件相關知識點也是自定義控制元件比較重要的內容,後面有空會詳細介紹。下面我們看看這個控制元件需要處理那些事件。當點選箭頭時需要增減月份,點選日期時需要置為選中。控制元件接受到事件之後,我要怎樣知道點選的是箭頭還是日期還是其他部位?只能通過事件的座標計算了,如果在某個範圍之內即可,在上面的分析圖中,將控制元件劃分成了很多小網格,這些小網格的座標範圍都是確定的(根據寬高等資料),事件發生後,只需要判斷事件點座標是否落入相應區域即可,然後邊測試邊修改一些細節問題,下面是事件處理先關的程式碼:
//焦點座標
private PointF focusPoint = new PointF();
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN:
focusPoint.set(event.getX(), event.getY());
touchFocusMove(focusPoint, false);
break;
case MotionEvent.ACTION_MOVE:
focusPoint.set(event.getX(), event.getY());
touchFocusMove(focusPoint, false);
break;
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
focusPoint.set(event.getX(), event.getY());
touchFocusMove(focusPoint, true);
break;
}
return true;
}
/**焦點滑動*/
public void touchFocusMove(final PointF point, boolean eventEnd) {
Log.e(TAG, "點選座標:("+point.x+" ,"+point.y+"),事件是否結束:"+eventEnd);
/**標題和星期只有在事件結束後才響應*/
if(point.y<=titleHeight){
//事件在標題上
if(eventEnd && listener!=null){
if(point.x>=rowLStart && point.x<(rowLStart+2*mMonthRowSpac+rowWidth)){
Log.w(TAG, "點選左箭頭");
listener.onLeftRowClick();
}else if(point.x>rowRStart && point.x<(rowRStart + 2*mMonthRowSpac+rowWidth)){
Log.w(TAG, "點選右箭頭");
listener.onRightRowClick();
}else if(point.x>rowLStart && point.x <rowRStart){
listener.onTitleClick(getMonthStr(month), month);
}
}
}else if(point.y<=(titleHeight+weekHeight)){
//事件在星期部分
if(eventEnd && listener!=null){
//根據X座標找到具體的焦點日期
int xIndex = (int)point.x / columnWidth;
Log.e(TAG, "列寬:"+columnWidth+" x座標餘數:"+(point.x / columnWidth));
if((point.x / columnWidth-xIndex)>0){
xIndex += 1;
}
if(listener!=null){
listener.onWeekClick(xIndex-1, WEEK_STR[xIndex-1]);
}
}
}else{
/**日期部分按下和滑動時重繪,只有在事件結束後才響應*/
touchDay(point, eventEnd);
}
}
//控制事件是否響應
private boolean responseWhenEnd = false;
/**事件點在 日期區域 範圍內*/
private void touchDay(final PointF point, boolean eventEnd){
//根據Y座標找到焦點行
boolean availability = false; //事件是否有效
//日期部分
float top = titleHeight+weekHeight+oneHeight;
int foucsLine = 1;
while(foucsLine<=lineNum){
if(top>=point.y){
availability = true;
break;
}
top += oneHeight;
foucsLine ++;
}
if(availability){
//根據X座標找到具體的焦點日期
int xIndex = (int)point.x / columnWidth;
if((point.x / columnWidth-xIndex)>0){
xIndex += 1;
}
// Log.e(TAG, "列寬:"+columnWidth+" x座標餘數:"+(point.x / columnWidth));
if(xIndex<=0)
xIndex = 1; //避免調到上一行最後一個日期
if(xIndex>7)
xIndex = 7; //避免調到下一行第一個日期
// Log.e(TAG, "事件在日期部分,第"+foucsLine+"/"+lineNum+"行, "+xIndex+"列");
if(foucsLine == 1){
//第一行
if(xIndex<=firstIndex){
Log.e(TAG, "點到開始空位了");
setSelectedDay(selectDay, true);
}else{
setSelectedDay(xIndex-firstIndex, eventEnd);
}
}else if(foucsLine == lineNum){
//最後一行
if(xIndex>lastLineNum){
Log.e(TAG, "點到結尾空位了");
setSelectedDay(selectDay, true);
}else{
setSelectedDay(firstLineNum + (foucsLine-2)*7+ xIndex, eventEnd);
}
}else{
setSelectedDay(firstLineNum + (foucsLine-2)*7+ xIndex, eventEnd);
}
}else{
//超出日期區域後,視為事件結束,響應最後一個選擇日期的回撥
setSelectedDay(selectDay, true);
}
}
/**設定選中的日期*/
private void setSelectedDay(int day, boolean eventEnd){
Log.w(TAG, "選中:"+day+" 事件是否結束"+eventEnd);
selectDay = day;
invalidate();
if(listener!=null && eventEnd && responseWhenEnd && lastSelectDay!=selectDay) {
lastSelectDay = selectDay;
listener.onDayClick(selectDay, getMonthStr(month) + selectDay + "日", map.get(selectDay));
}
responseWhenEnd = !eventEnd;
}
最終效果如下:
本篇部落格講解沒有特別細緻,但是關鍵的思路已經很清晰了,其實自定義控制元件也就那麼會事兒,在之前自定義控制元件系列部落格及案例中已經講解的非常詳細了;如果後面我再更新自定義系列文章也將側重講解思路,知識點不熟悉的還請移步自定義控制元件基礎。
原始碼下載:
注:沒有積分的童鞋 請點贊留言索要程式碼
http://download.csdn.net/detail/u010163442/9728781 CSDN下載平臺很噁心
相關推薦
Android自定義View(CustomCalendar-定製日曆控制元件)
目錄: 應專案需求,需要做一個日曆控制元件,效果圖如下: 接到需求後,沒有立即查詢是否有相關開源日曆控制元件可用、系統日曆控制元件是否能滿足 ,第一反應就是這個控制元件該怎麼畫?誰叫咱自定義控制元件技術牛逼呢O
Android自定義View(LineBreakLayout-自動換行的標籤容器)
最近一段時間比較忙,都沒有時間更新部落格,今天公司的事情忙完得空,繼續為我的自定義控制元件系列部落格添磚加瓦。本篇部落格講解的是標籤自動換行的佈局容器,正好前一陣子有個專案中需要,想了想沒什麼難度就自己弄了。而自定義控制元件系列文章中對於自定義ViewGro
Android自定義View(LimitScrollerView-仿天貓廣告欄上下滾動效果)
最近專案中需要在首頁做一個跑馬燈型別的廣告欄,最後上面決定仿照天貓的廣告欄效果做(中間部位),效果圖如下(右邊是我們的效果): 天貓上搶購那一欄的廣告條可以向上滾動,每次展示一條廣告,展示一定時間後,第二條廣告從下往上頂起。但
Android 自定義自由選擇時間區間的日曆控制元件
摸滾打爬在Android界小菜鳥,突然靜下心想想自己都做過些什麼呢!留下些什麼呢!感覺自己老是copy別人的東西自己都不好意思了,於是今天就 來分享下自己最近寫專案中的一個自定義日曆控制元件 (翻閱了各大開源日曆都沒有合適的),希望大家多多指點,第一次寫部落格如果有不足之處
Android 自定義view(1) --- Attr、Style和Theme詳解
轉載:https://www.jianshu.com/p/dd79220b47dd 概念說明: Attr:屬性,風格樣式的最小單元; Style:風格,它是一系列Attr的集合用以定義一個View
Android自定義View(三)-Draw原理篇
Android自定義View通常需要經過measure、layout和draw過程。 如果你沒有了解過measure過程,可以先看看這篇文章。 如果你沒有了解過layout過程,可以先看看這篇文章。 一、draw的作用:繪製View檢視 二、draw過程:類似meas
Android 自定義View(一)
前言:可是有時候我們總感覺官方定義的一些基本元件不夠用,自定義元件就不可避免了。那麼如何才能做到像官方提供的那些元件一樣用xml來定義他的屬性呢? 先總結下自定義View的步驟: 1、自定義View的屬性; 2、在View的構造方法中獲得自定義的屬性。 一、在re
android自定義View(一)、正弦波水波紋
文章目錄 1、正弦曲線知識 2、靜態正弦曲線繪製 3、動態正弦曲線繪製 4、完整原始碼 1、正弦曲線知識 對這個初中知識遺忘了的可以先看看正弦曲線百度百科詞條方便加深理解。
Android自定義view(一),打造絢麗的驗證碼
前言:我相信信念的力量,信念可以支撐起一個人,一個名族,一個國家。正如“人沒有夢想和鹹魚有什麼區別”一樣,我有信念,有理想,所以我正在努力向著夢想前進~。 自定義view,如果是我,我首先要看到自定義view的效果圖,然後再想想怎麼實現這種效果或功能,所以先貼
Android 自定義View(inflate()模式)
1.建立LayoutInflater例項 有兩種方式 1. LayoutInflater layoutInflater= LayoutInflater.from(MainActivity.this); LayoutInflater layoutInfla
Android自定義View(二、深入解析自定義屬性)
目錄: 繼承View,覆蓋構造方法 自定義屬性 重寫onMeasure方法測量寬高 重寫onDraw方法繪製控制元件 接下來幾篇部落格分別深入學習每一個步驟的知識點,第一步就不用多講了,這篇部落格我們看一
android自定義View(2):實現百分比自適應佈局
android介面適配難是歷史原因,我們只能想辦法解決。github上面已有一些佈局自適應的解決方案,今天我分享的是自定義控制元件:RelativieLayout自適應百分比寬高。直接上菜。 一,實現的效果圖 眼見為實,截圖所示,寬高都是50%,實現了自
android 自定義View(完美)
原文連結:http://www.jianshu.com/p/d507e3514b65 一、自定義View,你真的掌握了嗎? 什麼?你說你掌握了自定義View?來來來,回答老衲如下問題: Google提出View這個概念的目的是什麼?View這個概念與Activtiy、F
Android 自定義View(基礎)
一:引言 Android的開發中,在移動裝置上展示的所有內容,都是靠一個一個具體的檢視控制元件,按照一定的排列規則展示出來的,這些一個個控制元件,都是系統提供給我們的。但是我們看到,app商店上有些比較炫酷的頁面展示,我們會發現,系統根本沒有提供那些控制元件,
Android 自定義View(二),點,線的繪製
public class PointLine extends View { Paint mLinePaint; Paint mPointPaint; float width; float height; float pointAddress
Android 自定義View基礎(四)--ListView
這篇文章主要是總結ListView的使用: 首先來說說ListView,幾乎所有的app都在使用listview,所以熟練使用ListView是作為Android移動開發必不可少的。 Adapter ViewHolder setEnpty() 自動隱藏,顯
Android進階之自定義View實戰(一)仿iOS UISwitch控制元件實現
一.引言 個人覺得,自定義View一直是Android開發最變換莫測、最難掌握、最具吸引力的地方。因為它涉及到的知識點比較多,想在實際應用中駕輕就熟,由淺入深,你需要掌握以下知識點: 1. View的繪製機制以及Canvas、Paint、Rect等的常用方
Android自定義語音音訊對話識別翻譯動畫控制元件
最近做翻譯器專案,專案中用到科大語音翻譯,語音動畫需要自己寫,對於我稍微有點複雜,把程式碼貼出來供大家參考下,不足之處請指正! 自定義控制元件包含有語音動畫(音量大小波浪動畫),音柱(音量大小音柱改變高低),文字(請講話、識別中、翻譯中)。 /** *
自定義可以顯示考勤狀態的日曆控制元件
最近專案中要做一個可顯示考勤狀態的日曆,於是就整理了一下寫了一個demo。先看一下專案中的效果圖: 圖1: 圖2: 下面來看一下demo的效果圖: 不會做動圖,只能發圖片了。。。 初始化會顯示今天所在月的考勤,並選中今天(圖3):
Android從零擼美團(三) - Android多標籤tab滑動切換 - 自定義View快速實現高度定製封裝
這是【從零擼美團】系列文章第三篇 【從零擼美團】是一個高仿美團的開源專案,旨在鞏固 Android 相關知識的同時,幫助到有需要的小夥伴。 GitHub 原始碼地址:github.com/cachecats/L… Android從零擼美團(一) - 統一管理 Gradle 依賴 提取到單獨檔案中 Andr