廢話少說并扇,先上圖
1、自定義View的分類
2穷蛹、自定義View要點
- View需要支持wrap_content
- View需要支持padding
- 盡量不要再View中使用Handler,View已經有post系列方法
- View如果有線程或者動畫旺坠,需要及時停止(onDetachedFromWindow會在View被remove時調用)——避免內存泄露
- View如果有滑動嵌套情形扮超,需要處理好滑動沖突
3、直接繼承自View的實現(xiàn)步驟和方法
- 重寫onDraw璧疗,在onDraw中處理padding
- 重寫onMeasure馁龟,額外處理wrap_content的情況
- 設定自定義屬性attrs(屬性相關xml文件,以及在onDraw中進行處理)
4却音、實現(xiàn)效果圖步驟
- 重寫onMeasure矢炼,額外處理wrap_content的情況
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
mHeight = mDefaultTextSize + mDefaultTempHeight + mDefaultIndicatorHeight + textSpace;
if (widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, mHeight);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, mHeight);
}
}
- 重寫onDraw句灌,在onDraw中處理padding胰锌,并畫出溫度計及指針
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final int paddingLeft = getPaddingLeft();
final int paddingTop = getPaddingTop();
final int paddingRight = getPaddingRight();
final int paddingBottom = getPaddingBottom();
//確定圓角矩形的范圍,在TmepView的最底部,top位置為總高度-圓角矩形的高度
rectProgressBg = new RectF();
rectProgressBg.left = 0 + paddingLeft;
rectProgressBg.top = mHeight - mDefaultTempHeight;
rectProgressBg.right = mWidth - paddingRight;
rectProgressBg.bottom = mHeight - paddingBottom;
shader = new LinearGradient(0, mHeight - mDefaultTempHeight, mWidth, mHeight, SECTION_COLORS, null, Shader.TileMode.MIRROR);
mPaint.setShader(shader);
//繪制圓角矩形 mDefaultTempHeight / 2確定圓角的圓心位置
canvas.drawRoundRect(rectProgressBg, mDefaultTempHeight / 2, mDefaultTempHeight / 2, mPaint);
//當前位置占比
selction = currentCount / maxCount;
//繪制指針 指針的位置在當前溫度的位置 也就是三角形的頂點落在當前溫度的位置
//定義三角形的左邊點的坐標 x= tempView的寬度*當前位置占比-三角形的寬度/2 y=tempView的高度-圓角矩形的高度
indexPath.moveTo(mWidth * selction - mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
//定義三角形的右邊點的坐標 = tempView的寬度*當前位置占比+三角形的寬度/2 y=tempView的高度-圓角矩形的高度
indexPath.lineTo(mWidth * selction + mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
//定義三角形的左邊點的坐標 x= tempView的寬度*當前位置占比 y=tempView的高度-圓角矩形的高度-三角形的高度
indexPath.lineTo(mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight + paddingTop);
indexPath.close();
indexPaint.setShader(shader);
canvas.drawPath(indexPath, indexPaint);
//繪制文本
String text = (int) currentCount + "°c";
//確定文本的位置 x=tempViwe的寬度*當前位置占比 y=tempView的高度-圓角矩形的高度-三角形的高度-文本的間隙
canvas.drawText(text, mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight - textSpace, textPaint);
}
ps:繪制三角形指針,由于位置會變 所以要確定繪制的位置如圖
代碼還算比較好理解酬土,詳細代碼如下:
package androidtest.project.com.customview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by liuboyu on 2018/10/15.
*/
public class TemperatureView extends View {
private Context mContext;
/**
* 分段顏色
*/
private static final int[] SECTION_COLORS = {Color.GREEN, Color.YELLOW, Color.RED};
/**
* 默認寬度
*/
private int mWidth = 1000;
/**
* 默認高度
*/
private int mHeight = 200;
/**
* 設置溫度的最大范圍
*/
private float maxCount = 100f;
/**
* 設置當前溫度
*/
private float currentCount = 50f;
/**
* 當前刻度位置
*/
private float selction;
/**
* 主畫筆格带,畫刻度尺
*/
private Paint mPaint;
/**
* 文字畫筆
*/
private Paint textPaint;
/**
* 當前刻度指針
*/
private Path indexPath;
private Paint indexPaint;
/**
* 畫圓柱
*/
private RectF rectProgressBg;
private LinearGradient shader;
/**
* 指針的寬高
*/
private int mDefaultIndicatorWidth = dipToPx(10);
private int mDefaultIndicatorHeight = dipToPx(8);
/**
* 圓角矩形的高度
*/
private int mDefaultTempHeight = dipToPx(20);
/**
* 默認字體大小
*/
private int mDefaultTextSize = 30;
private int textSpace = dipToPx(5);
public TemperatureView(Context context) {
super(context);
init(context);
}
public TemperatureView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TemperatureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 初始化各種畫筆
*
* @param context
*/
private void init(Context context) {
this.mContext = context;
//圓角矩形paint
mPaint = new Paint();
//防止邊緣的鋸齒
mPaint.setAntiAlias(true);
//文本paint
textPaint = new TextPaint();
textPaint.setAntiAlias(true);
textPaint.setTextSize(mDefaultTextSize);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setColor(mContext.getResources().getColor(R.color.colorAccent));
//三角形指針paint
indexPath = new Path();
indexPaint = new Paint();
indexPaint.setAntiAlias(true);
indexPaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
mHeight = mDefaultTextSize + mDefaultTempHeight + mDefaultIndicatorHeight + textSpace;
if (widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, mHeight);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, mHeight);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final int paddingLeft = getPaddingLeft();
final int paddingTop = getPaddingTop();
final int paddingRight = getPaddingRight();
final int paddingBottom = getPaddingBottom();
//確定圓角矩形的范圍,在TmepView的最底部,top位置為總高度-圓角矩形的高度
rectProgressBg = new RectF();
rectProgressBg.left = 0 + paddingLeft;
rectProgressBg.top = mHeight - mDefaultTempHeight;
rectProgressBg.right = mWidth - paddingRight;
rectProgressBg.bottom = mHeight - paddingBottom;
shader = new LinearGradient(0, mHeight - mDefaultTempHeight, mWidth, mHeight, SECTION_COLORS, null, Shader.TileMode.MIRROR);
mPaint.setShader(shader);
//繪制圓角矩形 mDefaultTempHeight / 2確定圓角的圓心位置
canvas.drawRoundRect(rectProgressBg, mDefaultTempHeight / 2, mDefaultTempHeight / 2, mPaint);
//當前位置占比
selction = currentCount / maxCount;
//繪制指針 指針的位置在當前溫度的位置 也就是三角形的頂點落在當前溫度的位置
//定義三角形的左邊點的坐標 x= tempView的寬度*當前位置占比-三角形的寬度/2 y=tempView的高度-圓角矩形的高度
indexPath.moveTo(mWidth * selction - mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
//定義三角形的右邊點的坐標 = tempView的寬度*當前位置占比+三角形的寬度/2 y=tempView的高度-圓角矩形的高度
indexPath.lineTo(mWidth * selction + mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
//定義三角形的左邊點的坐標 x= tempView的寬度*當前位置占比 y=tempView的高度-圓角矩形的高度-三角形的高度
indexPath.lineTo(mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight + paddingTop);
indexPath.close();
indexPaint.setShader(shader);
canvas.drawPath(indexPath, indexPaint);
//繪制文本
String text = (int) currentCount + "°c";
//確定文本的位置 x=tempViwe的寬度*當前位置占比 y=tempView的高度-圓角矩形的高度-三角形的高度-文本的間隙
canvas.drawText(text, mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight - textSpace, textPaint);
}
/**
* 單位轉換
*
* @param dip
* @return
*/
private int dipToPx(int dip) {
float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dip * scale + 0.5f * (dip >= 0 ? 1 : -1));
}
/***
* 設置最大的溫度值
* @param maxCount
*/
public void setMaxCount(float maxCount) {
this.maxCount = maxCount;
}
/***
* 設置當前的溫度
* @param currentCount
*/
public void setCurrentCount(float currentCount) {
if (currentCount > maxCount) {
this.currentCount = maxCount - 5;
} else if (currentCount < 0f) {
currentCount = 0f + 5;
} else {
this.currentCount = currentCount;
}
invalidate();
}
/**
* 設置溫度指針的大小
*
* @param width
* @param height
*/
public void setIndicatorSize(int width, int height) {
this.mDefaultIndicatorWidth = width;
this.mDefaultIndicatorHeight = height;
}
/**
* 設置溫度計厚度
*
* @param height
*/
public void setTempHeight(int height) {
this.mDefaultTempHeight = height;
}
/**
* 設置文字大小
*
* @param textSize
*/
public void setTextSize(int textSize) {
this.mDefaultTextSize = textSize;
}
/**
* 獲取溫度計最大刻度
*
* @return
*/
public float getMaxCount() {
return maxCount;
}
/**
* 獲取當前刻度
*
* @return
*/
public float getCurrentCount() {
return currentCount;
}
}