/**
@作者 : 西野奈留
@博客:http://blog.csdn.net/narunishino
@声明:本文仅在【CSDN 博客】发表。
*/
附:
【一共5个类:MainActivity.java; TanmuBean.java; ScreenUtils.java; AnimationHelper.java; DecelerateAccelerateInterporator.java.】
【运行逻辑:
】
1.MainActivity.java;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
public class MainActivity extends AppCompatActivity {
private MyHandler handler;
/** * 弹幕内容 */
private TanmuBean tanmuBean;
/** * 放置弹幕内容的容器【containerVG】 */
private RelativeLayout containerVG;
//【containerVG】的高度
private int validHeightSpace;
private View startTanmuView;
private FrameLayout frameLayout;
//-----------------------分隔符----------------------------
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFrameTest();
init();
/** * 点击按钮后就开始弹幕 */
startTanmuView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/** * 容器里面还有子view的话就return(do nothing) */
if (containerVG.getChildCount() > 0) {
return;
}
/** * 清除【Set集合】里面的所有数据; */
existMarginValues.clear();
/** * 新开一个线程【工作线程】 */
new Thread(new CreateTanmuThread()).start();
}
});
}
//-----------------------分隔符----------------------------
/** * 这个方法是测试用的,没有任务意味,可以删除。 */
private void initFrameTest() {
TextView textViewFrame = new TextView(this);
textViewFrame.setTextSize(30);
textViewFrame.setText("我是一个test啦");
textViewFrame.setTextColor(Color.parseColor("#000000"));
frameLayout = (FrameLayout) findViewById(R.id.frame_container_test);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
textViewFrame.setLayoutParams(lp);
Animation animation = new TranslateAnimation(1080, -1080, 0, 0);
animation.setDuration(3000);
animation.setRepeatCount(20);
textViewFrame.startAnimation(animation);
frameLayout.addView(textViewFrame);
}
//-----------------------分隔符----------------------------
/** * 初始化 */
private void init() {
containerVG = (RelativeLayout) findViewById(R.id.tanmu_container);
startTanmuView = findViewById(R.id.startTanmu);
handler = new MyHandler(this);
tanmuBean = new TanmuBean();
/** * 弹幕的内容 */
tanmuBean.setItems(new String[]{"I need your help.", "测试一下",
"弹幕这东西真不好做啊", "总是出现各种问题~~",
"我最长--------------------------" +
"-------------我最长",
"也不知道都是为什么?麻烦!", "哪位大神可以帮帮我啊?", "I need your help.",
"测试一下", "弹幕这东西真不好做啊", "总是出现各种问题~~", "也不知道都是为什么?麻烦!",
"哪位大神可以帮帮我啊?", "I need your help.",
"测试一下", "弹幕这东西真不好做啊", "总是出现各种问题~~",
"也不知道都是为什么?麻烦!", "哪位大神可以帮帮我啊?", "I need your help.", "测试一下",
"弹幕这东西真不好做啊", "总是出现各种问题~~",
"也不知道都是为什么?麻烦!", "哪位大神可以帮帮我啊?", "I need your help.",
"测试一下", "弹幕这东西真不好做啊", "总是出现各种问题~~", "也不知道都是为什么?麻烦!",
"哪位大神可以帮帮我啊?", "I need your help.",
"测试一下", "弹幕这东西真不好做啊", "总是出现各种问题~~",
"也不知道都是为什么?麻烦!", "哪位大神可以帮帮我啊?", "I need your help."});
}
//-----------------------分隔符----------------------------
private class CreateTanmuThread implements Runnable {
@Override
public void run() {
/** * 通过【tanmuBean.getItems()】 * 获得弹幕的内容 */
int N = tanmuBean.getItems().length;
for (int i = 0; i < N; i++) {
/** * public final Message obtainMessage (int what, int arg1, int arg2) * 【obtainMessage().sendToTarget()】等同于【sendMessage()】,除了性能上的不同 * 作用:有多少条【弹幕】就给【handler】发送多少条【消息】 */
handler.obtainMessage(1, i, 0).sendToTarget();
/** * 类似【Thread.sleep(500)】;但是该方法会忽略【InterruptedException】 * 作用:每0.5s自动添加一条弹幕 */
SystemClock.sleep(500);
}
}
}
//-----------------------分隔符----------------------------
private static class MyHandler extends Handler {
private WeakReference<MainActivity> ref;
MyHandler(MainActivity ac) {
ref = new WeakReference<>(ac);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
MainActivity ac = ref.get();
if (ac != null && ac.tanmuBean != null) {
int index = msg.arg1;
/** * 要发送的弹幕的内容 */
String content = ac.tanmuBean.getItems()[index];
/** * Math.random()返回【0和1】之间的小数, * 计算结果返回【16和24】之间的大小。 */
float textSize = (float) (ac.tanmuBean.getMinTextSize() * (1 + Math.random() * ac.tanmuBean.getRange()));
/** * 返回【灰色】 */
int textColor = ac.tanmuBean.getColor();
ac.showTanmu(content, textSize, textColor);
}
}
}
}
//-----------------------分隔符----------------------------
private void showTanmu(String content, float textSize, int textColor) {
final TextView textView = new TextView(this);
textView.setTextSize(textSize);
textView.setText(content);
textView.setTextColor(textColor);
/** * 【containerVG.getRight()】:【containerVG】的最右边到它的【父控件】的最左边的长度。 * 【containerVG.getPaddingLeft()】:【containerVG】这个控件有没有【padding】,没有的话就为0. * 结果:得到这个控件的宽度。 */
int leftMargin = containerVG.getRight() - containerVG.getLeft() - containerVG.getPaddingLeft();
//计算本条弹幕的topMargin(随机值,但是与屏幕中已有的不重复)
/** * 【getRandomTopMargin()】returns 【marginValue】. * 【marginValue】为textView距离【containerVG】顶端的高度。 */
int verticalMargin = getRandomTopMargin();
/** * 在动画那里会用到【getTag】 */
textView.setTag(verticalMargin);
/** * LayoutParams(int w, int h) * 这个【RelativeLayout】就是【containerVG】,因为,请看【showTanmu方法】中最下面的代码, * 【containerVG.addView(textView)】:把【textView】add进了这个RelativeLayout中去了。 * 在【new RelativeLayout.LayoutParams()】设置的参数就是该控件(这里是textView)而非RelativeLayout的参数。 */
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams
(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
/** * 【params.addRule(RelativeLayout.ALIGN_PARENT_TOP)】 * 这里指【textView】与父布局【relativeLayout】顶端对齐 */
params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
params.topMargin = verticalMargin;
/** * 设置【textView】的参数 */
textView.setLayoutParams(params);
textView.setGravity(Gravity.CENTER_HORIZONTAL);
/** * 【leftMargin】指的是从控件【containerVG】的最右边开始。 */
Animation anim = AnimationHelper.createTranslateAnim(this, leftMargin, -ScreenUtils.getScreenW(this));
/** * 【动画基础】可参考【http://www.imooc.com/video/7362】 */
anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
//移除该组件
containerVG.removeView(textView);
//移除占位
int verticalMargin = (int) textView.getTag();
existMarginValues.remove(verticalMargin);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
textView.startAnimation(anim);
containerVG.addView(textView);
}
//-----------------------分隔符----------------------------
//记录当前仍在显示状态的弹幕的位置(避免重复)
private Set<Integer> existMarginValues = new HashSet<>();
private int linesCount;
private int getRandomTopMargin() {
if (validHeightSpace == 0) {
/** * 【containerVG.getBottom()】:【containerVG】的底部离它的父控件的顶部的距离。 * 结果:得到【containerVG】的高度。 */
validHeightSpace = containerVG.getBottom() - containerVG.getTop()
- containerVG.getPaddingTop() - containerVG.getPaddingBottom();
}
if (linesCount == 0) {
/** * 【tanmuBean.getMinTextSize() * (1 + tanmuBean.getRange())】=【16*1.5】=24 * 计算可用的行数【linesCount】 */
linesCount = validHeightSpace / ScreenUtils.dp2px(this, tanmuBean.getMinTextSize() * (1 + tanmuBean.getRange()));
if (linesCount == 0) {
throw new RuntimeException("Not enough space to show text.");
}
}
//检查重叠
while (true) {
/** * 假设【linesCount】是5行,则【randomIndex】是随机选到{0,1,2,3,4,5}中的其中一个(整数)。 * (int)使得小数变为整数。例:1.X都等于1;0.X都等于0。 */
int randomIndex = (int) (Math.random() * linesCount);
/** * 【总高度】除以【行数】=【每行的高度】。 * 【每行的高度】乘以【随机数】=【marginValue】 * 【marginValue】为textView距离【containerVG】顶端的距离。 */
int marginValue = randomIndex * (validHeightSpace / linesCount);
/** * boolean contains(Object o) 如果此 set 包含指定元素,则返回 true。 * 【Set集合】【existMarginValues】里面包含这个【marginValue长度】吗, * 如果不包含就可以把这个【长度】发给【TextView】 */
if (!existMarginValues.contains(marginValue)) {
existMarginValues.add(marginValue);
return marginValue;
}
}
}
//-----------------------分隔符----------------------------
}
2.TanmuBean.java
import android.graphics.Color;
public class TanmuBean {
private String[] items;
private int color;
private int minTextSize;
private float range;
public TanmuBean() {
//init default value
color = Color.parseColor("#444444");
minTextSize = 16;
range = 0.5f;
}
public String[] getItems() {
return items;
}
public void setItems(String[] items) {
this.items = items;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getMinTextSize() {
return minTextSize;
}
/** * 这个【方法】没有用到,只是多出来的没有删掉而已 */
public void setMinTextSize(int minTextSize) {
this.minTextSize = minTextSize;
}
public float getRange() {
return range;
}
/** * 这个【方法】没有用到,只是多出来的没有删掉而已 */
public void setRange(float range) {
this.range = range;
}
}
3.ScreenUtils.java
import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
public class ScreenUtils {
private static int screenW;
private static int screenH;
private static float screenDensity;
public static int getScreenW(Context context) {
if (screenW == 0) {
initScreen(context);
}
/** * 【screenW】是屏幕【宽度】 */
return screenW;
}
public static int getScreenH(Context context) {
if (screenH == 0) {
initScreen(context);
}
return screenH;
}
public static float getScreenDensity(Context context) {
if (screenDensity == 0) {
initScreen(context);
}
return screenDensity;
}
public static void initScreen(Context context) {
DisplayMetrics metric = context.getResources().getDisplayMetrics();
screenW = metric.widthPixels;
screenH = metric.heightPixels;
screenDensity = metric.density;
}
/** * 根据手机的屏幕密度从 dp 的单位 转成为 px(像素) */
public static int dp2px(Context context, float dpValue) {
return (int) (dpValue * getScreenDensity(context) + 0.5f);
}
/** * 根据手机的屏幕密度从 px(像素) 的单位 转成为 dp */
public static int px2dp(Context context, float pxValue) {
return (int) (pxValue / getScreenDensity(context) + 0.5f);
}
}
4.AnimationHelper.java
import android.content.Context;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
/** * 动画工具类 */
public class AnimationHelper {
/** * 创建平移动画 */
public static Animation createTranslateAnim(Context context, int fromX, int toX) {
/** * 第一个参数fromXDelta为动画起始时 X坐标上的移动位置 * 第二个参数toXDelta为动画结束时 X坐标上的移动位置 * 第三个参数fromYDelta为动画起始时Y坐标上的移动位置 * 第四个参数toYDelta为动画结束时Y坐标上的移动位置 * TranslateAnimation(float fromXDelta, float toXDelta,float fromYDelta, float toYDelta) */
TranslateAnimation tlAnim = new TranslateAnimation(fromX, toX, 0, 0);
/** * 【setDuration()】动画运行持续的时间。 * 【setInterpolator()】控制运行速度。 * 【setFillAfter()】让View对象在动画执行完毕后保留在终止位置。 */
tlAnim.setDuration(4000);
tlAnim.setInterpolator(new DecelerateAccelerateInterpolator());
tlAnim.setFillAfter(true);
tlAnim.setFillEnabled(true);
return tlAnim;
}
}
5.DecelerateAccelerateInterporator.java
import android.view.animation.Interpolator;
/** * 【Interpolator】是一个速度控制器,控制速度变化。 */
public class DecelerateAccelerateInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
/** * 把【input】return回去的话弹幕就是【匀速】从右到左。 */
return input;
}
}
参考:
『参考了很多文章,不一一写上,有需要的请联系补充。』
-2015/12/04-
-End-