When I use Java code to dynamically add a button to an Android layout I first have to declare a local copy of the button similar to this:
Button btn = new Button(this);
Then there is some typical code as follows (but varies with the application):
// sets button width and height
RelativeLayout.LayoutParams bparms = new RelativeLayout.LayoutParams(w, h);
// sets button left and top position inside layout
bparms.setMargins(l, t, 0, 0);
This may be followed by setting other button properties such as text, background etc. Finally the button gets added to the parent layout as follows:
// add the dynamic button to the keypad view
kpad.addView(btn, bparms);
Here is my question. Does the layout receiving the button make a copy of the locally created dynamic button? Or does it just take a reference to the that created button leaving the originally created object intact?
Looking at the source code, there is an addInArray method of ViewGroup class, and it just takes a reference of the child view and adds it to children array:
private void addInArray(View child, int index) {
View[] children = mChildren;
final int count = mChildrenCount;
final int size = children.length;
if (index == count) {
if (size == count) {
mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
System.arraycopy(children, 0, mChildren, 0, size);
children = mChildren;
}
children[mChildrenCount++] = child;
} else if (index < count) {
if (size == count) {
mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
System.arraycopy(children, 0, mChildren, 0, index);
System.arraycopy(children, index, mChildren, index + 1, count - index);
children = mChildren;
} else {
System.arraycopy(children, index, children, index + 1, count - index);
}
children[index] = child;
mChildrenCount++;
if (mLastTouchDownIndex >= index) {
mLastTouchDownIndex++;
}
} else {
throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
}
}
Generally, java is working with references. So your object will not be cloned, you just give a reference of the object to the layout. Also, whenever you receive the object, e.g. by iteration over all views in the layout programmatically, you will also only receive a reference to the object. Cloning may be possible, when you e.g. assign a new variable (with a new memory position).
Related
I need to develop the horizontal split value bar to show the remaining or completed data. There would be 2 horizontal bar - 1 which will have grayed out color and one which will have a filled color by which i need to show some amount of data which user has already used. Please give your input if some of you already have done this.
thanks in advance
private void showBillCycleProgress(int billCycleProgressBarId, int total, int progress, int fillProgressDrawable, int blankProgressDrawable, View view2) {
LinearLayout layout = (LinearLayout) view2.findViewById(billCycleProgressBarId);
layout.removeAllViews();
for (int i = 0; i < total; i++) {
View view = new View(getActivity());
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(35, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(0, 0, 3, 0);
view.setLayoutParams(layoutParams);
view.setBackgroundResource(fillProgressDrawable);
if (i >= progress) {
mHandler.postDelayed(new AnonymousClass(this, view, blankProgressDrawable), (long) (((total - i) * 50) + 200));
}
layout.addView(view);
}
}
I have a Sudoku grid image loaded into my app, but I'm having a lot of trouble getting EditTexts to appear in each square. When I run the app, the numbers in the EditTexts are hardly visible. I've tried changing the background color and text color and nothing has changed.
/* PROGRAMATICALLY ADD EDITVIEWS via waiting for draw */
layout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener()
{
public void onGlobalLayout()
{
layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
/* not used yet
int height = grid.getMeasuredHeight();
int width = grid.getMeasuredWidth();
int top = grid.getTop();
//int bottom = grid.getBottom();
int left = grid.getLeft();
int square_height = height/9;
int square_width = width/9;*/
for ( int r = 0; r < 9; r++ )
{
for ( int c = 0; c < 9; c++ )
{
text_boxes[r][c] = new EditText(getApplicationContext());
text_boxes[r][c].setId(r+c);
text_boxes[r][c].setText("1");
text_boxes[r][c].setInputType(InputType.TYPE_CLASS_NUMBER);
text_boxes[r][c].setFilters(new InputFilter[] {new InputFilter.LengthFilter(1)});
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(c*10, r*50, 0, 0);
layout.addView(text_boxes[r][c], params);
text_boxes[r][c].setBackgroundColor(0x0FF00);
}
}
}
});
you didn't remove the listener when you are finished using it, so it can keep being called.
also, about the color of the EditText, you can change it to whatever you wish , by just changing the background of the EditTexts, or use a global style that sets a default background for EditTexts. you can even use this tool
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/
I'm trying to display a bunch of text on the screen by placing TextViews inside rows of single-line LinearLayouts. Each word is stored in its own separate TextView, and I want to be able to place as many TextViews as will fit on a single LinearLayout line and detect when I've run out of horizontal space so that I can move to the next line.
The problem I'm facing is that I can't seem to find a way to measure the changing layout sizes as I create the display, because I can't get a reference width using getWidth() on the parent layout, and even after I add the TextViews, I can't seem to control the width.
We had a working version before, but it did everything using on hard-coded numbers based on the number of characters in a TextView at a fixed size. I'm trying to extend the app to work with all text and screen sizes. If this needs to be completely overhauled, I understand - I just want to be able to fill up the screen with an indefinite number of lines of text.
An obvious solution would be to just place all the text inside one TextView, but we need to be able to access each Word/Ponctuation object and its attributes through the displayed TextViews.
// layout_row is the current LinearLayout row I'm adding my TextViews to
// layout is the LinearLayout parent of all layout_rows
// text.text_content is a linked list of Word and Ponctuation objects
// each Word and Ponctuation object has a TextView attribute called view
private void display_views() {
if (text != null)
{
boolean prochainLigneSuivante; // if new line is to follow
int widthSoFar = 0;
int layoutWidth = layout_row.getWidth();
for (Object o : text.text_content) {
if (o instanceof Word ) {
Word w = (Word) o;
Object next = text.next(o);
if (noNeedForSpace(w)) {
// by default all TextViews have
// right padding to simulate spaces
w.view.setPadding(0, 0, 0, 0);
}
layout_row.addView(w.view);
widthSoFar += w.view.getWidth();
// Am I out of space?
prochainLigneSuivante = widthSoFar >= layoutWidth;
if(prochainLigneSuivante) {
layout_row.removeView(w.view);
widthSoFar = 0;
layout_row = new LinearLayout(context);
layout_row.setOrientation(LinearLayout.HORIZONTAL);
layout_row.addView(w.view);
layout_row.setBackgroundColor(Color.BLACK);
layout_row.setLayoutParams(new
LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT));
layout.addView(layout_row);
}
}
else if (o instanceof Ponctuation) {
Ponctuation p = (Ponctuation) o;
if (p.text.contains("CR")) {
layout_row = new LinearLayout(context);
layout_row.setOrientation(LinearLayout.HORIZONTAL);
layout_row.setLayoutParams(new
LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT));
widthSoFar = 0;
layout.addView(layout_row);
}
else {
if (p.view.getText().equals(" "))
p.view.setPadding(0, 0, 0, 0);
layout_row.addView(p.view);
if(!p.view.getText().equals(""))
widthSoFar += p.view.getWidth();
}
}
}
}
else {
Log.e("Text", "text est nul");
}
scroll.refreshDrawableState();
transition.startTransition(0);
}
After numerous questions about this topic I still haven't fully solved the problem.
In the end the first imageview always gets "overwritten" or better said overlapped by another view which should go right of that view.
After extending RelativeLayout is it enough to only override onLayout() method for children view placement? Does onLayout method place all children in one pass or is it called for every specific child? How should that onLayout method implement child placement if I want to use RelativeLayouts specific placemnts (RIGHT_OF, BELOW, etc ...)
In view creation, how can I create a view without layoutparams, is it even possible?
EDIT: Ok I avoided using getWidth in any form and still get bad layout. Icons get first row mixed but full (5 icons), next row has 1 icon only and 2 are missing. At this point I'm quite frustrated because it's a stupid issue and I can't seem to find what's wrong, why doesn't official developer tutorial have more help on dynamic layouts and views?
Log.e from down says:
2: RIGHT OF 1
3: RIGHT OF 2
4: RIGHT OF 3
5: RIGHT OF 4
6: BELOW 1
7: RIGHT OF 6
8: RIGHT OF 7
9: RIGHT OF 8
That's the way it's supposed to be yet it doesn't work, I can't set relative position to layouts that should have been layed out?
private void loadResources() {
cursor = managedQuery(Browser.BOOKMARKS_URI, projection, selection,
null, SORT_BY_COLUMN + " " + SORT_ORDER);
this.startManagingCursor(cursor);
ImageView previousBookmark;
int idOfViewToTheLeft = 1;
if(cursor.moveToFirst()) {
bookmarkCounter = 1;
ByteArrayInputStream blobImage;
int size = (int) scale * FAVICON_SIZE;
int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
int rowBookmarkCount = (int) (screenWidth/(size + scale*leftMargin));
do{
bookmark = new ImageView(this);
bookmark.setId(bookmarkCounter++);
bookmark.setBackgroundColor(Color.WHITE);
blobImage = new ByteArrayInputStream(
cursor.getBlob(cursor.getColumnIndex(BookmarkColumns.FAVICON)));
bookmark.setImageDrawable(
Drawable.createFromStream(blobImage, "" + bookmark.getId()));
urls.put(bookmark.getId(),
cursor.getString(
cursor.getColumnIndex(BookmarkColumns.URL)));
bookmark.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent browserIntent = new Intent(
Intent.ACTION_VIEW, Uri.parse(urls.get(v.getId())));
startActivity(browserIntent);
}
});
lp = new RelativeLayout.LayoutParams(size, size);
lp.topMargin = (int) (scale * topMargin);
lp.leftMargin = (int) (scale * leftMargin);
if(bookmark.getId() > 1) {
previousBookmark = (ImageView) findViewById(bookmark.getId() - 1);
if((bookmark.getId() % (rowBookmarkCount + 1)) != 0)
{
Log.e("" + bookmark.getId(), "RIGHT OF " + previousBookmark.getId());
lp.addRule(RelativeLayout.RIGHT_OF, previousBookmark.getId());
} else {
Log.e("" + bookmark.getId(), "BELOW " + idOfViewToTheLeft);
lp.addRule(RelativeLayout.BELOW, idOfViewToTheLeft);
idOfViewToTheLeft = bookmark.getId();
}
}
bookmarkLayout.addView(bookmark, lp);
} while (cursor.moveToNext());
}
}
For standard components you don't really need to override onLayout, if you want for them to layout components as usual that is. If you do override onLayout and don't call super.onLayout then you must position all your children manually.
Otherwise, just use RelativeLayout.LayoutParams with added rules (BELOW, ABOVE, etc..) and set it when adding a view addView(youView, yourLayoutParamsInstance)
Most of the logic in RelativeLayout actually happens during the measurement phase, so no, just overriding onLayout is most likely not enough.
If you want to extend RelativeLayout, I'm afraid you'll have to get accustomed to it's source code, otherwise it'll be quite hard to understand how to influence it's behavior. You should really consider finding another solution though, since you really picked one of the more complex classes with RelativeLayout.
What I did in the end is quit using relative parameter to describe view's position relative to other view. Instead I did all the math myself and put everything in setMargins, hope that's is equally if not faster than using Relativelayouts methods to figure it out.
private void loadResources() {
cursor = managedQuery(Browser.BOOKMARKS_URI, projection, selection,
null, SORT_BY_COLUMN + " " + SORT_ORDER);
this.startManagingCursor(cursor);
if(cursor.moveToFirst()) {
bookmarkCounter = 0;
ByteArrayInputStream blobImage;
int leftMargin = (int) (scale * this.leftMargin);
int topMargin = (int) (scale * this.topMargin);
int size = (int) scale * FAVICON_SIZE;
int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
int rowBookmarkCount = (int) (screenWidth/(size + leftMargin));
do{
bookmark = new ImageView(this);
bookmark.setId(bookmarkCounter++);
bookmark.setBackgroundColor(Color.WHITE);
blobImage = new ByteArrayInputStream(
cursor.getBlob(cursor.getColumnIndex(BookmarkColumns.FAVICON)));
bookmark.setImageDrawable(
Drawable.createFromStream(blobImage, "" + bookmark.getId()));
urls.put(bookmark.getId(),
cursor.getString(
cursor.getColumnIndex(BookmarkColumns.URL)));
bookmark.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent browserIntent = new Intent(
Intent.ACTION_VIEW, Uri.parse(urls.get(v.getId())));
startActivity(browserIntent);
}
});
lp = new RelativeLayout.LayoutParams(size, size);
lp.setMargins(
(int) (leftMargin + (bookmark.getId() % rowBookmarkCount) * (size + leftMargin)),
(int) (topMargin + (bookmark.getId() / rowBookmarkCount) * (size + topMargin)),
0, 0);
bookmarkLayout.addView(bookmark, lp);
} while (cursor.moveToNext());
setContentView(scrollView);
}
}