实现APP弹幕功能——Android

首先我觉得这边文章写的很详细,并且每一个步骤都有注释,代码功底比较差的我也建议进来看看。



送给大家一句话:志不强者智不达,言不信者行不果。——墨翟




首先我们要整理一下思绪我们大概需要实现哪个细节板块呢。

我们最直观的看来,弹幕就是从右往左出现到消失。我们要实现这个动画,弹幕的大小,颜色,出现方式,加速,弹幕的不重叠(这个我想了好久还没有实现,有实现方法可以联系下我)。

我们先来了解一下等会程序里面会用到的相关知识点,等会看代码会更轻松一点。

[java]  view plain  copy
  1. <span style="font-size:14px;"><span style="font-size:14px;">/*getHeight跟getMeasureHeight的区别 
  2.          * 实际上在当屏幕可以包裹内容的时候,他们的值相等,只有当view超出屏幕后,才能看出他们的区别: 
  3.          * getMeasuredHeight()是实际View的大小,与屏幕无关,而getHeight的大小此时则是屏幕的大小。 
  4.          * 当超出屏幕后, getMeasuredHeight() 等于 getHeight()加上屏幕之外没有显示的大小  
  5.          *  
  6.          * */</span></span>  

[java]  view plain  copy
  1. <span style="font-size:14px;"><span style="font-size:14px;">/Activity生命周期中,onStart, onResume, onCreate都不是真正visible的时间点,真正的visible时间点是onWindowFocusChanged()函数被执行时。  
  2.     //当你屏幕的焦点发生变化时候,想要操作什么也完全可以在这个方法里面执行  
  3.         // Interpolator 被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速),decelerated(减速),repeated(重复),bounced(弹跳)等。  
  4.         /*  
  5.          * AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速  
  6.           AccelerateInterpolator  在动画开始的地方速率改变比较慢,然后开始加速  
  7.               AnticipateInterpolator 开始的时候向后然后向前甩  
  8.           AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值  
  9.               BounceInterpolator   动画结束的时候弹起  
  10.             CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线  
  11.           DecelerateInterpolator 在动画开始的地方快然后慢  
  12.             LinearInterpolator   以常量速率改变  
  13.             OvershootInterpolator    向前甩一定值后再回到原来位置  
  14.   
  15.          * fillBefore是指动画结束时画面停留在此动画的第一帧;  
  16.            fillAfter是指动画结束是画面停留在此动画的最后一帧。  
  17.        Java代码设置如下:  
  18.       /*****动画结束时,停留在最后一帧*********  
  19.           setFillAfter(true);  
  20.           setFillBefore(false);    
  21.     /*****动画结束时,停留在第一帧*********  
  22.        setFillAfter(false);  
  23.         setFillBefore(true);    
  24.          *</span></span>  

下面我们就来看一下弹幕实现的效果。

\

弹幕会出现重叠,这个问题还未解决

让我们开始看代码结构吧。

\

我们字体颜色的xml都写在了colors.xml中了,BarrageItem里面存放着我们的一些变量,而核心代码都在View中

BraagetItem.Java

[java]  view plain  copy
  1. <span style="font-size:14px;"><span style="font-size:14px;">package com.example.bibibibibibibibi;  
  2.   
  3. import android.widget.TextView;  
  4.   
  5. import android.widget.TextView;  
  6.   
  7. /** 
  8.  * Created by lixueyong on 16/2/19. 
  9.  */  
  10. public class BarrageItem {  
  11.         public TextView textView;//文本框  
  12.     public int textColor;//文本颜色  
  13.     public String text;//文本对象  
  14.     public int textSize;//文本的大小  
  15.     public int moveSpeed;//移动速度  
  16.     public int verticalPos;//垂直方向显示的位置  
  17.     public int textMeasuredWidth;//字体显示占据的宽度  
  18. }</span></span>  

在BarrageItem里面处理了弹幕的速度,大小,颜色,动画,等事件 在这个文件中 我注释的内容是我对弹幕重叠的操作代码,但是除了问题,有兴趣的可以看一下

