Tre*_*vor 24 android android-layout android-relativelayout android-viewgroup
我见过一些人问过如何一次性缩放整个ViewGroup(例如RelativeLayout).目前,这是我需要实现的目标.对我来说,最明显的方法是将缩放比例因子保持为某个变量; 并且在每个子视图的onDraw()方法中,该比例因子将在绘制图形之前应用于Canvas.
然而,在这之前,我试图聪明(哈 - 通常是一个坏主意)并将RelativeLayout扩展到一个名为ZoomableRelativeLayout的类中.我的想法是,任何比例转换只能在重写的dispatchDraw()函数中应用于Canvas,因此绝对不需要在任何子视图中单独应用缩放.
这是我在ZoomableRelativeLayout中所做的.它只是RelativeLayout的一个简单扩展,其中dispatchDraw()被覆盖:
protected void dispatchDraw(Canvas canvas){
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
Run Code Online (Sandbox Code Playgroud)
mScaleFactor由同一类中的ScaleListener操作.
它确实有效.我可以捏缩放ZoomableRelativeLayout,并将所有视图保持在一起正确重新缩放.
除了有问题.其中一些子视图是动画的,因此我会定期对它们调用invalidate().当比例为1时,可以看到这些子视图定期重绘完全正常.当比例不是1时,这些动画子视图只能在其观看区域的一部分中更新 - 或者根本不会更新 - 具体取决于缩放比例.
我最初的想法是,当一个人子视图的无效()被调用,那么它可能被单独由系统重新绘制,而不是传递从父的RelativeLayout的dispatchDraw()画布,这意味着子视图最终刷新自己没有应用缩放比例.但奇怪的是,在屏幕上重绘的子视图的元素是正确的缩放比例.这几乎就好像系统决定在后备位图中实际更新的区域仍然未缩放 - 如果这有意义的话.换一种方式,如果我有一个动画儿童观和我逐渐成为1的初始规模进一步和进一步放大,如果我们把一个假想的地方发现孩子的看法是该区域时,缩放比例为1 ,然后调用invalidate()只会导致刷新该虚构框中的图形.但是,这被视为更新显卡都正在做正确的规模.如果放大到目前为止,子视图现在已经完全远离它的比例为1,那么它的任何部分都不会被刷新.我将举一个例子:想象我的孩子视图是一个通过在黄色和红色之间切换来动画的球.如果我放大一点使得球向右和向下移动,在某一点你只会看到球的左上角四分之一的颜色.
如果我不断放大和缩小,我会看到孩子正确完整地观看动画.这是因为正在重绘整个ViewGroup.
我希望这是有道理的; 我试图尽我所能解释.使用我的可缩放ViewGroup策略,我有点失败吗?还有另外一种方法吗?
谢谢,
崔佛
hac*_*bod 14
如果要将比例因子应用于孩子的绘图,则还需要将适当的比例因子应用于与其进行的所有其他交互 - 调度触摸事件,无效等.
因此除了dispatchDraw()之外,您还需要覆盖并适当调整至少这些其他方法的行为.要处理无效,您需要覆盖此方法以适当调整子坐标:
如果您希望用户能够与子视图进行交互,您还需要覆盖它以在将调度分配给子项之前适当调整触摸坐标:
另外,我强烈建议您在一个简单的ViewGroup子类中实现它,该子类具有它管理的单个子视图.这将消除RelativeLayout在其自己的ViewGroup中引入的任何行为复杂性,从而简化您在自己的代码中处理和调试所需的内容.将RelativeLayout作为特殊缩放ViewGroup的子项.
最后,对代码进行了一项改进 - 在dispatchDraw()中,您希望在应用缩放因子后保存画布状态.这可确保子项无法修改您设置的转换.
Tre*_*vor 14
来自hackbod的优秀答案提醒我,我需要发布我最终找到的解决方案.请注意,这个解决方案对我来说对我当时正在进行的应用程序有用,可以通过hackbod的建议进一步改进.特别是我不需要处理触摸事件,直到阅读hackbod的帖子,我没有想到,如果我这样做,那么我也需要扩展它们.
回顾一下,对于我的应用,我需要实现的是拥有一个大图(特别是建筑物的楼层布局),其上叠加了其他小的"标记"符号.背景图和前景符号都是使用矢量图形绘制的(即onDraw()方法中应用于Canvas的Path()和Paint()对象).与仅使用位图资源相反,想要以这种方式创建所有图形的原因是因为使用我的SVG图像转换器在运行时转换图形.
要求是图表和相关的标记符号都是ViewGroup的子元素,并且可以全部缩放缩放在一起.
很多代码看起来都很混乱(这是演示的一个繁忙的工作)所以不要只是全部复制,而是试着解释我是如何用引用的相关代码进行的.
首先,我有一个ZoomableRelativeLayout.
public class ZoomableRelativeLayout extends RelativeLayout { ...
Run Code Online (Sandbox Code Playgroud)
此类包含扩展ScaleGestureDetector和SimpleGestureListener的侦听器类,以便可以平移和缩放布局.这里特别感兴趣的是缩放手势监听器,它设置一个比例因子变量,然后调用invalidate()和requestLayout().如果有必要使用invalidate(),我现在并不严格确定,但无论如何 - 这里是:
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector){
mScaleFactor *= detector.getScaleFactor();
// Apply limits to the zoom scale factor:
mScaleFactor = Math.max(0.6f, Math.min(mScaleFactor, 1.5f);
invalidate();
requestLayout();
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
我在ZoomableRelativeLayout中要做的下一件事是覆盖onLayout().为此,我发现查看其他人在可缩放布局上的尝试很有用,而且我发现查看RelativeLayout的原始Android源代码非常有用.我重写的方法复制了RelativeLayout的onLayout()中的大部分内容,但进行了一些修改.
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
int count = getChildCount();
for(int i=0;i<count;i++){
View child = getChildAt(i);
if(child.getVisibility()!=GONE){
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)child.getLayoutParams();
child.layout(
(int)(params.leftMargin * mScaleFactor),
(int)(params.topMargin * mScaleFactor),
(int)((params.leftMargin + child.getMeasuredWidth()) * mScaleFactor),
(int)((params.topMargin + child.getMeasuredHeight()) * mScaleFactor)
);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这里重要的是,当在所有孩子上调用'layout()'时,我将比例因子应用于布局参数以及那些孩子.这是解决剪切问题的一个步骤,并且重要的是,它针对不同的比例因子正确地设置了儿童相对于彼此的x,y位置.
另一个关键是我不再尝试在dispatchDraw()中扩展Canvas.相反,每个子视图在通过getter方法从父ZoomableRelativeLayout获取比例因子后缩放其Canvas.
接下来,我将继续讨论我在ZoomableRelativeLayout的子视图中必须做的事情.在我的ZoomableRelativeLayout中,我只包含一种类型的View作为子项; 它是一个用于绘制SVG图形的视图,我称之为SVGView.当然SVG的东西在这里不相关.这是它的onMeasure()方法:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
float parentScale = ((FloorPlanLayout)getParent()).getScaleFactor();
int chosenWidth, chosenHeight;
if( parentScale > 1.0f){
chosenWidth = (int) ( parentScale * (float)svgImage.getDocumentWidth() );
chosenHeight = (int) ( parentScale * (float)svgImage.getDocumentHeight() );
}
else{
chosenWidth = (int) ( (float)svgImage.getDocumentWidth() );
chosenHeight = (int) ( (float)svgImage.getDocumentHeight() );
}
setMeasuredDimension(chosenWidth, chosenHeight);
}
Run Code Online (Sandbox Code Playgroud)
而onDraw():
@Override
protected void onDraw(Canvas canvas){
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(((FloorPlanLayout)getParent()).getScaleFactor(),
((FloorPlanLayout)getParent()).getScaleFactor());
if( null==bm || bm.isRecycled() ){
bm = Bitmap.createBitmap(
getMeasuredWidth(),
getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
... Canvas draw operations go here ...
}
Paint drawPaint = new Paint();
drawPaint.setAntiAlias(true);
drawPaint.setFilterBitmap(true);
// Check again that bm isn't null, because sometimes we seem to get
// android.graphics.Canvas.throwIfRecycled exception sometimes even though bitmap should
// have been drawn above. I'm guessing at the moment that this *might* happen when zooming into
// the house layout quite far and the system decides not to draw anything to the bitmap because
// a particular child View is out of viewing / clipping bounds - not sure.
if( bm != null){
canvas.drawBitmap(bm, 0f, 0f, drawPaint );
}
canvas.restore();
}
Run Code Online (Sandbox Code Playgroud)
再次 - 作为免责声明,我在那里发布的内容可能有些瑕疵,我还没有仔细查看hackbod的建议并将它们合并.我打算回来再进一步编辑.与此同时,我希望它能够开始为其他人提供有关如何实现可缩放ViewGroup的有用指针.
归档时间: |
|
查看次数: |
16789 次 |
最近记录: |