[置頂](表示電量機能を加える)魅族、華為、小米電池の航続管理ソフトウェアを模倣し、動的な水の波紋が転がる円形の小球View
53781 ワード
一、効果図:
図1は、プロジェクトの効果を示す図です.
図2は、このView単独の効果図である.
二、使用方法:
1は、カスタムビューなので、attrs.xmlに次の属性定義を追加する必要があります.
3、カスタムViewを定義する:
コードは次の図のとおりです:詳細なコメントがあります:
波を動的にスクロールさせる方法については、スレッドを開き、正弦波曲線関数のFAI(横座標のオフセット量)を動的に変更すると、波がスクロールします.
========================================
ソースコードとクイック使用説明ドキュメントを添付し、
すべてのリソースファイルが含まれています.
http://download.csdn.net/detail/zxt0601/9421642
図1は、プロジェクトの効果を示す図です.
図2は、このView単独の効果図である.
二、使用方法:
1は、カスタムビューなので、attrs.xmlに次の属性定義を追加する必要があります.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="batteryLevel" format="float" />
<attr name="radius" format="integer" />
<attr name="flowRadius" format="dimension" />
<attr name="textColor" format="color" />
<attr name="textSize" format="dimension" />
<attr name="flowViewColor" format="color" />
<declare-styleable name="BatteryView">
<attr name="batteryLevel" />
<attr name="radius" />
<attr name="flowRadius" />
<attr name="textColor" />
<attr name="textSize" />
<attr name="flowViewColor"/>
</declare-styleable>
</resources>
2レイアウトファイルに導入:xmlns:zhangxutong="http://schemas.android.com/apk/res-auto"
<mcxtzhang.weixin521.help.BatteryView
android:background="@drawable/battery_view_bg_circle"
android:id="@+id/batteryView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="10dp"
zhangxutong:flowViewColor="#ffaaffaa"/>
以上のカスタム属性は、波が転がるボールの色をカスタマイズする必要がある場合はflowViewColorに入力します.そうしないと、デフォルトの属性を完全に使用できます.既定のアトリビュートは、図1のように半透明の白です.3、カスタムViewを定義する:
コードは次の図のとおりです:詳細なコメントがあります:
package mcxtzhang.weixin521.help;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import mcxtzhang.weixin521.R;
import mcxtzhang.weixin521.utils.TipsUtils;
/** * @author zhangxutong */ public class BatteryView extends View {
private Context mContext;
/** * */ private float level;
/** * */ private int radius;
/** * ( , -padding ) */ private int flowRadius;
/** * */ private int flowColor;
/** * */ private int textColor;
/** * */ private int textSize;
/** * X */ private float FAI;
/** * */ private float A;
/** * */ private float W;
/** * Y */ private float K;
//
/** * ( ) */ private Paint mPaint;
/** * , */ private Paint mTextPaint;
/** * Path */ private Path mPath;
/** * */ private PaintFlagsDrawFilter mDrawFilter;
/** * */ private PorterDuffXfermode mPorterDuffXfermode;
/** * */ private Bitmap mBitmap;
/** * */ private int mWidth;
/** * */ private int mHeight;
/** * */ private int mCenterX;
/** * */ private int mCenterY;
/** * */ private Rect mSrcRect;
/** * */ private Rect mDestRect;
/** * */ private Rect mTextRect;
/** * */ private StringBuilder sb;
/** * , */ interface OnLevelChangeListener {
int onLevelChange();
}
OnLevelChangeListener mOnLevelChangeListener;
public void setOnLevelChangeListener(
OnLevelChangeListener onLevelChangeListener) {
mOnLevelChangeListener = onLevelChangeListener;
}
/** * */ private BroadcastReceiver mBatteryChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
LogI("onReceiver BatteryChanged");
level = intent.getIntExtra("level", -1);
int curScale = intent.getIntExtra("scale", -1);
sb.delete(0, sb.length());
sb.append("" + (int) level * 100 / curScale).append("%");
// AK, , ,
setA();
//setK();
}
}
};
/** * Y K, , , ( ) */ private void setK() {
// K:-5~+5 , level :0~100
/*float k = 20f/100f;
K = k*level;*/
}
/** * A,A , level = 50 , , , 0 100 0 */ private void setA() {
//y = ax;
/*float k = 10f / 50f ;
A = level<50? k*level:-k*(level-100);*/
//y = ax2+bx+c ( 0,3) (50,10 )( 100,3)
if (level == 0 || level == 100) {
A = 0;
} else {
float a = -7f / 2500;
float b = -100 * a;
float c = 3;
A = (float) (a * Math.pow(level, 2) + b * level + c);
}
//LogI("A:"+A);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
LogI("onAttachedToWindow");
//onAttachedToWindow onDraw ,
mContext.registerReceiver(mBatteryChangeReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
LogI("onDetachedFromWindow");
// ,
mContext.unregisterReceiver(mBatteryChangeReceiver);
}
public BatteryView(Context context) {
this(context, null);
}
public BatteryView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BatteryView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
LogI("inital func");
init();
// attr
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.BatteryView, defStyleAttr, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.BatteryView_batteryLevel:
level = a.getFloat(attr, 0);
break;
case R.styleable.BatteryView_flowRadius:
flowRadius = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150, getResources().getDisplayMetrics()));
break;
case R.styleable.BatteryView_radius:
radius = a.getInt(attr, getWidth());
break;
case R.styleable.BatteryView_textColor:
textColor = a.getColor(attr, Color.WHITE);
break;
case R.styleable.BatteryView_textSize:
textSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
case R.styleable.BatteryView_flowViewColor:
TipsUtils.showToast(context,"flowViewColor");
flowColor = a.getColor(attr,0x88FFFFFF);
break;
}
}
a.recycle();
initPaint();
// , FAI, X ,
new Thread() {
@Override
public void run() {
while (true) {
changeFAI();
try {
Thread.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
postInvalidate();
}
}
private void changeFAI() {
FAI += 0.2;
}
}.start();
}
/** * */ private void init() {
level = 0;
radius = getWidth();
flowRadius = 150;
flowColor = 0x88FFFFFF;
FAI = 0;
A = 9;
W = 0.75f;
K = 0;
}
/** * */ private void initPaint(){
//
//
mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
// , DST, SRC, , DST
mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPath = new Path();
//
mPaint.setDither(true);
//
mPaint.setFilterBitmap(true);
//
mPaint.setColor(flowColor);
//
mBitmap = ((BitmapDrawable) getResources().getDrawable(
R.drawable.battery_view_bg_round)).getBitmap();
// Paint Rect
mTextPaint = new Paint();
// , , ,
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setTextAlign(Paint.Align.CENTER);
/*Typeface tf = Typeface.createFromAsset(mContext.getAssets(), "fonts/Roboto-Thin.ttf");
mTextPaint.setTypeface(tf);*/
mTextRect = new Rect();
sb = new StringBuilder();
}
//private int temp = 0;
@Override
protected void onDraw(Canvas canvas) {
//LogI("flowRadius" + flowRadius + " getPaddingRight:" + getPaddingRight());
//if(temp++==0)
//LogI("onDraw");
// canvas
canvas.setDrawFilter(mDrawFilter);
// ,
int sc = canvas.saveLayer(0 + getPaddingLeft(), 0 + getPaddingTop(), mWidth - getPaddingRight(), mHeight - getPaddingBottom(), null,
Canvas.ALL_SAVE_FLAG);
// path
mPath.reset();
/* level = 0;
setA();
//setK();
*/
// Y = A sin(wx+FAI)+k, Path.quadTo() ( ) , 。
for (int x = mDestRect.left; x < mDestRect.right; x++) {
float y = (float) (A * Math.sin(Math.PI / 250 * W * x + FAI) + K + (mDestRect.bottom - mDestRect.top)
* 1.0f / 100 * (100 - level));
if (x == mDestRect.left) {
mPath.moveTo(x, y);
}
mPath.quadTo(x, y, x + 1, y);
}
// Path ,
mPath.lineTo(mDestRect.right, mDestRect.bottom);
mPath.lineTo(mDestRect.left, mDestRect.bottom);
mPath.close();
// ( , ) (DST)
canvas.drawPath(mPath, mPaint);
// ( )
mPaint.setXfermode(mPorterDuffXfermode);
// (SRC)
canvas.drawBitmap(mBitmap, mSrcRect, mDestRect, mPaint);
// null
mPaint.setXfermode(null);
// bitmap, ( )
canvas.restoreToCount(sc);
//canvas.restore();
// , 。
/*mTextPaint.setTextSize(textSize);
mTextPaint.setColor(textColor);
mTextPaint.getTextBounds(sb.toString(), 0, sb.toString().length(), mTextRect);
//
canvas.drawText(sb.toString(), mCenterX, mCenterY,mTextPaint);
mTextPaint.setTextSize(24);*/
}
/** * onSizeChanged view , */ @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
LogI("onSizeChanged:" + getPaddingLeft());
mWidth = w;
mHeight = h;
mCenterX = w / 2;
mCenterY = h / 2;
mSrcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
/*mDestRect = new Rect(mCenterX - flowRadius, mCenterY - flowRadius,
mCenterX + flowRadius, mCenterY + flowRadius);*/
// View padding,
mDestRect = new Rect(0 + getPaddingLeft(), 0 + getPaddingTop(), mWidth - getPaddingRight(), mHeight - getPaddingBottom());
//mDestRect = new Rect(mWidth/2-flowRadius, mHeight/2-flowRadius,mWidth/2+flowRadius , mHeight/2+flowRadius);
}
// wrap_content, AT_MOST , wrap_content 150dp
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
LogI("onMeasure");
setMeasuredDimension(measuredWidth(widthMeasureSpec), measuredHeight(heightMeasureSpec));
}
private int measuredHeight(int heightMeasureSpec) {
// weight 150dp
int measuredHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150, getResources().getDisplayMetrics());
//
int specMode = MeasureSpec.getMode(heightMeasureSpec);
int specSize = MeasureSpec.getSize(heightMeasureSpec);
// Match_parent
if (MeasureSpec.EXACTLY == specMode) {
measuredHeight = specSize;
} else { //AT_MOST:wrap_content ,UNSPECIFIED 。 View
//
measuredHeight = Math.min(specSize, measuredHeight);
}
return measuredHeight;
}
private int measuredWidth(int widthMeasureSpec) {
int measuredWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150, getResources().getDisplayMetrics());
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if (MeasureSpec.EXACTLY == specMode) {
measuredWidth = specSize;
} else {
measuredWidth = Math.min(specSize, measuredWidth);
}
return measuredWidth;
}
private void LogI(String msg) {
android.util.Log.i("zhangxutong/BatteryView", "" + msg);
}
}
ポイントはonDrawでpath.quaTo()メソッドを用いて、ベッセル曲線(滑らかな曲線)を描き、正弦関数曲線図を描くことです.波の効果をシミュレートします.波を動的にスクロールさせる方法については、スレッドを開き、正弦波曲線関数のFAI(横座標のオフセット量)を動的に変更すると、波がスクロールします.
========================================
ソースコードとクイック使用説明ドキュメントを添付し、
すべてのリソースファイルが含まれています.
http://download.csdn.net/detail/zxt0601/9421642