手机弹幕颜色异常,手机弹幕怎么变成了黑色

首页 > 实用技巧 > 作者:YD1662023-05-28 21:37:57

手机弹幕颜色异常,手机弹幕怎么变成了黑色(1)

之前在网上有看到过iOS的弹幕效果实现,搜了一下发现Android实现弹幕效果的帖子比较少,而且写得都不是很好理解,于是尝试自己做了一下,写成这篇博客,分享出来。

最终效果展示:

手机弹幕颜色异常,手机弹幕怎么变成了黑色(2)

实现思路:

1.自定义一个弹幕View,继承自TextView,专门用来显示一条弹幕

2.弹幕View能够自动从最右边匀速滚动到最左边

3.弹幕的颜色和大小设置为随机值

4.弹幕View的高度随机,区域在屏幕范围内

5.在Activity中循环定时加入自定义弹幕View,形成最后的弹幕

6.自定义文字资源,随机从文件资源中读取文字显示

详细过程:

1.改变应用属性为横屏,无标题栏,黑色背景在AndroidManifest.xml文件中,让MainActivity方向属性为landscape,并且加上主题设置

1 <activity android:name=".MainActivity"

2 android:screenOrientation="landscape"

3 android:theme="@style/AppTheme">

然后在styles.xml文件中,设置如下(parent是系统自动生成的,我用的版本比较新,可能大家的不一样,这个不要紧)

1 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBa r">

2 <item name="android:windowBackground">@color/black</item>

3 <item name="android:windowFullscreen">true</item>

4 <item name="android:windowNoTitle">true</item>

5 </style>

2.新建BarrageView

首先新建一个弹幕View,我取名为BarrageView,它继承自TextView。 需要为它实现两个构造方法和onDraw()方法。 在onDraw方法里我们绘制文字。

1 public class BarrageView extends TextView {

2 private Paint paint = new Paint(); //画布参数

3 4 public BarrageView(Context context) {

5 super(context); 6 init(); 7 }

8

9 public BarrageView(Context context, AttributeSet attrs) {

10 super(context, attrs);

11 init();

12 }

13 /**

14 * 初始化

15 */

16 protected void init() {}

17

18 @Override

19 protected void onDraw(Canvas canvas) { 20 paint.setTextSize(30); 21 paint.setColor(0xffffffff); //白色

22 canvas.drawText(getText(), 0, 30, paint);

23 }

24 }

PS:这里需要注意一点,就是y值我们没有设置为0而是30,是因为文字的坐标是从左下角开始算的,文字大小设为了30,y也要设为30文字才会刚刚好显示在屏幕的(0, 0)处。

我们把自定义View加到主布局中,因为需要从屏幕左侧滚动到右侧,我把宽高设置为屏幕宽高

1 <com.azz.azbarrage.BarrageView

2 android:layout_width="match_parent"

3 android:layout_height="match_parent"

4 android:text="hello"5 />

3.滚动动画

移动可以用Animation动画,但是我这里用的是线程重绘,只要能实现最终效果,都是可以的。在onDraw里面新建一个线程,该线程会一直运行,每次运行主函数时会对BarrageView的x值产生影响(减少)。

1 private int posX; //x坐标

2

3 class RollThread extends Thread {

4 @Override

5 public void run() {

6 while(true) {

7 //1.动画逻辑

8 animLogic();

9 //2.绘制图像

10 postInvalidate();

11 //3.延迟,不然会造成执行太快动画一闪而过

12 try {

13 Thread.sleep(30);

14 } catch (InterruptedException e) {

15 e.printStackTrace();

16 }

17 }

18 }

19 }

20

21 /**

22 * 动画逻辑处理

23 */

24 private void animLogic() {

25 posX -= 8;

26 }

可以看到线程里面就三步,1.把x坐标减少8像素,2.调用postInvalidate()方法重绘,该方法会自动调用onDraw()方法,3.停顿30毫秒,代码执行非常快,如果不停顿的话,我们甚至看不到滚动动画。

接下来我们把滚动线程加到上面的代码里去。

在加之前,需要思考一下,我们的posX第一次绘制时应该为屏幕宽,表示从屏幕最右边开始移动,然后调用滚动线程,直到滚出屏幕。

