#######IFloatingView
package com.xxx.xxxxx.view.floatingview;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.LayoutRes;
import org.jetbrains.annotations.NotNull;
public interface IFloatingView {
ChatFloatingView remove();
ChatFloatingView show(ViewGroup container, @LayoutRes int layoutId, @NotNull ViewGroup.LayoutParams layoutParams);
ChatFloatingView show(ViewGroup container, @LayoutRes int layoutId, int left, int top, int right, int bottom, FloatingGravity gravity);
View getContentView();
ChatFloatingView setListener(View.OnClickListener viewListener);
}
#######FloatingGravity
package com.xxx.xxxx.view.floatingview;
public enum FloatingGravity {
LEFT_TOP,
LEFT_BOTTOM,
RIGHT_TOP,
RIGHT_BOTTOM
}
#######ChatFloatingView
package com.xxx.xxxx.view.floatingview;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.view.ViewCompat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.WeakReference;
public class ChatFloatingView implements IFloatingView {
private static volatile ChatFloatingView mInstance;
private WeakReference<ViewGroup> mContainer;
private WeakReference<View> mFloatingView = null;
private ChatFloatingView() {
}
public static ChatFloatingView get() {
if (mInstance == null) {
synchronized (ChatFloatingView.class) {
if (mInstance == null) {
mInstance = new ChatFloatingView();
}
}
}
return mInstance;
}
@Override
public ChatFloatingView remove() {
new Handler(Looper.getMainLooper()).post(() -> {
if (getFloatingView() == null) {
return;
}
if (ViewCompat.isAttachedToWindow(getFloatingView()) && getContainer() != null) {
getContainer().removeView(getFloatingView());
}
});
return this;
}
@Override
public ChatFloatingView show(ViewGroup container, @LayoutRes int layoutId, @NonNull ViewGroup.LayoutParams layoutParams) {
add2ViewGroup(container, layoutId, layoutParams);
return this;
}
/**
* @param container | only support [FrameLayout,ConstraintLayout,RelativeLayout]
* @return this
*/
@Override
public ChatFloatingView show(ViewGroup container, @LayoutRes int layoutId, int left, int top, int right, int bottom, FloatingGravity gravity) {
ViewGroup.MarginLayoutParams marginLayoutParams = getMarginLayoutParams(container, gravity);
marginLayoutParams.setMargins(left, top, right, bottom);
add2ViewGroup(container, layoutId, marginLayoutParams);
return this;
}
private ViewGroup.MarginLayoutParams getMarginLayoutParams(ViewGroup container, FloatingGravity gravity) {
if (container instanceof FrameLayout) {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = getFrameLayoutGravity(gravity);
return layoutParams;
} else if (container instanceof ConstraintLayout) {
ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
if (gravity == FloatingGravity.LEFT_TOP) {
layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
} else if (gravity == FloatingGravity.LEFT_BOTTOM) {
layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
} else if (gravity == FloatingGravity.RIGHT_TOP) {
layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
} else {
layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
}
return layoutParams;
} else if (container instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
if (gravity == FloatingGravity.LEFT_TOP) {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
} else if (gravity == FloatingGravity.LEFT_BOTTOM) {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
} else if (gravity == FloatingGravity.RIGHT_TOP) {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
} else {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
}
return layoutParams;
} else {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = getFrameLayoutGravity(gravity);
return layoutParams;
}
}
private int getFrameLayoutGravity(FloatingGravity gravity) {
int newGravity;
if (gravity == FloatingGravity.LEFT_TOP) {
newGravity = Gravity.START | Gravity.TOP;
} else if (gravity == FloatingGravity.LEFT_BOTTOM) {
newGravity = Gravity.START | Gravity.BOTTOM;
} else if (gravity == FloatingGravity.RIGHT_TOP) {
newGravity = Gravity.END | Gravity.TOP;
} else {
newGravity = Gravity.END | Gravity.BOTTOM;
}
return newGravity;
}
private void add2ViewGroup(ViewGroup container, @LayoutRes int layoutId, @NotNull ViewGroup.LayoutParams layoutParams) {
if (container == null) {
return;
}
if (getFloatingView() == null) {
LayoutInflater factory = LayoutInflater.from(container.getContext());
mFloatingView = new WeakReference<>(factory.inflate(layoutId, null));
mContainer = new WeakReference<>(container);
getFloatingView().setLayoutParams(layoutParams);
container.addView(getFloatingView());
return;
}
ViewGroup parentView = (ViewGroup) getFloatingView().getParent();
if (parentView != container) {
mContainer = new WeakReference<>(container);
} else {
container.removeView(getFloatingView());
}
getFloatingView().setLayoutParams(layoutParams);
container.addView(getFloatingView());
}
@Override
public View getContentView() {
return getFloatingView();
}
@Override
public ChatFloatingView setListener(View.OnClickListener viewListener) {
return this;
}
@Nullable
private ViewGroup getContainer() {
if (mContainer == null) {
return null;
}
return mContainer.get();
}
@Nullable
private View getFloatingView() {
if (mFloatingView == null) {
return null;
}
return mFloatingView.get();
}
}
#######FloatingViewListener
package com.xxx.xxxxx.view.floatingview;
public interface FloatingViewListener {
void onRemove(FloatingMagnetLayout floatingLayout);
void onClick(FloatingMagnetLayout floatingLayout);
}
#######FloatingMagnetLayout
package com.xxx.xxxx.view.floatingview;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.greendotcorp.chatsdk.utils.AppUtils;
public class FloatingMagnetLayout extends ConstraintLayout {
public static final int MARGIN_EDGE = 13;
private float mOriginalRawX;
private float mOriginalRawY;
private float mOriginalX;
private float mOriginalY;
private FloatingViewListener mFloatingViewListener;
private static final int TOUCH_TIME_THRESHOLD = 150;
private long mLastTouchDownTime;
protected MoveAnimator mMoveAnimator;
protected int mScreenWidth;
private int mScreenHeight;
private int mStatusBarHeight;
private boolean isNearestLeft = true;
private float mPortraitY;
public void setFloatingViewListener(FloatingViewListener magnetViewListener) {
this.mFloatingViewListener = magnetViewListener;
}
public FloatingMagnetLayout(Context context) {
this(context, null);
}
public FloatingMagnetLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatingMagnetLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mMoveAnimator = new MoveAnimator();
// mStatusBarHeight = AppUtils.getStatusBarHeight(getContext());
mStatusBarHeight = 0;
setClickable(true);
// updateSize();
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event == null) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
changeOriginalTouchParams(event);
updateSize();
mMoveAnimator.stop();
break;
case MotionEvent.ACTION_MOVE:
updateViewPosition(event);
break;
case MotionEvent.ACTION_UP:
clearPortraitY();
moveToEdge();
if (isOnClickEvent()) {
dealClickEvent();
}
break;
}
return true;
}
protected void dealClickEvent() {
if (mFloatingViewListener != null) {
mFloatingViewListener.onClick(this);
}
}
protected boolean isOnClickEvent() {
return System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD;
}
private void updateViewPosition(MotionEvent event) {
setX(mOriginalX + event.getRawX() - mOriginalRawX);
// 限制不可超出屏幕高度
float desY = mOriginalY + event.getRawY() - mOriginalRawY;
if (desY < mStatusBarHeight) {
desY = mStatusBarHeight;
}
if (desY > mScreenHeight - getHeight()) {
desY = mScreenHeight - getHeight();
}
setY(desY);
}
private void changeOriginalTouchParams(MotionEvent event) {
mOriginalX = getX();
mOriginalY = getY();
mOriginalRawX = event.getRawX();
mOriginalRawY = event.getRawY();
mLastTouchDownTime = System.currentTimeMillis();
}
protected void updateSize() {
ViewGroup viewGroup = (ViewGroup) getParent();
if (viewGroup != null) {
mScreenWidth = viewGroup.getWidth() - getWidth();
mScreenHeight = viewGroup.getHeight();
}
}
public void moveToEdge() {
moveToEdge(isNearestLeft(), false);
}
public void moveToEdge(boolean isLeft, boolean isLandscape) {
float moveDistance = isLeft ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
float y = getY();
if (!isLandscape && mPortraitY != 0) {
y = mPortraitY;
clearPortraitY();
}
mMoveAnimator.start(moveDistance, Math.min(Math.max(0, y), mScreenHeight - getHeight()));
}
private void clearPortraitY() {
mPortraitY = 0;
}
protected boolean isNearestLeft() {
int middle = mScreenWidth / 2;
isNearestLeft = getX() < middle;
return isNearestLeft;
}
public void onRemove() {
if (mFloatingViewListener != null) {
mFloatingViewListener.onRemove(this);
}
}
protected class MoveAnimator implements Runnable {
private final Handler handler = new Handler(Looper.getMainLooper());
private float destinationX;
private float destinationY;
private long startingTime;
void start(float x, float y) {
this.destinationX = x;
this.destinationY = y;
startingTime = System.currentTimeMillis();
handler.post(this);
}
@Override
public void run() {
if (getRootView() == null || getRootView().getParent() == null) {
return;
}
float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f);
float deltaX = (destinationX - getX()) * progress;
float deltaY = (destinationY - getY()) * progress;
move(deltaX, deltaY);
if (progress < 1) {
handler.post(this);
}
}
private void stop() {
handler.removeCallbacks(this);
}
}
private void move(float deltaX, float deltaY) {
setX(getX() + deltaX);
setY(getY() + deltaY);
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (getParent() != null) {
final boolean isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
markPortraitY(isLandscape);
((ViewGroup) getParent()).post(() -> {
updateSize();
moveToEdge(isNearestLeft, isLandscape);
});
}
}
private void markPortraitY(boolean isLandscape) {
if (isLandscape) {
mPortraitY = getY();
}
}
}
-----------------------------End-----------------------------
网友评论