0

I have a very simple RelativeLayout subclass that adds an image view with a text view on top of it. I have a method, show(), which creates and adds the child views and sets the initial text.

At the point I call show() for the first time, the view does not know how big it is, so I can't set the textSize nor the padding for the textView.

I have a solution that mostly works, where I call setTextSize() and setPadding() for the textView within the overridden method, onSizeChanged(). The text does not show the first time it is displayed. However, it shows every time after that, perfectly sized and placed.

Here is the code for onSizeChanged():

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    Log.e(TAG, "onSizeChanged() called");
    if (_childTextView != null) {
        float textSize   = h / 2.0f;
        int   topPadding = (int)(h / 3.0f);
        Log.e(TAG, "setting textSize = " + textSize);
        Log.e(TAG, "topPadding = " + topPadding);
        _childTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
        _childTextView.setPadding(0, topPadding, 0, 0);
    }
    super.onSizeChanged(w, h, oldw, oldh);
    Log.e(TAG, "end onSizeChanged()");
}

The code for show() is as follows:

public void show(int val) {
    _val = val;

    Log.e(TAG, "in show(), val = " + val);

    // create and add background image if not already there
    if (_backgroundImageView == null) {
        _backgroundImageView = new ImageView(_context);
        _backgroundImageView.setImageResource(R.drawable.background);

        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        params.addRule(CENTER_IN_PARENT);
        addView(_backgroundImageView, params);
    }

    // create and add text view if not already there
if (_childTextView == null) {
        _childTextView = new TextView(_context);
        _childTextView.setTextColor(Color.BLACK);

        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

        params.addRule(CENTER_HORIZONTAL);
        addView(_childTextView, params);
    }
    Log.e(TAG, "setting text to: " + _val);

// update value and make visible
    _childTextView.setText(String.valueOf(_val));
    setVisibility(VISIBLE);
    Log.e(TAG, "end show()");
}

The background image displays correctly every time. The textView only displays correctly the second time show() is called and afterwards. Logging in onSizeChanged() shows that the calculated numbers are correct the first time. As expected, onSizeChanged() only gets called the first time, a bit after we return from show(). Subsequent calls to show() just set the value and visibility, and the text is displayed correctly.

My question is: is there a better way to do this? Or a better callback method to override?

Trying to set these values in show() doesn't work because the main view doesn't yet know its own size (at least the first time). I have tried putting invalidate() at the end of onSizeChanged(). I have also tried putting the call to setText() there.

I need to be able to do this based on size, because this class is reused in different contexts where the image needs to be smaller or larger.

Thank you for any insight you can give. I'd really like to keep this simple if possible.

Edit: What I am trying to do is size some text to be about 1/2 the size of the child image (which is the same as the parent size), and to have top padding set to about 1/3 of the image size. This would be easy if I just wanted it to be one size. However, I want it to be size-adjustable based on the needs of the display.

Imagine a postage stamp, where you want to place the value somewhere precisely in the image. So far so good. But what if this postage stamp needs to be displayed at different sizes on the same phone? You'd want both the placement offset (the padding) and the text size to adjust accordingly. If I hardcode this into the xml, then the text size and placement will not be adjusted when I size the layout. The text will be too big on the small version, and will be placed too far from the top of the image.

BeccaP
  • 1,452
  • 1
  • 13
  • 19
  • As a temporary workaround, I have added a slightly delayed call to show() after the first time, just to get it to display correctly. This is obviously a kludge, so if someone has a better answer, please do let me know. Thanks. – BeccaP Jan 30 '14 at 01:43

1 Answers1

-2

i have no idea why you override onSizeChanged(), normaly android handles all this nicely if you use it the way it is intendet. can you pls explain what you want to achive - maybe with example picture?

however i wondered that you don't override onMesure() when you override the rest and if a delayed call to show() helps it also might be because of onMesure is called in between.

edit:

  • in android you should never want to know a real size of some views. nearly every device has other sizes and there is portrait/landscape mode too. if you start coding vs real sizes you can give up at the start. instead you should use something more relative like dp and sp than you should never again worry about text sizes and similar.
  • you may also want and you should use LayoutInflater and xml files as much as possible in your application. in a Activity you can call setContentView(). in other cases there might be methods to overload like onCreateView. and if you have nothing else you can do it like this:

    LayoutInflater inflater = LayoutInflater.from(contextEgActivity);
    View rootView = inflater.inflate(R.layout.highscore_daily, parentCanBeNull);
    

edit II: so this is what you want - right? (on the ImageView and the TextView it would be even better to use wrap_content for height and width)

<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:id="@+id/imageView"
    android:layout_gravity="center"
    android:background="#ff0000" />
<TextView
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:text="New Text"
    android:id="@+id/textView"
    android:layout_gravity="center"
    android:background="#00ff00"
    android:textSize="20sp" />
</FrameLayout>

if you only have ~3 different sizes i would write 3 different xml files to match what you want. otherwise i think this code will fit your needs.

//http://stackoverflow.com/questions/4605527/converting-pixels-to-dp
public static float convertDpToPixel(float dp, Context context){
    Resources resources = context.getResources();
    DisplayMetrics metrics = resources.getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return px;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);//loads the xml above

    ImageView v = (ImageView) findViewById(R.id.imageView);
    int dp = 200;
    int px = (int) convertDpToPixel(dp, this);
    v.setMaxHeight(px);//no need for it
    v.setMinimumHeight(px);//should do more or less the same as next line
    v.setLayoutParams(new LinearLayout.LayoutParams(px, px));//is like android:layout_width="200dp" android:layout_height="200dp"
    v.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, px));//is like android:layout_width="wrap_content" android:layout_height="200dp"
    //basically you can do the same with the TextView + the Text styling
    TextView tv = (TextView) findViewById(R.id.textView);
    tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 50);
    tv.setPadding(30,30,30,30);//don't forget, this is also px so you may need dp to px conversion
}

this is the normal way, nice clean and easy. if you why ever still want to react on size changes of your parent you can try this but i don't suggest it. btw changing view stuff should only be executed from ui/main thread so if the method gets called from a other thread thry a Handler like new Handler(getMainLooper)

A. Binzxxxxxx
  • 2,812
  • 1
  • 21
  • 33
  • The only thing I'm overriding is onSizeChanged(). show() is not an override. I'll see if I can add a picture to the original post. – BeccaP Jan 30 '14 at 17:43
  • Thank you. I have edited the question. Please let me know if my description is clear enough. I don't have permission to show the image I'm trying to create. – BeccaP Jan 30 '14 at 21:41
  • if edit II is still not the answer to your question i can't help you because i don't get your question/problem right. – A. Binzxxxxxx Jan 30 '14 at 23:15
  • Thank you. I will give it a try this afternoon. If it works, I'll select yours as the answer. I sincerely appreciate you putting so much time into this. – BeccaP Jan 31 '14 at 18:18