1

そこで、アニメーションGIFを表示するための独自のカスタムGIFViewコンポーネントを作成しました。私の問題は、Androidがそれを中央の位置に正しく配置しないことです。私はさまざまなソースからこれを探しようとしましたが、ほとんどはこれを動的に行う方法に関するものでしたが、純粋にXMLレイアウトでこれを行いたいと思っています。

問題はこれです:

  1. 「onMeasure()」を上書きしないと、GIFViewが画面のすべてのスペースを占めるように見えます。これにより、レイアウト内のすべての要素が画面の最上部に描画されます。私のGIFはプログレスバー(高さ= 8ピクセル、幅= 152ピクセル)のようなものなので、高さはそれほどかかりません。とにかく、Androidはそう考えて、画面全体を表示します(onMeasureメソッドに与えられたMeasureSpecsに基づく私の解釈)。アニメーションはTextViewのすぐ下にありますが、中央の領域にはありません。代わりに、エリアの左上隅にあります。

  2. 「onMeasure()」を上書きすると、TextViewが画面の中央に正しく描画され、その下に進行状況のアニメーションが表示されます。ただし、この場合も、アニメーションGIFの場所は領域の左上にあり、中央にあるはずです。

  3. 「onMeasure()」を上書きし、必要に応じて「setMeasuredDimension()」を呼び出して測定値を設定しますが、super.onMeasure()を呼び出さない場合は、(1)の場合と同じように動作します。つまり、レイアウトは画面全体を占め、TextViewは画面の最上部から見つけることができます。

意味があるかどうかわからないので、ここで数学的な意味でアイデアを提供しようとしています(私は新しいユーザーであるため、スクリーンショットを投稿できませんでした)。したがって、相対レイアウトは、TextViewのすぐ下に、画面と同じ幅(screenWidth)で、必要な高さ(paddingTop + paddingBottom + GIFView.getHeight())の領域を提供します。ここで、アニメーションが次の位置から描画を開始することを確認します:(x = paddingLeft +(screenWidth --paddingLeft --paddingRight --GIFView.getWidth())/ 2)および(y = paddingTop)。代わりに、Androidがそれを(x = 0)と(y = 0)の位置に描画しているのがわかります。

ここでの問題は私が見落としたものだと確信していますが、誰かがこれを調べる時間があれば本当にありがたいです...

したがって、この問題が発生するスニペットは次のとおりです。

スタイリング可能な宣言:

<declare-styleable name="GIFView">
    <attr name="resourceId" format="reference" />
</declare-styleable>

メインレイアウトXML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical"
   android:paddingLeft="10dip"
   android:paddingRight="10dip"
   android:isScrollContainer="true"
   android:background="#EFEFEF" >  

   <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:custom="http://schemas.android.com/apk/res/com.myown.smthg"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_centerHorizontal="true"
      android:layout_centerVertical="true"
      android:orientation="vertical"
      android:background="@drawable/background_white_gray"
      android:paddingLeft="16dip"
      android:paddingRight="16dip"
      android:isScrollContainer="true" >

  <TextView
     android:id="@+id/TextViewWaitReason"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
         android:gravity="center"
         android:paddingTop="12dip"
         android:paddingLeft="12dip"
         android:paddingRight="12dip"
         android:textColor="#343434"
         android:textSize="20sp" />

  <com.myown.smthg.util.GIFView
     custom:resourceId="@drawable/progress_bar"
     android:id="@+id/progressGIF"
     android:layout_below="@id/TextViewWaitReason"
     android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
         android:gravity="center"
         android:paddingTop="0dip"
         android:paddingLeft="24dip"
         android:paddingRight="24dip"
         android:paddingBottom="16dip" />
  </RelativeLayout>

次に、GIFView.javaのコード:

