Android自定義View簡單版
阿新 • • 發佈:2018-12-05
1. 自繪控制元件
自繪控制元件的內容都是自己繪製出來時,在View的onDraw方法中完成繪製。下面是一個簡單的計數器 每點選一次 計數值就加一
執行效果如下:
建立CounterView類,繼承View,實現OnClickListener介面:
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; public class CounterView extends View implements View.OnClickListener { // 定義畫筆 private Paint mPaint; // 用於獲取文字的寬和高 private Rect mBounds; // 計數值,每點選一次本控制元件,其值增加1 private int mCount; public CounterView(Context context, AttributeSet attrs) { super(context, attrs); // 初始化畫筆、Rect mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBounds = new Rect(); // 本控制元件的點選事件 setOnClickListener(this); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint); mPaint.setColor(Color.YELLOW); mPaint.setTextSize(50); String text = String.valueOf(mCount); mPaint.getTextBounds(text, 0, text.length(), mBounds); float textWidth = mBounds.width(); float textHeight = mBounds.height(); // 繪製字串 canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2 + textHeight / 2, mPaint); } @Override public void onClick(View v) { mCount++; //重繪 invalidate(); } }
在activity_main.xml中引入該自定義佈局:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.example.day1.weight.CounterView android:id="@+id/counter_view" android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="center_horizontal|top" android:layout_margin="20dp" /> </android.support.constraint.ConstraintLayout>
2. 重寫View來實現全新控制元件
效果如下
<declare-styleable name="circleView"> <attr name="textSize" format="dimension" /> <attr name="text" format="string" /> <attr name="circleColor" format="color" /> <attr name="arcColor" format="color" /> <attr name="textColor" format="color" /> <attr name="startAngle" format="integer" /> <attr name="sweepAngle" format="integer" /> </declare-styleable>
Ⅰ、textSize——對應中間文字文字的大小
Ⅱ、text——對應中間文字
Ⅲ、circleColor——對應內圓的顏色
Ⅳ、arcColor——對應外環的顏色
Ⅴ、textColor——對應文字的顏色
Ⅵ、startAngle——對應外環的起始角度
Ⅶ、sweepAngle——對應外環掃描角度
建立CircleView類,繼承View
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import com.example.day1.R;
public class CircleView extends View {
private int startAngle;
private int sweepAngle;
private String text;
private float textSize;
private int textColor;
private int circleColor;
private int arcColor;
private int mCircleXY;
private float mRadius;
private Paint mCirclePaint;
private RectF mRectF;
private Paint mArcPaint;
private Paint mTextPaint;
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.circleView);
if (ta != null) {
circleColor = ta.getColor(R.styleable.circleView_circleColor, 0);
arcColor = ta.getColor(R.styleable.circleView_arcColor, 0);
textColor = ta.getColor(R.styleable.circleView_textColor, 0);
textSize = ta.getDimension(R.styleable.circleView_textSize, 50);
text = ta.getString(R.styleable.circleView_text);
startAngle = ta.getInt(R.styleable.circleView_startAngle, 0);
sweepAngle = ta.getInt(R.styleable.circleView_sweepAngle, 90);
ta.recycle();
}
}
private void init() {
int length = Math.min(getWidth(), getHeight());
mCircleXY = length / 2;
mRadius = length * 0.5f / 2;
mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setColor(circleColor);
mRectF = new RectF(length * 0.1f, length * 0.1f, length * 0.9f,
length * 0.9f);
mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mArcPaint.setColor(arcColor);
mArcPaint.setStyle(Paint.Style.STROKE);
mArcPaint.setStrokeWidth((getWidth() * 0.1f));
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(textSize);
mTextPaint.setColor(textColor);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
init();
drawSth(canvas);
}
public void drawSth(Canvas canvas) {
canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint);
canvas.drawArc(mRectF, startAngle, sweepAngle, false, mArcPaint);
canvas.drawText(text, 0, text.length(), mCircleXY, mCircleXY + textSize
/ 4, mTextPaint);
}
}
在activity_main.xml中引入該自定義佈局:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.day1.weight.CircleView
android:layout_width="200dp"
android:layout_height="200dp"
app:startAngle="0"
app:sweepAngle="270"
app:text="hehe"
app:textSize="30sp"
app:textColor="@color/colorPrimaryDark"
app:circleColor="@color/colorPrimary"
app:arcColor="@color/colorAccent" />
</android.support.constraint.ConstraintLayout>
3. 動態條形圖
首先,在OnMeasure()方法中計算出每個矩形寬與高,這裡為了方便起見,每個矩形預設的高為螢幕的高,每個矩形的寬這裡定義為螢幕的寬度乘以80%除以矩形的個數。然後根據寬與高來初始化筆刷(Paint)。為什麼要根據寬與高來初始化筆刷了,這裡我為了使自定義View更加的逼真,我這裡使用LinearGradient(線性渲染器)進行了渲染,這個物件需要使用矩形寬與高。需要指出來的是這個自定義控制元件是動態的,我只需要onDraw方法不斷髮生重繪,這裡為了防止控制元件重新整理太快,我這裡每隔300毫秒重新整理檢視。
最終,執行效果如下:
這個控制元件的完整原始碼如下:
<declare-styleable name="volumneView">
<attr name="count" format="integer" />
</declare-styleable>
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import com.example.day1.R;
public class VolumneView extends View {
private Paint mPaint;
private int mCount;
private int mWidth;
private int mRectHeight;
private int mRectWidth;
private LinearGradient mLinearGradient;
private double mRandom;
private float mcurrentHeight;
public static final int OFFSET = 5;
public VolumneView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs);
}
private void initView(Context context, AttributeSet attrs) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL);
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.volumneView);
if (ta != null) {
mCount = ta.getInt(R.styleable.volumneView_count, 6);
ta.recycle();
}
}
public VolumneView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VolumneView(Context context) {
this(context, null);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getMeasuredWidth();
mRectHeight = getMeasuredHeight();
mRectWidth = (int) (mWidth * 0.8 / mCount);
mLinearGradient = new LinearGradient(0, 0, mRectWidth, mRectHeight,
Color.GREEN, Color.YELLOW, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < mCount; i++) {
mRandom = Math.random();
mcurrentHeight = (float) (mRectHeight * mRandom);
float width = (float) (mWidth * 0.4 / 2 + OFFSET);
canvas.drawRect(width + i * mRectWidth, mcurrentHeight, width
+ (i + 1) * mRectWidth, mRectHeight, mPaint);
}
postInvalidateDelayed(300);
}
}