前两天在电脑上使用迅雷的时候发现,现在的迅雷有一个类似弹幕的东西,热门搜索词会循环的在屏幕上飞过,就想能不能做一个安卓的飞屏view,然后在一番尝试后做出来了一个比较像样的demo.然后由于技术有限也没有做深入研究.现在贴上来.
代码思路:
1.整个view是继承relativelayout的ViewGroup,然后飞过的每个”小蜜蜂”是一个textview.
2.ViewGroup需要根据设定的宽高值及飞屏的行数来确定自己的宽高
(当高度为fill_parent 或者 固定值时,如果设置的高度小于要显示的行数*行高,设置间隔高度等于行高,其实这是设置的固定值并不生效)
3.自定义MyTextView继承textView,并添加字段,point来标志自身的位置,speed表示每次更新位置时的增幅,相当于移动速度
4.在ViewGroup中维护两个list,一个表示等待显示的view的集合,一个表示正在显示的view的集合,当需要添加view到ViewGroup时,取waitList中第一个view加入ViewGroup中,然后不断更新正在显示的view的位置信息即可
表达的比较混乱,表达能力不好,但是程序员交流直接看代码就好.上面仅供参考.
FlyingStringLayout代码
package com.example.flyingstring;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.AvoidXfermode.Mode;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class FlyingStringLayout extends RelativeLayout {
private Context mContext;
/**
* 文字的行数(默认3)
*/
private int mStrLineNum = 5;
/**
* 间隔的行数=文字行数 + 1
*/
private int mSeparatorLineNum = mStrLineNum + 1;
/**
* 每行文字的高度
*/
private int mStrLineHeight;
/**
* 间隔的高度
*/
private int mSeparatorHeight;
/**
* 字体大小(sp)
*/
private int mTextSize = 20;
/**
* 画笔
*/
private Paint paint;
private int[] colors = { Color.parseColor("#bb45C9C2"), Color.parseColor("#bb2727E7"), Color.parseColor("#bbEDF21C"), Color.parseColor("#bb8C1AF4"), Color.parseColor("#bb65A967"), Color.parseColor("#bb32DC98"), Color.parseColor("#bbE72753"), Color.parseColor("#bb2E52E0"), Color.parseColor("#bb73CC42") };
private String[] strs = { "bigger", "哈尔的移动城堡", "龙猫", "千与千寻", "X战警", "火影忍者", "海贼王", "英雄联盟", "死神", "死亡来临前的一小时", "地球毁灭", "外星来客", "荆棘之兴", "攻速加成", "绿萝", "太阳花", "我的野蛮女友", "安卓开发", "玻璃栈道" };//
/**
* 用于存储将要显示的子view
*/
private List waitingList = new ArrayList<>();
/**
* 用于存储正在显示的子view
*/
private List showingList = new ArrayList<>();
/**
* 用于记录每一行的y轴的位置
*/
private List mLinePoints = new ArrayList<>();// 记录每一行的高度位置
private long lastTime, currentTime;// 记录显示时间
/**
* 用于标记两个子view显示之间的时间差(毫秒值)
*/
private long interval = 1000;
/**
* 用于记录上一个view显示所在行
*/
private int showAtLastLine = mStrLineNum - 1;
/**
* 记录被点击了的子view
*/
private FlyTextView mClickView;
private OnItemClickListener mItemClickListener;
public OnItemClickListener getOnItemClickListener() {
return mItemClickListener;
}
public void setOnItemClickListener(OnItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
public FlyingStringLayout(Context context) {
this(context, null);
}
public FlyingStringLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlyingStringLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
init();
}
private void init() {
// 初始化画笔
paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(TransUtil.sp2px(mContext, mTextSize));
Rect rect = new Rect();
String str = "例子";
paint.getTextBounds(str, 0, str.length() - 1, rect);
mStrLineHeight = rect.height();
Random random = new Random();
for (int i = 0; i < strs.length; i++) {
FlyTextView textView = new FlyTextView(mContext);
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
textView.setLayoutParams(params);
String string = strs[i];
// 设置TextView显示样式
initTextView(random, textView, string);
waitingList.add(textView);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width, height;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
width = 600;
} else {
width = widthSize;
}
mLinePoints.clear();
// 确定控件的宽高及行间隔
if (heightMode != MeasureSpec.EXACTLY) {
mSeparatorHeight = mStrLineHeight;
height = mStrLineHeight * (mStrLineNum + mSeparatorLineNum);
for (int i = 0; i < mStrLineNum; i++) {
mLinePoints.add((i + 1) * 2 * mStrLineHeight);
}
} else {
if (heightSize <= mStrLineHeight * mStrLineNum) {
mSeparatorHeight = mStrLineHeight;
height = mStrLineHeight * (mStrLineNum + mSeparatorLineNum);
for (int i = 0; i < mStrLineNum; i++) {
mLinePoints.add((i + 1) * 2 * mStrLineHeight);
}
} else {
mSeparatorHeight = (heightSize - mStrLineHeight * mStrLineNum) / mSeparatorLineNum;
for (int i = 0; i < mStrLineNum; i++) {
mLinePoints.add((i + 1) * (mSeparatorHeight + mStrLineHeight));
}
height = heightSize;
}
}
setMeasuredDimension(width, height);
}
@SuppressLint("DrawAllocation")
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
currentTime = System.currentTimeMillis();
//每间隔1秒出现一个新的
if (currentTime - lastTime > interval && waitingList.size() > 0) {
lastTime = currentTime;
FlyTextView textView = waitingList.get(0);
waitingList.remove(0);
//设置每一个将要显示的view的所在行,按顺序依次显示
if (showAtLastLine == mStrLineNum - 1) {
showAtLastLine = 0;
} else {
showAtLastLine++;
}
textView.setPoint(new Point(getMeasuredWidth(), mLinePoints.get(showAtLastLine)));
showingList.add(textView);
addView(textView);
}
//依次为每一个子view定位最新位置并更新下一次要显示的位置
for (int i = 0; i < getChildCount(); i++) {
FlyTextView ftv = showingList.get(i);
ftv.layout(ftv.getPoint().x, ftv.getPoint().y - ftv.getHeight(), ftv.getPoint().x + ftv.getWidth(), ftv.getPoint().y);
if (ftv != mClickView) {
ftv.refresh();
}
}
//将已经飘出屏幕的子view移除并添加到waitingList中待下一轮显示
Iterator it = showingList.iterator();
while (it.hasNext()) {
FlyTextView ftv = it.next();
if (ftv.getPoint().x <= -ftv.getWidth()) {
it.remove();
waitingList.add(ftv);
removeView(ftv);
}
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
requestLayout();
}
/**
* 条目点击的接口
*
*/
interface OnItemClickListener {
void onClick(String text);
}
/**
* 对子view进行设置
*
* @param random
* @param textView
* @param string
*/
private void initTextView(Random random, FlyTextView textView, String string) {
textView.setTextSize(mTextSize);
textView.setText(string);
textView.setBackgroundColor(colors[random.nextInt(9)]);
textView.setTextColor(Color.WHITE);
textView.setPadding(10, 0, 10, 0);
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (v == mClickView) {
mClickView = null;
return;
}
mClickView = (FlyTextView) v;
if (mItemClickListener != null) {
mItemClickListener.onClick(mClickView.getText().toString());
}
}
});
}
}
中间”小蜜蜂”的类
package com.example.flyingstring;
import java.util.Random;
import android.content.Context;
import android.graphics.Point;
import android.widget.TextView;
public class FlyTextView extends TextView {
private int speed;// 移动速度
private Point point;// 标记坐下角位置
public FlyTextView(Context context) {
super(context);
speed = new Random().nextInt(5) + 5;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
}
public void refresh() {
point.x -= speed;
}
}
源码点击下载