* NonoFloatView:悬浮窗控件V2,普通的实现
*
* @author Nonolive-杜乾 Created on 2017/12/12 - 17:16.
* E-mail:dusan.du@nonolive.com
*/
public class NonoFloatView extends FrameLayout implements IFloatView {
private static final String TAG = NonoFloatView.class.getSimpleName();
private float xInView;
private float yInView;
private float xInScreen;
private float yInScreen;
private float xDownInScreen;
private float yDownInScreen;
private Context context;
private TextView tv_player_status;
private VideoTextureRenderView videoTextureRenderView;
private VideoTextureRenderView.IRenderCallback renderCallback;
private RelativeLayout videoViewWrap;
private RelativeLayout content_wrap;
private ImageView iv_live_cover;
private ImageView iv_zoom_btn;
private FloatViewParams params = null;
private FloatViewListener listener;
private int statusBarHeight = 0;
private int screenWidth;
private int screenHeight;
private int mMinWidth;
private int mMaxWidth;
private float mRatio = 1.77f;
private int videoViewMargin;
private View floatView;
public NonoFloatView(Context context) {
super(context);
init();
}
public NonoFloatView(@NonNull Context context, FloatViewParams params, VideoTextureRenderView.IRenderCallback callback) {
super(context);
this.params = params;
this.renderCallback = callback;
init();
}
private void init() {
initData();
initView();
}
private void initView() {
LayoutInflater inflater = LayoutInflater.from(getContext());
floatView = inflater.inflate(R.layout.nn_liveroom_video_float_window, null);
content_wrap = (RelativeLayout) floatView.findViewById(R.id.content_wrap);
videoViewWrap = (RelativeLayout) floatView.findViewById(R.id.videoViewWrap);
tv_player_status = (TextView) floatView.findViewById(R.id.tv_player_status);
iv_live_cover = (ImageView) floatView.findViewById(R.id.iv_live_cover);
iv_zoom_btn = (ImageView) floatView.findViewById(R.id.iv_zoom_btn);
iv_zoom_btn.setOnTouchListener(onZoomBtnTouchListener);
content_wrap.setOnTouchListener(onMovingTouchListener);
content_wrap.addOnLayoutChangeListener(onLayoutChangeListener);
ImageView iv_close_window = (ImageView) floatView.findViewById(R.id.iv_close_window);
iv_close_window.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (null != NonoFloatView.this.listener) {
NonoFloatView.this.listener.onClose();
}
}
});
videoTextureRenderView = (VideoTextureRenderView) floatView.findViewById(R.id.float_textureView);
videoTextureRenderView.addRenderCallback(renderCallback);
int lastViewWidth = params.contentWidth;
int lastViewHeight = (int) (lastViewWidth * mRatio);
updateViewLayoutParams(lastViewWidth, lastViewHeight);
addView(floatView);
}
private void initData() {
context = getContext();
statusBarHeight = getStatusBarHeight();
if (params != null) {
screenWidth = params.screenWidth;
screenHeight = params.screenHeight - statusBarHeight;
videoViewMargin = params.videoViewMargin;
mMaxWidth = params.mMaxWidth;
mMinWidth = params.mMinWidth;
mRatio = params.mRatio;
}
oldX = params.x;
oldY = params.y;
mRight = params.x + params.width;
mBottom = params.y + params.height;
}
private void updateViewLayoutParams(int width, int height) {
if (content_wrap != null) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) content_wrap.getLayoutParams();
layoutParams.height = height;
layoutParams.width = width;
content_wrap.setLayoutParams(layoutParams);
params.width = width;
params.height = height;
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!isRestorePosition) {
content_wrap.layout(oldX, oldY, oldX + params.width, oldY + params.height);
isRestorePosition = true;
}
}
private boolean isRestorePosition = false;
private int oldX = 0;
private int oldY = 0;
private final OnLayoutChangeListener onLayoutChangeListener = new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (right != mRight || bottom != mBottom) {
int width = content_wrap.getWidth();
int height = content_wrap.getHeight();
int l = mRight - width;
int t = mBottom - height;
int r = mRight;
int b = mBottom;
if (l < -videoViewMargin) {
l = -videoViewMargin;
r = l + width;
}
if (t < -videoViewMargin) {
t = -videoViewMargin;
b = t + height;
}
content_wrap.layout(l, t, r, b);
params.x = l;
params.y = t;
}
}
};
private int mRight = 0;
private int mBottom = 0;
private final OnTouchListener onZoomBtnTouchListener = new OnTouchListener() {
float lastX = 0;
float lastY = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
isDragged = true;
lastX = event.getRawX();
lastY = event.getRawY();
mRight = content_wrap.getRight();
mBottom = content_wrap.getBottom();
break;
case MotionEvent.ACTION_MOVE:
showZoomView();
handleMoveEvent(event);
break;
case MotionEvent.ACTION_UP:
if (listener != null) {
listener.onDragged();
}
displayZoomViewDelay();
isDragged = false;
break;
default:
break;
}
return true;
}
private void handleMoveEvent(MotionEvent event) {
isDragged = true;
float moveX = event.getRawX();
float moveY = event.getRawY();
float dx = moveX - lastX;
float dy = moveY - lastY;
double distance = Math.sqrt(dx * dx + dy * dy);
if (distance >= 5) {
int contentWidth = content_wrap.getWidth();
if (moveY > lastY && moveX > lastX) {
if (contentWidth == mMinWidth) {
return;
}
distance = -distance;
} else {
if (contentWidth == mMaxWidth) {
return;
}
}
int changedWidth = (int) (distance * Math.cos(45));
updateContentViewSize(changedWidth);
}
lastX = moveX;
lastY = moveY;
}
};
public int getContentViewWidth() {
return content_wrap != null ? content_wrap.getWidth() : mMinWidth;
}
* 更新内部view的大小
*
* @param width 传入变化的宽度
*/
private void updateContentViewSize(int width) {
int currentWidth = content_wrap.getWidth();
int newWidth = currentWidth + width;
newWidth = checkWidth(newWidth);
int height = (int) (newWidth * mRatio);
updateViewLayoutParams(newWidth, height);
}
* 修正大小,限制最大和最小值
*
* @param width
* @return
*/
private int checkWidth(int width) {
if (width > mMaxWidth) {
width = mMaxWidth;
}
if (width < mMinWidth) {
width = mMinWidth;
}
return width;
}
private boolean isMoving = false;
private final OnTouchListener onMovingTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return onTouchEvent2(event);
}
};
public boolean onTouchEvent2(MotionEvent event) {
if (isDragged) {
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
showZoomView();
isMoving = false;
xInView = event.getX();
yInView = event.getY();
Rect rect = new Rect();
floatView.getGlobalVisibleRect(rect);
if (!rect.contains((int) xInView, (int) yInView)) {
return false;
}
xDownInScreen = event.getRawX();
yDownInScreen = event.getRawY();
xInScreen = xDownInScreen;
yInScreen = yDownInScreen;
break;
case MotionEvent.ACTION_MOVE:
showZoomView();
xInScreen = event.getRawX();
yInScreen = event.getRawY();
if (!isMoving) {
isMoving = !isClickedEvent();
} else {
updateViewPosition();
}
break;
case MotionEvent.ACTION_UP:
if (isClickedEvent()) {
if (null != listener) {
listener.onClick();
}
} else {
if (null != listener) {
listener.onMoved();
}
}
displayZoomViewDelay();
isMoving = false;
break;
default:
break;
}
return true;
}
* 是否为点击事件
*
* @return
*/
private boolean isClickedEvent() {
int scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
if (Math.abs(xDownInScreen - xInScreen) <= scaledTouchSlop
&& Math.abs(yDownInScreen - yInScreen) <= scaledTouchSlop) {
return true;
}
return false;
}
* 更新悬浮窗位置
*/
private synchronized void updateViewPosition() {
int x = (int) (xInScreen - xInView);
int y = (int) (yInScreen - yInView);
if (x <= -videoViewMargin) {
x = -videoViewMargin;
}
if (y <= -videoViewMargin) {
y = -videoViewMargin;
}
int dWidth = screenWidth - content_wrap.getWidth();
if (x >= dWidth) {
x = dWidth;
}
int dHeight = screenHeight - content_wrap.getHeight();
if (y >= dHeight) {
y = dHeight;
}
if (x >= dWidth) {
x = dWidth - 1;
}
reLayoutContentView(x, y);
}
* 重新布局
*
* @param x
* @param y
*/
private void reLayoutContentView(int x, int y) {
params.x = x;
params.y = y;
mRight = x + content_wrap.getWidth();
mBottom = y + content_wrap.getHeight();
content_wrap.layout(x, y, mRight, mBottom);
}
private boolean isDragged = false;
private boolean isEdit = false;
* 显示拖拽缩放按钮
*/
private void showZoomView() {
if (!isEdit) {
iv_zoom_btn.setVisibility(VISIBLE);
videoViewWrap.setBackgroundColor(getResources().getColor(R.color.float_window_bg_border_edit));
isEdit = true;
}
}
* 隐藏缩放按钮
*/
private void displayZoomView() {
isEdit = false;
iv_zoom_btn.setVisibility(GONE);
videoViewWrap.setBackgroundColor(getResources().getColor(R.color.float_window_bg_border_normal));
}
* 处理缩放按钮隐藏时视频的margin
*/
private void handleMarginStatus() {
boolean isLeft = params.x <= -videoViewMargin;
boolean isTop = params.y <= -videoViewMargin;
if (isLeft && isTop) {
updateVideoMargin(0, 0, videoViewMargin, videoViewMargin);
} else if (isLeft) {
updateVideoMargin(0, videoViewMargin, videoViewMargin, 0);
} else if (isTop) {
updateVideoMargin(videoViewMargin, 0, 0, videoViewMargin);
}
}
* 调整视频view的边距
*/
private void updateVideoMargin(int left, int top, int right, int bottom) {
if (videoViewWrap != null) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) videoViewWrap.getLayoutParams();
layoutParams.setMargins(left, top, right, bottom);
videoViewWrap.setLayoutParams(layoutParams);
}
}
private void displayZoomViewDelay() {
removeCallbacks(dispalyZoomBtnRunnable);
postDelayed(dispalyZoomBtnRunnable, 2000);
}
private final Runnable dispalyZoomBtnRunnable = new Runnable() {
@Override
public void run() {
displayZoomView();
}
};
private int getStatusBarHeight() {
int statusBarHeight = SystemUtils.getStatusBarHeightByReflect(context);
if (statusBarHeight == 0) {
statusBarHeight = SystemUtils.dip2px(context, 30);
}
return statusBarHeight;
}