这里要提一下getWidth()方法,这个方法如果在构造函数里面调用,得到的是0,在onDraw()方法里面调用得到的是本view的宽度,这是因为自定义View的机制,要在调用过onMeasure()后才能得到自身的宽高。

我这里用getWindowVisibleDisplayFrame(rect);能在初始化时就得到屏幕宽高。

1 private int posX; //x坐标

2

3 private int windowWidth; //屏幕宽

4 private int windowHeight; //屏幕高

5

6 private RollThread rollThread; //滚动线程

7

8 /**

9 * 初始化

10 */

11 protected void init() {

12 //得到屏幕宽高

13 Rect rect = new Rect();

14 getWindowVisibleDisplayFrame(rect);

15 windowWidth = rect.width();

16 windowHeight = rect.height();

17

18 //设置x为屏幕宽

19 posX = windowWidth;

20 }

21

22 protected void onDraw(Canvas canvas) {

23 paint.setTextSize(30);

24 paint.setColor(0xffffffff);//白色

25 canvas.drawText(getText(), posX, 30, paint);

26

27 if (rollThread == null) {

28 rollThread = new RollThread();

29 rollThread.start();

30 }

31 }

现在的效果是这个样子:

手机弹幕颜色异常,手机弹幕怎么变成了黑色(3)

当弹幕从左边完全滚出时,其实线程还是在运行的,这样积累多了线程对系统负荷加大,我们需要加上判断,如果滚出了屏幕,线程也跳出while(true)循环,然后由系统自己回收。

1 class RollThread extends Thread {

2 @Override

3 public void run() {

4 while(true) {

5

6 ...

7

8 //关闭线程逻辑判断

9 if (needStopRollThread()) {

10 Log.i("azzz", getText() " -线程停止!");

11 break;

12 }

13 }

14 }

15 }

16

17 private boolean needStopRollThread() {

18 if (posX <= -paint.measureText(getText())) {

19 return true;

20 }

21 return false;

22 }

用paint的measureText(String text)方法来得到文字的宽度,进行判断是否需要退出线程循环。

4.随机大小和颜色

在初始化的时候,我们就给定一条弹幕一个随机的大小和颜色,使得每一条弹幕看起来都不一样。

1 private int textSize = 30; //字体大小

2 public static final int TEXT_MIN = 10;

3 public static final int TEXT_MAX = 60;

4 //字体颜色

5 private int color = 0xffffffff;

6

7 /**

8 * 初始化

9 */

10 protected void init() {

11 //1.设置文字大小

12 textSize = TEXT_MIN random.nextInt(TEXT_MAX - TEXT_MIN);

13 paint.setTextSize(textSize);

14

15 //2.设置文字颜色

16 color = Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256));

17 paint.setColor(color);

18

19 ...

20 }

21

22 protected void onDraw(Canvas canvas) {

23 //paint.setTextSize(30);

24 //paint.setColor(0xffffffff); //白色

25 canvas.drawText(getText(), posX, 30, paint);

26

27 ...

28 }

这里得到随机数用到的是random的nextInt(n)方法,其中值域是[0,n)。

把通过随机数生成的字体大小和颜色加到paint里,在onDraw()方法里面就能起效果了。别忘了把之前在onDraw()里设置的颜色和字体大小去掉。

5.随机高度

高度也是在init()方法通过随机数生成。

1 /**

2 * 初始化

3 */

4 protected void init() {

5 ...

6

7 //3.得到屏幕宽高

8 Rect rect = new Rect();

9 getWindowVisibleDisplayFrame(rect);

10 windowWidth = rect.width();

11 windowHeight = rect.height();

12

13 //4.设置x为屏幕宽

14 posX = windowWidth;

15

16 //5.设置y为屏幕高度内内随机,需要注意的是,文字是以左下角为起始点计算坐标的,所以要加上TextSize的大小

17 posY = textSize random.nextInt(windowHeight - textSize);

18 }

之前说过文字的坐标系是在左下角,当y为0时文字是看不到的,所以这里高度的初始化要写在文字大小的后面,在随机数前加上一个字体大小的高度,同时最大值也应该减去一个字体大小的高度,不然最大y为textSize windowHeight就超出了屏幕显示。

