简体   繁体   English

Android UI:自定义GIF视图在RelativeLayout中的位置

[英]Android UI: Positioning of custom GIF View within RelativeLayout

So, I have created my own custom GIFView component to show animated GIFs. 因此,我创建了自己的自定义GIFView组件以显示动画GIF。 My problem is that the Android wont lay it correctly to the center position. 我的问题是Android无法将其正确放置在中央位置。 I tried to look for this from various sources, but most were about how to do this dynamically while I would like to do this purely with XML layouts. 我试图从各种来源寻找这种方法,但是大多数都是关于如何动态地执行此操作的,而我想仅使用XML布局来执行此操作。

The problem is this: 问题是这样的:

  1. If I do not overwrite "onMeasure()", then my GIFView seems to take all the space in the screen. 如果我不覆盖“ onMeasure()”,那么我的GIFView似乎占据了屏幕上的所有空间。 This causes that all elements in the layout are drawn to the very top of the screen. 这导致布局中的所有元素都绘制到屏幕的最上方。 My GIF is like a progress bar (height = 8px, width = 152px), so it does not take much height. 我的GIF就像一个进度条(高度= 8像素,宽度= 152像素),因此它不需要太多的高度。 Anyways the Android thinks so and gives it the whole screen (my interpretation based on MeasureSpecs given to onMeasure method). 无论如何,Android都会这样考虑并给出整个屏幕(我的解释是基于onMeasure方法的MeasureSpecs)。 The animation is now just below the TextView, but not in the center area. 动画现在位于TextView下方,但不在中心区域。 Instead, it is located to the top left corner of the area. 相反,它位于该区域的左上角。

  2. If I do overwrite "onMeasure()", then the TextView is drawn correctly in the middle of the screen and the progress animation under it. 如果我确实覆盖了“ onMeasure()”,则在屏幕中间及其下方的进度动画中将正确绘制TextView。 But, also in this case the location of the animated GIF is top left of the area while it should be in the middle of it. 但是,在这种情况下,动画GIF的位置也在该区域的左上方,而它应该在该区域的中间。

  3. If I do overwrite the "onMeasure()", then set the measurements with call to "setMeasuredDimension()" as required, but do not call super.onMeasure(), then it behaves just like in case (1). 如果确实覆盖了“ onMeasure()”,然后根据需要通过调用“ setMeasuredDimension()”来设置度量,但是不调用super.onMeasure(),则其行为与情况(1)相同。 Meaning that the layout takes the whole screen and my TextView can be found from the very top of the screen. 这意味着布局占据了整个屏幕,而我的TextView可以从屏幕的顶部找到。

I am not sure if I made any sense, so I try to give you the idea in mathematical sense here (I was not able to post screenshots because I am a new user). 我不确定我是否有意义,所以我尝试在数学上为您提供这个想法(由于我是新用户,所以我无法发布屏幕截图)。 So, relative layout gives area just below TextView that is as wide as the screen (screenWidth) and as tall as required (paddingTop + paddingBottom + GIFView.getHeight()). 因此,相对布局提供了TextView下方的区域,该区域与屏幕一样宽(screenWidth),并且与所需的高度一样高(paddingTop + paddingBottom + GIFView.getHeight())。 Now I would like to see my animation to start drawing from position where: (x = paddingLeft + (screenWidth - paddingLeft - paddingRight - GIFView.getWidth())/2) and (y = paddingTop). 现在,我希望看到动画从以下位置开始绘制:(x = paddingLeft +(screenWidth-paddingLeft-paddingRight-GIFView.getWidth())/ 2)和(y = paddingTop)。 Instead I see the Android drawing it to position (x = 0) and (y = 0). 相反,我看到Android将其绘制到位置(x = 0)和(y = 0)。

I am sure the problem here is something that I have just overlooked, but I would really appreciate if someone had the time to look into this... 我确信这里的问题是我刚刚忽略的,但是如果有人有时间研究这个问题,我将不胜感激。

So, the snippets that I get this problem with: 所以,我得到这个问题的片段:

Styleable declaration: 样式声明:

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

Main layout XML: 主布局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>

Then the code of GIFView.java: 然后是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();
}

} }

And if this is important, then the activity constructor looks like this: 如果这很重要,那么活动构造函数如下所示:

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 );

So, why cannot I get that animation to run below the text and in the center of the screen? 那么,为什么不能让该动画在文本下方和屏幕中央运行? I will post the fix here if I can figure it out before... It has something to do with the onMeasure() method as overwriting it changes everything. 如果可以先弄清楚,我将在此处发布此修复程序...它与onMeasure()方法有关,因为覆盖它会更改所有内容。

Disclaimer on snippets: I am not going to leave that invalidate() in the end of the onDraw()... 代码段免责声明:我不会在inDraw()的末尾留下那个invalidate()...

Br, Teemu Br,特木

Ok, thanks for all those that had time to look into this problem. 好的,感谢所有有时间调查此问题的人员。 I did so too, and as I expected, the solution was rather simple... 我也是这样做的,而且正如我所期望的,解决方案非常简单...

The problem was that I did not declare correct size to my View and just declared it to be exactly as wide as the GIF was. 问题是我没有为View声明正确的大小,只是声明它与GIF一样宽。 So, fixing onMeasure to set dimension to take all the given width, then taking the padding into consideration and drawing to correct place fixed everything. 因此,将onMeasure固定为设置尺寸以采用所有给定宽度,然后考虑填充并绘制正确的位置以固定所有内容。 Fixed code snippets: 固定代码段:

New variable: 新变量:

private int mDrawLeftPos;

Fixed onMeasure 固定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) ;
}

Fixed onDraw 固定的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 Br,特木

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM