編輯:關於Android編程
技術是永無止境的,如果真的愛技術,那就勇敢的堅持下去。我很喜歡這句話,當我在遇到問題的時候、當我覺得代碼枯燥的時候,我就會問自己,到底是不是真的熱愛技術,這個時候,我心裡總是起著波瀾,我的答案是肯定的,我深深的愛著這門技術。
今天我們繼續聊聊Android的自定義View系列。先看看效果吧:
這個是我手機殺毒軟件的一個動畫效果,類似於雷達搜索,所以用途還是很廣泛的,特別是先了解一下這裡的具體邏輯和寫法,對技術的進步一定很有用。
先簡單的分析一下這裡的元素,主要有四個圓、一個扇形、還有八條虛線。當知道這些以後,代碼就可以開始寫了。
先看看這裡用到了哪些變量。
private float mWidth;
private float mHeight;
private Paint mPaint, mPaint2, mPaint3, mPaint4, mLinePaint;
private RectF mRectF;
private float startAngle = 360;
private float radius1, radius2, radius3, radius4;
private Path path;
不是很多,有畫筆6個,然後是寬與高,還有就是角度以及四個圓的半徑以及一個矩形、當然還有繪制虛線的Path類。接下來是初始化的操作。
// 初始化畫筆操作
private void initData() {
mPaint = new Paint();
mPaint.setStrokeWidth(1);
mPaint.setAntiAlias(true);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.parseColor("#ffffff"));
mPaint2 = new Paint();
mPaint2.setStrokeWidth(5);
mPaint2.setColor(Color.parseColor("#00ff00"));
mPaint3 = new Paint();
mPaint3.setStrokeWidth(3);
mPaint3.setAntiAlias(true);
mPaint3.setStyle(Style.STROKE);
mPaint3.setColor(Color.parseColor("#ffffff"));
mPaint4 = new Paint();
mPaint4.setStrokeWidth(2);
mPaint4.setAntiAlias(true);
mPaint4.setStyle(Style.STROKE);
mPaint4.setColor(Color.parseColor("#ffffff"));
mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLinePaint.setStrokeWidth(1);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setAntiAlias(true);
mLinePaint.setColor(Color.parseColor("#ffffff"));
path = new Path();
}
這裡包括了五個畫筆的初始化操作、一個路徑的初始化操作。注意每個畫筆的具體樣式是不一樣的,這樣方便實現不同的效果。
初始化的操作完了之後,就是給變量賦值了,還是一樣的,我們選擇在onSizeChange()裡面對變量進行賦值。代碼如下:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getWidth();
mHeight = getHeight();
mRectF = new RectF((float) (mWidth * 0.1), (float) (mWidth * 0.1),
(float) (mWidth * 0.9), (float) (mWidth * 0.9));
// 繪制漸變效果
LinearGradient gradient = new LinearGradient((float) (mWidth * 0.3),
(float) (mWidth * 0.9), (float) (mWidth * 0.1),
(float) (mWidth * 0.5), new int[] {
Color.parseColor("#458EFD"), Color.GREEN,
Color.parseColor("#458EFD"), Color.WHITE,
Color.parseColor("#458EFD") }, null,
Shader.TileMode.CLAMP);
mPaint2.setShader(gradient);
// 四個圓的半徑
radius1 = (float) (mWidth * 0.4);
radius2 = (float) (mWidth * 0.3);
radius3 = (float) (mWidth * 0.2);
radius4 = (float) (mWidth * 0.1);
}
其實很明顯啊,我們在前面講的幾個自定義的View中,幾乎所有的變量初值都是在這個方法裡面寫的,這個方法究竟有什麼特點呢?其實這個是系統回調方法,是系統調用的,它的方法名已經告訴我們了,這個方法會在這個view的大小發生改變是被系統調用,我們要記住的就是view大小變化,這個方法就被執行就可以了。最主要的是,它還在onDraw方法之前調用。
還記得之前的效果嗎?裡面有一個漸變,這個漸變就是代碼裡LinearGradient 類的操作結果了,具體的用法,我在後面會專門去解釋它。
到了這裡,基本上所有的准備工作都完成了,接下來進行真真的繪圖。
先看看onDraw方法裡面做的操作:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvasArc(canvas);
canvasArc2(canvas);
canvasCircle(canvas);
canvasLine(canvas);
}
這裡有畫圓的,有畫扇形的,也有畫線的,所以可以發現,我們所有看到的效果,都是在onDraw方法裡面實現的。我們具體看看每一個方法:
第一個繪制扇形:
// 繪制旋轉的扇形
private void canvasArc(Canvas canvas) {
canvas.drawArc(mRectF, startAngle, 60, true, mPaint2);
}
只有兩行代碼,也很容易理解,那麼第二個扇形也是差不多:
// 繪制旋轉的扇形
private void canvasArc2(Canvas canvas) {
canvas.drawArc(mRectF, startAngle, 1, true, mPaint3);
}
值得注意的是,第二個的扇形的角度是1,為什麼是1呢,原來我這裡只是想要它旋轉角度的效果,並不需要它有多寬,所以在具體實現自定義View的時候,也要學會活學活用。
兩個扇形已經畫好了,那就看看四個圓怎麼畫:
// 繪制四個圓
private void canvasCircle(Canvas canvas) {
canvas.drawCircle(mWidth / 2, mHeight / 2, radius1, mPaint3);
canvas.drawCircle(mWidth / 2, mHeight / 2, radius2, mPaint);
canvas.drawCircle(mWidth / 2, mHeight / 2, radius3, mPaint);
canvas.drawCircle(mWidth / 2, mHeight / 2, radius4, mPaint4);
}
四個圓就是四行代碼,怎麼樣,很簡單吧?
注意他們所用的畫筆是不太一樣的,這裡是比較容易忽視的地方。
最後來看看我糾結很久的畫虛線的操作,這裡真是把我卡了好一會兒,先看看代碼:
// 繪制虛線
private void canvasLine(Canvas canvas) {
int lineCount = 8;
for (int i = 0; i < lineCount; i++) {
path.moveTo(mWidth / 2, mHeight / 2);
path.lineTo(radius1, radius4);
PathEffect effects = new DashPathEffect(new float[] {
(float) (mWidth * 0.005), (float) (mWidth * 0.02),
(float) (mWidth * 0.005), (float) (mWidth * 0.02) }, 0);
mLinePaint.setPathEffect(effects);
canvas.drawPath(path, mLinePaint);
canvas.rotate(45, mWidth / 2, mHeight / 2);
}
}
按理說,繪制虛線,本質也是畫線,但是我一開始是用canvas.drawLine方法,但是沒有效果,網上查找了資料,才知道可能是版本的問題,於是我換用了path。
到了這個時候,全部效果已經出來了,那麼為了讓她動起來,我們還是要加點邏輯,我的思路是這樣的:
定義一個線程,然後通過改變扇形的開始角度來實現動畫的效果。
代碼如下:
class MyThread extends Thread {
@Override
public void run() {
while (true) {
if (running) {
SystemClock.sleep(200);
handler.sendEmptyMessage(2);
}
}
}
}
private boolean running = true;
public Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
synchronized (this) {
if (startAngle < 1) {
startAngle = 360;
} else {
startAngle--;
invalidate();
}
}
};
};
到了這裡,基本可以實現動畫效果了,為了讓控件更好的被外面所用,我們還像外面暴露了一些方法。
// 開啟動畫
public void setStartAngle() {
thread = new MyThread();
thread.start();
}
// 重新開啟動畫
public void startAnge() {
running = true;
}
// 暫停動畫
public void stopAnge() {
running = false;
}
// 是否在運動
public boolean isRunning() {
return running;
}
最後看看我在MainActivity.java的操作:
/**
* 小瓶蓋 2016年7月12日16:35:26
*
* @author 自定義View——仿Vivo i管家病毒掃描動畫效果
*
* 博客地址:http://blog.csdn.net/qq_25193681
*/
public class MainActivity extends ActionBarActivity {
private VirusKilling mVirusKilling;
float mVirusKillingValue = 360;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
mVirusKilling = (VirusKilling) findViewById(R.id.Virus_Killing);
tv = (TextView) findViewById(R.id.tv);
// 傳入TextView,改變下方的文字的時候用到
mVirusKilling.setTextView(tv);
// 第一次打開的時候就開始更新動畫
mVirusKilling.setStartAngle();
}
/**
* 點擊事件的處理
*
* @param v
*/
public void onStop(View v) {
TextView tv = (TextView) v;
if (mVirusKilling.isRunning()) {
tv.setText("繼續掃描");
mVirusKilling.stopAnge();
} else {
tv.setText("停止掃描");
mVirusKilling.startAnge();
}
}
@Override
protected void onPause() {
mVirusKilling.stopAnge();
super.onPause();
};
@Override
protected void onStart() {
mVirusKilling.startAnge();
super.onStart();
}
}
還有布局文件:
一個背景效果的代碼:
到現在,所有的效果都出來了,可能講的不是很詳細,但是代碼是沒有問題的,代碼也不多,多看幾次也很容易理解。
最後我把源碼貼上來:源碼地址
現在還是開始寫博客,技術可能不是很高端,但是日子還長,期待我們一起進步。
前言這篇文章是這個系列的開篇,作為移動開發者,開發的應用不免會對網絡進行訪問,雖然現在已經有很多的開源庫幫助我們可以輕而易舉的訪問網絡,但是我們仍要去了解網絡訪問的原理,
在Android開發中,我們常用的布局方式主要有LinearLayout、RelativeLayout、FrameLayout等,通過這些布局我們可以實現各種各樣的界面。
Android中JNI的作用,就是讓Java能夠去調用由C/C++實現的代碼,為了實現這個功能,需要用到Anrdoid提供的NDK工具包,在這裡不講如何配
前言 變態問題常有,今年特別多,,, - - # 今天遇到的這個非處理不可,不然沒法在HTC One S使用SearchView,其軟鍵盤不支持action設置