Android LayoutWidth being disregarded by cascaded use of LayoutInflater - android

I am building a pyramid of buttons and want the size of the pyramid to be able to change dynamically. To accomplish this, I have extremely basic XML files representing the activity, each row of the activity, and each button. I am modeling the solution from the accepted response to this question. The pyramid constructs correctly, but the 50dip button width is not being adhered to. Any ideas why? Is there a better way of doing this?
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pyramid"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
/>
row.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/row"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal"/>
btn.xml:
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/button"
android:layout_width="50dip"
android:layout_height="wrap_content"/>
Main Activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
inflate();
}
private void inflate() {
LinearLayout pyramidLayout = (LinearLayout) findViewById(R.id.pyramid);
for (int row = 1 ; row <= mSize; ++row) {
View rowView = getLayoutInflater().inflate(R.layout.row, null);
LinearLayout rowLayout = (LinearLayout) rowView.findViewById(R.id.row);
for (int column = 1; column <= row; ++column) {
View btnView = getLayoutInflater().inflate(R.layout.btn, null);
Button btn = (Button) btnView.findViewById(R.id.button);
btn.setId(row*10 + column);
rowLayout.addView(btnView);
}
pyramidLayout.addView(rowView);
}
}

In btn.xml, change layout_width="50dip" to width="50dip".

Thanks you for this tutorial. It really helps me :)
In my case, I have a view (2 texts, 1 image, 1 button) who represents 1 result.
When the user click on "search" I need to display it many time. So your example was perferct for me

Related

How to fit a LinearLayout in a ScrollView?

Earlier on I asked a question about how to reach the last item of a scrollview and someone pointed out that I should be using NestedScrollView, at first it worked but now it's not what I want.
I want to fit my list of item in the ScrollView so only that part of the screen can be scrolled and the other parts stay at their place (the 3 TextView)
So basically my xml file is like this :
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/cl_framgnent_detail_apero"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary"
tools:context=".ui.home.AperoDetailFragment">
<TextView
android:id="#+id/name_apero"
/>
<TextView
android:id="#+id/date_apero"
/>
<TextView
android:id="#+id/ingredient_title_apero"
/>
<ScrollView
android:id="#+id/rv_apero_ingredient"
android:layout_width="408dp"
android:layout_height="603dp"
android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/ingredient_title_apero"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/ingredient_title_apero"
app:layout_constraintVertical_bias="1.0">
<LinearLayout
android:id="#+id/vertical_layout_ingredient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
And in my Java code I try to populate my list with the following code :
public class AperoDetailFragment extends Fragment {
private View root;
private Apero detailApero;
public AperoDetailFragment(Apero apero) {
this.detailApero = apero;
}
#Override
public View onCreateView(#NonNull final LayoutInflater inflater,
final ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_detail_apero, container, false);
TextView name = (TextView) root.findViewById(R.id.name_apero);
name.setText(detailApero.getName());
TextView date = (TextView) root.findViewById(R.id.date_apero);
date.setText(detailApero.getDate());
LinearLayout ll = (LinearLayout) root.findViewById(R.id.vertical_layout_ingredient);
LinearLayout a = new LinearLayout(root.getContext());
ConstraintLayout.LayoutParams lparams = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT);
a.setLayoutParams(lparams);
a.setOrientation(LinearLayout.VERTICAL);
for (int i = 0; i < 20; i++) {
Button b = new Button(root.getContext());
b.setText("Button " + i);
a.addView(b);
}
ll.addView(a);
return root;
}
}
The problem is that the item are covering the whole screen instead of staying in the parent container (ScrollView) :
How can I fit my list of item to stay in the parent ?
If this is your actual code, then it might help to set constraints to the text views too.
Also, if your ScrollView has a fixed height, then you should remove either the top or bottom constraint. So if you want it to stick to the bottom, remove the top constraint.
I solved this with removing the ScrollView and using a ListView

Load a layout from XML and add views

