国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自制精彩彈幕效果

2019-10-22 18:30:42
字體:
來源:轉載
供稿:網友

好久沒有寫過文章,最近發現直播特別的火,很多app都集成了直播的功能,發現有些直播是帶有彈幕的,效果還不錯,今天心血來潮,特地寫了篇制作彈幕的文章.

今天要實現的效果如下:

1.彈幕垂直方向固定

Android,彈幕

2.彈幕垂直方向隨機

Android,彈幕

上面效果圖中白色的背景就是彈幕本身,是一個自定義的FrameLayout,我這里是為了更好的展示彈幕的位置才設置成了白色,當然如果是疊加在VideoView上的話,就需要設置成透明色了.
制作彈幕需要考慮以下幾點問題:
1.彈幕的大小可以隨意調整
2.彈幕內移動的item(或者稱字幕)出現的位置,水平方向是從屏幕右邊移動到屏幕左邊,垂直方向是不能超出彈幕本身的高度的.
3.字幕移除屏幕后,需要將對應item(字幕)從其父容器(彈幕)中移除.
4.如果字幕出現的垂直方向的高度是隨機的,那么還需要避免字幕重疊的情況.

ok,下面是彈幕自定義view的代碼:

 

/** * Created by dell on 2016/9/28. */public class DanmuView extends FrameLayout { private static final String TAG = "DanmuView"; private static final long DEFAULT_ANIM_DURATION = 6000; //默認每個動畫的播放時長 private static final long DEFAULT_QUERY_DURATION = 3000; //遍歷彈幕的默認間隔 private LinkedList<View> mViews = new LinkedList<>();//彈幕隊列 private boolean isQuerying; private int mWidth;//彈幕的寬度 private int mHeight;//彈幕的高度 private Handler mUIHandler = new Handler(); private boolean TopDirectionFixed;//彈幕頂部的方向是否固定 private Handler mQueryHandler; private int mTopGravity = Gravity.CENTER_VERTICAL;//頂部方向固定時的默認對齊方式 public void setHeight(int height) {  mHeight = height; } public void setWidth(int width) {  mWidth = width; } public void setTopGravity(int gravity) {  this.mTopGravity = gravity; } public void add(List<Danmu> danmuList) {  for (int i = 0; i < danmuList.size(); i++) {   Danmu danmu = danmuList.get(i);   addDanmuToQueue(danmu);  } } public void add(Danmu danmu) {  addDanmuToQueue(danmu); } public DanmuView(Context context) {  this(context, null); } public DanmuView(Context context, AttributeSet attrs) {  this(context, attrs, 0); } public DanmuView(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  HandlerThread thread = new HandlerThread("query");  thread.start();  //循環取出彈幕顯示  mQueryHandler = new Handler(thread.getLooper()) {   @Override   public void handleMessage(Message msg) {    final View view = mViews.poll();    if (null != view) {     mUIHandler.post(new Runnable() {      @Override      public void run() {       //添加彈幕       showDanmu(view);      }     });    }    sendEmptyMessageDelayed(0, DEFAULT_QUERY_DURATION);   }  }; } /**  * 將要展示的彈幕添加到隊列中  *  * @param danmu  */ private void addDanmuToQueue(Danmu danmu) {  if (null != danmu) {   final View view = View.inflate(getContext(), R.layout.layout_danmu, null);   TextView usernameTv = (TextView) view.findViewById(R.id.tv_username);   TextView infoTv = (TextView) view.findViewById(R.id.tv_info);   ImageView headerIv = (ImageView) view.findViewById(R.id.iv_header);   usernameTv.setText(danmu.getUserName());//昵稱   infoTv.setText(danmu.getInfo());//信息   Glide.with(getContext()).//頭像     load(danmu.getHeaderUrl()).     transform(new CropCircleTransformation(getContext())).into(headerIv);   view.measure(0, 0);   //添加彈幕到隊列中   mViews.offerLast(view);  } } /**  * 播放彈幕  *  * @param topDirectionFixed 彈幕頂部的方向是否固定  */ public void startPlay(boolean topDirectionFixed) {  this.TopDirectionFixed = topDirectionFixed;  if (mWidth == 0 || mHeight == 0) {   getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {    @SuppressLint("NewApi")    @Override    public void onGlobalLayout() {     getViewTreeObserver().removeOnGlobalLayoutListener(this);     if (mWidth == 0) mWidth = getWidth() - getPaddingLeft() - getPaddingRight();     if (mHeight == 0) mHeight = getHeight() - getPaddingTop() - getPaddingBottom();     if (!isQuerying) {      mQueryHandler.sendEmptyMessage(0);     }    }   });  } else {   if (!isQuerying) {    mQueryHandler.sendEmptyMessage(0);   }  } } /**  * 顯示彈幕,包括動畫的執行  *  * @param view  */ private void showDanmu(final View view) {  isQuerying = true;  Log.d(TAG, "mWidth:" + mWidth + " mHeight:" + mHeight);  final LayoutParams lp = new LayoutParams(view.getMeasuredWidth(), view.getMeasuredHeight());  lp.leftMargin = mWidth;  if (TopDirectionFixed) {   lp.gravity = mTopGravity | Gravity.LEFT;  } else {   lp.gravity = Gravity.LEFT | Gravity.TOP;   lp.topMargin = getRandomTopMargin(view);  }  view.setLayoutParams(lp);  view.setTag(lp.topMargin);  //設置item水平滾動的動畫  ValueAnimator animator = ValueAnimator.ofInt(mWidth, -view.getMeasuredWidth());  animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {   @Override   public void onAnimationUpdate(ValueAnimator animation) {    lp.leftMargin = (int) animation.getAnimatedValue();    view.setLayoutParams(lp);   }  });  addView(view);//顯示彈幕  animator.setDuration(DEFAULT_ANIM_DURATION);  animator.setInterpolator(new LinearInterpolator());  animator.start();//開啟動畫  animator.addListener(new AnimatorListenerAdapter() {   @Override   public void onAnimationEnd(Animator animation) {    view.clearAnimation();    existMarginValues.remove(view.getTag());//移除已使用過的頂部邊距    removeView(view);//移除彈幕    animation.cancel();   }  }); } //記錄當前仍在顯示狀態的彈幕的垂直方向位置(避免重復) private Set<Integer> existMarginValues = new HashSet<>(); private int linesCount; private int range = 10; private int getRandomTopMargin(View view) {  //計算可用的行數  linesCount = mHeight / view.getMeasuredHeight();  if (linesCount <= 1) {   linesCount = 1;  }  Log.d(TAG, "linesCount:" + linesCount);  //檢查重疊  while (true) {   int randomIndex = (int) (Math.random() * linesCount);   int marginValue = randomIndex * (mHeight / linesCount);   //邊界檢查   if (marginValue > mHeight - view.getMeasuredHeight()) {    marginValue = mHeight - view.getMeasuredHeight() - range;   }   if (marginValue == 0) {    marginValue = range;   }   if (!existMarginValues.contains(marginValue)) {    existMarginValues.add(marginValue);    Log.d(TAG, "marginValue:" + marginValue);    return marginValue;   }  } }}

彈幕實體類:

 

/** * Created by dell on 2016/9/28. */public class Danmu { private String headerUrl;//頭像 private String userName;//昵稱 private String info;//信息 public String getHeaderUrl() {  return headerUrl; } public void setHeaderUrl(String headerUrl) {  this.headerUrl = headerUrl; } public String getUserName() {  return userName; } public void setUserName(String userName) {  this.userName = userName; } public String getInfo() {  return info; } public void setInfo(String info) {  this.info = info; }}測試類,MainActivitypublic class MainActivity extends AppCompatActivity { DanmuView mDanmuView; EditText mMsgEdt; Button mSendBtn; Handler mDanmuAddHandler; boolean continueAdd; int counter; @Override protected void onResume() {  super.onResume();  mDanmuView.startPlay(true);//true表示彈幕的垂直方向是固定的,false則隨機  continueAdd = true;  mDanmuAddHandler.sendEmptyMessageDelayed(0, 6000); } @Override protected void onPause() {  super.onPause();  continueAdd = false;  mDanmuAddHandler.removeMessages(0); } @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  initView();  initData();  initListener(); } private void initView() {  mDanmuView = (DanmuView) findViewById(R.id.danmuView);  mMsgEdt = (EditText) findViewById(R.id.edt_msg);  mSendBtn = (Button) findViewById(R.id.btn_send); } private void initData() {  List<Danmu> danmuList = new ArrayList<>();  for (int i = 0; i < 3; i++) {   Danmu danmu = new Danmu();   danmu.setHeaderUrl("http://tupian.qqjay.com/tou3/2016/0725/cb00091099ffbf09f4861f2bbb5dd993.jpg");   danmu.setUserName("Mr.chen" + i);   danmu.setInfo("我是彈幕啊,不要問我為什么不可以那么長!!!");   danmuList.add(danmu);  }  mDanmuView.add(danmuList);  //下面是模擬每秒添加一個彈幕的過程  HandlerThread ht = new HandlerThread("send danmu");  ht.start();  mDanmuAddHandler = new Handler(ht.getLooper()) {   @Override   public void handleMessage(Message msg) {    runOnUiThread(new Runnable() {     @Override     public void run() {      Danmu danmu = new Danmu();      danmu.setHeaderUrl("http://tupian.qqjay.com/tou3/2016/0803/87a8b262a5edeff0e11f5f0ba24fb22f.jpg");      danmu.setUserName("Mr.new" + (counter++));      danmu.setInfo("新的彈幕啊!!!新的彈幕啊!!!新的彈幕啊!!!新的彈幕啊!!!");      mDanmuView.add(danmu);     }    });    //繼續添加    if (continueAdd) {     sendEmptyMessageDelayed(0, 1000);    }   }  }; } private void initListener() {  //手動添加  mSendBtn.setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View v) {    String msg = mMsgEdt.getText().toString().trim();    if (TextUtils.isEmpty(msg)) {     Toast.makeText(MainActivity.this, "親,你想發送什么啊?", Toast.LENGTH_SHORT).show();     return;    }    mMsgEdt.setText("");    Danmu danmu = new Danmu();    danmu.setHeaderUrl("http://img0.imgtn.bdimg.com/it/u=2198087564,4037394230&fm=11&gp=0.jpg");    danmu.setUserName("I'am good man");    danmu.setInfo("我是新人:" + msg);    mDanmuView.add(danmu);   }  }); }}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 准格尔旗| 汕头市| 红原县| 富顺县| 彭水| 永春县| 怀宁县| 甘泉县| 思茅市| 喜德县| 海盐县| 长沙县| 崇文区| 睢宁县| 龙山县| 临桂县| 达孜县| 察雅县| 榆林市| 湾仔区| 普兰县| 临澧县| 长沙县| 绍兴市| 萍乡市| 门头沟区| 格尔木市| 东平县| 临汾市| 石林| 祁东县| 阿拉善盟| 工布江达县| 兴安县| 平阳县| 无锡市| 崇信县| 防城港市| 寿宁县| 江安县| 舒城县|