Android的簡(jiǎn)易彈幕


1.布局文件:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.skg.danmudemo.MainActivity">
    <!--自定義彈幕顯示框控件-->
    <com.skg.danmudemo.DanMuView
        android:id="@+id/danmu"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/text"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"/>

        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="發(fā)送"/>

    </LinearLayout>
</LinearLayout>

2.主類(lèi): MainActivity

/**
 * @author ClamLaw
 * @time 2016/11/26  
 * @desc MainActivity
 */
public class MainActivity extends AppCompatActivity {

    private EditText mText;
    private List<String> mList = new ArrayList<>();
    private DanMuView mDanMuView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();//初始化
        initData();

    }

    private void initData() {
        String[] array = getResources().getStringArray(R.array.danmuList);//先獲取部分?jǐn)?shù)據(jù)
        for (int i = 0; i < array.length; i++) {
            mList.add(array[i]);
        }
        mDanMuView.setData(mList);//向彈幕框添加數(shù)據(jù)
        mDanMuView.startDanmu();//開(kāi)始彈幕
    }

    //初始化
    private void initView() {
        mText = (EditText) findViewById(R.id.text);
        mDanMuView = (DanMuView) findViewById(R.id.danmu);
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {//發(fā)送按鈕
            @Override
            public void onClick(View v) {
                //發(fā)送評(píng)論
                String str = mText.getText().toString();
                mDanMuView.insertDanmu(str);
            }
        });
    }
}

3.自定義彈幕顯示框控件: DanMuView

/**
 * @author ClamLaw
 * @time 2016/11/26  19:01
 * @desc 自定義彈幕的view
 */
