直播

本文详细介绍了一款直播应用的UI布局与交互设计,包括礼物飘心效果、弹幕开关、摄像头切换等功能的实现,以及美颜、滤镜等直播特效的调整方式。通过具体的代码示例,展示了如何使用SurfaceView、DanmakuView等组件构建直播界面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <com.tencent.rtmp.ui.TXCloudVideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:visibility="gone"/>
    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.bwie.chuliangliang.suixinbo_demo.mvp.ui.view.GiftView
        android:id="@+id/gift_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true" />
    <com.itheima.danmaku.ui.widget.DanmakuView
        android:id="@+id/sv_danmaku"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <Button
        android:id="@+id/btn_addGift"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_margin="20dp"
        android:background="@mipmap/xin"
        android:padding="14dp"
        android:textSize="18sp" />

    <Button
        android:id="@+id/btn_xiangji"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentStart="true"
        android:layout_alignTop="@+id/btn_addGift"
        android:layout_marginStart="86dp"
        android:background="@mipmap/xiangji"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="86dp" />

    <Button
        android:id="@+id/btn_shan"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentStart="true"
        android:layout_alignTop="@+id/btn_addGift"
        android:layout_marginStart="156dp"
        android:background="@mipmap/shan"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="156dp" />

    <Button
        android:id="@+id/button3"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentEnd="true"
        android:layout_alignTop="@+id/btn_addGift"
        android:layout_marginEnd="108dp"
        android:background="@mipmap/meiyan"
        android:layout_alignParentRight="true"
        android:layout_marginRight="90dp" />

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentStart="true"
        android:layout_marginBottom="88dp"
        android:max="9"
        android:visibility="invisible"
        android:layout_alignParentLeft="true" />

    <Button
        android:id="@+id/btn_start"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentEnd="true"
        android:layout_alignTop="@+id/btn_addGift"
        android:layout_marginEnd="35dp"
        android:background="@mipmap/xiaoxi"
        android:layout_alignParentRight="true"
        android:layout_marginRight="35dp" />
    //卡片布局
    <android.support.v7.widget.CardView
        android:layout_width="140dp"
        android:layout_height="60dp"
        android:layout_marginLeft="8dp"
        android:layout_marginBottom="8dp"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        app:cardCornerRadius="16dp"
        app:cardUseCompatPadding="true"
        app:cardPreventCornerOverlap="false"
        app:cardBackgroundColor="@android:color/transparent"
        >
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">
            <com.facebook.drawee.view.SimpleDraweeView
                android:id="@+id/my_image_view"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_marginLeft="10dp"
                app:placeholderImage="@drawable/tou"
                app:roundedCornerRadius="35dp"
                />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_gravity="center"
                android:textColor="#fff"
                android:text="11111"/>
        </LinearLayout>
    </android.support.v7.widget.CardView>

    <Switch
        android:id="@+id/switch2"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_alignParentEnd="true"
        android:layout_alignParentTop="true"
        android:layout_marginEnd="15dp"
        android:layout_marginTop="11dp"
        android:switchMinWidth="20dp"
        android:text="弹幕"
        android:textOff="off"
        android:textOn="on"
        android:textColor="#fff"
        android:thumb="@drawable/thumb"
        android:track="@drawable/track"
        android:layout_alignParentRight="true"
        android:layout_marginRight="15dp" />

    <android.support.v7.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_marginBottom="119dp"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        app:cardBackgroundColor="@android:color/transparent"
        app:cardCornerRadius="16dp"
        app:cardPreventCornerOverlap="false"
        app:cardUseCompatPadding="true"
        android:visibility="invisible"
        android:layout_alignParentRight="true">

        <ListView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="10dp"></ListView>
    </android.support.v7.widget.CardView>
</RelativeLayout>


Activity类

public class StartLivesActivity extends BaseActivity<StartLivesPresenter> implements StartLivesContract.View, View.OnClickListener {

    @BindView(R.id.surfaceView)
    SurfaceView surfaceView;
    @BindView(R.id.video_view)
    TXCloudVideoView videoView;
    @BindView(R.id.btn_addGift)
    Button btnAddGift;
    @BindView(R.id.gift_view)
    GiftView giftView;
    @BindView(R.id.btn_xiangji)
    Button btnXiangji;
    @BindView(R.id.btn_shan)
    Button btnShan;
    @BindView(R.id.button3)
    Button button3;
    @BindView(R.id.seekBar)
    SeekBar seekBar;
    @BindView(R.id.btn_start)
    Button btnStart;
    @BindView(R.id.sv_danmaku)
    DanmakuView svDanmaku;
    @BindView(R.id.my_image_view)
    SimpleDraweeView myImageView;
    @BindView(R.id.switch2)
    Switch switch2;
    @BindView(R.id.listView)
    ListView listView;
    @BindView(R.id.cardView)
    CardView cardView;
    private TXLivePlayer mLivePlayer;
    private TXLivePusher mLivePusher;
    private TXLivePushConfig mLivePushConfig;
    private PopupWindow popupWindow;
    private View view;
    private DanmakuView danmakuView;
    private EditText edit_text;
    private Button but_text;
    private List<MyBean> list;
    private MyAdapter myAdapter;
    private MyBean myBean;

    @Override
    public void setupActivityComponent(@NonNull AppComponent appComponent) {
        DaggerStartLivesComponent //如找不到该类,请编译一下项目
                .builder()
                .appComponent(appComponent)
                .startLivesModule(new StartLivesModule(this))
                .build()
                .inject(this);
    }

