Android自定义View叶子旋转完整版(六)

上一篇实现多叶子飘动旋转,今天完成最后的功能。

1、添加右侧旋转枫叶

2、添加滑动条效果,显示百分比

3、修复叶子飘出边框问题

1、添加右侧旋转叶子

Bitmap turnBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.fengshan, null)).getBitmap();
 int turnLeafAngle = 0;
 private void setTurnLeaf(Canvas canvas) {
  Matrix matrix = new Matrix();
  turnLeafAngle = turnLeafAngle + 3;
  matrix.postTranslate((width - rightCircleWidth/2 - turnBitmap.getWidth()/2),
    (height - rightCircleWidth/2 - turnBitmap.getHeight()/2));
  matrix.postRotate(turnLeafAngle,
     width - rightCircleWidth/2 - turnBitmap.getWidth()/2 + turnBitmap.getWidth()/2,
     height - rightCircleWidth/2 - turnBitmap.getHeight()/2 + turnBitmap.getHeight()/2);
  canvas.drawBitmap(turnBitmap, matrix, new Paint());
 }

代码很明确,首先通过Matrix.postTranslate(float dx, float dy)把turnBitMap定位到最右侧圆圈

再通过Matrix.postRotate(float degress, float dx, float dy);设置旋转角度,每次角度+3°

其中degress为旋转角度,(dx,dy)为旋转中心点坐标

2、添加滑动效果

原理就是覆盖一层不同颜色的图层。根据当前百分比,分别画一个半圆,画一个正方形

a、定义一个圆形Rectf(为什么不是半圆?因为画圆弧的其实角度从水平线右侧开始)

progressArcRectf = new RectF(0, 0, height, height);

b、定义一个长方形Rectf,长方形x坐标起点即时圆形半径

progressRectf = new RectF(height/2,  0, width, height);

