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

Android自定义View实现直播点赞特效

程序员文章站 2022-04-09 16:56:39
由于开发的需要,需要开发类似直播点赞特效的需求,于是自定义view来实现这种效果案例图:自定义viewimport android.animation.animator;import android....

由于开发的需要,需要开发类似直播点赞特效的需求,于是自定义view来实现这种效果

案例图:

Android自定义View实现直播点赞特效

自定义view

import android.animation.animator;
import android.animation.animatorset;
import android.animation.objectanimator;
import android.animation.typeevaluator;
import android.animation.valueanimator;
import android.content.context;
import android.graphics.pointf;
import android.graphics.drawable.drawable;
import android.util.attributeset;
import android.view.view;
import android.view.animation.acceleratedecelerateinterpolator;
import android.view.animation.accelerateinterpolator;
import android.view.animation.decelerateinterpolator;
import android.view.animation.interpolator;
import android.view.animation.linearinterpolator;
import android.widget.imageview;
import android.widget.relativelayout;
import com.xinrui.ndkapp.r;
import java.util.random;

/**
 * created by liuyong
 * data: 2017/8/8
 * github:https://github.com/mrallright
 * 直播点赞view
 */

public class givepraiseview extends relativelayout {
  private relativelayout.layoutparams layoutparams;//图片布局参数
  private pointf mpointf0, mpointf1, mpointf2, mpointf3;//通过3阶贝塞尔曲线控制图片的移动轨迹
  private int mscreenwidth, mscreenheight;//屏幕宽高
  private drawable[] mimagedrawables;//加载点赞红心图片,红黄蓝
  private int mdrawablewidth, mdrawableheight;//图片的宽高
  private random mrandom = new random();
  private int count = 0;
  private interpolator[] interpolators = new interpolator[4];

  public givepraiseview(context context) {
    super(context);
    init();
  }

  public givepraiseview(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
    init();
  }

  public givepraiseview(context context, attributeset attrs) {
    super(context, attrs);
    init();
  }

  @override
  protected void onsizechanged(int w, int h, int oldw, int oldh) {
    super.onsizechanged(w, h, oldw, oldh);
    mscreenheight = h;
    mscreenwidth = w;
  }

  //初始化drawable,layoutparams
  private void init() {
    mimagedrawables = new drawable[4];
    mimagedrawables[0] = getresources().getdrawable(r.drawable.pl_blue);
    mimagedrawables[1] = getresources().getdrawable(r.drawable.pl_red);
    mimagedrawables[2] = getresources().getdrawable(r.drawable.pl_yellow);
    mimagedrawables[3] = getresources().getdrawable(r.drawable.pl_red);
    // 插值器
    interpolators[0] = new acceleratedecelerateinterpolator(); // 在动画开始与结束的地方速率改变比较慢,在中间的时候加速
    interpolators[1] = new accelerateinterpolator(); // 在动画开始的地方速率改变比较慢,然后开始加速
    interpolators[2] = new decelerateinterpolator(); // 在动画开始的地方快然后慢
    interpolators[3] = new linearinterpolator(); // 以常量速率改变
    mdrawablewidth = mimagedrawables[0].getintrinsicwidth();
    mdrawableheight = mimagedrawables[0].getintrinsicheight();
    layoutparams = new layoutparams(50, 50);
    layoutparams.addrule(align_parent_bottom, true);
    layoutparams.addrule(align_parent_right, true);
    layoutparams.setmargins(0, 0, 60, 60);//放置在屏幕的右下角
    //这里为了演示我们现在布局初始化的时候,放置一个imageview,颜色随机,设置点击屏幕出现点赞效果
    imageview iv = new imageview(getcontext());
    iv.setlayoutparams(layoutparams);
    iv.setimagedrawable(mimagedrawables[0]);
    addview(iv);
    this.setonclicklistener(new onclicklistener() {
      @override
      public void onclick(view v) {
        for(int i=0;i<10;i++) {
          addgivepraiseimg(count);
          count++;
          if (count == 4) count = 0;
        }
      }
    });
  }

  //点击图片是添加imageview到布局中,并添加动画
  private void addgivepraiseimg(int count) {
    final imageview givepraiseimg = new imageview(getcontext());
    givepraiseimg.setlayoutparams(layoutparams);
    givepraiseimg.setimagedrawable(mimagedrawables[count]);
    addview(givepraiseimg);
    addanimator(givepraiseimg);//添加动画效果,动画分两部分,第一部分是产生图片时缩放和透明度,第二部是移动图片再进行透明度变化
  }

  private void addanimator(final imageview imageview) {
    //点击的时候,让图片经过放大,缩放效果,之后再开始沿着贝塞尔曲线的轨迹移动
    objectanimator alpha = objectanimator.offloat(imageview, "alpha", 0.3f, 1f);
    objectanimator scalex = objectanimator.offloat(imageview, "scalex", 0.2f, 1f);
    objectanimator scaley = objectanimator.offloat(imageview, "scaley", 0.2f, 1f);
    animatorset set = new animatorset();
    set.setduration(100);
    set.playtogether(alpha, scalex, scaley);
    set.settarget(imageview);
    set.addlistener(new animator.animatorlistener() {
      @override
      public void onanimationstart(animator animation) {

      }

      @override
      public void onanimationend(animator animation) {
        //设置贝塞尔曲线移动效果
        valueanimator va = getbzieranimator(imageview);//第二部分动画
        va.start();
      }

      @override
      public void onanimationcancel(animator animation) {

      }

      @override
      public void onanimationrepeat(animator animation) {

      }
    });
    set.start();
  }