I'm trying to load an existing layout from XML and then create dynamically a button and a Circle, the reason which I cannot include them onto the XML is because the purpose is to create Circles dynamically, actually the following code is a snippet of what I want to create.
I know the way I am doing this (adding the layout) is wrong, but after reading a lot of Internet content I failed to did it by myself, because that I request help.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_etest);
LayoutInflater inflater;
inflater = this.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout layout = (LinearLayout)
inflater.inflate(R.layout.activity_etest ,
null);
LinearLayout viewGroup = layout;
Button b1 = new Button(this);
b1.setText("test");
viewGroup.addView(b1);
viewGroup.addView(new Circle(this));
}
}
And my class Circle, which extends from View and its method onDraw() consists of:
... onDraw(){
canvas.drawCircle(x, y, radius, paint);
}
The parameters of drawCircle are not rellevant to this question. I have defined them elsewhere.
I also add the XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Button"
android:id="#+id/button12"
android:layout_gravity="center_horizontal" />
</LinearLayout>
create one view group like linearlayout in your xml and using its instance add your dynamic views in that.
activity_etest.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="#+id/viewg" xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
activity
setContentView(R.layout.activity_etest);
Linearlayout viewgroup = (LinearLayout)findViewById(R.id.viewg);
Button b1 = new Button(this);
b1.setText("test");
viewGroup.addView(b1);
viewGroup.addView(new Circle(this));

How to show image in table row based on size

