編輯:關於Android編程
在做項目開發時,有個這樣的需求:
就中間的那個支付明細,要求點擊時能收縮,這個功能非常簡單,從界面來看,用LinearLayout或TableLayout來做,沒啥難度,但是如果是用布局來寫的話,那麼要寫的可多了,這只是列出了幾種支付方式,有可能還有更多的,也有可能沒這麼多,那麼用這種方式來寫,代碼非常啰嗦,維護起來更麻煩,針對這種情況,我采用的是自定義控件來寫,動態畫出來這些文本,詳細代碼:
package com.example.viewtest; import java.util.LinkedHashMap; import java.util.Map; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.FontMetrics; import android.graphics.Typeface; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; /** * 支付詳情自定義控件 *
* 傳入參數:Map
* key:支付方式,value:支付金額 * * @author xiec * */ public class PayDetailView extends View implements OnClickListener { Map
首先,支付方式是不固定的,在解析完成後,以Map
/** * 設置數據 * * @param map */ public void setData(Mapmap) { // 設置數據 data.clear(); if (map != null) { // String[] tmp = new String[map.size()]; // map.keySet().toArray(tmp); // LogUtils.d(Arrays.toString(tmp)); data.putAll(map); } // 重繪 invalidate(); }
重點是在onDraw方法,
@Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub LogUtils.d("onDraw isShow:" + isShow); drawTitle(canvas, isShow, p); if (isShow) { // 畫值 drawCotent(canvas, data, p); } }這個方法裡很簡單,首先是畫標題,再看下是否要畫內容區域(有個收縮功能),如果是縮的狀態,就不用往下走了。isShow這個變量在點擊的時候進行切換
@Override public void onClick(View v) { // 單擊時隱藏支付詳情 LogUtils.d("onClick isShow=" + isShow); isShow = !isShow; // invalidate(); requestLayout(); }
重新布局的時候會重新計算控件的大小(onMeasure()),並重新繪制界面,關於控件的大小計算,我這裡的寬,用的是父布局的寬:
ViewGroup parent = (ViewGroup) getParent();
width = parent.getMeasuredWidth();
要注意一下,getParent()要強轉成ViewGroup。關鍵是高度的計算:
// 計算控件高度,根據數據來算
hight = (int) (textSize * 2);
if (data != null && data.size() > 0 && isShow) {
hight += (data.keySet().size() + 1) * textSize * 2;
}
setMeasuredDimension(width, hight);
我這裡高度計算是根據傳入的Map的大小來算的,首先頭部高度是固定的,我用的是2倍的字體大小,字體大小是從dime中獲取的
textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
res.getDimension(R.dimen.textsize_middle),
res.getDisplayMetrics());
關鍵是setMeasuredDimension方法,使用重新布局時控件大小重新布局。
再回到onDraw方法來,drawTitle方法是畫頭部,即 線上支付明細
記住一條:在onDraw方法中,不要用new來新建對象,不然會有警告:onDraw會常調用,在這裡用new來create對象,內存會使用比較多。
drawTitle代碼就不貼了,上面有,只是簡單說一下,有幾個問題需要注意:
一、文字居中,
canvas.drawText有四個參數,特別是第二個參數和第三個參數,是決定這個文本從哪個地方開始寫的,
// 計算文字長度
float textlen = getTextlen(p, title);
float d_x = (width - textlen) / 2;
float d_y = textSize - fm.descent + (fm.bottom - fm.top) / 2;
canvas.drawText(title, d_x, d_y, p);
居中的方法:(總長度-文本長度)/2
文本長度的獲取:
ps:這裡的Paint參數,最好是全局的,不然會出現計算出來的文本長度與預期的不一樣,原因就是Paint使用的不是同一個對象
/** * 計算文本長度 * * @param p * @param text * @return */ private float getTextlen(Paint p, String text) { if (text == null) { return 0; } return p.measureText(text); }還有一個d_y的計算(可以理解成文本的y軸),Android比較坑的是:這個文本的y軸,有點難理解,drawText畫文本是基於baseLine來畫的,什麼是baseLine?見下圖:
因此,這個d_y不能像d_x那樣計算,得用FontMetrics來計算,計算方法:
fm = p.getFontMetrics();//獲取FontMetrice對象,根據Paint對象來獲取
float d_y = textSize - fm.descent + (fm.bottom - fm.top) / 2;
//控件的高度-fm.descent+(fm.bottom - fm.top) / 2;
寫完頭部文本後,再畫上一個箭頭(其實就是兩條線段組成,利用drawLine方法,參數是一個float[]數組,這個一維數據{x0,y0,x1,y1,x2,y2,x3,y3....},這有兩條線段,所以得要8個坐標):
/** * 畫右上角箭頭 * * @param canvas * @param isShow * @param p */ private void drawArrow(Canvas canvas, boolean isShow, Paint p) { float size = textSize; float d_x = width - size - size / 2; float d_y = textSize / 2; // 大小固定在80px // {x0,y0,x1,y1,x2,y2},總共四個點,八個坐標 float[] pts = new float[8]; if (isShow) { // 向下箭頭 pts[0] = d_x; pts[1] = d_y; pts[2] = d_x + size / 2; pts[3] = d_y + size / 2; pts[4] = d_x + size / 2; pts[5] = d_y + size / 2; pts[6] = d_x + size; pts[7] = d_y; } else { // 向上箭頭 pts[0] = d_x; pts[1] = d_y + size / 2; pts[2] = d_x + size / 2; pts[3] = d_y; pts[4] = d_x + size / 2; pts[5] = d_y; pts[6] = d_x + size; pts[7] = d_y + size / 2; } canvas.drawLines(pts, p); }
內容區域分兩部分來畫:
黑線組成的框和文字部分,
先畫框:框是由線段組成,根據data.size來決定有多少條橫線,豎線就一條,用float[]數組來存組成線段的點的坐標。
int size = data.size(); float[] pts = new float[8 + size * 4]; // 畫框 int ptsLen = pts.length; LogUtils.d("data size=" + size); LogUtils.d("pts size=" + ptsLen); // 豎線 pts[0] = (width - penSize) / 2; pts[1] = textSize * 2; pts[2] = (width - penSize) / 2; pts[3] = hight; pts[4] = 0; pts[5] = textSize * 4; pts[6] = width; pts[7] = textSize * 4; // 是否是左邊 for (int i = 8; i < ptsLen; i += 4) { pts[i] = 0; pts[i + 1] = textSize * (i / 4 + 1) * 2; pts[i + 2] = width; pts[i + 3] = textSize * (i / 4 + 1) * 2; } p.setColor(res.getColor(R.color.black_90)); canvas.drawLines(pts, p);
// 頭部 // 支付方式 float tmp = getTextlen(p, "支付方式"); p.setColor(res.getColor(R.color.main_color)); // 設置粗體 Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD); p.setTypeface(font); float d_x = (width / 2 - tmp) / 2; float d_y = textSize * 2 + (textSize - fm.descent + (fm.bottom - fm.top) / 2); canvas.drawText("支付方式", d_x, d_y, p); tmp = getTextlen(p, "金額"); d_x = (int) (width / 2 + ((width / 2 - tmp) / 2)); canvas.drawText("金額", d_x, d_y, p); size = data.keySet().size(); String[] payway = new String[size]; data.keySet().toArray(payway); // size = payway.length; font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL); p.setTypeface(font); for (int i = 0; i < size; i++) { // 每種支付方式都畫出來 p.setColor(res.getColor(R.color.black)); tmp = getTextlen(p, payway[i]); d_x = (width / 2 - tmp) / 2; d_y = (textSize * (2 + i) * 2) + (textSize - fm.descent + (fm.bottom - fm.top) / 2); canvas.drawText(payway[i], d_x, d_y, p); p.setColor(res.getColor(R.color.tab_spinner_color)); String value = data.get(payway[i]); tmp = getTextlen(p, value); d_x = (int) (width / 2 + ((width / 2 - tmp) / 2)); canvas.drawText(value, d_x, d_y, p); }
Android調用系統相冊和相機選擇圖片並顯示在imageview中,在系統調用相機拍攝中,直接返回的是經過壓縮處理後的圖像,當你直接把返還後的圖片放在imageview
上次我向大家介紹了SQLite的基本信息和使用過程,相信朋友們對SQLite已經有所了解了,那今天呢,我就和大家分享一下在Android中如何使用SQLite。現在的主流
本文實例講述了android編程實現懸浮窗體的方法。分享給大家供大家參考,具體如下:突然對懸浮窗體感興趣,查資料做了個小Demo,效果是點擊按鈕後,關閉當前Activit
目錄:? UIScrollView的常見屬性 ? UIScrollView的常用代理方法 ? UIScrollView的縮放 ? UIScr