Skip to content

Commit edf87c8

Browse files
committed
add ResizableFrameLayout
1 parent a70379e commit edf87c8

File tree

1 file changed

+275
-0
lines changed

1 file changed

+275
-0
lines changed
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* Copyright (c) 2021 WildFireChat. All rights reserved.
3+
*/
4+
5+
package cn.wildfire.chat.kit.voip;
6+
7+
import android.content.Context;
8+
import android.util.AttributeSet;
9+
import android.util.Log;
10+
import android.view.MotionEvent;
11+
import android.view.ScaleGestureDetector;
12+
import android.view.View;
13+
import android.view.ViewGroup;
14+
import android.widget.FrameLayout;
15+
16+
import androidx.annotation.Nullable;
17+
18+
/**
19+
* 支持缩放改变子 view 大小的FrameLayout
20+
* <p>
21+
*/
22+
public class ResizableFrameLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {
23+
24+
private enum Mode {
25+
NONE,
26+
// 下拉关闭
27+
DRAG,
28+
// 放大之后,拖拽查看
29+
DRAG_ZOOM,
30+
ZOOM
31+
}
32+
33+
private static final String TAG = "ResizableLayout";
34+
private static final float MIN_ZOOM = 1.0f;
35+
private static final float MAX_ZOOM = 4.0f;
36+
37+
private boolean enableZoom = true;
38+
private boolean enableDrag = true;
39+
private Mode mode = Mode.NONE;
40+
private float scale = 1.0f;
41+
private float lastScaleFactor = 0f;
42+
43+
// Where the finger first touches the screen
44+
private float startX = 0f;
45+
private float startY = 0f;
46+
47+
// How much to translate the canvas
48+
private float dx = 0f;
49+
private float dy = 0f;
50+
private float prevDx = 0f;
51+
private float prevDy = 0f;
52+
private int lastX, lastY;
53+
private int originalWidth;
54+
private int originalHeight;
55+
private OnClickListener onClickListener;
56+
57+
public ResizableFrameLayout(Context context) {
58+
super(context);
59+
init(context);
60+
}
61+
62+
public ResizableFrameLayout(Context context, AttributeSet attrs) {
63+
super(context, attrs);
64+
init(context);
65+
}
66+
67+
public ResizableFrameLayout(Context context, AttributeSet attrs, int defStyle) {
68+
super(context, attrs, defStyle);
69+
init(context);
70+
}
71+
72+
private void init(Context context) {
73+
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
74+
Log.d(TAG, "init");
75+
setOnTouchListener(new OnTouchListener() {
76+
@Override
77+
public boolean onTouch(View view, MotionEvent motionEvent) {
78+
Log.d(TAG, "onTouch 000");
79+
if (child() == null) {
80+
return false;
81+
}
82+
if (!enableZoom && !enableDrag) {
83+
return false;
84+
}
85+
Log.d(TAG, "onTouch");
86+
int y = (int) motionEvent.getY();
87+
int x = (int) motionEvent.getX();
88+
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
89+
case MotionEvent.ACTION_DOWN:
90+
Log.i(TAG, "DOWN");
91+
lastX = x;
92+
lastY = y;
93+
if (scale > MIN_ZOOM) {
94+
mode = Mode.DRAG_ZOOM;
95+
startX = motionEvent.getX() - prevDx;
96+
startY = motionEvent.getY() - prevDy;
97+
} else {
98+
mode = Mode.DRAG;
99+
}
100+
break;
101+
case MotionEvent.ACTION_MOVE:
102+
if (enableDrag) {
103+
if (mode == Mode.DRAG_ZOOM) {
104+
dx = motionEvent.getX() - startX;
105+
dy = motionEvent.getY() - startY;
106+
} else if (mode == Mode.DRAG) {
107+
// do nothing
108+
}
109+
}
110+
break;
111+
case MotionEvent.ACTION_POINTER_DOWN:
112+
mode = Mode.ZOOM;
113+
break;
114+
case MotionEvent.ACTION_POINTER_UP:
115+
mode = Mode.NONE; // changed from DRAG, was messing up zoom
116+
break;
117+
case MotionEvent.ACTION_UP:
118+
Log.i(TAG, "UP");
119+
mode = Mode.NONE;
120+
prevDx = dx;
121+
prevDy = dy;
122+
if (Math.abs(y - lastY) < 5 && Math.abs(x - lastX) < 5) {
123+
//todo 如果横纵坐标的偏移量都小于五个像素,那么就把它当做点击事件触发
124+
if (onClickListener != null) {
125+
Log.d(TAG, "click");
126+
onClickListener.onClick(ResizableFrameLayout.this);
127+
}
128+
}
129+
break;
130+
}
131+
scaleDetector.onTouchEvent(motionEvent);
132+
133+
if ((mode == Mode.DRAG_ZOOM && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
134+
getParent().requestDisallowInterceptTouchEvent(true);
135+
float maxDx = child().getWidth() - getWidth(); // adjusted for zero pivot
136+
float maxDy = child().getHeight() - getHeight(); // adjusted for zero pivot
137+
138+
dx = Math.min(Math.max(dx, -maxDx), 0); // adjusted for zero pivot
139+
dy = Math.min(Math.max(dy, -maxDy), 0); // adjusted for zero pivot
140+
Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
141+
+ ", dy " + dy + ", max " + maxDx);
142+
applyScaleAndTranslation();
143+
}
144+
145+
return true;
146+
}
147+
});
148+
Log.d(TAG, "init end");
149+
}
150+
151+
// ScaleGestureDetector
152+
@Override
153+
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
154+
Log.i(TAG, "onScaleBegin");
155+
return true;
156+
}
157+
158+
@Override
159+
public boolean onScale(ScaleGestureDetector scaleDetector) {
160+
float scaleFactor = scaleDetector.getScaleFactor();
161+
Log.i(TAG, "onScale(), scaleFactor = " + scaleFactor);
162+
if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
163+
float prevScale = scale;
164+
scale *= scaleFactor;
165+
scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
166+
lastScaleFactor = scaleFactor;
167+
float adjustedScaleFactor = scale / prevScale;
168+
// added logic to adjust dx and dy for pinch/zoom pivot point
169+
Log.d(TAG, "onScale, adjustedScaleFactor = " + adjustedScaleFactor);
170+
Log.d(TAG, "onScale, BEFORE dx/dy = " + dx + "/" + dy);
171+
float focusX = scaleDetector.getFocusX();
172+
float focusY = scaleDetector.getFocusY();
173+
Log.d(TAG, "onScale, focusX/focusy = " + focusX + "/" + focusY);
174+
dx += (dx - focusX) * (adjustedScaleFactor - 1);
175+
dy += (dy - focusY) * (adjustedScaleFactor - 1);
176+
Log.d(TAG, "onScale, dx/dy = " + dx + "/" + dy);
177+
} else {
178+
lastScaleFactor = 0;
179+
}
180+
return true;
181+
}
182+
183+
@Override
184+
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
185+
Log.i(TAG, "onScaleEnd");
186+
}
187+
188+
@Override
189+
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
190+
super.onLayout(changed, left, top, right, bottom);
191+
}
192+
193+
private void applyScaleAndTranslation() {
194+
if (child() == null) {
195+
return;
196+
}
197+
View view = child();
198+
if (this.originalWidth == 0) {
199+
// 等待子 view layout 之后,在获取其默认大小
200+
this.originalWidth = view.getWidth();
201+
this.originalHeight = view.getHeight();
202+
// view.addOnLayoutChangeListener(new OnLayoutChangeListener() {
203+
// @Override
204+
// public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
205+
// int newWidth = right - left;
206+
// int newHeight = bottom - top;
207+
// int oldWidth = oldRight - oldLeft;
208+
// int oldHeight = oldBottom - oldTop;
209+
//
210+
// // 判断是否宽高发生变化
211+
// if (newWidth != oldWidth || newHeight != oldHeight) {
212+
// v.setScaleX(1.0f);
213+
// v.setScaleY(1.0f);
214+
// }
215+
// }
216+
// });
217+
}
218+
219+
// 获取当前布局参数
220+
ViewGroup.LayoutParams lp = view.getLayoutParams();
221+
222+
// 假设原始大小已保存为原始宽高 originalWidth/originalHeight
223+
int newWidth = (int) (originalWidth * scale);
224+
int newHeight = (int) (originalHeight * scale);
225+
226+
// 先对原始 view 进行缩放
227+
// view.setScaleX(scale);
228+
// view.setScaleY(scale);
229+
230+
// 设置 view 大小,会触发surfaceView 重新 layout,及 video frame重新渲染
231+
if (lp.width != newWidth || lp.height != newHeight) {
232+
lp.width = newWidth;
233+
lp.height = newHeight;
234+
view.setLayoutParams(lp);
235+
} else {
236+
view.setTranslationX(dx);
237+
view.setTranslationY(dy);
238+
}
239+
240+
Log.d(TAG, "applyResizeAndTranslation " + scale + " " + newWidth + " " + newHeight + " " + originalWidth + " " + originalHeight);
241+
}
242+
243+
private View child() {
244+
return getChildAt(0);
245+
}
246+
247+
@Override
248+
public void setOnClickListener(@Nullable OnClickListener l) {
249+
Log.d(TAG, "setOnClickListener");
250+
this.onClickListener = l;
251+
}
252+
253+
/**
254+
* 是否开启缩放,默认开启
255+
*
256+
* @param enable
257+
*/
258+
public void setEnableZoom(boolean enable) {
259+
this.enableZoom = enable;
260+
}
261+
262+
public void setEnableDrag(boolean enable) {
263+
this.enableDrag = enable;
264+
}
265+
266+
public void reset() {
267+
View view = child();
268+
if (view != null && this.originalWidth > 0) {
269+
ViewGroup.LayoutParams lp = view.getLayoutParams();
270+
lp.width = originalWidth;
271+
lp.height = originalHeight;
272+
view.setLayoutParams(lp);
273+
}
274+
}
275+
}

0 commit comments

Comments
 (0)