Have 2x2 grid(Dynamic using TableLayout) need to show image on that. now based on image size, means-- if image fit for 1 cell means 1 cell,else big means 2 cells or 4 cells based on size( I know how many cells it will occupy)
i can show image in 1 cell, but problem is if image need 2 cells(1st column) how can show image in 2cell(With out disturbing the grid)
Without disturbing the grid, the workaround I see is to dynamically set image on top of your TableLayout.
Then you can archive this:
I've uploaded the code of the test project here;
You initialize overlappingImage and once you need to set image to your cell - you just add it to the layout and setting height and width params based on number of cells you want to fill.
TableLayout generates dynamically, the cell's layout xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<View
android:layout_margin="4dp"
android:background="#aacc00"
android:layout_height="40dp"
android:layout_width="40dp"/>
</FrameLayout>
The Activity's layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/container"
android:padding="16dp"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<TableLayout
android:id="#+id/tableLayout"
android:layout_width="match_parent"
android:layout_height="280dp"/>
<LinearLayout
android:id="#+id/buttonsLinearLayout"
android:layout_below="#+id/tableLayout"
android:layout_marginTop="16dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:text="1x1"
android:id="#+id/button11"
android:onClick="onClick11"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="4x1"
android:id="#+id/button21"
android:onClick="onClick41"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="2x3 at (2;2)"
android:id="#+id/button12"
android:onClick="onClick32"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="2x2"
android:id="#+id/button22"
android:onClick="onClick22"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
And the Activity code to handle button clicks & generates table:
public class MainActivity extends AppCompatActivity {
RelativeLayout container;
int cellWidth = 0, cellHeight = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
tableLayout.setStretchAllColumns(true);
for (int i = 0; i < 4; i++) {
TableRow tableRow = new TableRow(this);
for (int j = 0; j < 10; j++) {
LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View cell = layoutInflater.inflate(R.layout.table_cell, null, false);
if (cellHeight == 0 ) {
cell.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
cellWidth = cell.getMeasuredWidth();
cellHeight = cell.getMeasuredHeight();
}
tableRow.addView(cell);
}
tableLayout.addView(tableRow);
}
container = (RelativeLayout)findViewById(R.id.container);
overlappingImage = new ImageView(this);
overlappingImage.setScaleType(ImageView.ScaleType.FIT_XY);
}
ImageView overlappingImage;
private void restoreTableLayout() {
container.removeView(overlappingImage);
}
public void onClick11(View view) {
restoreTableLayout();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(cellWidth, cellHeight);
overlappingImage.setLayoutParams(params);
overlappingImage.setImageResource(R.drawable.horizontal_cat);
container.addView(overlappingImage);
}
public void onClick41(View view) {
restoreTableLayout();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(cellWidth*4, cellHeight);
overlappingImage.setLayoutParams(params);
overlappingImage.setImageResource(R.drawable.horizontal_cat);
container.addView(overlappingImage);
}
public void onClick32(View view) {
restoreTableLayout();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(cellWidth*3, cellHeight*2);
params.setMargins(cellWidth*2, cellHeight*2, 0 ,0);
overlappingImage.setLayoutParams(params);
overlappingImage.setImageResource(R.drawable.vertical_cat);
container.addView(overlappingImage);
}
public void onClick22(View view) {
restoreTableLayout();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(cellWidth*2, cellHeight*2);
overlappingImage.setLayoutParams(params);
overlappingImage.setImageResource(R.drawable.horizontal_cat);
container.addView(overlappingImage);
}
}
I hope, it helps.
Create separate layout files for rows that would need one cell and two cell as follows:
one_cell_table_row.xml (Notice the android:layout_span="2" for the ImageView
<?xml version="1.0" encoding="utf-8"?>
<TableRow
android:background="#drawable/bg_gray"
android:layout_marginTop="5dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="#+id/imgMyImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_span="2" />
</TableRow>
two_cell_table_row.xml (The TextView placed just as a placeholder for the second cell) (No layout_span required here as in the above layout)
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="#+id/imgMyImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_weight="1"
android:text="..."
android:textColor="#767575"
android:id="#+id/txtJustAPlaceholder"
android:textSize="14dp" />
</TableRow>
Note: The id for the ImageView to be kept same in both layout for the java code below to work correctly.
The above is assuming your grid is 2x2. If your grid size is different create more layout for each kind of row you want and add extra conditions in the java code below.
Adding the TableRow with the right layout inflated:
Then programatically determine which layout needs to be inflated. Inflate the required layout for table row and add it to your table layout:
Following code is assuming that you are using a fragnemt. If you are doing directly in an activity replace code to work for Activity accordingly.
TableLayout table = (TableLayout) getView().findViewById(R.id.youtTableLayout);
if(<your image size needs two cells>) {
TableRow row = (TableRow) LayoutInflater.from(getActivity().getApplicationContext())
.inflate(R.layout.two_cell_table_row, null);
}
else if(<your image size needs one cell) {
TableRow row = (TableRow) LayoutInflater.from(getActivity().getApplicationContext())
.inflate(R.layout.one_cell_table_row, null);
}
...
...
// add more conditions and respective layouts as you need.
...
...
ImageView myImgView = (ImageView) row.findViewById(R.id.txtCrdSectionHeader);
// set the image for your image view here.
table.addView(row);
table.requestLayout();
Again, the above was assuming that your TableLayout has a 2x2 grid. If you plan to use a different one, update the layout files for TableRows we created above accordingly or set them dynamically using your java code.
You can calculate the image size and the screen size at runtime.Based on the calculations you can set the table properties at runtime. For example if the image is going to take two columns set the span property on that row programmatically.
I would suggest for your requirement you can consider creating the layout in code itself-rather than using any xml.
You can also have a look at Recycler view. It has more powerful ways to control the layout of the children. Have a look at this video-Mastering Recycler View -It is trying to do similar thing what you are looking for.

add button in getView() GridView

I have tried to use the HelloGridView sample code.
I wish to add button to each grid.
From the research, it seems i have to create buttons instead of imageviews in getView adapter method.
However, I have no idea how to create button in the getView() method.
Can anyone kindly show me how to create a button in the method?
thanks!
I wanted to do the same thing, so what I did was use an xml file and a layoutinflater with some code around it.
XML Containing GridView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp" >
<GridView
android:id="#+id/gridview2"
android:layout_width="fill_parent"
android:layout_height="250dp"
android:columnWidth="90dp"
android:numColumns="auto_fit"
android:verticalSpacing="5dp"
android:horizontalSpacing="5dp"
android:stretchMode="columnWidth"
android:gravity="center" />
</LinearLayout>
That being my base GridView, I used an xml file for the GridView "cell" called grid_item.xml
<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/GridItem"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:id="#+id/GridItem_Button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Then in your adapter.getView method
public View getView( final int position, View convertView, ViewGroup parent ) {
View mView = convertView;
if( convertView == null ) {
// I use a cursor for the content which is setup elsewhere
cursor.moveToPosition( position );
// inflate the layout to edit it
LayoutInflater li = (LayoutInflater) Context.getSystemService( Context.LAYOUT_INFLATER_SERVICE);
mView = li.inflate( R.layout.grid_item, null );
// not we can get the button defined in grid_item.xml
Button b = (Button) mView.findViewById( R.id.GridItem_Button );
// set the button text based on the cursor/your content
b.setText( cursor.getString(0);
// now we can also do an OnClickListener
b.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick( View v ) {
// do something on button click
}
});
}
return mView;
}
This works great for me
At first I was confused on which onClickListener I should use and for buttons it works to add the onClickListener to the button, but otherwise it seems better to put the onItemClickListener to the GridView itself.
If anything isn't clear please let me know.
Nick