    @Override
    public int initView(@Nullable Bundle savedInstanceState) {
        return R.layout.activity_start_lives; //如果你不需要框架帮你设置 setContentView(id) 需要自行设置,请返回 0
    }
    //沉浸式
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus && Build.VERSION.SDK_INT >= 19) {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        }
    }

    @Override
    public void initData(@Nullable Bundle savedInstanceState) {
        //popupWindow
        view = View.inflate(this, R.layout.popup, null);
        edit_text = view.findViewById(R.id.edit_text);
        but_text = view.findViewById(R.id.but_text);
        but_text.setOnClickListener(this);
        popupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true);
        popupWindow.setBackgroundDrawable(new BitmapDrawable());
        popupWindow.setFocusable(true);
        popupWindow.setOutsideTouchable(true);
        list = new ArrayList<>();
        surfaceView.getHolder().setKeepScreenOn(true);
        surfaceView.getHolder().addCallback(new SurfaceViewLis());
        //BiBiLiLi弹幕
        switch2.setChecked(false);
        switch2.setSwitchTextAppearance(this, R.style.s_false);
        switch2.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    switch2.setSwitchTextAppearance(StartLivesActivity.this, R.style.s_true);
                    DanmakuContext context = DanmakuContext.create();
                    danmakuView = findViewById(R.id.sv_danmaku);
                    if (danmakuView != null) {
                        //根据数据源转化成弹幕解析器
                        BaseDanmakuParser mParser = DanmakuUtils.createParser(StartLivesActivity.this.getResources().openRawResource(R.raw.comments));
                        //设置回调监听
                        danmakuView.setCallback(new DrawHandler.Callback() {
                            @Override
                            public void updateTimer(DanmakuTimer timer) {

                            }

                            @Override
                            public void drawingFinished() {

                            }

                            @Override
                            public void danmakuShown(BaseDanmaku danmaku) {

                            }

                            //页面准备完毕就开始播放弹幕
                            @Override
                            public void prepared() {
                                danmakuView.start();
                            }
                        });

                        //准备数据
                        danmakuView.prepare(mParser, context);
                        //是否显示fps
                        danmakuView.showFPS(false);
                        //绘制缓存
                        danmakuView.enableDanmakuDrawingCache(true);
                    }
                } else {
                    switch2.setSwitchTextAppearance(StartLivesActivity.this, R.style.s_false);
                    danmakuView.stop();
                }
            }
        });
    }

    @Override
    public void showLoading() {

    }

    @Override
    public void hideLoading() {

    }

    @Override
    public void showMessage(@NonNull String message) {
        checkNotNull(message);
        ArmsUtils.snackbarText(message);
    }

    @Override
    public void launchActivity(@NonNull Intent intent) {
        checkNotNull(intent);
        ArmsUtils.startActivity(intent);
    }

    @Override
    public void killMyself() {
        finish();
    }

    @OnClick({R.id.btn_addGift, R.id.btn_xiangji, R.id.btn_shan, R.id.button3, R.id.btn_start})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.btn_addGift:
                giftView.addGifts();
                break;
            case R.id.btn_xiangji:
                // 默认是前置摄像头
                mLivePusher.switchCamera();
                break;
            case R.id.btn_shan:
                boolean mFlashTurnOn = false;
                if (!mLivePusher.turnOnFlashLight(mFlashTurnOn)) {
                    Toast.makeText(StartLivesActivity.this.getApplicationContext(),
                            "打开闪光灯失败:绝大部分手机不支持前置闪光灯!", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.button3:
                seekBar.setVisibility(View.VISIBLE);
                seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                        //配置:包括美颜、设置直播质量等等内容
                        //画质
                        mLivePusher.setVideoQuality(VIDEO_QUALITY_HIGH_DEFINITION, false, false);
                        //美颜
                        //style             磨皮风格:  0:光滑  1:自然  2:朦胧
                        //beautyLevel       磨皮等级: 取值为 0-9.取值为 0 时代表关闭美颜效果.默认值: 0,即关闭美颜效果.
                        //whiteningLevel    美白等级: 取值为 0-9.取值为 0 时代表关闭美白效果.默认值: 0,即关闭美白效果.
                        //ruddyLevel        红润等级: 取值为 0-9.取值为 0 时代表关闭美白效果.默认值: 0,即关闭美白效果.
                        mLivePusher.setBeautyFilter(1, progress, progress, progress);
                    }

                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {

                    }

                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {

                    }
                });
                //滤镜程度
                /*Bitmap bmp = null;
                bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
                if (mLivePusher != null) {
                    mLivePusher.setFilter(bmp);
                    mLivePusher.setSpecialRatio(0.1f);
                }*/
                break;
            case R.id.btn_start:
                popupWindow.showAtLocation(view, Gravity.BOTTOM, 0, 120);
                InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                inputMethodManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
                break;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // TODO: add setContentView(...) invocation
        ButterKnife.bind(this);
    }

    @Override
    public void onClick(View v) {
        //加载聊天内容
        cardView.setVisibility(View.VISIBLE);
        myBean = new MyBean();
        myBean.setName(edit_text.getText().toString());
        list.add(myBean);
        myAdapter = new MyAdapter(StartLivesActivity.this, list);
        listView.setAdapter(myAdapter);
    }


    private class SurfaceViewLis implements SurfaceHolder.Callback {

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            String sdkver = TXLiveBase.getSDKVersionStr();
            Log.e("liteavsdk", "liteav sdk version is : " + sdkver);
            mLivePusher = new TXLivePusher(StartLivesActivity.this);
            mLivePushConfig = new TXLivePushConfig();
            //直播加速
            boolean mHWVideoEncode = true;
            if (mHWVideoEncode) {
                if (mLivePushConfig != null) {
                    if (Build.VERSION.SDK_INT < 18) {
                        Toast.makeText(getApplicationContext(), "硬件加速失败,当前手机 API 级别过低(最低 18)",
                                Toast.LENGTH_SHORT).show();
                        mHWVideoEncode = false;
                    }
                }
            }

            mLivePushConfig.setHardwareAcceleration(mHWVideoEncode ?
                    TXLiveConstants.ENCODE_VIDEO_HARDWARE : TXLiveConstants.ENCODE_VIDEO_SOFTWARE);

            mLivePusher.setConfig(mLivePushConfig);
            mLivePusher.setMirror(true);
            String rtmpUrl = "rtmp://33071.livepush.myqcloud.com/live/33071_3cb8e8198c?bizid=33071&txSecret=ff430db14b6c187c19aabf383e0ef2e5&txTime=5BBF737F";
            mLivePusher.startPusher(rtmpUrl);
            mLivePusher.startCameraPreview(videoView);
            //创建 player 对象
            mLivePlayer = new TXLivePlayer(StartLivesActivity.this);

            //关键 player 对象与界面 view
            mLivePlayer.setPlayerView(videoView);
            String flvUrl = "http://33071.liveplay.myqcloud.com/live/33071_3cb8e8198c.flv";
            mLivePlayer.startPlay(flvUrl, TXLivePlayer.PLAY_TYPE_LIVE_FLV); //推荐 FLV
            // 设置填充模式
            mLivePlayer.setRenderMode(TXLiveConstants.RENDER_MODE_FULL_FILL_SCREEN);
            // 设置画面渲染方向
            mLivePlayer.setRenderRotation(TXLiveConstants.RENDER_ROTATION_PORTRAIT);
            TXLivePlayConfig mPlayConfig = new TXLivePlayConfig();
            mPlayConfig.setAutoAdjustCacheTime(true);
            mPlayConfig.setMinAutoAdjustCacheTime(1);
            mPlayConfig.setMaxAutoAdjustCacheTime(5);
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            mLivePlayer.stopPlay(true);
            videoView.onDestroy();
        }
    }
}
popup弹出输入框

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="horizontal">
    <EditText
        android:id="@+id/edit_text"
        android:layout_width="500dp"
        android:layout_height="30dp"
        android:background="@drawable/edit_bian"
        android:hint="请发表内容"
        android:textColor="#fff"
        android:layout_weight="9"/>
    <Button
        android:id="@+id/but_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="发表"/>