public class DanMuView extends RelativeLayout {
    private Context mContext;
    private View view;
    private RelativeLayout mContainerVG;
    private List<String> mList;
    //父組件的高度
    private int validHeightSpace;
    private Set existMarginValues = new HashSet<>();
    private int lastMarginValue;
    ExecutorService executorService = Executors.newFixedThreadPool(1);//線(xiàn)程池

    Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    int index = msg.arg1;
                    String data = mList.get(index);
                    showDanmu(data);
                    break;
            }
        }
    };

    public DanMuView(Context context) {
        super(context);
    }

    public DanMuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        view = LayoutInflater.from(context).inflate(R.layout.danmu_view, null, false);
        mContainerVG = (RelativeLayout) view.findViewById(R.id.danmu);
        mContainerVG.setBackgroundDrawable(null);
        addView(view);
    }

    /**
     * 添加數(shù)據(jù)到彈幕中
     *
     * @param list
     */
    public void setData(List<String> list) {
        this.mList = list;
    }

    /**
     * 用戶(hù)發(fā)送評(píng)論彈幕
     *
     * @param data
     */
    public void insertDanmu(String data) {
        showDanmu(data);//顯示彈幕
        mList.add(data);//將彈幕信息添加到集合的第一位
        startDanmu();//開(kāi)始彈幕
    }

    /**
     * 開(kāi)始彈幕
     */
    public void startDanmu() {
       existMarginValues.clear();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                for (int i =0 ;i<Integer.MAX_VALUE; i++){
                    int size = mList.size();
                    if (size==0){
                        break;
                    }
                    mHandler.obtainMessage(1,i%size,0).sendToTarget();
                    SystemClock.sleep(1000);
                }
            }
        });
    }

    /**
     * 顯示彈幕
     */
    public void showDanmu(String data) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_danmu, null, false);
        final RelativeLayout danmu_container = (RelativeLayout) view.findViewById(R.id.danmu_container);
        TextView danmu_text = (TextView) view.findViewById(R.id.danmu_text);
        ImageView danmu_img = (ImageView)view.findViewById(R.id.danmu_img);
        String str = clipLongTextByChineseCount(data, 12);//評(píng)論內(nèi)容長(zhǎng)度限制
        danmu_img.setImageResource(R.drawable.deta_shoucz);
        danmu_text.setText(str);//設(shè)置彈幕
        danmu_container.setBackgroundResource(R.drawable.textview_bg);//設(shè)置背景

        int leftMargin = mContainerVG.getRight() - mContainerVG.getLeft() - mContainerVG.getPaddingLeft();
        //計(jì)算本條彈幕的topMargin(隨機(jī)值,但是與屏幕中已有的不重復(fù))
        int verticalMargin = getRandomTopMargin();

        LayoutParams params = new LayoutParams(
            LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        params.addRule(RelativeLayout.ALIGN_PARENT_TOP);//設(shè)置節(jié)點(diǎn)屬性,上邊緣
        params.topMargin = verticalMargin;

        danmu_container.setLayoutParams(params);

        //動(dòng)畫(huà)
        Animation anim = new AnimationHelper().createTranslateAnim(mContext, leftMargin, -getScreenWidth((Activity) mContext));
        //動(dòng)畫(huà)監(jiān)聽(tīng)
        anim.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                //動(dòng)畫(huà)結(jié)束時(shí)移除控件
                mContainerVG.removeView(danmu_container);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        danmu_container.startAnimation(anim);//開(kāi)始動(dòng)畫(huà)
        mContainerVG.addView(danmu_container);
    }

    private int getRandomTopMargin() {
        if (validHeightSpace == 0) {
            validHeightSpace = mContainerVG.getBottom() - mContainerVG.getTop()
                - mContainerVG.getPaddingBottom() - mContainerVG.getTop();
        }
        if (existMarginValues.size() == 3) {
            existMarginValues.clear();
            while (true) {
                int randomIndex = (int) (Math.random() * 5);
                int marginValue = randomIndex * (validHeightSpace / 5);
                if (lastMarginValue != marginValue) {
                    existMarginValues.add(marginValue);
                    return marginValue;
                }
            }
        } else {
            //檢查重疊
            while (true) {
                int randomIndex = (int) (Math.random() * 5);
                int marginValue = randomIndex * (validHeightSpace / 5);

                if (!existMarginValues.contains(marginValue)) {
                    existMarginValues.add(marginValue);
                    if (existMarginValues.size() == 3) {
                        lastMarginValue = marginValue;
                    }
                    return marginValue;
                }
            }
        }
    }

    /**
     * 獲取屏幕的寬度
     * @param context
     * @return
     */
    private int getScreenWidth(Context context) {
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        return manager.getDefaultDisplay().getWidth();
    }

    /**
     * 評(píng)論的長(zhǎng)度限制
     * @param str
     * @param count
     * @return
     */
    public static String clipLongTextByChineseCount(String str, int count) {
        if (str != null) {
            final String encoding = "GBK";
            try {
                byte[] b = str.getBytes(encoding);
                if (b.length >= (count + 1) * 2) {
                    int end = count * 2;
                    String result = new String(b, 0, end, encoding);
                    if (str.indexOf(result) == -1) {
                        return new String(b, 0, end - 1, encoding) + "...";
                    }
                    return result + "...";
                }
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
        return str;
    }

    //動(dòng)畫(huà)
    public class AnimationHelper{

        public AnimationHelper() {
        }
        //fromX 開(kāi)始的位置 ,toX結(jié)束的位置
        public Animation createTranslateAnim(Context context, int fromX, int toX){
            TranslateAnimation translateAnimation = new TranslateAnimation(fromX, toX, 0, 0);
            int width = getScreenWidth((Activity)context);//獲取屏幕的寬度
            //自動(dòng)計(jì)算時(shí)間
            long duration = (long) (Math.abs(toX - fromX) * 1.0f / width * 6000);
            translateAnimation.setDuration(duration);//動(dòng)畫(huà)時(shí)間
           // translateAnimation.setInterpolator(new DecelerateAccelerateInterpolator());//動(dòng)畫(huà)速率
            translateAnimation.setFillAfter(true);//終止時(shí)停留最后一幀
            return translateAnimation;
        }
    }



    public class DecelerateAccelerateInterpolator implements Interpolator {
        @Override
        public float getInterpolation(float input) {
            return (float) (Math.tan((input * 2 - 1) / 4 * Math.PI)) / 2.0f + 0.5f;
        }
    }
}

4.彈幕容器布局:danmu_view.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <RelativeLayout
        android:id="@+id/danmu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

5.彈幕子控件布局:item_danmu.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/danmu_container"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    <ImageView
        android:id="@+id/danmu_img"
        android:layout_width="25dp"
        android:layout_height="25dp"
        android:layout_marginLeft="1px"
        android:layout_marginTop="1px"/>
    <TextView
        android:id="@+id/danmu_text"
        android:textColor="@android:color/white"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:layout_marginLeft="25dp"
        android:text="123123"
        android:textSize="20dp"/>
</RelativeLayout>

本Demo的源碼下載鏈接:DanMuDemo.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末徘钥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子光督,更是在濱河造成了極大的恐慌,老刑警劉巖残揉,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸥滨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡燕鸽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)啼辣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)啊研,“玉大人,你說(shuō)我怎么就攤上這事鸥拧〉吃叮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵富弦,是天一觀(guān)的道長(zhǎng)沟娱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)腕柜,這世上最難降的妖魔是什么济似? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮盏缤,結(jié)果婚禮上砰蠢,老公的妹妹穿的比我還像新娘。我一直安慰自己蛾找,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布赵誓。 她就那樣靜靜地躺著打毛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俩功。 梳的紋絲不亂的頭發(fā)上幻枉,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音诡蜓,去河邊找鬼熬甫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蔓罚,可吹牛的內(nèi)容都是我干的椿肩。 我是一名探鬼主播瞻颂,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼郑象!你這毒婦竟也來(lái)了贡这?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤厂榛,失蹤者是張志新(化名)和其女友劉穎盖矫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體击奶,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辈双,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柜砾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湃望。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖局义,靈堂內(nèi)的尸體忽然破棺而出喜爷,到底是詐尸還是另有隱情,我是刑警寧澤萄唇,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布檩帐,位于F島的核電站,受9級(jí)特大地震影響另萤,放射性物質(zhì)發(fā)生泄漏湃密。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一四敞、第九天 我趴在偏房一處隱蔽的房頂上張望泛源。 院中可真熱鬧,春花似錦忿危、人聲如沸达箍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)缎玫。三九已至,卻和暖如春解滓,著一層夾襖步出監(jiān)牢的瞬間赃磨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工洼裤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邻辉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像值骇,于是被迫代替她去往敵國(guó)和親莹菱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容