I have a Layout with a ScrollView where I want to programatically insert another layout. So this
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="96dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#56565A"
android:orientation="vertical">
inserted into this
<HorizontalScrollView
android:id="#+id/live_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/live_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</HorizontalScrollView>
And I insert it like that
for (int i = 0; i < exercise.targetSets; i++) {
LayoutInflater factory = LayoutInflater.from(getContext());
View myView = factory.inflate(R.layout.live_set_item, null);
TextView setName = (TextView) myView.findViewById(R.id.current_set);
setName.setText(String.format("SET %d", i+1));
scrollView.addView(myView);
}
But when I run the app the inserted layout is twice as wide as it should.
How it should look (How it looks in the Android Studio Editor)
Vs how it looks when inserted into the ScrollView
So far I have tried to use a ConstraintLayout which didn't work. Or using different layout weights.
I hope you can help me with this.
You can try use the Layout Parameters for setting view custom params for height and width
as
val linearLayout = LinearLayout(requireActivity())
val lp: LinearLayout.LayoutParams = (ParentView)findViewById(R.id.parent_view);
val left_margin: Int = 3 //e.g
val top_margin = 0
val right_margin: Int = 3 //e.g
val bottom_margin = 0
lp.setMargins(left_margin, top_margin, right_margin, bottom_margin)
val size = Point()
val w = requireActivity().windowManager
w.defaultDisplay.getSize(size)
lp.width = size.x - 40
lp.height = size.y
linearLayout.layoutParams = lp
Or for better results, you can use the recycler view to achieve the same result in better handling and just to create a single instance of adapter, which will be completely dependent on you list you are passing to the recycler view. also less code work to handle and debug.
Related
I got a problem with my for loop / with its content.
I want to have this loop in which an image is repeated n times.
In addition these images should have margins at the top so they have some space between each other.
For now these images are laying on top of each other or won't be generated as they should (Result: Drawable "bg_circle" is only displayed once).
The "userinput" will be filled from an EditText in an Alert Dialog.
Here is my code:
int n = Integer.parseInt(userInput.getText().toString());
RelativeLayout layout = findViewById(R.id.TableView);
for(int i = 0; i <= n; i++){
ImageView image = new ImageView(mContext);
image.setImageResource(R.drawable.bg_circle);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(100,100,0,0);
lp.height = 100;
lp.width = 100;
image.setLayoutParams(lp);
layout.addView(image);
}
Where is the mistake?
Use LinearLayout with vertical orientation instead of RelativeLayout. Also, you should use a ScrollView as root element of the layout:
<ScrollView ...>
<LinearLayout android:orientation="vertical" ...>
... programmatically added images...
</LinearLayout>
</ScrollView>
I'd like to use a GridLayout (not GridView) as board for a game like chess or checkers. As I'm a little reluctant to use an xml file with 64 child Views, I've tried adding them programmatically.
To keep things simple, I started with using TextViews as child Views for the GridLayout.
My problem is that the Views are not distributed evenly, and that I don't know how to get an even distribution in my java code. There is no method like "setWeight()" for setting layout_columnWeight and layout_rowWeight.
At present, this is my activity_dynamic_grid_layout.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="80dp"
android:id="#+id/ivLogo"
android:background="#ff0000"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
<android.support.v7.widget.GridLayout
android:id="#+id/grid_layout"
android:background="#004080"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/ivLogo"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="36dp">
</android.support.v7.widget.GridLayout>
I've set the GridLayout width and height to match_parent here, but I'm changing them at runtime using a ViewTreeObserver.OnPreDrawListener in order to get a square board. This works, the colored background is showing a square space as intended.
My onCreate()
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamic_grid_layout);
GridLayout gl = (GridLayout) findViewById(R.id.grid_layout);
gl.setColumnCount(8);
gl.setRowCount(8);
for(int i=0; i<gl.getRowCount(); i++)
{
GridLayout.Spec rowSpec = GridLayout.spec(i, 1, GridLayout.FILL);
for(int j=0;j<gl.getColumnCount();j++)
{
GridLayout.Spec colSpec = GridLayout.spec(j,1, GridLayout.FILL);
TextView tvChild = new TextView(this);
tvChild.setText("[ " + i + " | " + j + " ]");
tvChild.setTextSize(18f);
tvChild.setTextColor(Color.WHITE);
tvChild.setGravity(Gravity.CENTER);
GridLayout.LayoutParams myGLP = new GridLayout.LayoutParams();
myGLP.rowSpec = rowSpec;
myGLP.columnSpec = colSpec;
gl.addView(tvChild, myGLP );
}
}
final View rootView = findViewById(R.id.dynamic_root);
rootView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
int w = rootView.getMeasuredWidth();
int h = rootView.getMeasuredHeight();
int min = Math.min(w, h);
ViewGroup.LayoutParams lp = gl.getLayoutParams();
lp.width = min - min % 9;
lp.height = lp.width;
gl.setLayoutParams(lp);
rootView.getViewTreeObserver().removeOnPreDrawListener(this);
return true;
}
});
}
What I've tried already:
I put one TextView child in the layout file and tried to copy the layout_columnWeight and layout_rowWeight from its GridLayout.LayoutParams:
<android.support.v7.widget.GridLayout
...>
<TextView
android:id="#+id/clone_my_params"
android:text="[ 0 | 0 ]"
android:textColor="#ffffff"
android:textSize="18sp"
app:layout_column="0"
app:layout_row="0"
app:layout_columnWeight="1"
app:layout_rowWeight="1"
/>
</android.support.v7.widget.GridLayout>
Additional code in onCreate(), before the double loop:
TextView v = (TextView)gl.findViewById(R.id.clone_my_params);
v.setGravity(Gravity.CENTER);
GridLayout.LayoutParams gridLayoutParamsToCopy = new GridLayout.LayoutParams(v.getLayoutParams());
Inside the loop, I skipped (i,j) = (0,0) and changed
GridLayout.LayoutParams myGLP = new GridLayout.LayoutParams();
to
GridLayout.LayoutParams myGLP = new GridLayout.LayoutParams(gridLayoutParamsToCopy);
Before the change, all elements were in the upper left corner, the excess space was given to the last row/ column. After the change, the first row/ column had the excess space, no change for the other elements.
Calling gl.invalidate() and/or gl.requestLayout() after the double loop had no effect.
So it seems that I did not manage to set the desired weight by using the copy constructor.
-Here you are ! :>
Button button = new Button(this);
GridLayout.LayoutParams param= new GridLayout.LayoutParams(GridLayout.spec(
GridLayout.UNDEFINED,GridLayout.FILL,1f),
GridLayout.spec(GridLayout.UNDEFINED,GridLayout.FILL,1f));
param.height = 0;
param.width = 0;
button.setLayoutParams(param);
To set the weight on the children of your Gridlayout use one of the spec()(like this one) methods that takes a float value.
Another approach would be a either make a custom View(in which case you'll need to manually draw the pieces) or a custom ViewGroup(in which case the custom ViewGroup will just take care of the pieces positioning, this will be appropriate if you plan to have more complex view hierarchies than a simple TextView).
I have a page that consists of an ImageView, and a RecyclerView. The RecyclerView contains a small number of items (currently three) and only takes up around a quarter of the screen on my test device. However, despite trying numerous layout options, I cannot get the RecyclerView to effectively wrap its content and take up just enough space required to contain these three rows, and leave the rest of the space for my ImageView.
To help illustrate what I mean, I have drawn two diagrams. The first shows what I would like to happen, and the second what is happening:
So far, I have tried several different combinations of RelativeLayout - for instance, I will set RecyclerView to layout:align_ParentBottom and the second RelativeLayout that contains the ImageView to layout:alignComponent so that its bottom matches the RecyclerView top (this would normally drag the ImageView layout so that it fills any reminaing space, which is what I would like to happen.)
The RecyclerView though just keeps occupying all the space it can, even though it only contains a few rows. The current "solution" I have is to place everything inside a LinearLayout and set less gravity to the RecyclerView, but it isn't ideal, because on different emulators it wont line up completely with the bottom of the screen, and in others there isn't enough room and the RecyclerView becomes scrollable.
Thanks in advance for any help and suggestions anyone can offer.
Many thanks to everyone who contributed, but I have found a programmatic solution outside of the layout files. In case anyone else is looking for a solution to this problem, I found one here.
It appears as if there is an issue with RecyclerView currently where it doesn't wrap content. The answer is to construct a custom class that extends LinearLayoutManager. I have posted the solution that worked for me below - most of it is copy and pasted from the answer given in the link I quoted. The only small issue is that it doesn't account for the extra space added by decorations, which is why I had to make a small tweak to the following line near the end of the code:
//I added the =2 at the end.
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin + 2;
Here is the code in its entirety:
public class HomeLinearLayoutManager extends LinearLayoutManager{
HomeLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin + 2;
recycler.recycleView(view);
}
}
}
Thanks again.
Put the two in a RelativeLayout and make ImageView fill parent:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<RecyclerView
android:id="#+id/recyclerView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#id/recyclerView"
/>
</RelativeLayout>
Edit: Wrote TextView by accident. Fixed.
The only solution I can think of is using layout weight. Specify 70% of the screen for the image and 30% for the Recyclerview as you said you have just 3 rows. Use adjustViewByBounds to ensure the images aspect ratio is maintained.
My code below:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:src="#drawable/ic_round_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_weight=".9"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight=".1"/>
</LinearLayout>
I have a tabWidget defined as:
<TabWidget
android:layout_alignParentBottom="true"
android:id="#android:id/tabs"
android:paddingBottom="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center|bottom"
android:weightSum="5"
android:background="#drawable/bottom_bar_without_bg"
/>
Now I am adding custom ImageViews in it:
Drawable d = getResources().getDrawable(R.drawable.tabbar_button_home);
ImageView img1 = new ImageView(this);
img1.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
img1.setImageDrawable(d);
TabHost.TabSpec tSpecHome = tHost.newTabSpec("timeline");
tSpecHome.setIndicator(img1);
tSpecHome.setContent(new MyTabContent(getBaseContext()));
tHost.addTab(tSpecHome);
The problem with this code is that all the images are spaced with each other in middle.
e.g abcdef
I want them to stretch to full width and have space in between them e.g a b c d e
Update the code as follows:
Drawable d = getResources().getDrawable(R.drawable.tabbar_button_home);
ImageView img1 = new ImageView(this);
img1.setLayoutParams(new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1.0f)); // this line changed
img1.setImageDrawable(d);
Explanation:
The third parameter in the LayoutParams constructor is the weight. Each image view now has a weight of 1.0f. So they will spread across the layout.
You have them with WRAP_CONTENT width.
Instead they should be set to a width of 0dp and each one have a layout gravity of 1.
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:src="#drawable/music" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:src="#drawable/music" />
<ImageView
android:id="#+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:src="#drawable/music" />
<ImageView
android:id="#+id/imageView4"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:src="#drawable/music" />
</TableLayout>
This is the original XML, i just need to add more. i have an imageView that's big , so it needs to be shrunk down and copied 16 times into a 4x4 grid. I can only get it to go 4 images in one column
I don't understand the problem with the large image, but I will tell you my suggestion:
there are multiple possible solutions:
Since you have 16 imageViews that you wish to create, you can use a GridView together with a BaseAdapter . If it's important for you to see it in the xml, use isInEditMode for a custo GridView, and set the adapter there to be your adapter with fake items. You should be aware of problems with the sizes of the columns/rows on the gridView , especially when changing orientations.
Another alternative could be the GridLayout
If you insist on using the TableLayout, you can have 4 TableRow instances, each has a weight of 1 . in each of them , add 4 imageViews and there each has a weight of 1.
Add 4 TableRow's and put your ImageView's into them.
Or you can create this grid from code. Like this:
LinearLayout container = null;
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1);
for (int i = 0; i < 16; ++i) {
if (i % 4 == 0) {
container = new LinearLayout(getActivity());
container.setOrientation(LinearLayout.HORIZONTAL);
mGrid.addView(container);
}
LinearLayout view = (LinearLayout) mLayoutInflater
.inflate(R.layout.view_item, container, false);
//populate the view in loop
view.setLayoutParams(params);
container.addView(view);
}