編輯:關於Android編程
在bilibili推出彈幕功能,我也愛上了邊看視頻邊看吐槽了,現在讓我們也來實現這一個功能吧。
首先我們要整理一下思緒我們大概需要實現哪個細節板塊呢。
我們最直觀的看來,彈幕就是總右往左出現到消失。我們要實現這個動畫,彈幕的大小,顏色,出現方式,加速,彈幕的不重疊(這個我想了好久還沒有實現,有實現方法可以聯系下我)。
我們先來了解一下等會程序裡面會用到的相關知識點,等會看代碼會更輕松一點。
/*getHeight跟getMeasureHeight的區別
* 實際上在當屏幕可以包裹內容的時候,他們的值相等,只有當view超出屏幕後,才能看出他們的區別:
* getMeasuredHeight()是實際View的大小,與屏幕無關,而getHeight的大小此時則是屏幕的大小。
* 當超出屏幕後, getMeasuredHeight() 等於 getHeight()加上屏幕之外沒有顯示的大小
*
* */
/Activity生命周期中,onStart, onResume, onCreate都不是真正visible的時間點,真正的visible時間點是onWindowFocusChanged()函數被執行時。 //當你屏幕的焦點發生變化時候,想要操作什麼也完全可以在這個方法裡面執行 // Interpolator 被用來修飾動畫效果,定義動畫的變化率,可以使存在的動畫效果accelerated(加速),decelerated(減速),repeated(重復),bounced(彈跳)等。 /* * AccelerateDecelerateInterpolator 在動畫開始與結束的地方速率改變比較慢,在中間的時候加速 AccelerateInterpolator 在動畫開始的地方速率改變比較慢,然後開始加速 AnticipateInterpolator 開始的時候向後然後向前甩 AnticipateOvershootInterpolator 開始的時候向後然後向前甩一定值後返回最後的值 BounceInterpolator 動畫結束的時候彈起 CycleInterpolator 動畫循環播放特定的次數,速率改變沿著正弦曲線 DecelerateInterpolator 在動畫開始的地方快然後慢 LinearInterpolator 以常量速率改變 OvershootInterpolator 向前甩一定值後再回到原來位置
* fillBefore是指動畫結束時畫面停留在此動畫的第一幀; fillAfter是指動畫結束是畫面停留在此動畫的最後一幀。 Java代碼設置如下: /*****動畫結束時,停留在最後一幀********* setFillAfter(true); setFillBefore(false); /*****動畫結束時,停留在第一幀********* setFillAfter(false); setFillBefore(true); *
下面我們就來看一下彈幕實現的效果。

彈幕會出現重疊,這個問題還未解決
讓我們開始看代碼結構吧。

我們字體顏色的xml都寫在了colors.xml中了,BarrageItem裡面存放著我們的一些變量,而核心代碼都在View中
BraagetItem.java
package com.example.bibibibibibibibi;
import android.widget.TextView;
import android.widget.TextView;
/**
* Created by lixueyong on 16/2/19.
*/
public class BarrageItem {
public TextView textView;//文本框
public int textColor;//文本顏色
public String text;//文本對象
public int textSize;//文本的大小
public int moveSpeed;//移動速度
public int verticalPos;//垂直方向顯示的位置
public int textMeasuredWidth;//字體顯示占據的寬度
}
在BarrageItem裡面處理了彈幕的速度,大小,顏色,動畫,等事件 在這個文件中 我注釋的內容是我對彈幕重疊的操作代碼,但是除了問題,有興趣的可以看一下
package com.example.bibibibibibibibi;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import com.example.bibibibibibibibi.R.integer;
/**
* Created by nzx on 16/5/30.
*/
public class BarrageView extends RelativeLayout {
private Context mContext;
private BarrageHandler mHandler = new BarrageHandler();
private Random random = new Random(System.currentTimeMillis());//System.currentTimeMillis()產生一個當前的毫秒
private static final long BARRAGE_GAP_MIN_DURATION = 1000;//兩個彈幕的最小間隔時間
private static final long BARRAGE_GAP_MAX_DURATION = 2000;//兩個彈幕的最大間隔時間
private int maxSpeed = 10000;//速度,ms
private int minSpeed = 5000;//速度,ms
private int maxSize = 30;//文字大小,dp
private int minSize = 15;//文字大小,dp
private int totalHeight = 0;//整個的高度
private int lineHeight = 0;//每一行彈幕的高度
private int totalLine = 0;//彈幕的行數
private String[] itemText = {"大頭死變態", "老圩人最屌了", "唉這把中單是火男,難玩了", "大頭是傻子", "世界上最長的路是套路", "英雄聯盟最強的是補丁",
"我不會輕易的go die", "嘿嘿", "加班加班"};
private int textCount;//文本的組數
//private RelativeLayout Rparams;
// private List itemList = new ArrayList();
//實現RelativeLayout的重寫的構造方法。
/*
* //content 上下文
//AttributeSet 屬性集
//defStyleAttr 預設樣式屬性集
//defStyleRes 預設樣式資源屬性集
*
* */
public BarrageView(Context context) {
this(context, null);
}
public BarrageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BarrageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
textCount = itemText.length;
int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random());
mHandler.sendEmptyMessageDelayed(0, duration);
}
@Override
//Activity生命周期中,onStart, onResume, onCreate都不是真正visible的時間點,真正的visible時間點是onWindowFocusChanged()函數被執行時。
//當你屏幕的焦點發生變化時候,想要操作什麼也完全可以在這個方法裡面執行。
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
totalHeight = getMeasuredHeight();
/*getHeight跟getMeasureHeight的區別
* 實際上在當屏幕可以包裹內容的時候,他們的值相等,只有當view超出屏幕後,才能看出他們的區別:
* getMeasuredHeight()是實際View的大小,與屏幕無關,而getHeight的大小此時則是屏幕的大小。
* 當超出屏幕後, getMeasuredHeight() 等於 getHeight()加上屏幕之外沒有顯示的大小
*
* */
//獲取每一行彈幕的最大高度
lineHeight = getLineHeight();
//我們整個彈幕的高度view/每一行的最大彈幕高度=
totalLine = totalHeight / lineHeight;
}
private void generateItem() {
BarrageItem item = new BarrageItem();
//把我們的每行彈幕的行數順序跟彈幕進行一個隨機
String tx = itemText[(int) (Math.random() * textCount)];
//隨機彈幕大小
int sz = (int) (minSize + (maxSize - minSize) * Math.random());
item.textView = new TextView(mContext);
item.textView.setText(tx);
item.textView.setTextSize(sz);
item.textView.setTextColor(Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
//這裡我們需要傳入三個參數 文本對象,文字行數跟大小
item.textMeasuredWidth=(int) getTextWidth(item, tx, sz);
//這是設置彈幕移動速度,實現有快有慢的感覺
item.moveSpeed = (int) (minSpeed + (maxSpeed - minSpeed) * Math.random());
//這裡為了實現一個彈幕循環播放的項目,在我們實際中看情況而定
if (totalLine == 0) {
totalHeight = getMeasuredHeight();
lineHeight = getLineHeight();
totalLine = totalHeight / lineHeight;
}
//彈幕在y軸上出現的位置
item.verticalPos = random.nextInt(totalLine) * lineHeight;
// itemList.add(item);
showBarrageItem(item);
}
private void showBarrageItem(final BarrageItem item) {
//paddingLeft是設置布局裡面的內容左邊的距離,這樣我們這就可以讓這個彈幕的textview完全消失
int leftMargin = this.getRight() - this.getLeft() - this.getPaddingLeft();
//這裡我們通過動態的方式去設置一些我們布局的屬性。
// int verticalMargin = getRandomTopMargin();
// item.textView.setTag(verticalMargin);
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
params.topMargin = item.verticalPos;
this.addView(item.textView, params);
Animation anim = generateTranslateAnim(item, leftMargin);
anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
//當我們動畫結束的時候,清除該條彈幕
public void onAnimationEnd(Animation animation) {
item.textView.clearAnimation();
BarrageView.this.removeView(item.textView);
}
@Override
//動畫被取消的時候出發
public void onAnimationRepeat(Animation animation) {
}
});
item.textView.startAnimation(anim);
}
//
private TranslateAnimation generateTranslateAnim(BarrageItem item, int leftMargin) {
//這裡我們有四個參數(動畫開始的x點,結束點,開始y軸點,結束的y點)
TranslateAnimation anim = new TranslateAnimation(leftMargin, -item.textMeasuredWidth, 0, 0);
//我們設置動畫的持續時間,彈幕移動多久,我們就持續多久動畫
anim.setDuration(item.moveSpeed);
// Interpolator 被用來修飾動畫效果,定義動畫的變化率,可以使存在的動畫效果accelerated(加速),decelerated(減速),repeated(重復),bounced(彈跳)等。
/*
* AccelerateDecelerateInterpolator 在動畫開始與結束的地方速率改變比較慢,在中間的時候加速
AccelerateInterpolator 在動畫開始的地方速率改變比較慢,然後開始加速
AnticipateInterpolator 開始的時候向後然後向前甩
AnticipateOvershootInterpolator 開始的時候向後然後向前甩一定值後返回最後的值
BounceInterpolator 動畫結束的時候彈起
CycleInterpolator 動畫循環播放特定的次數,速率改變沿著正弦曲線
DecelerateInterpolator 在動畫開始的地方快然後慢
LinearInterpolator 以常量速率改變
OvershootInterpolator 向前甩一定值後再回到原來位置
* */
anim.setInterpolator(new AccelerateDecelerateInterpolator());
/*
* fillBefore是指動畫結束時畫面停留在此動畫的第一幀;
fillAfter是指動畫結束是畫面停留在此動畫的最後一幀。
Java代碼設置如下:
/*****動畫結束時,停留在最後一幀*********
setFillAfter(true);
setFillBefore(false);
/*****動畫結束時,停留在第一幀*********
setFillAfter(false);
setFillBefore(true);
*
* */
anim.setFillAfter(true);
return anim;
}
/**
* 計算TextView中字符串的長度
*
* @param text 要計算的字符串
* @param Size 字體大小
* @return TextView中字符串的長度
*/
//因為我們的彈幕包裹在一個矩形中
public float getTextWidth(BarrageItem item, String text, float Size) {
Rect bounds = new Rect();
TextPaint paint;
paint = item.textView.getPaint();
//這裡參數是獲取文本對象,開始的長度,結束的長度,我們繪制好的矩形框
paint.getTextBounds(text, 0, text.length(), bounds);
return bounds.width();
}
/**
* 獲得每一行彈幕的最大高度
*
* @return
*/
private int getLineHeight() {
BarrageItem item = new BarrageItem();
String tx = itemText[0];
item.textView = new TextView(mContext);
item.textView.setText(tx);
item.textView.setTextSize(maxSize);
Rect bounds = new Rect();
TextPaint paint;
paint = item.textView.getPaint();
paint.getTextBounds(tx, 0, tx.length(), bounds);
return bounds.height();
}
class BarrageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
generateItem();
//每個彈幕產生的間隔時間隨機
int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random());
//多個消息可以使用同一個handler, 通過what不同區分不同的消息來源, 從而獲取消息內容
this.sendEmptyMessageDelayed(0, duration);
}
}
//記錄一下當前在顯示彈幕的高度,避免彈幕出現重疊
private Set existMarginValues = new HashSet<>();
private int linesCount;
// private int getRandomTopMargin()
// {
// //計算彈幕的空間高度
// if(totalLine==0)
// {
// totalLine=Rparams.getBottom()-Rparams.getTop()-Rparams.getPaddingTop()
// -Rparams.getPaddingBottom();
// if (totalHeight==0) {
// totalHeight = getMeasuredHeight();
// lineHeight = getLineHeight();
// totalLine = totalHeight / lineHeight;
// }
// //檢查重疊
// while (true) {
// int randomIndex = (int) (Math.random() * linesCount);
// int marginValue = (int) (randomIndex * (totalLine / linesCount));
//
// if (!existMarginValues.contains(marginValue)) {
// existMarginValues.add(marginValue);
// return marginValue;
// }
// }
//
//
// }
}
BarrageActivity.java
在這個類裡面我們可以去進行一些事件,但是我這裡沒有去處理,大家按自己的需求來。
package com.example.bibibibibibibibi;
import android.app.Activity;
import android.os.Bundle;
/**
* Created by lixueyong on 16/2/19.
*/
public class BarrageActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barrage);
}
}
還有一些關於顏色的xml 大家可以通過demo去看下了,這樣我們就實現了類似於bibibi彈幕的功能,是不是很簡單.
淺談Android app開發中Fragment的Transaction操作
在Android中,對Fragment的操作都是通過FragmentTransaction來執行。而從Fragment的結果來看,FragmentTransaction中
android 自定義view更新的時候遇到的問題(求解答)
今天遇到一個很奇怪的問題,關於在view裡面更新LRC歌詞的,view裡面有一個成員變量,lrcindex ,在draw裡面會用到它來更新歌詞,歌詞裡面有一行是紅色的,表
Android畫板的實現及demo
今天給大家帶來Android畫板功能的簡單實現,以下是效果圖: 以下是關鍵源碼: import android.content.Conte
Android開發之監聽發出的短信
運行效果圖: 預備知識: 為了監聽指定的ContentProvider的數據的改變,需要通過ContentResolver向指定Uri注冊CotentO