public class GIFView extends View {
// The resource id of the GIF
private int mResourceId;

// Movie to be shown
private Movie mMovie;
private long mStartTime;

// Size of this View
private int mHeight, mWidth;

/**
 * Constructor
 * 
 * @param context
 * @param attributes
 */
public GIFView( Context context, AttributeSet attributes ) {
    super( context, attributes );

    // Get the attribute for resource id
    TypedArray t = context.getTheme().obtainStyledAttributes( 
            attributes, R.styleable.GIFView, 0, 0 );

    mResourceId = -1;
    mMovie = null;
    mStartTime = 0;
    mHeight = 0;
    mWidth = 0;

    // This call might fail
    try
    {
        mResourceId = t.getResourceId ( R.styleable.GIFView_resourceId, -1 );

        mMovie = Movie.decodeStream( context.getResources().openRawResource( mResourceId ) );
        if( mMovie != null )
        {
            mWidth = mMovie.width();
            mHeight = mMovie.height();
        }
    }
    finally
    {
        t.recycle();
    }
}

@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{   
    final int desiredHSpec = MeasureSpec.makeMeasureSpec( mHeight, MeasureSpec.EXACTLY );
    final int desiredWSpec = MeasureSpec.makeMeasureSpec( mWidth, MeasureSpec.EXACTLY );
    setMeasuredDimension( desiredWSpec, desiredHSpec );

    super.onMeasure( desiredWSpec, desiredHSpec );
}

@Override
protected void onDraw( Canvas canvas )
{
    super.onDraw( canvas );

    // Return if we have no movie
    if( mMovie == null ) return;

    // Catch the time now
    long now = System.currentTimeMillis();

    // Catch the start time if needed
    if( mStartTime == 0 ) mStartTime = now;

    int relTime = (int)( (now- mStartTime) % mMovie.duration() );
    mMovie.setTime( relTime );
    mMovie.draw( canvas, 0, 0 );

    this.invalidate();
}

}

これが重要な場合、アクティビティコンストラクタは次のようになります。

super.onCreate( savedInstanceState );
// Set the content view
setContentView( R.layout.layout_wait );

// Get the String to be shown
Intent intent = getIntent();
String waitStr = intent.getStringExtra( myService.EXTRA_TEXT );

// Set the done text
TextView t = (TextView)findViewById( R.id.TextViewWaitReason );
if( t != null && waitStr != null )
    t.setText( waitStr );

では、なぜそのアニメーションをテキストの下と画面の中央で実行できないのでしょうか。前に理解できれば、ここに修正を投稿します... onMeasure()メソッドを上書きすると、すべてが変更されるため、これはonMeasure()メソッドと関係があります。

スニペットに関する免責事項:onDraw()の最後にそのinvalidate()を残すつもりはありません...

Br、Teemu

4

1 に答える 1

1

わかりました、この問題を調査する時間を持っていたすべての人に感謝します。私もそうしました、そして私が予想したように、解決策はかなり単純でした...

問題は、ビューに正しいサイズを宣言せず、GIFとまったく同じ幅であると宣言したことです。したがって、onMeasureを修正して、指定されたすべての幅を取得するように寸法を設定し、次にパディングを考慮して、正しい場所に描画することですべてを修正しました。修正されたコードスニペット:

新しい変数:

private int mDrawLeftPos;

onMeasureを修正

@Override
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec )
{   
    int p_top = this.getPaddingTop(), p_bottom = this.getPaddingBottom();

    // Calculate new desired height
    final int desiredHSpec = MeasureSpec.makeMeasureSpec( mHeight + p_top + p_bottom , MeasureSpec.EXACTLY );

    setMeasuredDimension( widthMeasureSpec, desiredHSpec );
    super.onMeasure( widthMeasureSpec, desiredHSpec );

    // Update the draw left position
    mDrawLeftPos = Math.max( ( this.getWidth() - mWidth ) / 2, 0) ;
}

onDrawを修正

@Override
protected void onDraw( Canvas canvas )
{
    super.onDraw( canvas );

    // Return if we have no movie
    if( mMovie == null ) return;

    // Catch the time now
    long now = System.currentTimeMillis();

    // Catch the start time if needed
    if( mStartTime == 0 ) mStartTime = now;

    int relTime = (int)( (now- mStartTime) % mMovie.duration() );
    mMovie.setTime( relTime );
    mMovie.draw( canvas, mDrawLeftPos, this.getPaddingTop() );
}

Br、Teemu

于 2012-10-19T09:02:17.193 に答える