缩放布局
Java代码
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
public class ZoomView extends FrameLayout {
private static final String TAG = "ZoomView";
private boolean isEnableZoom = true;
private boolean isEnableGestureZoom = false;
public ZoomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ZoomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ZoomView(final Context context) {
super(context);
}
/**
* Zooming view listener interface.
*/
public interface ZoomViewListener {
void onZoomStarted(float zoom, float zoomx, float zoomy);
void onZooming(float zoom, float zoomx, float zoomy);
void onZoomEnded(float zoom, float zoomx, float zoomy);
}
// zooming
float zoom = 1.0f;
float maxZoom = 2.0f;
float smoothZoom = 1.0f;
float zoomX, zoomY;
float smoothZoomX, smoothZoomY;
private boolean scrolling; // NOPMD by karooolek on 29.06.11 11:45
// minimap variables
private boolean showMinimap = false;
private int miniMapColor = Color.WHITE;
private int miniMapHeight = -1;
private String miniMapCaption;
private float miniMapCaptionSize = 10.0f;
private int miniMapCaptionColor = Color.WHITE;
// touching variables
private long lastTapTime;
private float touchStartX, touchStartY;
private float touchLastX, touchLastY;
private float startd;
private boolean pinching;
private float lastd;
private float lastdx1, lastdy1;
private float lastdx2, lastdy2;
// drawing
private final Matrix m = new Matrix();
private final Paint p = new Paint();
// listener
ZoomViewListener listener;
private Bitmap ch;
public float getZoom() {
return zoom;
}
public float getMaxZoom() {
return maxZoom;
}
public void setMaxZoom(final float maxZoom) {
if (maxZoom < 1.0f) {
return;
}
this.maxZoom = maxZoom;
}
public void setMiniMapEnabled(final boolean showMiniMap) {
this.showMinimap = showMiniMap;
}
public boolean isMiniMapEnabled() {
return showMinimap;
}
public void setMiniMapHeight(final int miniMapHeight) {
if (miniMapHeight < 0) {
return;
}
this.miniMapHeight = miniMapHeight;
}
public int getMiniMapHeight() {
return miniMapHeight;
}
public void setMiniMapColor(final int color) {
miniMapColor = color;
}
public int getMiniMapColor() {
return miniMapColor;
}
public String getMiniMapCaption() {
return miniMapCaption;
}
public void setMiniMapCaption(final String miniMapCaption) {
this.miniMapCaption = miniMapCaption;
}
public float getMiniMapCaptionSize() {
return miniMapCaptionSize;
}
public void setMiniMapCaptionSize(final float size) {
miniMapCaptionSize = size;
}
public int getMiniMapCaptionColor() {
return miniMapCaptionColor;
}
public void setMiniMapCaptionColor(final int color) {
miniMapCaptionColor = color;
}
public void zoomTo(final float zoom, final float x, final float y) {
this.zoom = Math.min(zoom, maxZoom);
zoomX = x;
zoomY = y;
smoothZoomTo(this.zoom, x, y);
}
public void smoothZoomTo(final float zoom, final float x, final float y) {
smoothZoom = clamp(1.0f, zoom, maxZoom);
smoothZoomX = x;
smoothZoomY = y;
if (listener != null) {
listener.onZoomStarted(smoothZoom, x, y);
}
}
public ZoomViewListener getListener() {
return listener;
}
public void setListener(final ZoomViewListener listener) {
this.listener = listener;
}
public float getZoomFocusX() {
return zoomX * zoom;
}
public float getZoomFocusY() {
return zoomY * zoom;
}
@Override
public boolean dispatchTouchEvent(final MotionEvent ev) {
if (!isEnableZoom) {
return super.dispatchTouchEvent(ev);
}
// single touch
if (ev.getPointerCount() == 1) {
processSingleTouchEvent(ev);
}
// // double touch
if (ev.getPointerCount() == 2) {
processDoubleTouchEvent(ev);
}
// redraw
getRootView().invalidate();
invalidate();
if (smoothZoom == 1F) {
return super.dispatchTouchEvent(ev);
}
return true;
}
private void processSingleTouchEvent(final MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final float w = miniMapHeight * (float) getWidth() / getHeight();
final float h = miniMapHeight;
final boolean touchingMiniMap = x >= 10.0f && x <= 10.0f + w
&& y >= 10.0f && y <= 10.0f + h;
if (showMinimap && smoothZoom > 1.0f && touchingMiniMap) {
processSingleTouchOnMinimap(ev);
} else {
processSingleTouchOutsideMinimap(ev);
}
}
private void processSingleTouchOnMinimap(final MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final float w = miniMapHeight * (float) getWidth() / getHeight();
final float h = miniMapHeight;
final float zx = (x - 10.0f) / w * getWidth();
final float zy = (y - 10.0f) / h * getHeight();
smoothZoomTo(smoothZoom, zx, zy);
}
private void processSingleTouchOutsideMinimap(final MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
float lx = x - touchStartX;
float ly = y - touchStartY;
final float l = (float) Math.hypot(lx, ly);
float dx = x - touchLastX;
float dy = y - touchLastY;
touchLastX = x;
touchLastY = y;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStartX = x;
touchStartY = y;
touchLastX = x;
touchLastY = y;
dx = 0;
dy = 0;
lx = 0;
ly = 0;
scrolling = false;
break;
case MotionEvent.ACTION_MOVE:
if (scrolling || (smoothZoom > 1.0f && l > 30.0f)) {
if (!scrolling) {
scrolling = true;
ev.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(ev);
}
smoothZoomX -= dx / zoom;
smoothZoomY -= dy / zoom;
return;
}
break;
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_UP:
// tap
if (l < 30.0f) {
// check double tap
if (System.currentTimeMillis() - lastTapTime < 500) {
if (smoothZoom == 1.0f) {
smoothZoomTo(maxZoom, x, y);
} else {
smoothZoomTo(1.0f, getWidth() / 2.0f,
getHeight() / 2.0f);
}
lastTapTime = 0;
ev.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(ev);
return;
}
lastTapTime = System.currentTimeMillis();
performClick();
}
break;
default:
break;
}
ev.setLocation(zoomX + (x - 0.5f * getWidth()) / zoom, zoomY
+ (y - 0.5f * getHeight()) / zoom);
ev.getX();
ev.getY();
super.dispatchTouchEvent(ev);
}
private void processDoubleTouchEvent(final MotionEvent ev) {
final float x1 = ev.getX(0);
final float dx1 = x1 - lastdx1;
lastdx1 = x1;
final float y1 = ev.getY(0);
final float dy1 = y1 - lastdy1;
lastdy1 = y1;
final float x2 = ev.getX(1);
final float dx2 = x2 - lastdx2;
lastdx2 = x2;
final float y2 = ev.getY(1);
final float dy2 = y2 - lastdy2;
lastdy2 = y2;
// pointers distance
final float d = (float) Math.hypot(x2 - x1, y2 - y1);
final float dd = d - lastd;
lastd = d;
final float ld = Math.abs(d - startd);
Math.atan2(y2 - y1, x2 - x1);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startd = d;
pinching = false;
break;
case MotionEvent.ACTION_MOVE:
if (isEnableGestureZoom) {
if (pinching || ld > 30.0f) {
pinching = true;
final float dxk = 0.5f * (dx1 + dx2);
final float dyk = 0.5f * (dy1 + dy2);
smoothZoomTo(Math.max(1.0f, zoom * d / (d - dd)), zoomX - dxk
/ zoom, zoomY - dyk / zoom);
}
}
break;
case MotionEvent.ACTION_UP:
default:
pinching = false;
break;
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(ev);
}
private float clamp(final float min, final float value, final float max) {
return Math.max(min, Math.min(value, max));
}
private float lerp(final float a, final float b, final float k) {
return a + (b - a) * k;
}
private float bias(final float a, final float b, final float k) {
return Math.abs(b - a) >= k ? a + k * Math.signum(b - a) : b;
}
@Override
protected void dispatchDraw(final Canvas canvas) {
// do zoom
zoom = lerp(bias(zoom, smoothZoom, 0.05f), smoothZoom, 0.2f);
smoothZoomX = clamp(0.5f * getWidth() / smoothZoom, smoothZoomX,
getWidth() - 0.5f * getWidth() / smoothZoom);
smoothZoomY = clamp(0.5f * getHeight() / smoothZoom, smoothZoomY,
getHeight() - 0.5f * getHeight() / smoothZoom);
zoomX = lerp(bias(zoomX, smoothZoomX, 0.1f), smoothZoomX, 0.35f);
zoomY = lerp(bias(zoomY, smoothZoomY, 0.1f), smoothZoomY, 0.35f);
if (zoom != smoothZoom && listener != null) {
listener.onZooming(zoom, zoomX, zoomY);
}
final boolean animating = Math.abs(zoom - smoothZoom) > 0.0000001f
|| Math.abs(zoomX - smoothZoomX) > 0.0000001f
|| Math.abs(zoomY - smoothZoomY) > 0.0000001f;
// nothing to draw
if (getChildCount() == 0) {
return;
}
// prepare matrix
m.setTranslate(0.5f * getWidth(), 0.5f * getHeight());
m.preScale(zoom, zoom);
m.preTranslate(
-clamp(0.5f * getWidth() / zoom, zoomX, getWidth() - 0.5f
* getWidth() / zoom),
-clamp(0.5f * getHeight() / zoom, zoomY, getHeight() - 0.5f
* getHeight() / zoom));
// get view
final View v = getChildAt(0);
m.preTranslate(v.getLeft(), v.getTop());
// get drawing cache if available
if (animating && ch == null && isAnimationCacheEnabled()) {
v.setDrawingCacheEnabled(true);
ch = v.getDrawingCache();
}
// draw using cache while animating
if (animating && isAnimationCacheEnabled() && ch != null) {
p.setColor(0xffffffff);
canvas.drawBitmap(ch, m, p);
} else { // zoomed or cache unavailable
ch = null;
canvas.save();
canvas.concat(m);
v.draw(canvas);
canvas.restore();
}
// draw minimap
if (showMinimap) {
if (miniMapHeight < 0) {
miniMapHeight = getHeight() / 4;
}
canvas.translate(10.0f, 10.0f);
p.setColor(0x80000000 | 0x00ffffff & miniMapColor);
final float w = miniMapHeight * (float) getWidth() / getHeight();
final float h = miniMapHeight;
canvas.drawRect(0.0f, 0.0f, w, h, p);
if (miniMapCaption != null && miniMapCaption.length() > 0) {
p.setTextSize(miniMapCaptionSize);
p.setColor(miniMapCaptionColor);
p.setAntiAlias(true);
canvas.drawText(miniMapCaption, 10.0f,
10.0f + miniMapCaptionSize, p);
p.setAntiAlias(false);
}
p.setColor(0x80000000 | 0x00ffffff & miniMapColor);
final float dx = w * zoomX / getWidth();
final float dy = h * zoomY / getHeight();
canvas.drawRect(dx - 0.5f * w / zoom, dy - 0.5f * h / zoom, dx
+ 0.5f * w / zoom, dy + 0.5f * h / zoom, p);
canvas.translate(-10.0f, -10.0f);
}
// redraw
getRootView().postInvalidate();
postInvalidate();
}
/**
* 开启手势 缩放
*
* @param isEnableGestureZoom false 关闭手势缩放 true 开启手势缩放 默认 false
*/
public void isEnableGestureZoom(boolean isEnableGestureZoom) {
this.isEnableGestureZoom = isEnableGestureZoom;
}
/**
* 设置 false 时不拦截事件
*
* @param isEnableZoom true 开启缩放功能 false 关闭缩放功能,默认开启缩放
*/
public void isEnableZoom(boolean isEnableZoom) {
this.isEnableZoom = isEnableZoom;
}
/**
* 恢复原来状态
*/
public void setDefaultZoomSize() {
smoothZoomTo(1.0f, getWidth() / 2.0f,
getHeight() / 2.0f);
}
}
Kotlin 代码
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.FrameLayout
class ZoomView : FrameLayout {
private var isEnableZoom = true
private var isEnableGestureZoom = false
constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context!!, attrs, defStyle) {}
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs) {}
constructor(context: Context?) : super(context!!) {}
/**
* Zooming view listener interface.
*/
interface ZoomViewListener {
fun onZoomStarted(zoom: Float, zoomx: Float, zoomy: Float)
fun onZooming(zoom: Float, zoomx: Float, zoomy: Float)
fun onZoomEnded(zoom: Float, zoomx: Float, zoomy: Float)
}
// zooming
private var zoom = 1.0f
private var maxZoom = 2.0f
private var smoothZoom = 1.0f
private var zoomX = 0f
private var zoomY = 0f
private var smoothZoomX = 0f
private var smoothZoomY = 0f
private var scrolling // NOPMD by karooolek on 29.06.11 11:45
= false
// minimap variables
var isMiniMapEnabled = false
var miniMapColor = Color.WHITE
private var miniMapHeight = -1
var miniMapCaption: String? = null
var miniMapCaptionSize = 10.0f
var miniMapCaptionColor = Color.WHITE
// touching variables
private var lastTapTime: Long = 0
private var touchStartX = 0f
private var touchStartY = 0f
private var touchLastX = 0f
private var touchLastY = 0f
private var startd = 0f
private var pinching = false
private var lastd = 0f
private var lastdx1 = 0f
private var lastdy1 = 0f
private var lastdx2 = 0f
private var lastdy2 = 0f
// drawing
private val m = Matrix()
private val p = Paint()
// listener
var listener: ZoomViewListener? = null
private var ch: Bitmap? = null
fun getMaxZoom(): Float {
return maxZoom
}
fun setMaxZoom(maxZoom: Float) {
if (maxZoom < 1.0f) {
return
}
this.maxZoom = maxZoom
}
fun setMiniMapHeight(miniMapHeight: Int) {
if (miniMapHeight < 0) {
return
}
this.miniMapHeight = miniMapHeight
}
fun getMiniMapHeight(): Int {
return miniMapHeight
}
fun zoomTo(zoom: Float, x: Float, y: Float) {
this.zoom = Math.min(zoom, maxZoom)
zoomX = x
zoomY = y
smoothZoomTo(this.zoom, x, y)
}
private fun smoothZoomTo(zoom: Float, x: Float, y: Float) {
smoothZoom = clamp(1.0f, zoom, maxZoom)
smoothZoomX = x
smoothZoomY = y
if (listener != null) {
listener!!.onZoomStarted(smoothZoom, x, y)
}
}
val zoomFocusX: Float
get() = zoomX * zoom
val zoomFocusY: Float
get() = zoomY * zoom
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
if (!isEnableZoom) {
return super.dispatchTouchEvent(ev)
}
// single touch
if (ev.pointerCount == 1) {
processSingleTouchEvent(ev)
}
// // double touch
if (ev.pointerCount == 2) {
processDoubleTouchEvent(ev)
}
// redraw
rootView.invalidate()
invalidate()
return if (smoothZoom == 1f) {
super.dispatchTouchEvent(ev)
} else true
}
private fun processSingleTouchEvent(ev: MotionEvent) {
val x = ev.x
val y = ev.y
val w = miniMapHeight * width.toFloat() / height
val h = miniMapHeight.toFloat()
val touchingMiniMap = x >= 10.0f && x <= 10.0f + w && y >= 10.0f && y <= 10.0f + h
if (isMiniMapEnabled && smoothZoom > 1.0f && touchingMiniMap) {
processSingleTouchOnMinimap(ev)
} else {
processSingleTouchOutsideMinimap(ev)
}
}
private fun processSingleTouchOnMinimap(ev: MotionEvent) {
val x = ev.x
val y = ev.y
val w = miniMapHeight * width.toFloat() / height
val h = miniMapHeight.toFloat()
val zx = (x - 10.0f) / w * width
val zy = (y - 10.0f) / h * height
smoothZoomTo(smoothZoom, zx, zy)
}
private fun processSingleTouchOutsideMinimap(ev: MotionEvent) {
val x = ev.x
val y = ev.y
var lx = x - touchStartX
var ly = y - touchStartY
val l = Math.hypot(lx.toDouble(), ly.toDouble()).toFloat()
var dx = x - touchLastX
var dy = y - touchLastY
touchLastX = x
touchLastY = y
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
touchStartX = x
touchStartY = y
touchLastX = x
touchLastY = y
dx = 0f
dy = 0f
lx = 0f
ly = 0f
scrolling = false
}
MotionEvent.ACTION_MOVE -> if (scrolling || smoothZoom > 1.0f && l > 30.0f) {
if (!scrolling) {
scrolling = true
ev.action = MotionEvent.ACTION_CANCEL
super.dispatchTouchEvent(ev)
}
smoothZoomX -= dx / zoom
smoothZoomY -= dy / zoom
return
}
MotionEvent.ACTION_OUTSIDE, MotionEvent.ACTION_UP ->
// tap
if (l < 30.0f) {
// check double tap
if (System.currentTimeMillis() - lastTapTime < 500) {
if (smoothZoom == 1.0f) {
smoothZoomTo(maxZoom, x, y)
} else {
smoothZoomTo(1.0f, width / 2.0f,
height / 2.0f)
}
lastTapTime = 0
ev.action = MotionEvent.ACTION_CANCEL
super.dispatchTouchEvent(ev)
return
}
lastTapTime = System.currentTimeMillis()
performClick()
}
else -> {
}
}
ev.setLocation(zoomX + (x - 0.5f * width) / zoom, zoomY
+ (y - 0.5f * height) / zoom)
ev.x
ev.y
super.dispatchTouchEvent(ev)
}
private fun processDoubleTouchEvent(ev: MotionEvent) {
val x1 = ev.getX(0)
val dx1 = x1 - lastdx1
lastdx1 = x1
val y1 = ev.getY(0)
val dy1 = y1 - lastdy1
lastdy1 = y1
val x2 = ev.getX(1)
val dx2 = x2 - lastdx2
lastdx2 = x2
val y2 = ev.getY(1)
val dy2 = y2 - lastdy2
lastdy2 = y2
// pointers distance
val d = Math.hypot(x2 - x1.toDouble(), y2 - y1.toDouble()).toFloat()
val dd = d - lastd
lastd = d
val ld = Math.abs(d - startd)
Math.atan2(y2 - y1.toDouble(), x2 - x1.toDouble())
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
startd = d
pinching = false
}
MotionEvent.ACTION_MOVE -> if (isEnableGestureZoom) {
if (pinching || ld > 30.0f) {
pinching = true
val dxk = 0.5f * (dx1 + dx2)
val dyk = 0.5f * (dy1 + dy2)
smoothZoomTo(Math.max(1.0f, zoom * d / (d - dd)), zoomX - dxk
/ zoom, zoomY - dyk / zoom)
}
}
MotionEvent.ACTION_UP -> pinching = false
else -> pinching = false
}
ev.action = MotionEvent.ACTION_CANCEL
super.dispatchTouchEvent(ev)
}
private fun clamp(min: Float, value: Float, max: Float): Float {
return Math.max(min, Math.min(value, max))
}
private fun lerp(a: Float, b: Float, k: Float): Float {
return a + (b - a) * k
}
private fun bias(a: Float, b: Float, k: Float): Float {
return if (Math.abs(b - a) >= k) a + k * Math.signum(b - a) else b
}
override fun dispatchDraw(canvas: Canvas) {
// do zoom
zoom = lerp(bias(zoom, smoothZoom, 0.05f), smoothZoom, 0.2f)
smoothZoomX = clamp(0.5f * width / smoothZoom, smoothZoomX,
width - 0.5f * width / smoothZoom)
smoothZoomY = clamp(0.5f * height / smoothZoom, smoothZoomY,
height - 0.5f * height / smoothZoom)
zoomX = lerp(bias(zoomX, smoothZoomX, 0.1f), smoothZoomX, 0.35f)
zoomY = lerp(bias(zoomY, smoothZoomY, 0.1f), smoothZoomY, 0.35f)
if (zoom != smoothZoom && listener != null) {
listener!!.onZooming(zoom, zoomX, zoomY)
}
val animating = Math.abs(zoom - smoothZoom) > 0.0000001f || Math.abs(zoomX - smoothZoomX) > 0.0000001f || Math.abs(zoomY - smoothZoomY) > 0.0000001f
// nothing to draw
if (childCount == 0) {
return
}
// prepare matrix
m.setTranslate(0.5f * width, 0.5f * height)
m.preScale(zoom, zoom)
m.preTranslate(
-clamp(0.5f * width / zoom, zoomX, width - 0.5f
* width / zoom),
-clamp(0.5f * height / zoom, zoomY, height - 0.5f
* height / zoom))
// get view
val v = getChildAt(0)
m.preTranslate(v.left.toFloat(), v.top.toFloat())
// get drawing cache if available
if (animating && ch == null && isAnimationCacheEnabled) {
v.isDrawingCacheEnabled = true
ch = v.drawingCache
}
// draw using cache while animating
if (animating && isAnimationCacheEnabled && ch != null) {
p.color = -0x1
canvas.drawBitmap(ch!!, m, p)
} else { // zoomed or cache unavailable
ch = null
canvas.save()
canvas.concat(m)
v.draw(canvas)
canvas.restore()
}
// draw minimap
if (isMiniMapEnabled) {
if (miniMapHeight < 0) {
miniMapHeight = height / 4
}
canvas.translate(10.0f, 10.0f)
p.color = -0x80000000 or 0x00ffffff and miniMapColor
val w = miniMapHeight * width.toFloat() / height
val h = miniMapHeight.toFloat()
canvas.drawRect(0.0f, 0.0f, w, h, p)
if (miniMapCaption != null && miniMapCaption!!.length > 0) {
p.textSize = miniMapCaptionSize
p.color = miniMapCaptionColor
p.isAntiAlias = true
canvas.drawText(miniMapCaption!!, 10.0f,
10.0f + miniMapCaptionSize, p)
p.isAntiAlias = false
}
p.color = -0x80000000 or 0x00ffffff and miniMapColor
val dx = w * zoomX / width
val dy = h * zoomY / height
canvas.drawRect(dx - 0.5f * w / zoom, dy - 0.5f * h / zoom, dx
+ 0.5f * w / zoom, dy + 0.5f * h / zoom, p)
canvas.translate(-10.0f, -10.0f)
}
// redraw
rootView.postInvalidate()
postInvalidate()
}
/**
* 开启手势 缩放
*
* @param isEnableGestureZoom false 关闭手势缩放 true 开启手势缩放 默认 false
*/
fun isEnableGestureZoom(isEnableGestureZoom: Boolean) {
this.isEnableGestureZoom = isEnableGestureZoom
}
/**
* 设置 false 时不拦截事件
*
* @param isEnableZoom true 开启缩放功能 false 关闭缩放功能,默认开启缩放
*/
fun isEnableZoom(isEnableZoom: Boolean) {
this.isEnableZoom = isEnableZoom
}
/**
* 恢复原来状态
*/
fun setDefaultZoomSize() {
smoothZoomTo(1.0f, width / 2.0f,
height / 2.0f)
}
companion object {
private const val TAG = "ZoomView"
}
}
如何使用?
ZoomView仅支持直接包住一个子View若多个View请用FrameLayout等布局包住使用
<com.xxx.common.views.ZoomView
android:id="@+id/wpViewRoom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.xxx.pdflib.PDfViewPager2
android:id="@+id/pdfViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<com.xxx.live.ui.course.view.WhitePanView
android:id="@+id/wpView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:visibility="visible" />
</FrameLayout>
</com.xxx.common.views.ZoomView>
若侵权留言删