編輯:關於Android編程
之前在新浪微博看到一個雷達效果,可以查看圖
先說說這個效果
1.程序默認提示點擊雷達,開始探索
2.當點擊雷達,提示正在探索周邊的人,同時展示雷達掃描效果,即雷達按鈕繞中心旋轉,展現波紋達到掃描效果,
3.當觸摸雷達時,雷達按鈕會縮小,然後手指離開時,雷達按鈕會縮回原位
4.最後如果掃描到周邊的人,卡片顯示周邊人的信息,如果沒有掃描到,則提示未能探索到周邊的人,請稍後再試,同時關閉掃描效果
這裡就不卡片展示周邊人這個效果,畢竟是仿制,我們只需要關注動畫效果,所以做個每兩秒執行掃描效果一周,執行3遍顯示為沒有搜索到周邊的人就行了
說說當中的技術點
1.首先是要熟悉canvas,paint,,其中canvas的畫圓,畫圖片(之所以要熟悉畫圖片,是因為我們有四張圖來顯示成按鈕,這四張圖從新浪微博的app裡獲取,估計它也是這麼干的),畫文字等要熟悉。
2.雷達按鈕繞中心旋轉,這就要涉及到矩陣了,因為雷達按鈕繞中心旋轉,實際上也是對圖片進行旋轉操作,這樣的話Matrix就比較合適了,至於旋轉的角度怎麼得到,用ValueAnimator比較合適,它可以在你設定的時間內,獲取到你設定的兩個值(這裡指旋轉角度,0,到360)之間的速率的變化值,然後刷新View的旋轉角度就可以達到旋轉的效果了
3.雷達按鈕的縮放,原理同旋轉,只不過我們關注的是縮放的比率而已
4.雷達的掃描效果,這個可以具體看看微博的效果,它是首先啟動灰色波紋效果,沒多久白色波紋效果與灰色效果同時抵達,就像波浪,後邊的波浪把前一次波浪沖抵,當到達一定程度的時候顯示灰色線波紋,達到波浪消失的效果,這裡灰色波浪與白色波浪的沖抵效果,我用到了ValueAnimator裡的Interpolator,其中分別為灰色波紋為減速DecelerateInterpolator,白色為AccelerateInterpolator,灰色線波紋為LinearInterpolator,一個減速,一個加速,達到追擊的效果,一個線性,達到逐漸消失的效果
來看看我們的效果圖,請查看附件:
gif錄制效果不是很好,大家會給大家提供鏈接,自行下載查看效果
現在上自定義源碼:
package com.RadarScanView.app;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
/**
* Created by Administrator on 2015/8/31.
*/
public class RadarScanView extends View {
//默認掃描圖標
private Bitmap mIconScanBitmap;
//掃描時圖標
private Bitmap mIconScaningBitmap;
//白色掃描圖
private Bitmap mScanBitmap;
//黑色掃描背景
private Bitmap mScanBackgroundBitmap;
//掃描按鈕區域
private Rect mButtonArea = new Rect();
//縮放矩陣
private Matrix mScaleMatrix = new Matrix();
//旋轉矩陣
private Matrix mRotateMatrix = new Matrix();
//掃描圖標旋轉動畫
private ValueAnimator mRotateAnimator = new ValueAnimator();
//手指點擊時白色掃描圖片縮小動畫
private ValueAnimator mScaleMinAnimator = new ValueAnimator();
//手指放開時白色掃描圖片放大動畫
private ValueAnimator mScaleMaxAnimator = new ValueAnimator();
//掃描波紋灰色線動畫
private ValueAnimator mOutGrayAnimator = new ValueAnimator();
//掃描波紋白色動畫
private ValueAnimator mInnerWhiteAnimator = new ValueAnimator();
//掃描波紋灰色動畫
private ValueAnimator mBlackAnimator = new ValueAnimator();
//畫筆
private Paint mOutGrayPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mInnerWhitePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mBlackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//掃描圖標旋轉角度
private float mRotateDegree;
//縮放比例,默認1:1
private float mScaleRatio = 1;
//設定自定義View半徑
private int mRadius;
//掃描波紋灰色線半徑
private float mOutGrayRadius = 0;
//掃描波紋白色部分半徑
private float mInnerWhiteRadius = 0;
//掃描波紋灰色部分半徑
private float mBlackRadius = 0;
//默認掃描文字提示
private String mTipText = "點擊雷達,開始探索";
//測量掃描文字提示邊界
private Rect mTextBound = new Rect();
//是否點擊按鈕,默認沒有點擊
private boolean isButtonClick = false;
public RadarScanView(Context context) {
this(context, null);
}
public RadarScanView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RadarScanView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//加載圖片
mIconScanBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_icon_scan);
mIconScaningBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_icon_scaning);
mScanBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_scan);
mScanBackgroundBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_scan_background);
//初始化畫筆
initViewPaint();
}
private void initViewPaint() {
mOutGrayPaint.setStrokeWidth(4f);
mOutGrayPaint.setColor(Color.rgb(228, 228, 228));
mOutGrayPaint.setDither(true);
mOutGrayPaint.setStyle(Paint.Style.STROKE);
mInnerWhitePaint.setStrokeWidth(1f);
mInnerWhitePaint.setColor(Color.rgb(241, 241, 241));
mInnerWhitePaint.setDither(true);
mInnerWhitePaint.setStyle(Paint.Style.FILL);
mBlackPaint.setStrokeWidth(1f);
mBlackPaint.setColor(Color.rgb(228, 228, 228));
mBlackPaint.setDither(true);
mBlackPaint.setStyle(Paint.Style.FILL);
mTextPaint.setTextSize(37f);
mTextPaint.setColor(Color.rgb(185, 185, 185));
mTextPaint.setDither(true);
mTextPaint.setStyle(Paint.Style.FILL);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
//當按鈕只有在圖片即按鈕區域內則認定為點擊,不作點擊
isButtonClick = false;
if (mButtonArea.contains((int) event.getX(), (int) event.getY())) {//手指按下,執行縮小動畫
if (!mScaleMinAnimator.isRunning() && !mScaleMaxAnimator.isRunning() && !mRotateAnimator.isRunning()) {//如果動畫正在執行則不執行動畫
isButtonClick = true;
//點擊了按鈕,啟動白色圖片縮小動畫
mScaleMinAnimator.start();
}
}
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (isButtonClick) {
//當點擊了按鈕,啟動白色圖片放大動畫與掃描圖片旋轉動畫
if (!mScaleMaxAnimator.isRunning()) {//如果動畫正在執行則不執行動畫
mScaleMaxAnimator.start();
}
if (!mRotateAnimator.isRunning()) {//如果動畫正在執行則不執行動畫
mRotateAnimator.start();
}
}
break;
}
return true;
}
private void initScaleMinAnimator() {
mScaleMinAnimator.setFloatValues(mRadius, mRadius * 0.87f);
mScaleMinAnimator.setDuration(500);
mScaleMinAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mScaleRatio = ((Float) animation.getAnimatedValue()) / mRadius;
invalidateView();
}
});
}
private void initScaleMaxAnimator() {
mScaleMaxAnimator.setFloatValues(mRadius * 0.87f, mRadius);
mScaleMaxAnimator.setDuration(500);
mScaleMaxAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mScaleRatio = ((Float) animation.getAnimatedValue()) / mRadius;
invalidateView();
}
});
}
private void initRoateAnimator() {
mRotateAnimator.setFloatValues(0, 360);
mRotateAnimator.setDuration(2000);
//重復三次,模仿正在掃描
mRotateAnimator.setRepeatCount(3);
mRotateAnimator.setInterpolator(new LinearInterpolator());
mRotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mRotateDegree = (Float) animation.getAnimatedValue();
invalidateView();
}
});
mRotateAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mTipText = "正在探索周邊的人...";
//旋轉動畫啟動後啟動掃描波紋動畫
mOutGrayAnimator.start();
mInnerWhiteAnimator.start();
mBlackAnimator.start();
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//取消掃描波紋動畫
mOutGrayAnimator.cancel();
mInnerWhiteAnimator.cancel();
mBlackAnimator.cancel();
//重置界面要素
mOutGrayRadius = 0;
mInnerWhiteRadius = 0;
mBlackRadius = 0;
mTipText = "未能探索到周邊的人,請稍後再試";
invalidateView();
}
});
}
private void initOutGrayAnimator() {
mOutGrayAnimator.setFloatValues(mBlackRadius, getMeasuredWidth() / 2);
mOutGrayAnimator.setDuration(1000);
mOutGrayAnimator.setRepeatCount(-1);
mOutGrayAnimator.setInterpolator(new LinearInterpolator());
mOutGrayAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mOutGrayRadius = (Float) animation.getAnimatedValue();
}
});
}
private void initInnerWhiteAnimator() {
mInnerWhiteAnimator.setFloatValues(0, getMeasuredWidth() / 3);
mInnerWhiteAnimator.setDuration(1000);
mInnerWhiteAnimator.setRepeatCount(-1);
mInnerWhiteAnimator.setInterpolator(new AccelerateInterpolator());
mInnerWhiteAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mInnerWhiteRadius = (Float) animation.getAnimatedValue();
}
});
}
private void initBlackAnimator() {
mBlackAnimator.setFloatValues(0, getMeasuredWidth() / 3);
mBlackAnimator.setDuration(1000);
mBlackAnimator.setRepeatCount(-1);
mBlackAnimator.setInterpolator(new DecelerateInterpolator());
mBlackAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mBlackRadius = (Float) animation.getAnimatedValue();
}
});
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//掃描按鈕區域,取自掃描灰色背景圖片區域即可
mButtonArea.set(getMeasuredWidth() / 2 - mScanBackgroundBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mScanBackgroundBitmap.getHeight() / 2, getMeasuredWidth() / 2 +
mScanBackgroundBitmap
.getWidth() / 2, getMeasuredHeight() / 2 + mScanBackgroundBitmap.getHeight() / 2);
//View半徑,取自View寬高最小者
mRadius = mScanBitmap.getWidth() / 2 > mScanBitmap.getHeight() / 2 ? mScanBitmap.getHeight() / 2 : mScanBitmap.getWidth() / 2;
//初始化動畫
initScaleMinAnimator();
initScaleMaxAnimator();
initRoateAnimator();
initBlackAnimator();
initInnerWhiteAnimator();
initOutGrayAnimator();
}
@Override
protected void onDraw(Canvas canvas) {
//繪制波紋
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mBlackRadius, mBlackPaint);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mInnerWhiteRadius, mInnerWhitePaint);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mOutGrayRadius, mOutGrayPaint);
//繪制背景
Bitmap mScanBgBitmap = getScanBackgroundBitmap();
if (mScanBgBitmap != null) {
canvas.drawBitmap(mScanBgBitmap, getMeasuredWidth() / 2 - mScanBgBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mScanBgBitmap.getHeight() / 2, new Paint(Paint
.ANTI_ALIAS_FLAG));
}
//繪制按鈕背景
Bitmap mButtonBgBitmap = getButtonBackgroundBitmap();
canvas.drawBitmap(mButtonBgBitmap, getMeasuredWidth() / 2 - mButtonBgBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mButtonBgBitmap.getHeight() / 2, new Paint(Paint.ANTI_ALIAS_FLAG));
//繪制掃描圖片
Bitmap mScanBitmap = getScanBitmap();
canvas.drawBitmap(mScanBitmap, getMeasuredWidth() / 2 - mScanBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mScanBitmap.getHeight() / 2, new Paint(Paint.ANTI_ALIAS_FLAG));
//繪制文本提示
mTextPaint.getTextBounds(mTipText, 0, mTipText.length(), mTextBound);
//此處50為文本與按鈕之間間隔,可以自己設定
canvas.drawText(mTipText, getMeasuredWidth() / 2 - mTextBound.width() / 2, getMeasuredHeight() / 2 + mScanBackgroundBitmap.getHeight() / 2 + mTextBound.height() + 50, mTextPaint);
}
//繪制白色按鈕背景,根據縮放矩陣與縮放比例,復制圖片達到手指點擊與手指放開時按鈕的縮小與放大效果
private Bitmap getButtonBackgroundBitmap() {
mScaleMatrix.reset();
mScaleMatrix.postScale(mScaleRatio, mScaleRatio);
return Bitmap.createBitmap(mScanBitmap, 0, 0, mScanBitmap.getWidth(), mScanBitmap.getHeight(), mScaleMatrix, true);
}
//判斷是否正在執行旋轉動畫,如果正在執行動畫,則取消灰色白淨
private Bitmap getScanBackgroundBitmap() {
if (mRotateAnimator.isRunning()) {
return null;
}
return mScanBackgroundBitmap;
}
//判斷是否正在執行動畫,如果正在執行,根據旋轉矩陣,與旋轉的角度復制掃描圖標,則實現圖標不斷旋轉,如果未執行,則返回未掃描圖片
private Bitmap getScanBitmap() {
if (mRotateAnimator.isRunning()) {
mRotateMatrix.reset();
mRotateMatrix.postRotate(mRotateDegree, mIconScaningBitmap.getWidth() / 2, mIconScaningBitmap.getHeight() / 2);
return Bitmap.createBitmap(mIconScaningBitmap, 0, 0, mIconScaningBitmap.getWidth(), mIconScaningBitmap.getHeight(), mRotateMatrix, true);
}
return mIconScanBitmap;
}
//刷新View
public void invalidateView() {
if (Looper.getMainLooper() == Looper.myLooper()) {
invalidate();
} else {
postInvalidate();
}
}
}
就整體感覺而言,個人感覺不是很好,不斷刷新View對內存,CPU的性能有很高的要求,如果大家有好的思路,請大家共享一下,一起學習吧
相比主頁鍵(HOME)和最近應用鍵(APP_SWITCH)的處理,返回鍵比較簡單,復寫onKeyDown就可以實現,如下:
前言在移動互聯網浪潮中,聯網APP已經把單機拍死在沙灘上,很多公司都希望自家應用能夠有一套帳號系統,可是許多用戶卻並不一定買賬:我憑啥注冊你家應用的帳號?微博,微信,QQ
裡面評論有很多人提到了這個問題,我也是其中一員,但是問遍了所有人,自己也發帖(http://bbs.csdn.net/topics/390769358) 尋
ExpandableListView是一個垂直滾動顯示兩級列表項的視圖,與ListView不同的是,它可以有兩層:每一層都能夠被獨立的展開並顯示其子項。好友QQ列表,可以