編輯:關於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 ListitemList = 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中,對Fragment的操作都是通過FragmentTransaction來執行。而從Fragment的結果來看,FragmentTransaction中
今天遇到一個很奇怪的問題,關於在view裡面更新LRC歌詞的,view裡面有一個成員變量,lrcindex ,在draw裡面會用到它來更新歌詞,歌詞裡面有一行是紅色的,表
今天給大家帶來Android畫板功能的簡單實現,以下是效果圖: 以下是關鍵源碼: import android.content.Conte
運行效果圖: 預備知識: 為了監聽指定的ContentProvider的數據的改變,需要通過ContentResolver向指定Uri注冊CotentO