</LinearLayout>

自定义点赞飘心效果

public class GiftView extends SurfaceView implements SurfaceHolder.Callback {

    private SurfaceHolder mHolder;
    private ArrayList<GiftBean> mDatas;
    private ArrayList<GiftBean> newest;
    private int mWidth, mHeight;
    private MyThread myThread;


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

    public GiftView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHolder = this.getHolder();

        setZOrderOnTop(true);    // necessary
        mHolder.setFormat(PixelFormat.TRANSPARENT);

        mHolder.addCallback(this);
        myThread = new MyThread();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mWidth = w;
        mHeight = h;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {


        mDatas = new ArrayList<>();

        newest = new ArrayList<>();

        Collections.synchronizedList(newest);//这样就可以多线程访问了

        //刚进来就让他先刷一波
        for (int i = 0; i < 10; i++) {
            Point[] randomPoint = createRandomPoint();
            GiftBean bean = new GiftBean(randomPoint);
            mDatas.add(bean);
        }

        if (!myThread.isAlive()) {
            myThread = new MyThread();
            myThread.start();
        }
    }


    //添加新的小礼物
    public void addGifts() {

        for (int i = 0; i < Math.random() * 5; i++) {
            Point[] randomPoint = createRandomPoint();
            GiftBean bean = new GiftBean(randomPoint);
            newest.add(bean);
        }

        if (!myThread.isAlive()) {
            myThread = new MyThread();
            myThread.start();
        }

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.i("Alex" , "surfaceChanged");
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.i("Alex", "surfaceDestroyed");
    }


    class MyThread extends Thread {

        private Bitmap mBitmap;
        private Paint paint;

        public MyThread() {

            paint = new Paint();

            //这个很重要,要把之前的清空
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));

