hackbodからの優れた回答は、私が最終的に到達した解決策を投稿する必要があることを私に思い出させました。当時私が行っていたアプリケーションで機能していたこのソリューションは、hackbodの提案によってさらに改善される可能性があることに注意してください。特に、タッチイベントを処理する必要はありませんでした。ハックボッドの投稿を読むまで、処理する場合はそれらもスケーリングする必要があるとは思いもしませんでした。
要約すると、私のアプリケーションでは、他の小さな「マーカー」記号を重ね合わせた大きな図(具体的には、建物のフロアレイアウト)を作成する必要がありました。背景図と前景シンボルはすべて、ベクターグラフィック(つまり、onDraw()メソッドでCanvasに適用されるPath()オブジェクトとPaint()オブジェクト)を使用して描画されます。ビットマップリソースを使用するだけでなく、この方法ですべてのグラフィックを作成する理由は、グラフィックが実行時にSVGイメージコンバーターを使用して変換されるためです。
要件は、ダイアグラムと関連するマーカーシンボルがすべてViewGroupの子であり、すべて一緒にピンチズームできることでした。
多くのコードは乱雑に見えるので(デモンストレーションの急ぎの仕事でした)、すべてをコピーするのではなく、引用された関連するコードを使用してどのように実行したかを説明しようと思います。
まず、ZoomableRelativeLayoutがあります。
public class ZoomableRelativeLayout extends RelativeLayout { ...
このクラスには、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;
}
}
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)
);
}
}
}
ここで重要なのは、すべての子で「layout()」を呼び出すときに、それらの子のレイアウトパラメーターにもスケール係数を適用していることです。これは、クリッピングの問題を解決するための1つのステップであり、さまざまなスケール係数に対して、子のx、y位置を相互に正しく設定することも重要です。
さらに重要なことは、dispatchDraw()でCanvasをスケーリングしようとしないことです。代わりに、各子ビューは、getterメソッドを介して親ZoomableRelativeLayoutから倍率を取得した後、キャンバスをスケーリングします。
次に、ZoomableRelativeLayoutの子ビュー内で実行する必要があったことに移ります。ZoomableRelativeLayoutに子として含めるビューのタイプは1つだけです。これは、私がSVGViewと呼んでいるSVGグラフィックを描画するためのビューです。もちろん、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);
}
そして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();
}
繰り返しますが、免責事項として、私がそこに投稿したものにはおそらくいくつかの疣贅があり、私はまだハックボッドの提案を注意深く調べてそれらを取り入れていません。戻ってきて、これをさらに編集するつもりです。それまでの間、ズーム可能なViewGroupを実装する方法について他の人に役立つポインタを提供し始めることができることを願っています。