19/12/11 13:51:先发个标题,有空了填充内容。
最近在忙着嵌入腾讯云直播SDK到项目里去,所以很久没更博了,直播做完后续会出一篇接入腾讯云直播的文章。废话不多说,直接上码。-19/12/24
正文:
1.获取悬浮窗权限代码
private boolean checkLivePermission() {
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE_AUDIO_RECORD);
return false;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(mContext)) {
Toast.makeText(mContext, "直播需要悬浮窗权限~", Toast.LENGTH_LONG).show();
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
isRequestOverlay = true;
startActivityForResult(intent, REQUEST_CODE_OVERLAY);
return false;
}
hasLivePermission = true;
return true;
}
进入页面监测是否获取到悬浮窗权限和录音权限(直播需要),如果已有返回true。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_AUDIO_RECORD) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(mContext)) {
isRequestOverlay = true;
}
} else {
Toast.makeText(mContext, "直播需要录音权限~", Toast.LENGTH_LONG).show();
ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE_AUDIO_RECORD);
}
}
}
权限回调实现代码,REQUEST_CODE_AUDIO_RECORD自己随便定义一个int值即可。
if (isRequestOverlay) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(mContext)) {
Toast.makeText(mContext, "直播需要悬浮窗权限~", Toast.LENGTH_LONG).show();
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
isRequestOverlay = true;
startActivityForResult(intent, REQUEST_CODE_OVERLAY);
} else {
LocalBroadcastManager.getInstance(mContext).registerReceiver(netBreakReceiver, new IntentFilter(Constant.ACTION_LIVE_NET_BREAK));
isRequestOverlay = false;
TXLiveUtils.getInstance().startLive(liveId);
//开始直播计时
c_timer.setBase(SystemClock.elapsedRealtime() - mRecordTime);
c_timer.start();
fillComments();
}
}
上面这段代码写在onResume中,isRequestOverLay是一个bool值变量,初始为false。
写在onResume不写在权限申请回调中的原因是:申请悬浮窗权限不走权限回调。
这里我检测到获取悬浮窗权限成功后就会开启直播,按照自己的需求替换一下即可。
2.悬浮窗代码
/**
* 弹出直播悬浮窗
*/
private void showLiveFloatWindow() {
if (windowManager == null) {
windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
}
if (topToolBar == null) {
topToolBar = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.live_top_tool_bar, null);
//设置参数
toolBarParams = new WindowManager.LayoutParams();
toolBarParams.gravity = Gravity.LEFT | Gravity.TOP;
toolBarParams.x = SizeUtils.dp2px(20);
toolBarParams.y = SizeUtils.dp2px(15.5f);
toolBarParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
toolBarParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
toolBarParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
toolBarParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
//设置图片格式,效果为背景透明
toolBarParams.format = PixelFormat.RGBA_8888;
//设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
//toolBarParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;
//设置可以显示在状态栏上
toolBarParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR |
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
}
if (chatFloatWindow == null) {
chatFloatWindow = (RelativeLayout) LayoutInflater.from(mContext).inflate(R.layout.chat_float_window, null);
//设置参数
chatFloatParams = new WindowManager.LayoutParams();
// chatFloatParams.gravity = Gravity.BOTTOM;
chatFloatParams.width = WindowManager.LayoutParams.MATCH_PARENT;
chatFloatParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
chatFloatParams.y = ScreenUtils.getScreenHeight() / 2 - SizeUtils.dp2px(120);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
chatFloatParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
chatFloatParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
//设置图片格式,效果为背景透明
chatFloatParams.format = PixelFormat.RGBA_8888;
//设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
//chatFloatParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;
//设置可以显示在状态栏上
chatFloatParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR |
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
}
windowManager.addView(topToolBar, toolBarParams);
windowManager.addView(chatFloatWindow, chatFloatParams);
initEvent();
}
博主需要展示两个悬浮布局,因此使用WindowManager添加了两个view进去。
下面是初始化控件、设置点击平移事件:
private void initEvent() {
LinearLayout ll_top_toolbar = topToolBar.findViewById(R.id.ll_top_toolbar);
c_timer = topToolBar.findViewById(R.id.c_timer);
c_timer.setBase(SystemClock.elapsedRealtime() - mRecordTime);
c_timer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
@Override
public void onChronometerTick(Chronometer chronometer) {
mRecordTime = SystemClock.elapsedRealtime() - chronometer.getBase();
Constant.LIVE_DURATION = mRecordTime;
SharedPreferencesUtil.setLongDate(Constant.SPKEY_LIVE_DURATION, Constant.LIVE_DURATION);
}
});
c_timer.start();
ImageView iv_back_app = topToolBar.findViewById(R.id.iv_back_app);
iv_back_app.setOnClickListener(onClickListener);
RelativeLayout rl_comments = chatFloatWindow.findViewById(R.id.rl_comments);
rl_comments.setOnClickListener(onClickListener);
rl_comments.setOnTouchListener(onTouchListener);
rl_bottom_comments = chatFloatWindow.findViewById(R.id.rl_bottom_comments);
iv_close = chatFloatWindow.findViewById(R.id.iv_close);
iv_close.setOnClickListener(onClickListener);
recyclerView_comments = chatFloatWindow.findViewById(R.id.recyclerView_comments);
recyclerView_comments.setLayoutManager(new LinearLayoutManager(mContext));
recyclerView_comments.addItemDecoration(new OffsetRecyclerDivider(0, SizeUtils.dp2px(9.5f)));
commentsAdapter = new LiveCommentsAdapter(mContext, chatBeanList);
recyclerView_comments.setAdapter(commentsAdapter);
if (!Utils.isListEmpty(chatBeanList)) {
recyclerView_comments.scrollToPosition(chatBeanList.size() - 1);
}
TXLiveUtils.getInstance().addOnReceiveMessage(onReceiveMessage);
TXLiveUtils.getInstance().addOnWatchCountChanged(new TXLiveUtils.OnWatchCountChangedListener() {
@Override
public void onWatchCountChanged(int watchCount) {
// LogUtil.i(TAG+" watchCount : "+watchCount);
}
});
}
博主的可移动悬浮窗宽度是铺满屏幕的,因此只支持竖直方向平移,需要加上水平方向平移的自行添加,原理一样。
private float touchX, touchY;
private float moveX, moveY;
private View.OnTouchListener onTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
touchX = event.getRawX();
touchY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
moveX = event.getRawX();
moveY = event.getRawY();
// chatFloatParams.x += moveX - touchX;
chatFloatParams.y += moveY - touchY;
LogUtil.i(TAG + " dx = " + (moveX - touchX) + " ,dy = " + (moveY - touchY) + ",currentX = " + chatFloatParams.x + ",currentY = " + chatFloatParams.y);
windowManager.updateViewLayout(chatFloatWindow, chatFloatParams);
touchX = moveX;
touchY = moveY;
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}
};
private View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.iv_back_app:
Intent intent = new Intent(mContext, LiveRoomActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mRecordTime = getLiveDuration();
intent.putExtra(Constant.INTENT_KEY_DURATION, mRecordTime);
mContext.startActivity(intent);
stopSelf();
break;
case R.id.rl_comments:
if (rl_bottom_comments.getVisibility() == View.GONE) {
rl_bottom_comments.setVisibility(View.VISIBLE);
} else {
rl_bottom_comments.setVisibility(View.GONE);
}
break;
case R.id.iv_close:
rl_bottom_comments.setVisibility(View.GONE);
break;
}
}
};
上面有些API是腾讯云直播SDK里的,遇到android找不到的api自行删除即可。
最后附上两个悬浮控件的xml文件,可作为参考:
1.顶部计时器布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_top_toolbar"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_marginTop="15.5dp"
android:layout_marginStart="@dimen/dp_20"
>
<ImageView
android:id="@+id/iv_record_switch"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:src="@drawable/ic_record_finish"
/>
<Chronometer
android:id="@+id/c_timer"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:focusable="true"
android:textColor="@color/color_maintext_title"
android:textSize="12sp"
android:layout_marginStart="@dimen/dp_5"
/>
<ImageView
android:id="@+id/iv_back_app"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_15"
android:src="@drawable/ic_runon_background"
/>
</LinearLayout>
2.可移动弹幕布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/rl_comments"
android:layout_width="@dimen/dp_40"
android:layout_height="@dimen/dp_40"
android:background="@drawable/bg_78_80_circle"
android:layout_alignParentEnd="true"
android:layout_marginEnd="8.5dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_comments"
android:layout_centerInParent="true"
/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_bottom_comments"
android:layout_width="match_parent"
android:layout_height="210dp"
android:background="@color/black69_80"
android:visibility="visible"
android:layout_below="@id/rl_comments"
android:layout_marginTop="11dp"
>
<TextView
android:id="@+id/tv_comments"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_marginTop="10dp"
android:layout_marginStart="14.5dp"
android:text="全部评论"
android:textSize="@dimen/sp_12"
/>
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_close_white"
android:layout_alignParentEnd="true"
android:layout_marginEnd="11dp"
android:layout_marginTop="12.5dp"
/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView_comments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_15"
android:layout_marginEnd="17dp"
android:layout_below="@id/tv_comments"
android:layout_marginTop="12.5dp"
android:layout_marginBottom="32.5dp"
/>
</RelativeLayout>
</RelativeLayout>
应该没有漏掉的文件,如果还有不清楚的可以在下方评论。
如果对你有帮助给我一个免费的赞吧,在右上角喔~。~也可关注我,会不定期更新一些常用的干货。