在线直播源码,自定义气泡效果(BubbleView)
在线直播源码,自定义气泡效果(BubbleView)
代码如下:
1 | <br>package com.example.myapplication;<br>import android.content.Context;<br>import android.graphics.BlurMaskFilter;<br>import android.graphics.Canvas;<br>import android.graphics.Color;<br>import android.graphics.Paint;<br>import android.graphics.RectF;<br>import android.util.AttributeSet;<br>import android.util.Log;<br>import android.util.TypedValue;<br>import android.view.View;<br>import androidx.annotation.Nullable;<br>import java.util.ArrayList;<br>import java.util.List;<br>import java.util.Random;<br> public class BubbleView extends View {<br> private int mBubbleMaxRadius = 15; // 气泡最大半径 px<br> private int mBubbleMinRadius = 8; // 气泡最小半径 px<br> private int mBubbleMaxSize = 50; // 气泡数量<br> private int mBubbleRefreshTime = 50; // 刷新间隔<br> private int mBubbleMaxSpeedY = 2; // 气泡速度<br> private int mBubbleMaxSpeedX = 4; // 气泡速度<br> private int mBubbleAlpha = 128; // 气泡画笔<br> private float mContentWidth; // 瓶子宽度<br> private float mContentHeight; // 瓶子高度<br> private RectF mContentRectF; // 实际可用内容区域<br> private Paint mBubblePaint; // 气泡画笔<br> public BubbleView(Context context) {<br> this(context, null);<br> }<br> public BubbleView(Context context, @Nullable AttributeSet attrs) {<br> this(context, attrs, 0);<br> }<br> public BubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {<br> super(context, attrs, defStyleAttr);<br> mContentWidth = dp2px(130);<br> mContentHeight = dp2px(260);<br> mBubblePaint = new Paint();<br> mBubblePaint.setColor(Color.GREEN);<br> mBubblePaint.setAlpha(mBubbleAlpha);<br> }<br> @Override<br> protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {<br> super.onMeasure(widthMeasureSpec, heightMeasureSpec);<br> int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);<br> int widthSize = MeasureSpec.getSize(widthMeasureSpec);<br> int heightSize = MeasureSpec.getSize(heightMeasureSpec);<br> int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);<br> int width;<br> int height;<br> if (widthSpecMode == MeasureSpec.EXACTLY || widthSpecMode == MeasureSpec.AT_MOST) {<br> width = widthSize;<br> mContentWidth = width;<br> } else {<br> width = (int) mContentWidth;<br> }<br> if (heightSpecMode == MeasureSpec.EXACTLY || heightSpecMode == MeasureSpec.AT_MOST) {<br> height = heightSize;<br> mContentHeight = height;<br> } else {<br> height = (int) mContentHeight;<br> }<br> setMeasuredDimension(width, height);<br> }<br> @Override<br> protected void onSizeChanged(int w, int h, int oldw, int oldh) {<br> super.onSizeChanged(w, h, oldw, oldh);<br> mContentRectF = new RectF(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - getPaddingBottom());<br> }<br> @Override<br> protected void onDraw(Canvas canvas) {<br> super.onDraw(canvas);<br> drawBubble(canvas);<br> }<br> @Override<br> protected void onAttachedToWindow() {<br> super.onAttachedToWindow();<br> startBubbleSync();<br> }<br> @Override<br> protected void onDetachedFromWindow() {<br> super.onDetachedFromWindow();<br> stopBubbleSync();<br> }<br> private static class Bubble {<br> int radius; // 气泡半径<br> float speedY; // 上升速度<br> float speedX; // 平移速度<br> float x; // 气泡x坐标<br> float y; // 气泡y坐标<br> }<br> private ArrayList<Bubble> mBubbles = new ArrayList<>();<br> private Random random = new Random();<br> private Thread mBubbleThread;<br> // 开始气泡线程<br> private void startBubbleSync() {<br> stopBubbleSync();<br> mBubbleThread = new Thread() {<br> public void run() {<br> while (true) {<br> try {<br> Thread.sleep(mBubbleRefreshTime);<br> tryCreateBubble();<br> refreshBubbles();<br> postInvalidate();<br> } catch (InterruptedException e) {<br> break;<br> }<br> }<br> }<br> };<br> mBubbleThread.start();<br> }<br> // 停止气泡线程<br> private void stopBubbleSync() {<br> if (null == mBubbleThread) return;<br> mBubbleThread.interrupt();<br> mBubbleThread = null;<br> }<br> // 绘制气泡<br> private void drawBubble(Canvas canvas) {<br> List<Bubble> list = new ArrayList<>(mBubbles);<br> for (Bubble bubble : list) {<br> if (null == bubble) continue;<br> canvas.drawCircle(bubble.x, bubble.y,<br> bubble.radius, mBubblePaint);<br> }<br> }<br> // 创建气泡<br> private void tryCreateBubble() {<br> if (null == mContentRectF) return;<br> if (mBubbles.size() >= mBubbleMaxSize) {<br> return;<br> }<br> if (random.nextFloat() < 0.95) {<br> return;<br> }<br> Bubble bubble = new Bubble();<br> int radius = random.nextInt(mBubbleMaxRadius - mBubbleMinRadius);<br> radius += mBubbleMinRadius;<br> float speedX = 0.5f * mBubbleMaxSpeedX;<br>// while (speedX < 1) {<br>// speedX = random.nextFloat() * mBubbleMaxSpeedX;<br>// }<br> bubble.radius = radius;<br> bubble.speedX = speedX;<br> bubble.x = mContentRectF.left;<br> bubble.y = mContentRectF.centerY();<br> float speedY = random.nextFloat() - 0.5f;<br> while (speedY == 0) {<br> speedY = random.nextFloat() - 0.5f;<br> }<br> bubble.speedY = speedY * 2;<br> mBubblePaint.setMaskFilter(new BlurMaskFilter(radius, BlurMaskFilter.Blur.SOLID));<br> mBubbles.add(bubble);<br> }<br> // 刷新气泡位置,超出的气泡进行移除<br> private void refreshBubbles() {<br> List<Bubble> list = new ArrayList<>(mBubbles);<br> for (Bubble bubble : list) {<br> if (bubble.x + bubble.speedX >= mContentRectF.right + bubble.radius) {<br> mBubbles.remove(bubble);<br> } else {<br> int i = mBubbles.indexOf(bubble);<br> if (bubble.y + bubble.speedY <= mContentRectF.top + bubble.radius) {<br> bubble.y = mContentRectF.top + bubble.radius;<br> } else {<br> bubble.y = Math.min(bubble.y + bubble.speedY, mContentRectF.bottom - bubble.radius);<br> }<br> bubble.x = bubble.x + bubble.speedX;<br> mBubbles.set(i, bubble);<br> }<br> }<br> }<br> private float dp2px(float dpValue) {<br> return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());<br> }<br>} |
目前为自定义属性,布局文件中直接引用即可。
1 | <br> <com.example.myapplication.BubbleView<br> android:layout_width= "300dp" <br> android:layout_height= "100dp" /> |
以上就是 在线直播源码,自定义气泡效果(BubbleView),更多内容欢迎关注之后的文章
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现