网站建设服务市场细分,做课件ppt网站,一起作业网站英语作业怎么做,app联盟推广平台参考文章 此方案使用动画方式实现#xff0c;只适合轻量级别的弹幕滚动效果实现#xff0c;数据量过大时会出现内存激增的情况。 效果#xff1a; 自定义view代码
public class TumbleLayout extends ViewGroup {private final String TAG TumbleLayout;priva… 参考文章 此方案使用动画方式实现只适合轻量级别的弹幕滚动效果实现数据量过大时会出现内存激增的情况。 效果 自定义view代码
public class TumbleLayout extends ViewGroup {private final String TAG TumbleLayout;private int parentWidth;private int parentHeight;private long currentHshCode 0;// 弹幕数据缓存池private DataPool dataPool new DataPoolContentBeen(100);private DataPool userDataPool new DataPoolContentBeen(10);private boolean isDetached false;public TumbleLayout(NonNull Context context) {super(context);initView();}public TumbleLayout(NonNull Context context, Nullable AttributeSet attrs) {super(context, attrs);initView();}public TumbleLayout(NonNull Context context, Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initView();}Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);if (getParent() instanceof ViewGroup) {parentWidth ((ViewGroup) getParent()).getWidth();parentHeight ((ViewGroup) getParent()).getHeight();}}Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int viewWidth getViewWidth(widthMeasureSpec);int viewHeight getViewHeight(heightMeasureSpec);parentWidth viewWidth;parentHeight viewHeight;// 设置子view的宽高setMeasuredDimension(viewWidth, viewHeight);}Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {// 宽高计算完毕 开始显示弹幕if (changed) {showNextData();}}Overrideprotected void onFinishInflate() {super.onFinishInflate();}private void initView() {}public void addUserChildView(ContentBeen contentBeen) {if (userDataPool ! null) {userDataPool.release(contentBeen);if (!dataPool.hasNext()) {showNextData();}}}public void addChildView(ContentBeen contentBeen) {if (dataPool ! null) {// 将数据加入队列dataPool.release(contentBeen);}}private void startAnimator(View child) {ObjectAnimator animator new ObjectAnimator();animator.setIntValues(0, parentWidth child.getMeasuredWidth());animator.setDuration(3000);animator.setInterpolator(new LinearInterpolator());animator.addUpdateListener(animation - {// view已经退出 则停止所有动画 防止内存泄露if (isDetached) {animation.cancel();return;}int x (int) animation.getAnimatedValue();int left parentWidth - x;int right parentWidth child.getMeasuredWidth() - x;// 控制弹幕密集度 当上一条数据离开屏幕右侧边框时 展示下一条弹幕数据if (currentHshCode child.hashCode() right 50 parentWidth) {// 展示下一条弹幕showNextData();}child.layout(left, child.getTop(), right, child.getBottom());if (child.getRight() 0) {// 动画结束 移除viewremoveView(child);}});animator.start();}private void showNextData() {ContentBeen acquire null;if (userDataPool null dataPool null) {return;}// 用户本地弹幕优先级最高 若有本地用户弹幕 则先展示用户弹幕if (userDataPool.hasNext()) {acquire (ContentBeen) userDataPool.acquire();} else if (dataPool.hasNext()) {acquire (ContentBeen) dataPool.acquire();}// 执行一下条弹幕出现if (acquire ! null) {// 小于最大数量时 添加新的子viewcurrentHshCode acquire.getChildView().hashCode();addView(acquire.getChildView());int childCount getChildCount();if (childCount ! 0) {int index childCount - 1;View child getChildAt(index);measureMyChild(child);int left parentWidth 30;int num laneNum(child);int top num * child.getMeasuredHeight();int right parentWidth child.getMeasuredWidth() 30;int bottom top child.getMeasuredHeight();MLog.e(TAG, measureMyChild hashCode child.hashCode() top top bottom bottom parentHeight getHeight());child.layout(left, top, right, bottom);startAnimator(child);}}}private int getViewWidth(int measureSpec) {int size 100;int specSize MeasureSpec.getSize(measureSpec);int specMode MeasureSpec.getMode(measureSpec);if (specMode MeasureSpec.EXACTLY) {size specSize;} else if (specMode MeasureSpec.AT_MOST) {size Math.max(size, specSize);}return size;}private int getViewHeight(int measureSpec) {int size 100;int specSize MeasureSpec.getSize(measureSpec);int specMode MeasureSpec.getMode(measureSpec);if (specMode MeasureSpec.EXACTLY) {size specSize;} else if (specMode MeasureSpec.AT_MOST) {size Math.max(size, specSize);}return size;}/*** 测量某一个child的宽高*/protected void measureMyChild(View child) {final int childWidthMeasureSpec MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);final int childHeightMeasureSpec MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}/*** 计算随机高度值 起到随机位置展示效果*/private int laneNum(View child) {// 计算出大概有几条泳道int laneCount getHeight() / child.getMeasuredHeight();// 给弹幕随机分配泳道Random random new Random();// 返回泳道编号return random.nextInt(laneCount);}public void destroy() {// 回收资源 防止泄露userDataPool.clean();dataPool.clean();userDataPool null;dataPool null;}Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();isDetached true;MLog.e(TAG, onDetachedFromWindow);}
}存储数据队列的代码
public class DataPoolT implements Pools.PoolT {private Object[] mPool;private int mPoolSize;private int l 0;private int curIndex 0;public DataPool(int maxPoolSize) {if (maxPoolSize 0) {throw new IllegalArgumentException(The max pool size must be 0);}// 构造池对象容器mPool new Object[maxPoolSize];}NullableOverridepublic T acquire() {// 从容器中取出对象if (l 0) {T instance (T) mPool[curIndex];mPool[curIndex] null;l--;curIndex;if(l 0){curIndex 0;}return instance;}return null;}Overridepublic boolean release(NonNull T instance) {if (isInPool(instance)) {throw new IllegalStateException(Already in the pool!);}// 存储对象if (l mPool.length) {mPool[l] instance;l;return true;}return false;}// 判断对象是否在池中private boolean isInPool(NonNull T instance) {// 遍历池对象for (int i 0; i l; i) {if (mPool[i] instance) {return true;}}return false;}public boolean hasNext(){return l 0;}public void clean(){l 0;curIndex 0;}
}数据格式
public class ContentBeen {private String content;private View childView;public ContentBeen(String content,View childView){this.content content;this.childView childView;}public void setChildView(View childView) {this.childView childView;}public void setContent(String content) {this.content content;}public String getContent() {return content;}public View getChildView() {return childView;}
}布局文件内容 ?xml version1.0 encodingutf-8?
RelativeLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsandroid:layout_widthmatch_parentandroid:layout_heightmatch_parenttools:context.ui.activity.BarrageActivitycom.example.app_view_model.view.TumbleLayoutandroid:idid/tumble_layoutandroid:layout_widthmatch_parentandroid:layout_height300dpandroid:backgroundcolor/black/com.example.app_view_model.view.TumbleLayoutLinearLayoutandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:layout_alignParentBottomtrueandroid:layout_marginBottom20dpandroid:orientationhorizontalEditTextandroid:idid/txt_editandroid:layout_weight1android:layout_widthmatch_parentandroid:layout_heightwrap_content /Buttonandroid:layout_weight4android:idid/send_btnandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:text发送 //LinearLayout/RelativeLayout使用方法 // 使用方法addUserChildView(); // 添加用户输入弹幕addChildView(); // 加载数据弹幕destroy(); // 资源回收 此方法一定要调用 防止大量动画无法回收导致oom