技术标签: 安卓Android弹幕效果实现 Android弹幕功能实现 安卓SurfaceView实现弹幕效果 Android弹幕 安卓Android弹幕
安卓Android弹幕功能实现-使用SurfaceView |
---|
先来一张效果图
安卓Android弹幕实现-思路 |
---|
每一条弹幕其实都是一个文本,用drawText即可绘制,我们可以建立一个集合用来存储弹幕,动态改变弹幕的X轴,即可移动弹幕,当弹幕移出屏幕时,再进行销毁操作。
下面我们一步一步来实现弹幕效果: |
---|
第一步:新建mSurfaceView类,继承SurfaceView,并实现SurfaceHolder.Callback以及Runnable接口 |
---|
public class mSurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable{ @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { Flag=false; } @Override public void run() { } }
第二步:定义如下成员变量,并实现构造方法: |
---|
//用于标注线程是否继续 private boolean Flag=true; //SurfaceHolder SurfaceHolder surfaceHolder; //弹幕的集合 public List<mText> Barrages=new ArrayList<>(); //用于随机生成弹幕的Y轴坐标 Random random=new Random(); public mSurfaceView(Context context) { super(context); } public mSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); surfaceHolder=getHolder(); surfaceHolder.addCallback(this); //设置背景透明 this.setZOrderOnTop(true); surfaceHolder.setFormat(PixelFormat.TRANSLUCENT); } public mSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
前面说过,每一条弹幕都需要存入集合,绘制的时候再遍历集合取出里面的弹幕进行绘制,那下面第三步:添加弹幕类 |
---|
public class mText { //文字 private String text; //文字大小 private float size; //文字颜色 private Integer color; //文字X轴 private float x; //文字Y轴 private float y; //文字移动速度 private int speed; //画笔 private Paint paint; public String getText() { return text; } public void setText(String text) { this.text = text; } public float getSize() { return size; } public void setSize(float size) { this.size = size; } public Integer getColor() { return color; } public void setColor(Integer color) { this.color = color; } public float getX() { return x; } public void setX(float x) { this.x = x; } public float getY() { return y; } public void setY(float y) { this.y = y; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public Paint getPaint() { return paint; } public void setPaint(Paint paint) { this.paint = paint; } }
疑问: |
---|
其实大家可能注意到,上方弹幕类中,定义有文字内容、文字颜色、文字大小等成员变量,既然有颜色、大小等,那我们为什么还有定义画笔呢?
其实画笔可以传一个null,考虑到有时候仅靠颜色和大小可能无法完全满足我们的需求,所以我们可以定义一个画笔,更方便扩展,如果传过来了画笔,则直接用画笔进行绘制,如果画笔为null,则用默认的画笔,通过类中的颜色、大小等进行绘制。
既然已经定义弹幕类,那肯定也需要添加弹幕到弹幕集合的方法,第四步:定义add方法: |
---|
//添加弹幕 public void add(mText mText) { if (mText.getX()==0){ mText.setX(getWidth()); } if (mText.getY()==0){ int i=getHeight(); if (i>0){ mText.setY(random.nextInt(i)); } } Barrages.add(mText); }
大家看到,上面有对x和y轴参数的非空判断,这样做的目的是当我们在调用add方法时,可以不给mText类的x、y赋值,节省我们的时间,如果检测到y轴为0,就随机指定一个y轴,如果x轴为0,则默认从屏幕的最右侧开始。
截止目前,准备工作已经做完,下面我们开始绘制: |
---|
@Override public void run() { Canvas canvas=null; mText mtext = null; Paint paint=null; while (Flag){ //如果集合为0,则跳过本次循环 if (Barrages.size()==0){ continue; } try { //获取画布 canvas=surfaceHolder.lockCanvas(); //清空画布 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); }catch (Exception e){ e.printStackTrace(); break; } //遍历弹幕集合 for (int i = 0; i < Barrages.size(); i++) { mtext=Barrages.get(i); //如果弹幕类中的画笔为空,则在此处定义画笔,根据弹幕类中的颜色大小等进行绘制 if (mtext.getPaint()==null){ if (paint==null){ paint=new Paint(); } paint.setColor(mtext.getColor()); paint.setTextSize(mtext.getSize()); paint.setStrokeWidth(3f); }else { //如果弹幕类中的画笔不为空,则直接用弹幕类中的画笔绘制 paint=mtext.getPaint(); } //绘制文本 canvas.drawText(mtext.getText(),mtext.getX(),mtext.getY(),paint); //如果弹幕超出屏幕左侧,则从集合中删除,否则进行移动 if (mtext.getX()<-getWidth()){ Barrages.remove(mtext); }else { mtext.setX((mtext.getX()-mtext.getSpeed())); } } //解锁画布 surfaceHolder.unlockCanvasAndPost(canvas); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } }
大功告成,我们来看看如何调用,只需要执行msurfaceview.add方法添加弹幕即可。 |
---|
public class MainActivity extends Activity { private mSurfaceView msurfaceView; private VideoView videoView; private Random random=new Random(); private String[] strings={ "6666","厉害了我的国","加油!!!","欢迎收看晨间新闻","程序猿很苦逼","我能怎么办,我也很无奈"}; private int[] colors={Color.WHITE,Color.MAGENTA,Color.CYAN,Color.RED,Color.BLUE,Color.GREEN}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { msurfaceView = findViewById(R.id.msv); videoView = findViewById(R.id.mvv); //申请播放网络视频权限 per(); startVideo(); //动态生成弹幕 startBarrage(); } //生成弹幕 private void startBarrage() { new Thread(new Runnable() { @Override public void run() { while (true){ mText mText=new mText(); mText.setText(strings[random.nextInt(strings.length)]); mText.setSpeed(3); mText.setColor(colors[random.nextInt(colors.length)]); mText.setSize(40); msurfaceView.add(mText); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } //申请视频播放权限 private void per() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //检查是否有了权限 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { } else { //没有权限即动态申请 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1); } } } //开始播放视频 private void startVideo() { String url1 = "http://flashmedia.eastday.com/newdate/news/2016-11/shznews1125-19.mp4"; Uri uri=Uri.parse(url1); //设置视频控制器 videoView.setMediaController(new MediaController(this)); //设置视频路径 videoView.setVideoURI(uri); //开始播放视频 videoView.start(); }
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <VideoView android:id="@+id/mvv" android:layout_width="match_parent" android:layout_height="match_parent" /> <barrage.surfaceview.com.surfaceviewbarragedemo.mSurfaceView android:id="@+id/msv" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
github完整代码地址:安卓Android弹幕效果实现
PS:如有不正确的地方欢迎指出!
转载请注明:https://blog.csdn.net/weixin_41549915/article/details/80306476
文章浏览阅读1.3w次。转载自 http://www.miui.com/thread-2003672-1-1.html 当手机在刷错包或者误修改删除系统文件后会出现无法开机或者是移动定制(联通合约机)版想刷标准版,这时就会用到线刷,首先就是安装线刷驱动。 在XP和win7上线刷是比较方便的,用那个驱动自动安装版,直接就可以安装好,完成线刷。不过现在也有好多机友换成了win8/8.1系统,再使用这个_mt65驱动
文章浏览阅读1k次。SonarQube是一个代码质量管理平台,可以扫描监测代码并给出质量评价及修改建议,通过插件机制支持25+中开发语言,可以很容易与gradle\maven\jenkins等工具进行集成,是非常流行的代码质量管控平台。通CheckStyle、findbugs等工具定位不同,SonarQube定位于平台,有完善的管理机制及强大的管理页面,并通过插件支持checkstyle及findbugs等既有的流..._sonar的客户端区别
文章浏览阅读3.4k次,点赞2次,收藏27次。神经图灵机是LSTM、GRU的改进版本,本质上依然包含一个外部记忆结构、可对记忆进行读写操作,主要针对读写操作进行了改进,或者说提出了一种新的读写操作思路。神经图灵机之所以叫这个名字是因为它通过深度学习模型模拟了图灵机,但是我觉得如果先去介绍图灵机的概念,就会搞得很混乱,所以这里主要从神经图灵机改进了LSTM的哪些方面入手进行讲解,同时,由于模型的结构比较复杂,为了让思路更清晰,这次也会分开几..._神经图灵机方法改进
文章浏览阅读2.8k次。一、模型迭代方法机器学习模型在实际应用的场景,通常要根据新增的数据下进行模型的迭代,常见的模型迭代方法有以下几种:1、全量数据重新训练一个模型,直接合并历史训练数据与新增的数据,模型直接离线学习全量数据,学习得到一个全新的模型。优缺点:这也是实际最为常见的模型迭代方式,通常模型效果也是最好的,但这样模型迭代比较耗时,资源耗费比较多,实时性较差,特别是在大数据场景更为困难;2、模型融合的方法,将旧模..._模型迭代
文章浏览阅读2.3k次。1、前言上传图片一般采用异步上传的方式,但是异步上传带来不好的地方,就如果图片有改变或者删除,图片服务器端就会造成浪费。所以有时候就会和参数同步提交。笔者喜欢base64图片一起上传,但是图片过多时就会出现数据丢失等异常。因为tomcat的post请求默认是2M的长度限制。2、解决办法有两种:① 修改tomcat的servel.xml的配置文件,设置 maxPostSize=..._base64可以装换zip吗
文章浏览阅读1k次,点赞17次,收藏22次。Opencv自然场景文本识别系统(源码&教程)_opencv自然场景实时识别文字
文章浏览阅读1.3k次。拷贝虚拟机文件时间比较长,因为虚拟机 flat 文件很大,所以要等。脚本完成后,以复制虚拟机文件夹。将以下脚本内容写入文件。_exsi6.7快速克隆centos
文章浏览阅读2k次。本文主要实现基于二度好友的推荐。数学公式参考于:http://blog.csdn.net/qq_14950717/article/details/52197565测试数据为自己随手画的关系图把图片整理成文本信息如下:a b c d e f yb c a f gc a b dd c a e h q re f h d af e a b gg h f bh e g i di j m n ..._本关任务:使用 spark core 知识完成 " 好友推荐 " 的程序。
文章浏览阅读367次。南京大学高级程序设计期末复习总结,c++面向对象编程_南京大学高级程序设计
文章浏览阅读3.1k次,点赞2次,收藏12次。实现朴素贝叶斯分类器,并且根据李航《统计机器学习》第四章提供的数据训练与测试,结果与书中一致分别实现了朴素贝叶斯以及带有laplace平滑的朴素贝叶斯%书中例题实现朴素贝叶斯%特征1的取值集合A1=[1;2;3];%特征2的取值集合A2=[4;5;6];%S M LAValues={A1;A2};%Y的取值集合YValue=[-1;1];%数据集和T=[ 1,4,-1;..._朴素贝叶斯 matlab训练和测试输出
文章浏览阅读1.6k次。Markdown 文本换行_markdowntext 换行
文章浏览阅读6.7w次,点赞2次,收藏37次。win10 2016长期服务版激活错误解决方法:打开“注册表编辑器”;(Windows + R然后输入Regedit)修改SkipRearm的值为1:(在HKEY_LOCAL_MACHINE–》SOFTWARE–》Microsoft–》Windows NT–》CurrentVersion–》SoftwareProtectionPlatform里面,将SkipRearm的值修改为1)重..._错误: 0xc0000022 在运行 microsoft windows 非核心版本的计算机上,运行“slui.ex