How to use the xml setting in a view of a activity?

I want to show two views in one activity. If I clicked on button in the first view I want to see the second and other way round.
The views should not have the same size as the screen so I want e.g. to center it, like you see in first.xml.
But if I add the views with
addContentView(mFirstView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
the views are not centered. They are shown at top left.
How can I use the xml settings to e.g. center it?
first.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:background="#drawable/background"
android:layout_gravity="center"
android:minWidth="100dp"
android:minHeight="100dp"
android:paddingBottom="5dp"
>
<LinearLayout android:id="#+id/head"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton android:id="#+id/first_button"
android:src="#drawable/show_second"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#null" />
</LinearLayout>
second.xml same as first.xml but with
<ImageButton android:id="#+id/second_button"
android:src="#drawable/show_first"
... />
ShowMe.java
public class ShowMe extends Activity {
View mFirstView = null;
View mSecondView = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initFirstLayout();
initSecondLayout();
showFirst();
}
private void initFirstLayout() {
LayoutInflater inflater = getLayoutInflater();
mFirstView = inflater.inflate(R.layout.first, null);
getWindow().addContentView(mFirstView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
ImageButton firstButton = (ImageButton)mMaxiView.findViewById(R.id.first_button);
firstButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
ShowMe.this.showSecond();
}
});
}
private void initSecondLayout() {
// like initMaxiLayout()
}
private void showFirst() {
mSecondView.setVisibility(View.INVISIBLE);
mFirstView.setVisibility(View.VISIBLE);
}
private void showSecond() {
mFirstView.setVisibility(View.INVISIBLE);
mSecondView.setVisibility(View.VISIBLE);
}}
Hope someone can help.
Thanks
Why don't you use setContentView(R.layout.yourlayout)? I believe the new LayoutParams you're passing in addContentView() are overriding those you defined in xml.
Moreover, ViewGroup.LayoutParams lacks the layout gravity setting, so you would have to use the right one for the layout you're going to add the view to (I suspect it's a FrameLayout, you can check with Hierarchy Viewer). This is also a general rule to follow. When using methods that take layout resources as arguments this is automatic (they might ask for the intended parent).
With this consideration in mind, you could set your layout params with:
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(/* wrap wrap */);
lp.setGravity(Gravity.CENTER);
addContentView(mYourView, lp);
But I would recommend setContentView() if you have no particular needs.
EDIT
I mean that you create a layout like:
~~~/res/layout/main.xml~~~
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="....."
android:id="#+id/mainLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
then in your onCreate() or init...Layout():
setContentView(R.layout.main);
FrameLayout mainLayout = (FrameLayout)findViewById(R.id.mainLayout);
// this version of inflate() will automatically attach the view to the
// specified viewgroup.
mFirstView = inflater.inflate(R.layout.first, mainLayout, true);
this will keep the layout params from xml, because it knows what kind it needs. See reference.

Categories

Resources