            //小礼物就长这样
            mBitmap = getBitmapFromDrawable(R.mipmap.ic_launcher);
        }

        @Override
        public void run() {


            while (mDatas.size() > 0 || newest.size() > 0) {

                Canvas canvas = mHolder.lockCanvas();
                if (canvas == null) {
                    mDatas.clear();
                    newest.clear();
                    return;
                }

                //需要把爬出去的小礼物给清空
                List<GiftBean> delete = new ArrayList<>();

                Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
                Canvas canvas_temp = new Canvas(bitmap);
                //这里新创建一个canvas起到了双缓冲的作用,在这里其实作用不大,但是这个用法值得学习
                //双缓冲是一种在内存中保留屏幕的副本或缓存的技术,先将所有图形都加载在内存中,然后一起绘制到屏幕上,避免了直接在屏幕上绘图时出现的明显闪烁。


                //先看看有没有刚加进来的小礼物
                mDatas.addAll(newest);
                newest.clear();

                for (GiftBean giftSimple : mDatas) {
                    giftSimple.time += 1;
                    float x = 0.5f * giftSimple.a * giftSimple.time * giftSimple.time;
                    giftSimple.t = x / mHeight;
                    giftSimple.alpha = 1f - giftSimple.t;
                    if (giftSimple.t >= 1f) {
                        delete.add(giftSimple);
                        continue;
                    }
                    Point point = evaluate(giftSimple.t, giftSimple.pointStart, giftSimple.pointFirst, giftSimple.pointSecond, giftSimple.pointEnd);
                    Paint paint_temp = new Paint();
                    paint_temp.setAlpha((int) (giftSimple.alpha * 255));
                    canvas_temp.drawBitmap(mBitmap, point.x, point.y, paint_temp);
                }


                canvas.drawBitmap(bitmap, 0, 0, paint);
                SystemClock.sleep(10);
                mHolder.unlockCanvasAndPost(canvas);
                mDatas.removeAll(delete);//把爬出去的小礼物都给清空
            }


        }
    }

    protected Bitmap getBitmapFromDrawable(@DrawableRes int resId) {
        BitmapDrawable drawable = (BitmapDrawable) getContext().getResources().getDrawable(resId);
        Bitmap bitmap = drawable.getBitmap();
        return Bitmap.createScaledBitmap(bitmap, 50, 50, false);
    }

    //利用三阶贝塞尔曲线公式算出中间点坐标,不知道公式的可以上网了解下,
    public Point evaluate(float t, Point startValue, Point first, Point second, Point endValue) { //利用三阶贝塞尔曲线公式算出中间点坐标
        int x = (int) (startValue.x * Math.pow((1 - t), 3) + 3 * first.x * t * Math.pow((1 - t), 2) + 3 * second.x * Math.pow(t, 2) * (1 - t) + endValue.x * Math.pow(t, 3));
        int y = (int) (startValue.y * Math.pow((1 - t), 3) + 3 * first.y * t * Math.pow((1 - t), 2) + 3 * second.y * Math.pow(t, 2) * (1 - t) + endValue.y * Math.pow(t, 3));
        return new Point(x, y);
    }

    //随机生成小礼物的四个点
    private Point[] createRandomPoint() {
        Point point_start = new Point(mWidth / 8, mHeight);//我希望小礼物都从控件的底部中间出来,都在相同的地方出生,然后人生就瞎跑吧。
        Point point_end = new Point((int) (Math.random() * mWidth), 0);//这个没什么好说,从哪出去都行,反正都在控件的最顶部

        //随机生成的两个控制点
        Point point_first = new Point((int) (Math.random() * (mWidth / 2)), (int) (Math.random() * (mHeight / 2) + mHeight / 2));
        Point point_second = new Point((int) (Math.random() * (mWidth / 2) + mWidth / 2), (int) (Math.random() * (mHeight / 2)));

        //下面的代码我就想表达一个意思,小礼物出生后我不想让所有的都先往左跑,或者都往又跑,而是随机的,这个方法有点烂,大家轻喷
        double leftOrRight = Math.random()-0.5d;
        if (leftOrRight>0){
            point_first.x = point_first.x+mWidth/2;
            point_second.x = point_second.x-mWidth/2;

        }
        return new Point[]{point_start, point_first, point_second, point_end};
    }
}

飘心Bean类

public class GiftBean {
    public float alpha = 1f;//小礼物会慢慢的变透明,0<=alpha<=1
    public Point pointStart;//开始点
    public Point pointEnd;//结束
    public Point pointFirst;//我么采用三阶贝塞尔曲线,这是第一个参考点
    public Point pointSecond;//这是第二个参考点
    public float a;//加速度,我们并不希望小礼物慢吞吞的运动,更不希望小礼物一闪而过,大家也可以用一下插值器,
    //在这我就用大家最熟悉的加速度了,毕竟大家的物理都不是白学的
    public int time;
    public float t;//贝塞尔曲线的变量 并且  0<=t<=1

    public GiftBean(Point[] randomPoint) {
        //随机生成的加速度,毕竟偶们不希望每个小礼物都有相同的速度,看起来像部队一样,不够调皮
        float random = (float) Math.random();
        this.a = Math.min(0.1f,random);
        this.a = Math.max(0.075f,a);//我调的比较靠谱的加速度,大家也可以改改试试

        this.pointStart = randomPoint[0];
        this.pointFirst = randomPoint[1];
        this.pointSecond = randomPoint[2];
        this.pointEnd = randomPoint[3];
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值