c、画出圆弧Canvas.drawArc(Rectf rectf, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

startAngle:起始角度,默认从右侧水平线开始

sweepAngle:为旋转的角度,顺时针旋转

useCenter:true只画出弧线,false则画出圆心到弧线的区域

//画滑动后的背景条
int currentProgressWidht = currentProgress * (width - borderWidth)/100;
if(currentProgressWidht < leftCircleWidth/2) {
  //angle取值范围0~90
  int angle = 90 * currentProgressWidht / (leftCircleWidth/2);
  // 起始的位置
  int startAngle = 180 - angle;
  // 扫过的角度
  int sweepAngle = 2 * angle;
  canvas.drawArc(progressArcRectf, startAngle, sweepAngle, false, progressBgPaint);
}else {
  //画左边半圆形滑过部分
  canvas.drawArc(progressArcRectf, 90, 180, false, progressBgPaint);
  progressRectf.left = borderWidth + leftCircleWidth/2;
  progressRectf.right = borderWidth + currentProgressWidht;
  //画中间滑过部分
  canvas.drawRect(progressRectf, progressBgPaint);
 }

给LeafView.java添加一个

 public void setCurrentProgress(int currentProgress) {
  this.currentProgress = currentProgress;

 }

3、修复叶子飘动范围

这个简单,就是设置叶子的rect坐标起点+边框距离

赋上所有代码

1、activity_leaf.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/content_leaf"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
  <RelativeLayout
   android:layout_width="226dp"
   android:layout_height="45dp">
    <com.zjcpo.t170313_countdowntimer.LeafView
     android:id="@+id/leafView"
     android:layout_width="226dp"
     android:layout_height="45dp"
     android:layout_centerHorizontal="true"
     />
  </RelativeLayout>
</RelativeLayout>

2、LeafView.java

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.jar.Attributes;

/**
 * Created by jiemiao.zhang on 2017-3-15.
 */
public class LeafView extends View {
 private String TAG = "--------LeafView";
 private Resources mResources;
 //背景图、叶子
 private Bitmap mLeafBitmap, bgBitmap, turnBitmap;
 //整个控件的宽度和高度
 private int width, height;
 //最外层边框宽度
 private int borderWidth;
 //右侧圆形直径
 private int rightCircleWidth;
 //左侧圆形直径
 private int leftCircleWidth;
 private Paint bgPaint;
 private RectF bgRect;
 private Rect bgDestRect;
 //进度条实时背景
 private Paint progressBgPaint;
 //进度条左侧半圆,进度条中间长方形部分Rect
 private RectF progressArcRectf, progressRectf;
 //当前百分比0~100
 private int currentProgress = 0;
 //存放叶子lsit
 private List<Leaf> leafList;
 //叶子的宽和高
 private int mLeafWidth, mLeafHeight;
 //叶子滑动一周的时间5秒
 private final static long cycleTime = 5000;
 //叶子数量
 private final static int leafNumber = 6;

 public LeafView(Context context, AttributeSet attrs) {
  super(context, attrs);
  mResources = getResources();
  mLeafBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.leaf, null)).getBitmap();
  mLeafWidth = mLeafBitmap.getWidth();
  mLeafHeight = mLeafBitmap.getHeight();
  turnBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.fengshan, null)).getBitmap();
  bgBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.leaf_kuang, null)).getBitmap();
  bgPaint = new Paint();
  bgPaint.setColor(mResources.getColor(R.color.bg_color));
  //进度条实时背景
  progressBgPaint = new Paint();
  progressBgPaint.setColor(mResources.getColor(R.color.progress_bg_color));
  //获取所有叶子的信息,放入list
  leafList = getLeafs(leafNumber);
 }

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = w;
  height = h;
  borderWidth = height * 10/64;
  rightCircleWidth = width * 62/303;
  leftCircleWidth = height - 2 * borderWidth;
  bgDestRect = new Rect(0, 0 , width, height);
  bgRect = new RectF(0, 0 , width, height);
  progressArcRectf = new RectF(borderWidth, borderWidth, height - borderWidth, height - borderWidth);
  progressRectf = new RectF(borderWidth+(height-2*borderWidth)/2, borderWidth,
          width-rightCircleWidth/2, height-borderWidth);

  Log.i("leftMarginWidth", (borderWidth + leftCircleWidth/2) + "");

 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  //画背景颜色到画布
  canvas.drawRect(bgRect, bgPaint);
  if(currentProgress <= 100) {
   //画叶子
   int size = leafList.size();
   for (int i=0; i<size; i++) {
    Leaf leaf = leafList.get(i);
    //获取叶子坐标
    getLocation(leaf);
    //获取叶子旋转角度
    getRotate(leaf);
    canvas.save();
    Matrix matrix = new Matrix();
    //设置滑动
    matrix.postTranslate(leaf.x, leaf.y);
    //设置旋转
    matrix.postRotate(leaf.rotateAngle, leaf.x + mLeafWidth / 2, leaf.y + mLeafHeight / 2);
    //添加叶子到画布
    canvas.drawBitmap(mLeafBitmap, matrix, new Paint());
    canvas.restore();

    //画滑动后的背景条
    int currentProgressWidht = currentProgress * (width - borderWidth - rightCircleWidth/2)/100;
    if(currentProgressWidht < leftCircleWidth/2) {
     //angle取值范围0~90
     int angle = 90 * currentProgressWidht / (leftCircleWidth/2);
     Log.i(TAG, "angle :" + angle);
     // 起始的位置
     int startAngle = 180 - angle;
     // 扫过的角度
     int sweepAngle = 2 * angle;
     canvas.drawArc(progressArcRectf, startAngle, sweepAngle, false, progressBgPaint);
    }else {
     //画左边半圆形滑过部分
     canvas.drawArc(progressArcRectf, 90, 180, false, progressBgPaint);
     progressRectf.left = borderWidth + leftCircleWidth/2;
     progressRectf.right = borderWidth + currentProgressWidht;
     //画中间滑过部分
     canvas.drawRect(progressRectf, progressBgPaint);
    }
   }

   //调用onDraw()重复滑动
   if(currentProgress < 100) {
    postInvalidate();
   }
  }

  //画背景图片到画布
  canvas.drawBitmap(bgBitmap, null, bgDestRect, null);
  //画右边选择风叶
  setTurnLeaf(canvas);
  //画百分比
  setText(canvas);
 }

 int turnLeafAngle = 0;
 private void setTurnLeaf(Canvas canvas) {
  Matrix matrix = new Matrix();
  turnLeafAngle = turnLeafAngle + 3;
  matrix.postTranslate((width - rightCircleWidth/2 - turnBitmap.getWidth()/2),
       (height - rightCircleWidth/2 - turnBitmap.getHeight()/2));
  matrix.postRotate(turnLeafAngle,
       width - rightCircleWidth/2 - turnBitmap.getWidth()/2 + turnBitmap.getWidth()/2,
       height - rightCircleWidth/2 - turnBitmap.getHeight()/2 + turnBitmap.getHeight()/2);
  canvas.drawBitmap(turnBitmap, matrix, new Paint());
 }

 //显示百分比数字,大于3%开始显示,到50%停止滑动
 private void setText(Canvas canvas) {
  Paint paintText = new Paint();
  paintText.setColor(Color.WHITE);
  paintText.setTextSize(30);
  int textX = currentProgress * width / 100;
  textX = currentProgress < 50 ? (currentProgress * width / 100) : (width/2);
  if(currentProgress > 3) {
   canvas.drawText(currentProgress + "%", textX, height/2 + 10,paintText);
  }
 }

 //获取每片叶子在XY轴上的滑动值
 private void getLocation(Leaf leaf) {
  float betweenTime = leaf.startTime - System.currentTimeMillis();
  //周期结束再加一个cycleTime
  if(betweenTime < 0) {
   leaf.startTime = System.currentTimeMillis() + cycleTime + new Random().nextInt((int) (cycleTime));
   betweenTime = cycleTime;
  }

  //通过时间差计算出叶子的坐标
  float fraction = (float) betweenTime / cycleTime;
  float x = (int)(width * fraction);
  //防止叶子飘出边框
  leaf.x = x < borderWidth ? borderWidth : x;
  float w = (float) ((float) 2 * Math.PI / width);
  int y = (int) (18 * Math.sin(w * x)) + (height-mLeafHeight)/2;
  //防止叶子飘出边框
  y = y > (height - borderWidth) ? (height - borderWidth) : y;
  y = y < borderWidth ? borderWidth : y;
  leaf.y = y;
 }

 //获取每片叶子的旋转角度
 private void getRotate(Leaf leaf) {
  float scale = ((leaf.startTime - System.currentTimeMillis())%cycleTime)/ (float)cycleTime;
  int rotate = (int)(scale * 360);
  leaf.rotateAngle = rotate;
 }

 private class Leaf {
  // 叶子的坐标
  float x, y;
  // 旋转角度
  int rotateAngle;
  // 起始时间(ms)
  long startTime;
 }

 private List<Leaf> getLeafs(int leafSize) {
  List<Leaf> list = new LinkedList<Leaf>();
  for (int i=0; i<leafSize; i++) {

   list.add(getLeaf());
  }
  return list;
 }

 //使叶子初始时间有间隔
 int addTime;
 private Leaf getLeaf() {
  Random random = new Random();
  Leaf leaf = new Leaf();
  leaf.rotateAngle = random.nextInt(360);
  addTime += random.nextInt((int) (cycleTime));
  leaf.startTime = System.currentTimeMillis() + cycleTime + addTime;
  return leaf;
 }

 public void setCurrentProgress(int currentProgress) {
  this.currentProgress = currentProgress;
 }
}
3、LeafActivity.java

