編輯:關於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中
Android調用系統相冊和相機選擇圖片並顯示在imageview中,在系統調用相機拍攝中,直接返回的是經過壓縮處理後的圖像,當你直接把返還後的圖片放在imageview
Android中SQLite應用詳解
上次我向大家介紹了SQLite的基本信息和使用過程,相信朋友們對SQLite已經有所了解了,那今天呢,我就和大家分享一下在Android中如何使用SQLite。現在的主流
android編程實現懸浮窗體的方法
本文實例講述了android編程實現懸浮窗體的方法。分享給大家供大家參考,具體如下:突然對懸浮窗體感興趣,查資料做了個小Demo,效果是點擊按鈕後,關閉當前Activit
UIScrollView
目錄:? UIScrollView的常見屬性 ? UIScrollView的常用代理方法 ? UIScrollView的縮放 ? UIScr