Android: GridView width not wrapping to content? - android

I am trying to display a GridView in a Dialog. Despite all my efforts, the GridView width grows to the entire screen, instead of wrapping to the columns. The layout and an image depicting the issue are below (I set a background color for the GridView to illustrate the issue).
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/colorgridview"
android:background="#FF00CCBB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numColumns="4"
android:verticalSpacing="5dp"
android:horizontalSpacing="5dp"
android:columnWidth="70dp"
android:stretchMode="none"
/>

I know that this post is a bit outdated. But if someone needs a solution to this, this answer may come in handy.
It is possible to set the view's width after screen measurement.
To do this:
Let your class implement the OnGlobalLayoutListener.
The screen is measured when the onGlobalLayout method is called. We can do our magic here.
GridView.getLayoutParams().width = ....
edit:
I wasn't very clear on how to add the onGlobalLayoutListener. See plugmind's post, he shows how to add it.
Can't figure out how to get view/layout width/height
Kind regards,
Bram

I think you should use android:layout_width="fill_parent" instead of android:layout_width="wrap_content" because wrap content use the minimum place it needs. On the other hand, fill_parent use all space needed. More over you should get rid of "android:columnWidth="70dp".

It's certainly possible to set a fixed layout_width (in dp). Since the number of your columns is also fixed, could this be a workaround for you?

Had the same problem...
I solved it with overridden onMeasure()
public class GridViewEx extends GridView {
private int mRequestedNumColumns = 0;
public GridViewEx(Context context) {
super(context);
}
public GridViewEx(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GridViewEx(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setNumColumns(int numColumns) {
super.setNumColumns(numColumns);
if (numColumns != mRequestedNumColumns) {
mRequestedNumColumns = numColumns;
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mRequestedNumColumns > 0) {
int width = (mRequestedNumColumns * getColumnWidth())
+ ((mRequestedNumColumns-1) * getHorizontalSpacing())
+ getListPaddingLeft() + getListPaddingRight();
setMeasuredDimension(width, getMeasuredHeight());
}
}
}

Related

How can I limit the area of chip items (the wrap area) to 2 lines max inside the ChipGroup?

I am trying to implement a ChipGroup with some chip items inside it. Which works fine so far. But My requirement is to show these items inside the ChipGroup with certain number of rows. As example: I have 20 items as chip inside my group, And only 6/7 items fits in between the 2 lines. So, I want to show the chips which fit into the ChipGroup between 2 lines only.
Currently I am getting this (sample) output:
And my expectation is like below:
Additional queries:
Is it possible to get the current row count of chip items from the
ChipGroup? If yes how?
Can I set the maximum visible rows for the ChipGroup
programatically?
I use customer view to make this feature.
public class AppChipGroup extends ChipGroup {
private static final int[] MAX_HEIGHT_ATTRS = {android.R.attr.maxHeight};
private int mMaxHeight = Integer.MAX_VALUE;
public AppChipGroup(Context context) {
super(context);
}
public AppChipGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AppChipGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, MAX_HEIGHT_ATTRS);
mMaxHeight = typedArray.getDimensionPixelSize(0, Integer.MAX_VALUE);
typedArray.recycle();
}
#SuppressLint("RestrictedApi")
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = Math.min(getMeasuredHeight(), mMaxHeight);
setMeasuredDimension(getMeasuredWidth(), height);
}
}
then when use ChipGroup, set the maxHeight value.
<packagename.chip.AppChipGroup
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="#style/Widget.MaterialComponents.ChipGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="#dimen/search_input_history_max_height"
app:chipSpacing="#dimen/search_input_history_chip_space" />
Or you can get the chip height and calculate the max_height in onMeasure method and set the measure dimension.
ExtendedChipGroup
U can set max lines and show/hide button
For this I created a class and inherited it from ChipGroup, next rewrote the onLayout method so that all elements that are after the visible line are hidden
<io.github.tiarait.extendedchipgroup.ExtendedChipGroup
android:id="#+id/chip_group"
app:maxRow="2"
app:itemSpacing="6dp"
app:additionalTextColor="#eee"
app:additionalChipColor="#color/colorDarkBtn"
app:additionalChipColorPressed="#color/colorDarkBtnSecondary"
app:additionalChipMore="#string/btn_show"
app:additionalChipHide="#string/btn_hide"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

