I have a layout as follows
Horizontal Scroll View: id - container
Scroll View: width & height - match_parent
Relative Layout: id - wrapper, width & height - match_parent
View
LinearLayout: id - timeline, width & height - match_parent
LinearLayout: id - body, width & height - match_parent
The "container" has an initial width. I want to add a LinearLayout inside "body" which has the width equal to the container. This linear layout has multiple text views inside it. But I want to increase the width of the "container" and also the linear layout when the text views exceed the initial width so that text views are not clipped when added. Here's how I'm doing it:
This is the LinearLayout im trying to add into the "body"
public class Layer extends LinearLayout {
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), 100);
};
public void addTextView(CharSequence text) {
TextView tv = new TextView(text);
addView(tv);
if(this.getWidth() < tv.getX() + tv.getWidth()) {
RelativeLayout wrapper = (RelativeLayout) findViewById(R.id.wrapper);
wrapper.setMinimumWidth(size);
wrapper.invalidate();
}
}
I'm getting a NullPointerException at this line 'wrapper.setMinimumWidth(size);'. Is there any other better way to do it? I'm kind of stuck. Any help would be appreciated!
Ok so I found the solution. I had a fragment which contained my custom view as a private member variable. onCreateView() method of the fragment returned the custom view variable. Inside my custom view class, findViewById() was returning null. So I passed the context by getActivity() from my fragment to my custom view class and casted it into Activity to make it work!!
((Activity)context).findViewById()
Hope this helps!!
Related
I have made class called ProgressButton that extended RelativeLayout.Now in main xml i added this class:
<com.tazik.progressbutton.ProgressButton
android:id="#+id/pb_button"
android:layout_width="200dp"
android:layout_height="wrap_content"/>
As you can see i added android:layout_width="200dp", now in ProgressButton class i want to get this size to create a button with this size:
public class ProgressButton extends RelativeLayout {
private AppCompatButton button;
public ProgressButton(Context context) {
super(context);
initView();
}
private void initView() {
initButton();
}
private void initButton() {
button = new AppCompatButton(getContext());
LayoutParams button_params = new LayoutParams(????, ViewGroup.LayoutParams.WRAP_CONTENT);
button_params.addRule(RelativeLayout.CENTER_IN_PARENT,RelativeLayout.TRUE);
button.setLayoutParams(button_params);
button.setText("click");
addView(button);
}
I want to create button exactly to size of relativeLayout, so how can i get layout_width in my custom view to set button_params width?
now in ProgressButton class i want to get this size to create a button with this size
As #MikeM. suggested in a comment. It could be as easy as giving that child view a width of MATCH_PARENT. See below...
LayoutParams button_params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
With that in place you don't need to worry about the actual size because MATCH_PARENT will stretch your child view to occupy the whole parent's width...obviosuly respecting margins and paddings.
However, if you do need to know the parent's width, you should query that in onMeasure. I strongly suggest you to stay away from onMeasure whenever possible because it is a bit complex and it might take a lot of your development time.
Either way, in onMeasure you can know what measurements the parent view wants to give to its child views, this is based on the space available to render inside the parent and the layout params specified...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int childWidth = 0;
if(widthSpecMode == MeasureSpec.AT_MOST){
//The parent doesn't want the child to exceed "childWidth", it doesn't care if it smaller than that, just not bigger/wider
childWidth = MeasureSpec.getSize(widthMeasureSpec);
}
else if(widthSpecMode == MeasureSpec.EXACTLY){
//The parent wants the child to be exactly "childWidth"
childWidth = MeasureSpec.getSize(widthMeasureSpec);
}
else {
//The parent doesn't know yet what its children's width will be, probably
//because it's still taking measurements
}
//IMPORTANT!!! set your desired measurements (width and height) or call the base class's onMeasure method. Do one or the other, NOT BOTH
setMeasuredDimension(dimens, dimens);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Add a few Log.d calls inside onMeasure for a better understanding of what's happening. Be aware that this method will be called multiple times.
Again, this is an unnecessary overkill for your case scenario. Setting MATCH_PARENT to the button should produce the results you want
I make some custom view, and now im stuck with one specific issue.
Now ill try to explain: in my case the view hierarchy is
<view extends linear layout>
<child-view extends relative layout>
<some content>
<relative-layout>
<progressbar> (align text view right, match parent)
<textview> (align parent right, wrap content) <- there's i have to make some changes
<relative-layout>
<child-view>
<...>
<view>
the child items are added according to the data coming from the server.
I want to make all textviews with the same width - width of textview which content length is max. How can I implement this? I tried to calculate width in onMeasure callback, and then set width to another views, but getMeasuredWidth() and getWidth() returns 0 every time. I Heard something about ViewTreeObserver, but i can not understand how to use it right. Thanks in advance!
Try this,
int maxWidth = 0;
....
tv1.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int width = layout.getMeasuredWidth();
if(width > maxWidth){
maxWidth = width;
// update all textView width with max width.
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
this.layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
this.layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
same code for tv2,tv3 and tv4. it will work.
I am not a native English user. Sorry for the bad grammar and spelling.
I have a Dialog that contains a View. The View is inflated by an XML file which contains a CustomView. The CustomView, which inherited from a LinearLayout, add views with addView() method.
My CustomView: https://gist.github.com/TomazWang/e1ddcc32fb2f733d874022ee9c4cffdd
Codes that generate the dialog: https://gist.github.com/TomazWang/61d9ad5df3c24bfaa06a78d8a1bcab13
The problems were:
When the layout_height of CustomView is set to match_parent, the child views in CustomView won't show.
After the layout_height of CustomView is set to wrap_content, the child views were shown, but the onMeasure() method takes too long.
I override the onMeasure() method. The method was not slowing my app. However, the child views are gone again.
Can't test it, but there seem to be a number of mistakes in your onMeasure() method. The first one is about missing parantheses.
int desiredHeight = mChildHeight * mEditors.size() - 1
+ (mViewOther == null ? 0 : mViewOther.getHeight())
+ (mViewTitle == null ? 0 : mViewTitle.getHeight());
should probably read
int desiredHeight = mChildHeight * (mEditors.size() - 1)
+ (mViewOther == null ? 0 : mViewOther.getHeight())
+ (mViewTitle == null ? 0 : mViewTitle.getHeight());
The second mistake is about your call to setMeasuredDimension(). It is not necessary since this is what the call to super.onMeasure(...) does. Go check the source.
The third mistake is about when to call super.onMeasure(...). You want to change how the superclass measures, so do not call super.onMeasure(...) at the beginning, but at the end of your custom onMeasure.
The fourth mistake is connected to the third; it is about the parameters you use when calling super.onMeasure(...). Do not use the regular LinearLayout parameters, but your computed values. In order to make a new MeasureSpec from your height variable, do:
int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, heightMode);
And then call super.onMeasure(widthMeasureSpec, newHeightMeasureSpec);.
I guess that problem is that default orientation of LinearLayout is horizontal (doc), so if the first child's width of your custom view fits parent's width (match_parent) you will see only one item - first.
Try to modify init() method of your custom view with following changes:
private void init() {
this.mEditors = new HashMap<>();
mEditors.put("Other", new DataVO("other"));
setOrientation(VERTICAL);
}
I hope it will help.
I have the a LinearLayout with width set in xml as fill_parent , now i need its width at runtime programatically. So i did this:
LinearLayout layoutGet=(LinearLayout) findViewById(R.id.GameField1);
LayoutParams layParamsGet= layoutGet.getLayoutParams();
int width=layParamsGet.width;
But width value on debugging was found to be -1, anybody with idea why can't i get the exact width at runtime for LinearLayout with fill_parent set in layout xml.
I got a simple approach working.
myLayout = (RelativeLayout) findViewById(R.id.my_layout);
myLayout.post(new Runnable()
{
#Override
public void run()
{
Log.i("TEST", "Layout width : "+ myLayout.getWidth());
}
});
Jens Vossnack's approach mentioned below works fine. However, I found that the onGlobalLayout() method of GlobalLayoutListener is called repeatedly, which may not be appropriate in certain cases.
You can try to listen to the globalLayout event, and get the width in there. You probably get the -1 because you are trying to get the width before the views are layed-out.
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//Do it here
LinearLayout layoutGet=(LinearLayout) findViewById(R.id.GameField1);
LayoutParams layParamsGet= layoutGet.getLayoutParams();
int width=layParamsGet.width;
removeOnGlobalLayoutListener(layoutGet, this); // Assuming layoutGet is the View which you got the ViewTreeObserver from
}
});
#SuppressLint("NewApi")
public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener){
if (Build.VERSION.SDK_INT < 16) v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
else v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
(vto is the view you want to get the width of)
If you look at the value for FILL_PARENT (you should be using MATCH_PARENT since FILL_PARENT is now deprecated) you will notice the value for it is -1. LayoutParams are simply attributes for how your view should look. Once the view is inflated and looks the way the params specify, the view does not go back and change those Params to reflect the actual values of the view (width/height/etc).
If you wanted to get the actual width of your view you would have to call getWidth() on your view once the layout has been inflated and displayed. Calling getWidth() before your layout has been displayed will result in 0.
LinearLayout layoutGet=(LinearLayout) findViewById(R.id.GameField1);
int width = layoutGet.getWidth();
First of all, you should use match_parent instead of fill_parent (deprecated).
When you do int width=layParamsGet.width;, you take the right result which is -1 corresponding at fill_parent.
If you wan't to get the real width, you need to wait onMesure() call and use
LinearLayout layoutGet=(LinearLayout) findViewById(R.id.GameField1);
int width = layoutGet. getMeasuredHeight();
layoutGet.getWidth() should work out
i have a list view defined in the xml, now i am setting the content view setContentView(R.layout.topic_layout); , i have 5 items in it,currently its filling only half the height of the list view but i want it to completely fill the height so that i dont have any space at the bottom.
i have searched for it but couldnt find any solution, please help me to acheive this :
and also i am setting the adapter like this:
adapter = new MyAdapter(this);
if (adapter != null) {
setListAdapter(adapter);
}
If you have a fixed number of items and want them to stretch all the way to the end of the screen, ListView is not the best choice for you. Use a LinearLayout which takes up all the space and add all the items to it. This is assuming you want the items to take up all the space every time.
Using LinearLayout, you can spread the items out evenly without doing any calculations yourself.
LinearLayout linearLayout = new LinearLayout(getSupportActivity());
linearLayout.setOrientation(android.widget.LinearLayout.VERTICAL);
RelativeLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
for (int i = 0; i < 5; i++) {
View individualView = new View(getSupportActivity());
// Create your custom view here and add it to the linear layout
// Leave the height as 0, LinearLayout will calculate the height properly.
params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
additionalOption.setLayoutParams(params);
// As a we are adding it to the linear layout, they will all have a weight of 1, which will make them spread out evenly.
linearLayout.addView(additionalOption);
}
mainView.addView(linearLayout);
EDIT: If you have already implemented it with ListView and is troublesome to change it, you can do the following.
Make sure the list view width and height are set to match_parent in the xml. Then in getView() of the adapter where you create your custom view, do the following
// Get the height of the ListView
int totalHeight = listView.getHeight();
int rowHeight = totalHeight/getCount(); // Divide by number of items.
// Create custom view with the height calculated above.
Be careful about the totalHeight being 0. If you create the ListView in onCreate() and set the adapter in onCreate() as well, the ListView will most likely not have the width or height calculated yet. Try setting the adapter in onResume() instead. By this point, the dimensions of the ListView would have been calculated and laid out on the screen.
Hope this helps.
I totally agree with #Aswin, using LinearLayout with layout_weight property would be your solution. But if you insist to use listView, i can offer you a workout which is not so recommended.
You can get your screenHeight when activity created by using these codes:
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
int height = dm.heightPixels;
int listItemHeight = height / YOUR_ITEM_COUNT;
Then you can use this listItemHeight in your listAdapter, by setting inflated view's height on getView method.
This is what I use to do:
Set your xml listview height to match_parent instead of wrap_content, in order to fill all the available space.
Set the minimum height of each item in the listview to match the listview height divided by the number of elements.
Activity Layout:
(...)
<ListView
android:id="#+id/myListView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
(...)
ArrayAdapter for the Listview:
private class MyAdapter extends ArrayAdapter<MyObject> {
(...)
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MyObject myobject = getItem(position);
ObjectHolder oh;
if (convertView == null) {
LayoutInflater mInflater = (LayoutInflater) myContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.myitemlayout, parent, false);
// This is the important line:
convertView.setMinimumHeight(parent.getHeight()/getCount());
(...)
return convertView;
}
}