[java]  view plain  copy
  1. <span style="font-size:14px">package com.example.bibibibibibibibi;  
  2. import android.content.Context;  
  3. import android.graphics.Color;  
  4. import android.graphics.Rect;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.text.TextPaint;  
  8. import android.util.AttributeSet;  
  9. import android.view.animation.AccelerateDecelerateInterpolator;  
  10. import android.view.animation.Animation;  
  11. import android.view.animation.TranslateAnimation;  
  12. import android.widget.RelativeLayout;  
  13. import android.widget.TextView;  
  14. import java.util.HashSet;  
  15. import java.util.Random;  
  16. import java.util.Set;  
  17. import com.example.bibibibibibibibi.R.integer;  
  18. /** 
  19.  * Created by nzx on 16/5/30. 
  20.  */  
  21. public class BarrageView extends RelativeLayout {  
  22.     private Context mContext;  
  23.     private BarrageHandler mHandler = new BarrageHandler();  
  24.     private Random random = new Random(System.currentTimeMillis());//System.currentTimeMillis()产生一个当前的毫秒  
  25.     private static final long BARRAGE_GAP_MIN_DURATION = 1000;//两个弹幕的最小间隔时间  
  26.     private static final long BARRAGE_GAP_MAX_DURATION = 2000;//两个弹幕的最大间隔时间  
  27.     private int maxSpeed = 10000;//速度,ms  
  28.     private int minSpeed = 5000;//速度,ms  
  29.     private int maxSize = 30;//文字大小,dp  
  30.     private int minSize = 15;//文字大小,dp  
  31.     private int totalHeight = 0;//整个的高度  
  32.     private int lineHeight = 0;//每一行弹幕的高度  
  33.     private int totalLine = 0;//弹幕的行数  
  34.     private String[] itemText = {"大头死变态""老圩人最屌了""唉这把中单是火男,难玩了""大头是傻子""世界上最长的路是套路""英雄联盟最强的是补丁",  
  35.             "我不会轻易的go die""嘿嘿""加班加班"};  
  36.     private int textCount;//文本的组数  
  37.   //private  RelativeLayout Rparams;  
  38. //    private List itemList = new ArrayList();  
  39. //实现RelativeLayout的重写的构造方法。  
  40.    /* 
  41.     * //content 上下文  
  42.     //AttributeSet 属性集 
  43.     //defStyleAttr 预设样式属性集 
  44.     //defStyleRes 预设样式资源属性集 
  45.     *  
  46.     * */  
  47.     public BarrageView(Context context) {  
  48.         this(context, null);  
  49.     }  
  50.   
  51.     public BarrageView(Context context, AttributeSet attrs) {  
  52.         this(context, attrs, 0);  
  53.     }  
  54.   
  55.     public BarrageView(Context context, AttributeSet attrs, int defStyleAttr) {  
  56.         super(context, attrs, defStyleAttr);  
  57.         mContext = context;  
  58.         init();  
  59.     }  
  60.   
  61.     private void init() {  
  62.         textCount = itemText.length;  
  63.         int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random());  
  64.         mHandler.sendEmptyMessageDelayed(0, duration);  
  65.     }  
  66.   
  67.     @Override  
  68.     //Activity生命周期中,onStart, onResume, onCreate都不是真正visible的时间点,真正的visible时间点是onWindowFocusChanged()函数被执行时。  
  69.     //当你屏幕的焦点发生变化时候,想要操作什么也完全可以在这个方法里面执行。  
  70.     public void onWindowFocusChanged(boolean hasWindowFocus) {  
  71.         super.onWindowFocusChanged(hasWindowFocus);  
  72.         totalHeight = getMeasuredHeight();  
  73.         /*getHeight跟getMeasureHeight的区别 
  74.          * 实际上在当屏幕可以包裹内容的时候,他们的值相等,只有当view超出屏幕后,才能看出他们的区别: 
  75.          * getMeasuredHeight()是实际View的大小,与屏幕无关,而getHeight的大小此时则是屏幕的大小。 
  76.          * 当超出屏幕后, getMeasuredHeight() 等于 getHeight()加上屏幕之外没有显示的大小  
  77.          *  
  78.          * */  
  79.         //获取每一行弹幕的最大高度  
  80.         lineHeight = getLineHeight();  
  81.         //我们整个弹幕的高度view/每一行的最大弹幕高度=  
  82.         totalLine = totalHeight / lineHeight;  
  83.     }  
  84.   
  85.     private void generateItem() {  
  86.         BarrageItem item = new BarrageItem();  
  87.         //把我们的每行弹幕的行数顺序跟弹幕进行一个随机  
  88.         String tx = itemText[(int) (Math.random() * textCount)];  
  89.         //随机弹幕大小  
  90.         int sz = (int) (minSize + (maxSize - minSize) * Math.random());  
  91.         item.textView = new TextView(mContext);  
  92.         item.textView.setText(tx);  
  93.         item.textView.setTextSize(sz);  
  94.         item.textView.setTextColor(Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256)));  
  95.          //这里我们需要传入三个参数 文本对象,文字行数跟大小  
  96.         item.textMeasuredWidth=(int) getTextWidth(item, tx, sz);  
  97.         //这是设置弹幕移动速度,实现有快有慢的感觉  
  98.         item.moveSpeed = (int) (minSpeed + (maxSpeed - minSpeed) * Math.random());  
  99.           
  100.        //这里为了实现一个弹幕循环播放的项目,在我们实际中看情况而定  
  101.         if (totalLine == 0) {  
  102.             totalHeight = getMeasuredHeight();  
  103.             lineHeight = getLineHeight();  
  104.             totalLine = totalHeight / lineHeight;  
  105.         }  
  106.           
  107.         //弹幕在y轴上出现的位置  
  108.         item.verticalPos = random.nextInt(totalLine) * lineHeight;  
  109. //        itemList.add(item);  
  110.         showBarrageItem(item);  
  111.     }  
  112.     
  113.     private void showBarrageItem(final BarrageItem item) {  
  114. //paddingLeft是设置布局里面的内容左边的距离,这样我们这就可以让这个弹幕的textview完全消失  
  115.         int leftMargin = this.getRight() - this.getLeft() - this.getPaddingLeft();  
  116. //这里我们通过动态的方式去设置一些我们布局的属性。  
  117. //        int verticalMargin = getRandomTopMargin();  
  118. //        item.textView.setTag(verticalMargin);  
  119.   
  120.         LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
  121.     params.addRule(RelativeLayout.ALIGN_PARENT_TOP);  
  122.         params.topMargin = item.verticalPos;  
  123.         this.addView(item.textView, params);  
  124.           
  125.         Animation anim = generateTranslateAnim(item, leftMargin);  
  126.         anim.setAnimationListener(new Animation.AnimationListener() {  
  127.             @Override  
  128.             public void onAnimationStart(Animation animation) {  
  129.   
  130.            }  
  131.             @Override  
  132.             //当我们动画结束的时候,清除该条弹幕  
  133.             public void onAnimationEnd(Animation animation) {  
  134.                 item.textView.clearAnimation();  
  135.                 BarrageView.this.removeView(item.textView);  
  136.             }  
  137.   
  138.             @Override  
  139.             //动画被取消的时候出发  
  140.             public void onAnimationRepeat(Animation animation) {  
  141.   
  142.             }  
  143.         });  
  144.         item.textView.startAnimation(anim);  
  145.     }  
  146.       
  147.   //  
  148.     private TranslateAnimation generateTranslateAnim(BarrageItem item, int leftMargin) {  
  149.         //这里我们有四个参数(动画开始的x点,结束点,开始y轴点,结束的y点)  
  150.         TranslateAnimation anim = new TranslateAnimation(leftMargin, -item.textMeasuredWidth, 00);  
  151.          //我们设置动画的持续时间,弹幕移动多久,我们就持续多久动画  
  152.         anim.setDuration(item.moveSpeed);  
  153.         // Interpolator 被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速),decelerated(减速),repeated(重复),bounced(弹跳)等。  
  154.         /* 
  155.          * AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速 
  156.           AccelerateInterpolator  在动画开始的地方速率改变比较慢,然后开始加速 
  157.               AnticipateInterpolator 开始的时候向后然后向前甩 
  158.           AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值 
  159.               BounceInterpolator   动画结束的时候弹起 
  160.             CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线 
  161.           DecelerateInterpolator 在动画开始的地方快然后慢 
  162.             LinearInterpolator   以常量速率改变 
  163.             OvershootInterpolator    向前甩一定值后再回到原来位置 
  164.          * */  
  165.         anim.setInterpolator(new AccelerateDecelerateInterpolator());  
  166.         /* 
  167.          * fillBefore是指动画结束时画面停留在此动画的第一帧; 
  168.            fillAfter是指动画结束是画面停留在此动画的最后一帧。 
  169.        Java代码设置如下: 
  170.       /*****动画结束时,停留在最后一帧********* 
  171.           setFillAfter(true); 
  172.           setFillBefore(false);   
  173.     /*****动画结束时,停留在第一帧********* 
  174.        setFillAfter(false); 
  175.         setFillBefore(true);   
  176.          *  
  177.          * */  
  178.         anim.setFillAfter(true);  
  179.         return anim;  
  180.     }  
  181.   
  182.     /** 
  183.      * 计算TextView中字符串的长度 
  184.      * 
  185.      * @param text 要计算的字符串 
  186.      * @param Size 字体大小 
  187.      * @return TextView中字符串的长度 
  188.      */  
  189.     //因为我们的弹幕包裹在一个矩形中  
  190.     public float getTextWidth(BarrageItem item, String text, float Size) {  
  191.         Rect bounds = new Rect();  
  192.         TextPaint paint;  
  193.         paint = item.textView.getPaint();  
  194.         //这里参数是获取文本对象,开始的长度,结束的长度,我们绘制好的矩形框  
  195.         paint.getTextBounds(text, 0, text.length(), bounds);  
  196.         return bounds.width();  
  197.     }  
  198.      
  199.   
  200.     /** 
  201.      * 获得每一行弹幕的最大高度 
  202.      * 
  203.      * @return 
  204.      */  
  205.     private int getLineHeight() {  
  206.         BarrageItem item = new BarrageItem();  
  207.         String tx = itemText[0];  
  208.         item.textView = new TextView(mContext);  
  209.         item.textView.setText(tx);  
  210.         item.textView.setTextSize(maxSize);  
  211.         Rect bounds = new Rect();  
  212.         TextPaint paint;  
  213.         paint = item.textView.getPaint();  
  214.         paint.getTextBounds(tx, 0, tx.length(), bounds);  
  215.         return bounds.height();  
  216.     }  
  217.   
  218.     class BarrageHandler extends Handler {  
  219.         @Override  
  220.         public void handleMessage(Message msg) {  
  221.             super.handleMessage(msg);  
  222.             generateItem();  
  223.             //每个弹幕产生的间隔时间随机  
  224.             int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random());  
  225.             //多个消息可以使用同一个handler, 通过what不同区分不同的消息来源, 从而获取消息内容  
  226.             this.sendEmptyMessageDelayed(0, duration);  
  227.         }  
  228.     }  
  229.         //记录一下当前在显示弹幕的高度,避免弹幕出现重叠  
  230.     private Set existMarginValues = new HashSet<>();  
  231.     private int linesCount;  
  232. //    private int getRandomTopMargin()  
  233. //    {  
  234. //      //计算弹幕的空间高度  
  235. //      if(totalLine==0)  
  236. //      {  
  237. //          totalLine=Rparams.getBottom()-Rparams.getTop()-Rparams.getPaddingTop()  
  238. //                  -Rparams.getPaddingBottom();  
  239. //          if (totalHeight==0) {  
  240. //               totalHeight = getMeasuredHeight();  
  241. //                  lineHeight = getLineHeight();  
  242. //                  totalLine = totalHeight / lineHeight;  
  243. //          }  
  244. //          //检查重叠  
  245. //            while (true) {  
  246. //                int randomIndex =  (int) (Math.random() * linesCount);  
  247. //                int marginValue = (int) (randomIndex * (totalLine / linesCount));  
  248. //       
  249. //                if (!existMarginValues.contains(marginValue)) {  
  250. //                    existMarginValues.add(marginValue);  
  251. //                    return marginValue;  
  252. //                }  
  253. //            }  
  254. //  
  255. //            
  256.       
  257.           
  258.    // }  
  259.   
  260. }  
  261. </span>  

BarrageActivity.java

在这个类里面我们可以去进行一些事件,但是我这里没有去处理,大家按自己的需求来。

[java]  view plain  copy
  1. <span style="font-size:14px">package com.example.bibibibibibibibi;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5.   
  6. /** 
  7.  * Created by lixueyong on 16/2/19. 
  8.  */  
  9. public class BarrageActivity extends Activity {  
  10.   
  11.     @Override  
  12.     protected void onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.         setContentView(R.layout.activity_barrage);  
  15.     }  
  16. }</span><span style="font-size:14px">  
  17. </span>  

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值