开通博客的初衷是为了督促自己记录,打开主页,惭愧啊。最近在学习的过程中,发现之前所掌握的内容又忘得一干二净。所以啊,记录真的很重。久未提笔,瞬间感觉小学的500字作文都要很困难。写作,记录脚步,锻炼思维,坚持吧!
准备工作之余开发一款金融相关App,哈哈,刚好切合自身知识和爱好。如果你打开手机里金融类应用,会发现它们都有一个特点,就是很多图、线条,很直观的向用户展示指数、涨跌等信息。所以,这篇文章就通过自定义“日落日出动画”效果复习一下自定义View的知识点,let go!
思路:
- 绘制日落时间到日出时间的圆弧
- 通过当前时间与总时间的比,计算出需要动态画弧的角度
- 通过属性动画获取当前角度,并通过角度计算出当前点的坐标
- 实时绘制
效果图:
ok,直接上代码了!
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SunAnimationView">
<attr name="circle_color" format="color"></attr>
<attr name="font_color" format="color"></attr>
<attr name="font_size" format="dimension"></attr>
<attr name="circle_radius" format="integer"></attr>
<attr name="current_color" format="color"></attr>
</declare-styleable>
</resources>
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.sunanimation.MainActivity">
<LinearLayout
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/time_et"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Set time here"/>
<Button
android:id="@+id/set_bt"
android:layout_marginLeft="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Begin" />
</LinearLayout>
<com.example.sunanimation.SunView
android:id="@+id/sun"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:circle_color = "#00BFFF"
app:font_color = "#00BFFF"
app:font_size = "20sp"
app:circle_radius = "400"
app:current_color = "#F5DEB3"/>
</LinearLayout>
MainActivity
package com.example.sunanimation;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private EditText mET;
private Button mBegin;
private SunView mSunView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mET = (EditText) findViewById(R.id.time_et);
mBegin = (Button) findViewById(R.id.set_bt);
mSunView = (SunView) findViewById(R.id.sun);
mBegin.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.set_bt:
String time = String.valueOf(mET.getText());
mSunView.setTime(time);
break;
}
}
}
自定义SunView
package com.example.sunanimation;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
/**
* Created by 阿正 on 2017/9/14 0014.
*/
public class SunView extends View {
private int mCircleColor;
private int mFontColor;
private int mFontSize;
private int mCricleRadius;
private int mCurrentCricleColor;
private Paint mPaint;
private RectF mRectF;
private WindowManager wm;
private int mWidth;
private int mTop;
private float mCurrentTime;//当前时间
private float mAngle;
private final static String START_TIME = "06:00";//日出时间
private final static String END_TIME = "20:00";//日落时间
private final static String TAG = "SunView";
private Bitmap mBitmap;
private float mPositionX;
private float mPositionY;
private final static float mIconRadius = 20;
public SunView(Context context) {
this(context,null);
}
public SunView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public SunView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
intView(context, attrs);
}
public void setTime(String time){
mCurrentTime = transTime(time);
int start = transTime(START_TIME);
int end = transTime(END_TIME);
mAngle = (mCurrentTime - start)/ (end - start) * 180;
Log.d(TAG,"mCurrentTime:" + mCurrentTime + " start:" + start + " end:" + end + " mAngle:" + mAngle);
startAnimation(0,mAngle,2000);
}
private void startAnimation(float startAngle, float currentAngle, int duration) {
ValueAnimator sunAnimator = ValueAnimator.ofFloat(startAngle, currentAngle);
sunAnimator.setDuration(duration);
sunAnimator.setTarget(currentAngle);
sunAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
//每次要绘制的圆弧角度
mAngle = (float) animation.getAnimatedValue();
invalidateView();
}
});
sunAnimator.start();
}
private void invalidateView() {
mPositionX = mWidth / 2 - (float) (mCricleRadius * Math.cos(mAngle * Math.PI / 180)) - mIconRadius;
mPositionY = mTop + mCricleRadius - (float) (mCricleRadius * Math.sin(mAngle * Math.PI / 180) + 15);
Log.d(TAG,"mPositionX:" + mPositionX + " mPositionY" + mPositionY);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
//画圆
drawCricle(canvas);
canvas.save();
//画文字
drawText(canvas);
//画太阳
drawSun(canvas);
//画太阳走过的圆弧
drawOld(canvas);
super.onDraw(canvas);
}
private void drawOld(Canvas canvas) {
mPaint.setColor(mCurrentCricleColor);
canvas.drawArc(mRectF,180,mAngle,false,mPaint);
}
private void drawSun(Canvas canvas) {
canvas.drawBitmap(mBitmap,mPositionX,mPositionY,mPaint);
}
private void drawText(Canvas canvas) {
mPaint.setColor(mFontColor);
mPaint.setTextSize(mFontSize);
canvas.drawText(START_TIME,mWidth/2 - mCricleRadius,mTop + mCricleRadius + 50,mPaint);
canvas.drawText(END_TIME,mWidth/2 + mCricleRadius - 150,mTop + mCricleRadius + 50,mPaint);
}
private void drawCricle(Canvas canvas) {
mRectF = new RectF(mWidth/2 - mCricleRadius, mTop, mWidth/2 + mCricleRadius, 2*mCricleRadius + mTop);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setDither(true); //防抖动
mPaint.setColor(mCircleColor);
canvas.drawArc(mRectF,180,180,true,mPaint);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, mWidth/2 - mCricleRadius, mTop, mWidth/2 + mCricleRadius, 2*mCricleRadius + mTop);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
mWidth = wm.getDefaultDisplay().getWidth();
mTop = wm.getDefaultDisplay().getHeight() / 6;
mPositionX = mWidth/2 - mCricleRadius - mIconRadius;
mPositionY = mCricleRadius + mTop - mIconRadius;
}
private void intView(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SunAnimationView);
mCircleColor = array.getColor(R.styleable.SunAnimationView_circle_color,
getContext().getResources().getColor(R.color.blue));
mCurrentCricleColor = array.getColor(R.styleable.SunAnimationView_current_color,
getContext().getResources().getColor(R.color.red));
mCricleRadius = array.getInteger(R.styleable.SunAnimationView_circle_radius,
200);
mFontSize = (int) array.getDimension(R.styleable.SunAnimationView_font_size,20);
mFontColor = array.getColor(R.styleable.SunAnimationView_font_color,
getContext().getResources().getColor(R.color.red));
array.recycle();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//压缩图片
BitmapFactory.Options newOpts = new BitmapFactory.Options();
newOpts.inSampleSize = 3;
mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.sun,newOpts);
}
//将时间转换为分钟
private int transTime(String time){
int value = 0;
if (time == null){
return value;
}
String[] s = time.split(":");
int hour = Integer.parseInt(s[0]);
int minute = Integer.parseInt(s[1]);
value = hour * 60 + minute;
return value;
}
}
以上就是代码实现了。需要说明的是,代码很多地方都可以在优化,比如可以动态设置日落日出时间,对输入时间格式进行错误判断等。由于主要是为了实现效果,这里就偷个懒了。
另外,参考了 http://blog.csdn.net/zoujian1990520/article/details/77862495 ,感谢!