欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-01-09 14:27:50
上一篇实现多叶子飘动旋转,今天完成最后的功能。 1、添加右侧旋转枫叶 2、添加滑动条效果,显示百分比 3、修复叶子飘出边框问题 1、添加右侧旋转叶子...

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

1、添加右侧旋转枫叶

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

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

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

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、添加滑动效果

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

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

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);
 }
}

最后再看下效果

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

总结

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

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

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

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

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

参考 https://github.com/ajian-studio/galeafloading

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