RemoteView setLayoutParams - Change ImageView Size Inside HomeScreen Widget - android

My app shows a widget with a total of 8 buttons. I would like to give the user the possibility to style that widget and customize the size of the ImageViews which are below the buttons.
The aim is to leave the Buttons as they are, but to change the ImageView sizes dynamically.
For this the User gets to set an icon Size, which is stored in a SharedPreference as an Integer iconSize.
How can I change the size of an ImageView in a Widget?
For now, the ImageViews are created with the size set in the xml file. How can I achieve a redraw with another size?
I assume there is not much code to post here but I will be glad to do so if necessary, just tell me if you have an idea what code could help here.
What I don't want to do:
resize the entire widget
create tons of layout files to switch according to the iconSize
Some Code:
This is how I set the ImageView size in my activity as a preview of the widget. icons is an array of ImageViews, progress refers to a ProgressBar that is used to choose iconSize.
for (ImageView icon : icons) {
icon.requestLayout();
// convert into actual Pixels
progress = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_PX,
progress,
getResources().getDisplayMetrics()
);
// set width and height
icon.getLayoutParams().height = progress;
icon.getLayoutParams().width = progress;
sizeText.setText("" + progress);
}

Here is a little workaround I found:
simply use
views.setViewPadding(R.id.vieId, left, top, right, bottom);
(views = RemoteViews)
You just have to make some calculation, so that an iconSize of 100% (of the biggest possible size) equals 0 padding and 1% iconSize equals max padding.
It worked without, too but I think it can'T harm to add the
android:cropToPadding="true"
attribute to ImageViews if you use this method.
Edit:
I forgot to mention that at some point after setting the padding you should update the widget (I do it in onPause() when the user quits the application to look at the widget).
Using the setPadding() in an activity will also lead to nowhere without calling invalidate() on the View, to force a redraw/update of it.
Here more code:
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// Map the values, 100 is the custom max of my seekBar
// I am adding 1 to because the seekBar goes from 0-99,
// but the size from 1% to 100%
int iconSize = 100 - (progress+1);
for (ImageView icon : icons) {
// set the padding
icon.setPadding(iconSize, iconSize, iconSize, iconSize);
// force the ImageView to redraw with the new padding, this
// serves as a live preview of the icons' sizes.
icon.invalidate();
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// you might want to do something here
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// map the value
int iconSize = 100 - (seekBar.getProgress()+1);
// save it in your SharedPreferences or somewhere else
Utils.setDefaultsInt(Con._ICONSIZE, iconSize, MainPage.this);
}
});

Related

setPadding on ImageView doesnt work after setLayoutParams

I'm making a custom View thats derived from an ImageViewand I control where the image has to be within this ImageView using padding.
I have set OnClickListener on my custom ImageView that resizes it:
image.setOnClickListener(new ImageView.OnClickListener(){
#Override
public void onClick(View v) {
image.resize_image();
}
});
and this is how this function is looking like
public void resize_image(){
ViewGroup.LayoutParams params = getLayoutParams();
params.height = new_heigth;
params.width = new_width;
setLayoutParams(params);
}
After this resizing is done I don't want my displayed image to change size (only the custom ImageView is changing size so I can draw an extra stuff around this image) within my custom ImageView so inside onDraw(Canvas) member function I set the new padding
class custom_ImageView extends ImageView{
//...
#Override
protected void onDraw(Canvas canvas) {
//...
setPadding(new_left_padding, new_top_padding, new_right_padding, new_bottom_padding);
//...
}
//...
}
Result is that width and heigth are changed like I wanted but my displayed image is neither in the right position or size.
Interesting this is that if I add an extra line of invalidate(); in the end of my resize_image() and I click on my custom_ImageView twice - on the 2nd click image draws itself in right size and position like I wanted.
Can anyone tell me why this is happening?
I kind of worked around this by not using ImageView at all but just drawing image where it needs to be by using Canvas.drawBitmap(Bitmap, RectF, RectF, Paint)
This way I don't have to specify images location using setPadding within onDraw, but I can do it simply by specifying position using RectF.

Android: When to call methods that affect child layout upon parent view size change?

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