is there possible to give marginTop as much as views height at xml?

I have one imageView and I am trying to give it minus margin top as much as its height / 2. I can do it at programmatically but i wondred is it possible at xml also andorid published percentrelative layout . I don't know how to to do it or possible?
--Edit: As #aga suggests, there seems to be a way to achieve it via the Percent Support Library--
If you want to use this type of imageView more often throughout your application you could extend imageview and put your margin-code inside of it's onMeasure:
public class HalfMarginImageView extends ImageView {
public HalfMarginImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
((ViewGroup.MarginLayoutParams) getLayoutParams()).topMargin = -getMeasuredHeight() / 2;
}
}
for this to work the view must be part of a ViewGroup.Also make sure you use the constructor with AttributeSet, oltherwise you can't create the View from xml. Yo then just include a CustomView in your layout xml, select HalfMarginImageView and use it as normal imageView.

Weight sum and square form

I have LinearLayout which contains two Button widgets with layout_weight=1. Depends on length of Button's text or screen resolution I get buttons with rectangular form (gray rectangles) but I need to keep the square form (blue squares).
I was trying to change height of LinearLayout param in onStart() method, depends on Button's width, but getWidth() returns 0. I understand that it's because view at that moment still not rendered. Please, help me to solve my problem.
There are many ways to achieve this. If you need to find the real width of an element, you can:
1) attach an OnGlobalLayoutListener to the view's ViewTreeObserver (but remember to remove it when you are done)
2) or you can manually measure what you need:
if(view.getMeasuredHeight() == 0){
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(metrics);
view.measure( metrics.widthPixels, metrics.heightPixels );
}
int realHeight = view.getMeasuredHeight();
//...your code
You are exactly right because at that time view will not be drawn so for that you have to use viewtreeobserver:
like this:
final ViewTreeObserver treeObserver = viewToMesure
.getViewTreeObserver();
treeObserver
.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
System.out.println(viewToMesure.getHeight()
+ "SDAFASFASDFas");
heith = viewToMesure.getHeight();
}
});
this view tree observer will be called after creating the view based on that you calculate and you can change.
You can use a custom view to set the view's height to be tha same as its width by overriding onMeasure:
public class SquareButton extends Button {
public SquareButton (Context context) {
super(context);
}
public SquareButton (Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareButton (Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
}
}
All you have to is use the custom button in your xml layout and you don't have to do anything in the activity:
<com.package.name.SquareButton
android:layout_with="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />

How to reduce TextView line spacing

I am trying to reduce the line spacing in a TextView by setting a negative 'add' to TextView.setLineSpacing(). It works well except that the bottom line get truncated.
Main layout
<TextView
android:id="#+id/text_view"
android:padding="dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
tools:context=".MainActivity" />
Main activity: (notice the
package com.font_test;
import android.app.Activity;
import android.graphics.Typeface;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/custom_fonts.ttf");
final TextView tv = (TextView) findViewById(R.id.text_view);
tv.setTypeface(typeface);
tv.setTextSize(60);
tv.setLineSpacing(-30f, 1f); // *** -30 to reduce line spacing
tv.setBackgroundColor(0x280000ff);
tv.setText("gggkiiikkk" + "\n" + "gikgikgik" + "\n" + "kigkigkig");
}
}
This results in truncation at the bottom of the view (notice the 'g' at the bottom line):
It seems that the problem is related to incorrect layout measurement. If I set the TextView to
android:layout_height="fill_parent"
It does render properly:
Any idea how to fix it? I don't mind to have ugly workarounds if it helps. I also have access to FontForge and I can modify the font file if needed.
littleFluffyKittys answer is good but it didn't work on some devices if the linespacing was set through xml
I calculate the additional height needed by comparing the original height of the font with the height the textview calculates for a line.
If the line height is smaller than the height of the font the diffrence is added one time.
This works down to at least API 10 propably lower (just not tested any lower)
public class ReducedLineSpacingTextView extends TextView {
public ReducedLineSpacingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ReducedLineSpacingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ReducedLineSpacingTextView(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int truncatedHeight = getPaint().getFontMetricsInt(null) - getLineHeight();
if (truncatedHeight > 0) {
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + truncatedHeight);
}
}
}
I ran into this same problem but when I was trying to use a spacing multiplier less than 1.
I created a subclass of TextView that fixes the truncation of the last line automatically and doesn't require you set a known/fixed spacing at the bottom.
Just use this class and you can use it normally, you don't need to apply any additional spacing or fixes.
public class ReducedLineSpacingTextView extends TextView {
private boolean mNegativeLineSpacing = false;
public ReducedLineSpacingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ReducedLineSpacingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ReducedLineSpacingTextView(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mNegativeLineSpacing) { // If you are only supporting Api Level 16 and up, you could use the getLineSpacingExtra() and getLineSpacingMultiplier() methods here to check for a less than 1 spacing instead.
Layout layout = getLayout();
int truncatedHeight = layout.getLineDescent(layout.getLineCount()-1);
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + truncatedHeight);
}
}
#Override
public void setLineSpacing(float add, float mult) {
mNegativeLineSpacing = add < 0 || mult < 1;
super.setLineSpacing(add, mult);
}
}
Nice!
That'll make the job but it's never a good idea to put constants values wherever we have variables. You can use the lineSpacing values to add them to the onMeasure method in a dinamyc way.
Note that this values are always available through "getLineSpacingExtra()" and "getLineSpacingMultiplier()". Or even easier you can get the value of both summed up: "getLineHeight()".
Although it feels for me that this value should be included in the onMeasure method, you can always measure the exact height you need and then make a simple check:
final int measuredHeight = getMeasuredHeight();
if (measuredHeight < neededHeight) {
setMeasuredDimension(getMeasuredWidth, neededHeight);
}
One last thing, you don't need to pass the context along in a separated attribute. If you have a look to your constructors, the context is already there. If you needed along the code of your component you can just use "getContext()".
Hope it helps.
Use this to reduce line spacing in text view
**
android:lineSpacingMultiplier="0.8"
**
If padding doesn't work, margin should do the job. If you still have problem you can always apply the line spacing value to the onMeasure method of the view. You'll have to create a custom component for that and extend onMeasure.
Just add paddingBottom to declaration of your TextView xml, pick a value which produces a good result. And consequently set values for other paddings (top, let and right). This should fix your problem
This is what I did based on Jose's answer here and it seems to work. I am not very familiar with the intricate of the layout mechanism. Is this code safe? Any problem with it?
Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.font_test.MyTextView
android:id="#+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
tools:context=".MainActivity" />
</RelativeLayout>
Added custom TextView that extends the vertical height by N pixels:
package com.font_test;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;
public class MyTextView extends TextView {
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// TODO: provide an API to set extra bottom pixels (50 in this example)
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + 50);
}
}
Result text view rendering without truncation at the bottom:

Android - Why is the TextView shown as having a height?

I've got an odd problem here. Basically I have a TextView with no default set text. I would've expected it to have a height of 0 since it hsas no content but there seems to be a gap between the elements above and below it. If I set the height to 0 in the XML and then try and change it through Java code then it does not reset the height.
How do I set the height to be 0 if the content is blank but then allow it to be changed programmatically?
Here is the code that I have:
<TextView
android:gravity="center_horizontal|center_vertical"
android:id="#+id/connectionStatus"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:textSize="18px"
android:textStyle="bold">
</TextView>
and the Java code is this:
private void getConnectionStatus()
{
if (hasConnection() == true)
{
//do something
}
else
{
connectionStatus.setHeight(48);
connectionStatus.setText("No Internet Access");
}
}
Use visibility "gone" inside of the xml layout. Then in the Java code call connectionStatus.setVisibility(View.VISIBLE);
Components may still display themselves even if they don't have content. For example, the may display a border or their viewable area. In order to make it not show up at all you need to use setVisibility(View.GONE).
I've often wondered if this behaviour is intuitive. If you want a TextView that has no height when the text is empty you can make one:
import android.content.Context;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
public class NoHeightWhenEmptyTextView extends android.support.v7.widget.AppCompatTextView {
public NoHeightWhenEmptyTextView(Context context) {
super(context);
}
public NoHeightWhenEmptyTextView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public NoHeightWhenEmptyTextView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int newHeightMeasureSpec = heightMeasureSpec;
if (TextUtils.isEmpty(getText())) {
newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, newHeightMeasureSpec);
}
#Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
// ConstraintLayout totally ignores the new measured height after non-empty text is set.
// A second call to requestLayout appears to work around the problem :(
requestLayout();
}
}

Categories

Resources