  //初始化贝塞尔曲线的4个点
  private void initpointf() {
    mpointf0 = new pointf(mscreenwidth - 60 - 50, mscreenheight - 60 - 50);//起点是初始化时的点
    mpointf1 = new pointf(mrandom.nextint(mscreenwidth), mrandom.nextint((int) mpointf0.y));//第一个控制点必须要在起始点的上方
    mpointf2 = new pointf(mrandom.nextint(mscreenwidth), mrandom.nextint((int) mpointf1.y));//第二个控制点必须在第一个点的上方
    mpointf3 = new pointf(mrandom.nextint(mscreenwidth), -50);//终点在屏幕的最顶部0-图片的高度
  }


  /**
   *  自定义估值器计算图片移动的轨迹
   *  计算公式参考贝塞尔曲线3阶计算公式
   *  自定义估值器的方法可百度搜索
   *  其中估值器定义返回的结果为pointf
   */
  public class bezierevaluator implements typeevaluator<pointf> {
    private pointf pointf1, pointf2;

    public bezierevaluator(pointf p1, pointf p2) {
      this.pointf1 = p1;
      this.pointf2 = p2;
    }

    @override
    public pointf evaluate(float t, pointf p0, pointf p3) {
      pointf point = new pointf();
      point.x = p0.x * (1 - t) * (1 - t) * (1 - t) //
          + 3 * pointf1.x * t * (1 - t) * (1 - t)//
          + 3 * pointf2.x * t * t * (1 - t)//
          + p3.x * t * t * t;//

      point.y = p0.y * (1 - t) * (1 - t) * (1 - t) //
          + 3 * pointf1.y * t * (1 - t) * (1 - t)//
          + 3 * pointf2.y * t * t * (1 - t)//
          + p3.y * t * t * t;//
      return point;
    }
  }

  private valueanimator getvalueanimator(final imageview imageview) {
    initpointf();
    bezierevaluator bezierevaluator = new bezierevaluator(mpointf1, mpointf2);
    valueanimator valueanimator = valueanimator.ofobject(bezierevaluator, mpointf0, mpointf3);
    valueanimator.setduration(3000);
    valueanimator.settarget(imageview);
    valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
      @override
      public void onanimationupdate(valueanimator animation) {
        //改变imageview位置实现移动效果
        pointf point = (pointf) animation.getanimatedvalue();
        imageview.setx(point.x);
        imageview.sety(point.y);
        imageview.setalpha(1 - animation.getanimatedfraction());
        //动画结束移除imageview
        if (animation.getanimatedfraction() >= 1) {
          removeview(imageview);
        }
      }
    });
    return valueanimator;
  }

  /**
   * 贝塞尔动画
   * */
  private valueanimator getbzieranimator(final imageview iv) {
    // todo auto-generated method stub
    pointf[] pointfs = getpointfs(iv); // 4个点的坐标
    bezierevaluator evaluator = new bezierevaluator(pointfs[1], pointfs[2]);
    valueanimator valueanim = valueanimator.ofobject(evaluator, pointfs[0], pointfs[3]);
    valueanim.addupdatelistener(new valueanimator.animatorupdatelistener() {

      @override
      public void onanimationupdate(valueanimator animation) {
        // todo auto-generated method stub
        pointf p = (pointf) animation.getanimatedvalue();
        iv.setx(p.x);
        iv.sety(p.y);
        iv.setalpha(1- animation.getanimatedfraction()); // 透明度
        //动画结束移除imageview
        if (animation.getanimatedfraction() >= 1) {
          removeview(iv);
        }
      }
    });
    valueanim.settarget(iv);
    valueanim.setduration(3000);
    valueanim.setinterpolator(interpolators[new random().nextint(4)]);
    return valueanim;
  }

  private pointf[] getpointfs(imageview iv) {
    // todo auto-generated method stub
    pointf[] pointfs = new pointf[4];
    pointfs[0] = new pointf(); // p0
    pointfs[0].x = (mscreenwidth- layoutparams.width)/ 2;
    pointfs[0].y = mscreenheight - layoutparams.height;

    pointfs[1] = new pointf(); // p1
    pointfs[1].x = new random().nextint(mscreenwidth);
    pointfs[1].y = new random().nextint(mscreenheight /2) + mscreenheight / 2 + layoutparams.height;

    pointfs[2] = new pointf(); // p2
    pointfs[2].x = new random().nextint(mscreenwidth);
    pointfs[2].y = new random().nextint(mscreenheight /2);

    pointfs[3] = new pointf(); // p3
    pointfs[3].x = new random().nextint(mscreenwidth);
    pointfs[3].y = 0;
    return pointfs;
  }
}

2.givepraise_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="@android:color/darker_gray">
  <com.xinrui.ndkapp.view.givepraiseview
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
  <!--<com.xinrui.ndkapp.view.lovelayout-->
    <!--android:layout_width="match_parent"-->
    <!--android:layout_height="match_parent"/>-->
</relativelayout>

3.activity 部分代码

import android.app.activity;
import android.os.bundle;

public class givepraiseactivity extends activity {
  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.givepraise_layout);
  }
}

4.估值器的运算

Android自定义View实现直播点赞特效

p0坐标:x坐标((布局的宽-心形图片宽)除以2),y坐标(布局的高 -心形图片高),这样获得的是顶部部水平中心点的坐标。
p1坐标:x坐标(横坐标中的随机位置),y坐标(布局一半的高度 加上 0到一半高度范围内的随机坐标+心形的高度的一半)。这样取到的横坐标是在布局宽度之内的随机坐标,纵坐标为整个路径高度中部以上的随机坐标。
p2坐标:与p1类似,横坐标是在布局宽度之内的随机坐标,纵坐标为整个路径高度中部以下的随机坐标。
p3坐标:控件底部中心点
知道4个坐标了,那么就可以开始计算路径

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