Text in TextView drawed out of 9patch padding borders

I needed to get constant aspect ratio of TextView with background image (9patch). So i used the code below in my activity:
#Override
public void onResume() {
super.onResume();
// adding contact image resize callback for adjusting image height
findViewById(R.id.contactText).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// aspect ratio
float resizeRatio = 1.13f;
// getting contact frame
TextView contactView = (TextView) findViewById(R.id.contactText);
ViewGroup.LayoutParams layout = contactView.getLayoutParams();
// resizing contact text
layout.height = (int) (contactView.getWidth() * resizeRatio);
contactView.setLayoutParams(layout);
contactView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
}
This code does what i expect - TextView is resized. But the borders (padding) for text, described in nine-patch are get from not resized image. So text is displayed in the center of TextView instead of bottom, where the padding borders are located.
How to fix this problem?
The problem is because of constant top padding of image even after stretch. When android stretches image vertically it keeps top padding value. So if padded area is not in stretched area it increases though it is not stretched at all.
This problem is described here

Android TextView: can I stop text that is partially displayed from appearing

In my app I display several text views containing text of various length that is loaded in at run time. I do not know the dimensions of the text view or the length of the text until run time. Sometimes, when the text is long and the textview small some of the text is partially visible, for example:
I want to remove the partially visible text as it looks a bit naff, but I can't find a way to do this. Any help would be appreciated!
Thanks,
Dave
You can hard code the TextView height in a way that the second row of text will not be visible.
Or use:
android:maxLines , Makes the TextView be at most this many lines tall.
as suggested above.
Put your textviews in a scrollview layout.And specify a specific width to your textview and make the height wrap content.So that your text doesn't get cut.
This is how I did it. I ran this code after the activity had loaded by posting the method CheckTextIsVisible to the parent relativelayout's handler queue, otherwise the height of the textviews will not be known:
m_eventsLayout.Post(new Action(CheckTextIsVisible));
Then the method CheckTextIsVisible finds each textview with text in it, calculates the height of the font, works out how many lines can fit in the textview, and sets the number of maximum lines accordingly:
private void CheckTextIsVisible()
{
View view;
TextView tView;
Android.Text.TextPaint tPaint;
float height;
int heightOfTextView;
int noLinesInTextView;
for (int i = 0; i < m_eventsLayout.ChildCount; i++)
{
view = m_eventsLayout.GetChildAt(i);
if (view is TextView)
{
tView = (TextView)view;
if (tView.Text != "")
{
//calculate font height
tPaint = tView.Paint;
height = CalculateTextHeight(tPaint.GetFontMetrics());
//calculate the no of lines that will fit in the text box based on this height
heightOfTextView = tView.Height;
noLinesInTextView = (int)(heightOfTextView / height);
//set max lines to this
tView.SetMaxLines(noLinesInTextView);
}
}
}
}
private float CalculateTextHeight(Android.Graphics.Paint.FontMetrics fm)
{
return fm.Bottom - fm.Top;
}
This results in no partially visible text!

Button with Image and Text, ways to place the image

In my app, I have several buttons with little images with them. I've added them to the buttons with
android:drawableLeft="picture"
Now if my button is fill_parent, the picture is shown at the left, of course and my text is centered. Now my question is, is there any way to center these images or put them next to the text?
you can use this as it help me
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
// Call here getWidth() and getHeight()
applyTextOffset(yourButton, yourButton.getWidth(),R.drawable.recents); // where R.drawable.recents is your image which you want to set as background
}
This the method when you can set your image near to your text
public void applyTextOffset(Button button, int buttonWidth, int image) {
int textWidth = (int) button.getPaint().measureText(
button.getText().toString());
int padding = (buttonWidth / 2)
- ((textWidth / 2)
+ ((BitmapDrawable) this.getResources().getDrawable(image)).getBitmap().getWidth());
button.setPadding(padding, 0, 0, 0);
button.setCompoundDrawablePadding(-padding);
}
Maybe it's not the best way to solve your problem, but did you try to work with a layout instead of a button. For example, a LinearLayout with everything you want in the right order, and then just add a click listener to make it work like a button. Hope this helps.

Categories

Resources