6.动态生成弹幕

单条弹幕属性差不多定义完了,现在我们去MainActivity中动态加入多条弹幕。

1 //两两弹幕之间的间隔时间

2 public static final int DELAY_TIME = 800;

3

4 @Override

5 protected void onCreate(Bundle savedInstanceState) {

6 ...

7

8 //设置宽高全屏

9 final ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

10

11 final Handler handler = new Handler();

12 Runnable createBarrageView = new Runnable() {

13 @Override

14 public void run() {

15 //新建一条弹幕,并设置文字

16 final BarrageView barrageView = new BarrageView(MainActivity.this);

17 barrageView.setText("你好");

18 addContentView(barrageView, lp);

19

20 //发送下一条消息

21 handler.postDelayed(this, DELAY_TIME);

22 }

23 };

24 handler.post(createBarrageView);

25 }

在Activity中有个addContentView(view, lp)方法能够新加view到根视图下。

lp动态设置为全屏的方法是new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);handler.postDelayed(this, DELAY_TIME);的意思是弹幕出来的时间不一致,这样就不会像兵列一样整齐地出来了。

7.自定义文字资源

我们可以在string.xml中自定义字符串数组<string-array>,然后再Activity中随机引用。

1 private Random random = new Random();

2

3 protected void onCreate(Bundle savedInstanceState) {

4 ...

5

6 //读取文字资源

7 final String[] texts = getResources().getStringArray(R.array.default_text_array);

8

9 Runnable createBarrageView = new Runnable() {

10 @Override

11 public void run() {

12 //新建一条弹幕,并设置文字

13 final BarrageView barrageView = new BarrageView(MainActivity.this);

14 barrageView.setText(texts[random.nextInt(texts.length)]); //随机设置文字

15 addContentView(barrageView, lp);

16

17 ..

18 }

19 };

20 ...

21 }

好了!再来一张效果:

手机弹幕颜色异常,手机弹幕怎么变成了黑色(4)

关于资源回收

在iOS中的UIView中有个方法叫removeFromSuperView,调用该方法就能达到资源回收的作用,在Android中没有这种方法,只能从父控件调用remove方法,我查了很久资料,也没查到是否这样做可以达到销毁View的作用,不过为此我还是在BarrageView中留了一个监听器,用来做销毁动作的。

1 /**

2 * 滚动结束接听器

3 */

4 interface OnRollEndListener {

5 void onRollEnd();

6 }

7

8 private OnRollEndListener mOnRollEndListener;

9

10 /**

11 * @param onRollEndListener 设置滚动结束监听器

12 */

13 public void setOnRollEndListener(OnRollEndListener onRollEndListener) {

14 this.mOnRollEndListener = onRollEndListener;

15 }

16

17 class RollThread extends Thread {

18 @Override

19 public void run() {

20 while(true) {

21 ...

22

23 //关闭线程逻辑判断

24 if (needStopRollThread()) {

25 Log.i("azzz", getText() " -线程停止!");

26 if (mOnRollEndListener != null) {

27 mOnRollEndListener.onRollEnd();

28 }

29 break;

30 }

31 }

32 }

33 }

可以在子控件中通过getParent()方法得到父控件,这样就可以直接在子控件中把自己从父控件中移除,达到回收资源的效果。

1 class RollThread extends Thread {

2 @Override

3 public void run() {

4 while(true) {

5 ...

6

7 //关闭线程逻辑判断

8 if (needStopRollThread()) {

9 ...

10

11 post(new Runnable() { //从父类中移除本view

12 @Override

13 public void run() {

14 ((ViewGroup) BarrageView.this.getParent()).removeView(BarrageView.this);

15 }

16 });

17 break;

18 } //if-end

19 } //while-end

20 } //run-end

21 } //RollThread-end

以上注意两点:

第一:一定要在主线程调用移除方法,因为只有主线程可以更改UI。View方法本身自带post(runnable)方法能够在主线程中运行。

第二:getParent()得到的返回类型是View,而View方法并没有remove(view)方法,所以只要强制转换成ViewGroup就可以了。

来源:csdn 作者:阿曌

关注微信boxuegu

加入高校老师俱乐部,天天学干货

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.