I have an Android related issue:
I am trying to centre a logo on the screen of my device, but it won't position correctly.
I am using the following function:
public void ImageCentered(int ID){
ImageView iv = (ImageView) findViewById(ID);
int x = 0;
int y = 0;
RelativeLayout.LayoutParams position = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
x = (screenWidth/2)-(iv.getWidth()/2);
y = (screenHeight/2)-(iv.getHeight()/2);
position.setMargins(x, y, 0, 0);
iv.setLayoutParams(position);
}
This could should work,but it won't. The image is set off slightly to the right and bottom like in this image:
https://www.dropbox.com/s/tf7r1u1xqcmb9t9/2014-08-16%2018.36.04.png
Now, the strange thing is, when I use the following code:
public void ImageCentered(int ID){
ImageView iv = (ImageView) findViewById(ID);
int x = 0;
int y = 0;
RelativeLayout.LayoutParams position = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
x = (screenWidth/2)-(iv.getWidth()/2);
y = (screenHeight/2)-(iv.getHeight()/2);
position.setMargins(x, y, 0, 0);
Message(IntToStr(x)+", "+IntToStr(y));
iv.setLayoutParams(position);
}
this is the result:
https://www.dropbox.com/s/mjw80zlzkav6dzs/2014-08-16%2018.41.16.png
Side note: The text in the Message() function does not matter, nor does its position within the ImageCentered() function.
I am not calling the function in my OnCreate(), as the width and height of the image would always return 0, so I looked something up and found this:
#Override
public void onWindowFocusChanged(boolean hasFocus){
ImageCentered(R.id.image);
}
This piece of code is in my MainActivity.java file, whereas the ImageCentered() function is in my UtilLib.java file.
So, I was wondering: What's going on here? Why does the code work when I pop in a Message() but not when I leave it out?
Sure, I can try hardcoding the data, but what about smaller/bigger screens?
I hope an Android guru can help me out here, as I've been struggling with this for quite some time now.
EDIT
Just noticed something interesting when pressing "OK" on my Message:
https://www.dropbox.com/s/a7lu6588hy1opw7/2014-08-16%2018.51.55.png
My guess is that my problem lies there, but after clicking the "OK" button once more, the data is "492, 207" again. scratches head
Assuming rom your code that the ImageView is inside a RelativeLayout, You could also do:
// get imageview layout params or create new ones
RelativeLayout.LayoutParams params = imageView.getLayoutParams();
params.addRule(RelativeLayout.CENTER_HORIZONTAL);
this way the image will be automatically centered without all those manual calculations!.
You can also specify it in the XML with
<ImageView .....
android:layout_centerHorizontal="true"/>
I've ultimately decided to just hardcode the x and y coordinates, and later on use some sort of scaling-conversion to position them properly, unless someone can provide me with a better method of fixing this.
UPDATE
So, after googling after a while (again), I have finally found the answer and created two functions:
public int GetImageHeight(int ID){
ImageView iv = (ImageView)findViewById(ID);
iv.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
iv.layout(0, 0, iv.getMeasuredWidth(), iv.getMeasuredHeight());
return iv.getMeasuredHeight();
}
public int GetImageWidth(int ID){
ImageView iv = (ImageView)findViewById(ID);
iv.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
iv.layout(0, 0, iv.getMeasuredWidth(), iv.getMeasuredHeight());
return iv.getMeasuredWidth();
}
All you have to do is pass the ID of the image you made in your xml file, like so:
GetImageHeight(R.id.logo)
Related
Maybe I'm not entering the right keywords, but I'm not finding an answer. I want to know what the dimensions of a TextView would be if I were set it with a certain string. However, I want to know before everything gets laid out in the activity.
My TextView has a fixed width and a variable height. I can get the height like this:
myTextView.setText(myString);
// ... UI gets laid out ...
myTextView.getHeight()
I want to change the width of the TextView if the height gets past a certain point. (But not before then.) And rather than waiting until after the UI gets laid out, I want to know beforehand what the height would be if it had myString and then change the width if I needed to.
I looked at the Layout class but I couldn't figure out what to do. I wonder if it might have something to do with overriding the TextView's onMeasure but I really don't know how to attempt that. Any help is appreciated.
Update
Thanks to both #user3249477 and #0xDEADC0DE for their answers. I'm marking #user3249477's answer as the solution for now (although since I need multiple resizes of the view I'm not sure about repeatedly turning the visibility on and off) but also +1 to #0xDEADC0DE for giving me the keywords I needed to further look into this problem.
I need to do more research and testing on this. Here are some links that I have found helpful so far:
OnLayoutChangeListener:
View.OnLayoutChangeListener
Capture Layout resize before API 11
After changing a property on a LayoutParams object, do I need to call setLayoutParams again?
measureText() and getTextBounds():
Android Paint: .measureText() vs .getTextBounds()
Paint.getTextBounds() returns to big height
Gettextbounds in android
Overriding onSizeChanged of the parent view also looks intriguing: https://stackoverflow.com/a/14399163/3681880
Set your TextView to invisible:
android:visibility="invisible"
and measure it. Once you're done set it to visible:
TextView myTextView = (TextView) findViewById(R.id.text);
final int maxHeight = 500;
myTextView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
v.removeOnLayoutChangeListener(this);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) v.getLayoutParams();
Log.e("TAG", "H: " + v.getHeight() + " W: " + v.getWidth());
if (v.getWidth() > maxHeight) {
params.width += 100;
v.setLayoutParams(params);
}
v.setVisibility(View.VISIBLE);
}
});
You could do it without overriding. If you get the TextViews Paint with getPaint(), you can use measureText(string) the get the minimal with of the TextView when it is drawn with that Paint. I looks like this:
TextView textView = new TextView(this);
float textWidth = textView.getPaint().measureText("Some Text");
Update
To get the height, you can call getTextBounds() on the Paint object like this:
String text = "Some Text";
Rect textBounds = new Rect();
textView.getPaint().getTextBounds(text, 0, text.length(), textBounds);
float height = textBounds.height();
float width = textBounds.width();
I am creating a Table of buttons, to control a LED Matrix via Bluetooth.
I have found on the web Brian's Video Tutorials and followed his Dynamic Buttons and Images video to implement this.
Here is the code:
public class DrawerMode extends Activity {
private static final int NUMOFCOL = 15;
private static final int NUMOFROW = 8;
Button buttons[][] = new Button[NUMOFROW][NUMOFCOL];
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Assign content
setContentView(R.layout.activity_draw_mod);
fillTable();
}
private void fillTable() {
TableLayout tableLayout = (TableLayout) findViewById(R.id.drawer_table);
for( int iter_R = 0; iter_R!= NUMOFROW; iter_R++){
TableRow tableRow = new TableRow(this);
tableRow.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT,TableLayout.LayoutParams.WRAP_CONTENT,1.0f));
tableLayout.addView(tableRow);
for(int iter_C = 0; iter_C != NUMOFCOL; iter_C++){
final int FINAL_COL = iter_C;
final int FINAL_ROW = iter_R;
Button button = new Button(this);
button.setLayoutParams(new TableRow.LayoutParams( TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.MATCH_PARENT, 1.0f));
button.setText("" + iter_C + "," + iter_R);
button.setPadding(0, 0, 0, 0);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
whenBtnClicked(FINAL_COL, FINAL_ROW);
}
});
tableRow.addView(button);
buttons[iter_R][iter_C] = button;
}
}
}
private void whenBtnClicked(int col, int row) {
//Toast.makeText(this, "Button clicked: " + FINAL_COL + "," + FINAL_ROW, Toast.LENGTH_SHORT).show();
Button button = buttons[row][col];
// Lock Button Sizes:
lockButtonSizes();
int newWidth = button.getWidth();
int newHeight = button.getHeight();
Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_dark_blue);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, true);
Resources resource = getResources();
button.setBackground(new BitmapDrawable(resource, scaledBitmap)); // Change text on button:
button.setText(" ");
}
private void lockButtonSizes(){
for (int row = 0; row < NUMOFROW; row++){
for (int col = 0; col < NUMOFCOL; col++){
Button button = buttons[row][col];
int width = button.getWidth();
button.setMinWidth(width);
button.setMaxWidth(width);
int height = button.getHeight();
button.setMinHeight(height);
button.setMaxHeight(height);
}
}
}
}
It works great, but while testing I have found the following issue.
When I click random buttons it works great:
[img]http://i.imgur.com/OYFJ6zJ.png?1[/img]
But when I complete a row (all elements on row are clicked), and I mean any row it starts to rescale the buttons in the whole table:
[img]http://i.imgur.com/ttAz4U0.png?1[/img]
I was thinking that maybe the LayoutParams of the TableRow should be changed, but not sure about that. What am I missing here?
I think you're right about the layout parameters needing to change. This line
tableRow.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT,TableLayout.LayoutParams.WRAP_CONTENT,1.0f));
would cause the row's height to shrink in size if no button had displayed text, which seems to be what's happening. The TableLayout.LayoutParams does support setting fixed width/height, which you could sensibly calculate by first getting the device's screen width/height and dividing accordingly.
Or, if that gets to cumbersome, you could set -- though this may be too much of a hack -- the default text in the TextViews in the "unset" buttons with some transparent text (e.g., "1,1") so that the height is the same as a set button. This SO answer answer shows how to make transparent text.
I am certain that this is not a good solution for all cases. But just as I thought, the problem was with
button.setLayoutParams(new TableRow.LayoutParams( TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.MATCH_PARENT, 1.0f));
If I understand correctly after the entire row was clicked, since the LayoutParams change according to the MATCH_PARENT value, the TableRow rescales the whole row to meet this criteria,since the height of the entire row is the same now. Not sure if it happens exactly this way, but I think this is the case because of my solution.
My work around is to add specific values for the LayoutParams, instead of leaving it the system to figure it out:
button.setLayoutParams(new TableRow.LayoutParams( 75, 50, 1.0f));
I am aware this is not how it should be done. But since I have a deadline to met soon, I can't spend any more time with it. Most likely the correct way to do this is Jason's suggestion to get the screen size and calculate it. You can do this with:
Display display = getWindowManager().getDefaultDisplay(); Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
The problem is to come up with a correct formula to calculate this values you can pass to the LayoutParam. If anyone can figure this out please do post your solution and I will accept that answer. At this point I accept Jason's suggestion.
Hey I have a grid of ImageButtons that is being scaled by display width and contains a PNG file as image. I add the whole thing to the linear layout like this:
public void createButtons(){
int buttonX = 9;
int buttonY = 9;
int size = 80;
int tag = 0;
TableLayout layout = new TableLayout (this);
layout.setLayoutParams( new TableLayout.LayoutParams(900,900) );
layout.setPadding(1,1,1,1);
layout.setBackgroundColor(0xff00af00); //green
RelativeLayout ll = (RelativeLayout)findViewById(R.id.rel);
ll.addView(layout);
for(int x=0;x<buttonX;x++) {
TableRow tr = new TableRow(this);
for(int y=0;y<buttonY;y++) {
but[x][y] = new ImageButton(this);
but[x][y].setBackgroundColor(0xff0000af); //blue
but[x][y].setImageResource(R.drawable.buttonmask3);
but[x][y].setScaleType(ImageButton.ScaleType.CENTER_INSIDE);
tr.addView(but[x][y], height/10,height/10);
}
layout.addView(tr);
}
}
The problem is, that the layout now looks like the following:
whereas it is supposed to look like:
(this one was a quick photoshop but you get the idea, that the scale is supposed to fill the button completely)
What can I do about these tiny images? I tried CENTER_INSIDE, FITXY and all the other ScaleTypes but I haven't been lucky so far :/
The width is taken from the screen width (or height in landscape)
the buttonmask3.png is about 170*170 px.
The problem is the padding that the ImageButton comes with naturally.
adding
but[x][y].setPadding(0, 0, 0, 0);
along with
but[x][y].setScaleType(ImageButton.ScaleType.CENTER_CROP);
Solved it for me.
I'm now trying to resolve an issue with somehow overlapping text in TextView.
I've posted a question about that , but I've also tried to solve it myself. I decided to count the amount of text which causes the textview to break the line. I came up with this unpolished code, which basically should inflate the view, set it's layout params according to the displayed size and then run onmeasure and return lineCount. I plan than to use probably binary search to find exact text length which fits into the textview, but even before I've just tried to run the code and see how it behaves. It's kind of weird, because it gives me different results, than what I see on screen than. I even tried to alter the textsize according to scaled density, because I wasn't sure whether it's been taken into account.
Here is the code. It returns three,but when I render the layout to screen the text takes up only two lines.
public int getCountOfLines(Context context, int widgetDp){
LayoutInflater mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout layout = (LinearLayout) mLayoutInflater.inflate(R.layout.event_basic_large ,null, false);
TextView titleTextView = (TextView) layout.findViewById(R.id.itemTitle);
titleTextView.setText(TEST_TEXT);
float density = context.getResources().getDisplayMetrics().density;
float textDensity = context.getResources().getDisplayMetrics().scaledDensity;
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextView.getTextSize()/density*textDensity);
layout.setLayoutParams(
new LinearLayout.LayoutParams(
(int) (widgetDp*density),
ViewGroup.LayoutParams.WRAP_CONTENT)
);
layout.measure(View.MeasureSpec.makeMeasureSpec(
(int) (widgetDp*density), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
);
Log.d(TAG, titleTextView.getLineCount() + " lines, width "+titleTextView.getMeasuredWidth());
return titleTextView.getLineCount();
}
I ran into this a while back and after searching and trying finally got the following function to work:
public int getLineCount(String testString, float textSize,
float width) {
Rect bounds = new Rect();
Paint paint = new Paint();
paint.setTypeface(<font>);//set font that you are using for this
paint.setTextSize(textSize);
paint.getTextBounds(testString, 0, testString.length(), bounds);
return (int) Math.ceil(bounds.width() / width);
}
This function uses textSize and width to give number of lines.
This functions give number of line in a textview before it is displayed.
I'm trying to make a dynamic grid layout, it being API 10+ is the part that's been making it slow going. I tried to make it wrap automatically.. but in the end found it easier just to try to force it into a grid pattern using coordinates. This script was working by itself when I did the positioning at time of creation, but now I am trying to loop through each item as a sort. So if one item is deleted, they all float back into a grid without a hole in the middle.
Problem is, it seems the layout parameters are only applying to the last object.
Here's some base variables and onCreate setup:
int screenWidth;
int screenHeight;
int distStep = 130;
int leftPad = 20;
int numCols;
int baseID = 0;
android.util.DisplayMetrics metrics = this.getResources().getDisplayMetrics();
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
numCols = (int) (screenWidth - leftPad) / distStep;
int scrRemain = screenWidth - ((numCols * distStep) + leftPad);
distStep += (int) scrRemain / numCols;
Then on to the main function for adding:
public void addObjToLayout() {
RelativeLayout relLay = (RelativeLayout) this.findViewById(R.id.mainWindow);
for(int i = 1; i <= currQuantity; i++){
TextView tv=new TextView(this);
tv.setTextSize(40);
tv.setId(baseID + i);
tv.setPadding(24, 4, 24, 4);
tv.setBackgroundColor(0x110000FF);
tv.setText(String.valueOf(baseID + i)); //Val for debugging
tv.setTextColor(0xFFFFFFFF);
relLay.addView(tv);
}
baseID += currQuantity;
sortLayout();
}
Then the sorting:
public void sortLayout() {
int leftNum = 20;
int topNum = 0;
for(int i = 1; i <= baseID; i++){
TextView tv= (TextView) this.findViewById(baseID);
MarginLayoutParams mp = new MarginLayoutParams(tv.getLayoutParams());
mp.setMargins(leftNum, topNum, 0, 0);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(mp);
tv.setLayoutParams(lp);
leftNum += distStep;
if(leftNum >= distStep * numCols){
leftNum = leftPad;
topNum += distStep;
}
}
}
What I am getting is all the textViews pile up in the top left corner, except the last one which is positioned exactly where it should be. So it seems in my head, the params object isn't applying until the loop ends or something.. but logically I don't see why.
As I said, this worked when I set the params at the get go, problem is mass updating them all at once. I am pretty new to android, so I hope I'm not just doing something stupid.
Thanks for your time
Margin means it will set a gap between the previous view and current view.
When you add view1, view2 and view3 to grid layout and if you remove view2 at some point of time, then the margin for view3 is set according to view1. So, it won't leave empty space in place of view2. Instead of removing view2 at run time, set the background for view2 as null and set the text as empty as below.
textView.setBackground(null);
textView.setText("");
So that the view is still available but looks as deleted.
Started looking into GridView using an extended baseAdapter. Looks promising:
For more (see #2):
http://www.mkyong.com/android/android-gridview-example/