public class LeafActivity extends Activity {
 private LeafView leafView;
 private int mProgress = 0;
 Handler mHandler = new Handler() {
  public void handleMessage(Message msg) {
   if (mProgress < 40) {
    mProgress += 1;
    // 随机800ms以内刷新一次
    mHandler.sendEmptyMessageDelayed(1,
      new Random().nextInt(800));
    leafView.setCurrentProgress(mProgress);
   } else {
    mProgress += 1;
    // 随机1200ms以内刷新一次
    mHandler.sendEmptyMessageDelayed(1,
      new Random().nextInt(100));
    leafView.setCurrentProgress(mProgress);

   }

  };
 };

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_leaf);
  leafView = (LeafView) findViewById(R.id.leafView);
  mHandler.sendEmptyMessageDelayed(1, 3000);
 }
}

最后再看下效果

总结

看过前5篇的很好理解,用到的技术点之前都讲到了。这篇主要就是几个百分比函数的计算。

比如设置半圆时弧度如何计算,圆弧对应的百分比,滑动区域长方形的起点坐标计算,去掉边框后的坐标计算

画半圆必须要有一个完整圆形Rect,因为drawArc()从右侧半径水平起始角度,顺时针。然功能要求我们从左侧圆形开始画,所以要通过一个算法,假如当前百分比为4%,需要画30°的圆弧,那么起始角度为165°=180°-15°,画出角度30%

通过matrix.postRotate()实现旋转功能时,必须加上当前view的坐标及二分之一长宽

需要图片等信息的可以从下面的Github地址下载,不过原文比较复杂

参考 https://github.com/Ajian-studio/GALeafLoading

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义View简易折线图控件(二)

    继续练习自定义View,这次带来的是简易折线图,支持坐标点点击监听,效果如下: 画坐标轴.画刻度.画点.连线..x.y轴的数据范围是写死的 1 <= x <= 7 ,1 <= y <= 70 ..写活的话涉及到坐标轴刻度的动态计算.坐标点的坐标修改,想想就头大,这里只练习自定义View. 1.在res/values文件夹下新建attrs.xml文件,编写自定义属性: <?xml version="1.0" encoding="utf-8"

  • Android自定义View绘制的方法及过程(二)

    上一篇<Android 自定义View(一) Paint.Rect.Canvas介绍>讲了最基础的如何自定义一个View,以及View用到的一些工具类.下面讲下View绘制的方法及过程 public class MyView extends View { private String TAG = "--------MyView"; private int width, height; public MyView(Context context, AttributeSet a

  • Android自定义View设定到FrameLayout布局中实现多组件显示的方法 分享

    如果想在自定义的View上面显示Button 等View组件需要完成如下任务 1.在自定义View的类中覆盖父类的构造(注意是2个参数的) 复制代码 代码如下: public class MyView2 extends View{ public MyView2(Context context,AttributeSet att) {super(context,att); } public void onDraw(Canvas c) { // 这里绘制你要的内容 } } 2.定义布局文件 复制代码

  • Android自定义View实现飘动的叶子效果(三)

    上一篇对自定义View及一些方法有所了解,下面做一个简单的叶子飘动的例子 主要技术点 1.添加背景图片canvas.drawBitmap() 2.Matrix动画类 3.Matrix添加到画布上 步骤 1.添加黄色背景颜色 public LeafView(Context context, AttributeSet attrs) { super(context, attrs); bgPaint = new Paint(); bgPaint.setColor(mResources.getColor(

  • Android自定义View实现多片叶子旋转滑动(五)

    上一篇<Android 自定义View(四) 叶子飘动+旋转效果>实现了单片叶子的滑动及旋转,下面实现多片叶子的滑动旋转功能 实现思路比较简单,就是添加一个叶子Leaf类,储存每片叶子的信息, 然后随机产生叶子的坐标及旋转角度,最后实时获取每片叶子信息,添加到画布中 1.Leaf.java 叶子类 private class Leaf { // 叶子的坐标 float x, y; // 旋转角度 int rotateAngle; // 起始时间(ms) long startTime; } 2.

  • Android自定义View中Paint、Rect、Canvas介绍(一)

    自定义View对于新手而言貌似是一个很复杂的东西.格式,各函数的意义.对于大神经常忘记各函数及一些参数的具体写法及意义,刚好在做一个风车效果,把过程及遇到的问题都写下来 1.如何自定义一个View public class LeafView extends View { private String TAG = "--------LeafView"; public LeafView(Context context, AttributeSet attrs) { super(context

  • Android自定义View实现广告信息上下滚动效果

    先看看效果: 实现代码: public class ScrollBanner extends LinearLayout { private TextView mBannerTV1; private TextView mBannerTV2; private Handler handler; private boolean isShow; private int startY1, endY1, startY2, endY2; private Runnable runnable; private Li

  • Android自定义View圆形进度条控件(三)

    继续练习自定义View,这次带来的圆形进度条控件与之前的圆形百分比控件大同小异,这次涉及到了渐变渲染以及画布旋转等知识点,效果如下: 虽然步骤类似,但是我还是要写,毕竟基础的东西就是要多练 1.在res/values文件夹下新建attrs.xml文件,编写自定义属性: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="Circ

  • Android自定义View实现叶子飘动旋转效果(四)

    上一篇实现了叶子飘动功能,<Android自定义叶子飘动> 现在实现旋转效果 要实现这个效果,要在之前的功能上添加2个功能 1.通过matrix.postTranslate(int x, int y)在添加在Y轴上滑动 2.通过matrix.postRotate(float degrees, float px, float py)实现叶子旋转 代码实现 1.获取Y坐标 private float getMatrixY() { float w = (float) ((float) 2 * Mat

  • Android 自定义View的使用介绍

    在项目开发中,可能系统自带的一些widget不能满足我们的需求,这时就需要自定义View. 通过查看系统中的常用widget如Button,TextView,EditText,他们都继承自View,所以我们在继承自定义View的时候也自然的需要继承View.1.首先新建一个类LView继承自View 复制代码 代码如下: public class LView extends View { private Paint paint; public LView(Context context) {  

随机推荐