TextView is trimmed by it's parent (RelativeLayout/FrameLayout) - android

I am developing some kind of a scrollable horizontal chart. I have a RecyclerView.Adapter with view holder of two views:
1. View - will just represent a bar with defined height.
2. TextView - on the top of (1)View with angle -45 it should represent some data to which this column belongs.
Width of bar should be equal for each column, but text has different length.
But boundaries of parent (no metter is it Frame or Relative layouts are cutting off the rest of the text.
P.S. clipChildren and clipToPadding didn't help.
My .xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="#dimen/chart_column_width"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false">
<View
android:id="#+id/cc_progress"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_alignParentBottom="true"
android:background="#color/colorPrimaryDark"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="#+id/cc_tagname"
android:layout_above="#id/cc_progress"
android:elevation="2dp"
android:textSize="20sp"
android:padding="2dp"
android:textAlignment="textStart"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>

This is happening because you are just rotating the TextView. So, the TextView retaining its original height and width.
Try using this VerticalTextView :
`public class VerticalTextView extends TextView{
final boolean topDown;
public VerticalTextView(Context context, AttributeSet attrs){
super(context, attrs);
final int gravity = getGravity();
if(Gravity.isVertical(gravity) && (gravity&Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
setGravity((gravity&Gravity.HORIZONTAL_GRAVITY_MASK) | Gravity.TOP);
topDown = false;
}else
topDown = true;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
#Override
protected void onDraw(Canvas canvas){
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
textPaint.drawableState = getDrawableState();
canvas.save();
if(topDown){
canvas.translate(getWidth(), 0);
canvas.rotate(90);
}else {
canvas.translate(0, getHeight());
canvas.rotate(-90);
}
canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop());
getLayout().draw(canvas);
canvas.restore();
}
}`
All you have to set the gravity of the text. By default, text is from top to bottom. If you set android:gravity="bottom", then it's drawn from bottom to top.
Hope it helps you.

Its little tough task but it is possible. I'm explaining idea how you can make it possible: –
You have make recyclerview wrap_content and keep it inside the relativelayout and the scrollview.
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:fitsSystemWindows="true">
<RelativeLayout
android:id="#+id/rlRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
</ScrollView>
Now calculate the width of an item of the recylerview and add the textview in rlRoot at position (width * itemPosition). It places the textview at exact X position. Now to calculate Y coordinate, you can get height of the items as you are setting the bar height and Y position of the textView should be(rlRoot.getHeight()- itemHeight). And then give the whatever angle you want, it will never ellipsis. Repeat the loop for all the items.
I never tried it, but i guess, it will work with your efforts.

Override OnMeasure of Textview and increase the height on super.OnMeasure method

Related

Dynamically adding a View to a LinearLayout with height of wrap_content doesn't display anything

I'm trying to dynamically add a view to a LinearLayout using the wrap_content height. However it doesn't work unless I add a defined dp height such as 300dp. Why can't I programmatically add the View and having the Parent LinearLayout wrap the contents?
The view I'm trying to add:
public class ImageCanvas extends View {
private static final String TAG = "ImageCanvas";
private Bitmap icon;
public ImageCanvas(Context context) {
super(context);
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.piggy);
if(drawable != null){
icon = ((BitmapDrawable) drawable).getBitmap();
}
Log.d(TAG, "ImageCanvas: " + icon);
}
public ImageCanvas(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public ImageCanvas(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(icon, 0, 0, null);
}
}
The LinearLayout I'm trying to add the above view to:
<LinearLayout
android:id="#+id/drawingPad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#+id/btn_create_image"
app:layout_constraintBottom_toBottomOf="parent"
android:paddingBottom="32dp"
android:orientation="vertical"
android:paddingTop="32dp"/>
Creating the view programmatically and adding it:
LinearLayout drawingPad = findViewById(R.id.drawingPad);
ImageCanvas imageCanvas = new ImageCanvas(context);
drawingPad.addView(imageCanvas);
It works when I set the XML LinearLayout to a defined height like 200dp but it doesn't work with wrap_content, how can I get it to do so with wrap_content?
_________________________________________________________
Update 1:
I simplified everything down to the XML layout and the issue turned out to be a Parent NestedScrollView.
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/parentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="#+id/btn_create_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="Create image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="#+id/drawingPad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="32dp"
android:paddingBottom="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btn_create_image" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
This is the entire layout. When you add a NestedScrollView and you try to add a view programmatically, the Bitmap WILL NOT draw on screen. When you remove the NestedScrollView the Bitmap will appear. If anyone knows how to do it with a NestedScrollView please share!
You need to provide measurements of ImageCanvas with an onMeasure() override:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(icon.getWidth(),icon.getHeight());
}
See Custom View Components.
When you explicitly supply a height, that height is set in the custom component. With wrap content, the height gets set to zero since it is neither specified nor explicitly measured. The above code will provide the correct measurements.
(icon.getHeight() is what you really need and corresponds to wrap_content. You may need a different value for icon.getWidth() depending upon your design.)
Not sure why a NestedScrollView has any effect, but the code above will help.
The problem may lay with your constraint layout.
for constraint layout >= 1.1
app:layout_constrainedHeight=”true"
android:layout_height="wrap_content"
for constraint layout <1.1
android:layout_height="wrap_content"
app:layout_constraintHeight_default="wrap"
This always gets me maybe it got you.
(edit) this needs to be in the child of the constraint layout not the constraint layout itself, ie LinearLayout.

Custom Square Layout not working as expected

I stumbled across this problem when working with custom Square Layout : by extending the Layout and overriding its onMeasure() method to make the dimensions = smaller of the two (height or width).
Following is the custom Layout code :
public class CustomSquareLayout extends RelativeLayout{
public CustomSquareLayout(Context context) {
super(context);
}
public CustomSquareLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSquareLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public CustomSquareLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Width is smaller
if(widthMeasureSpec < heightMeasureSpec)
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
//Height is smaller
else
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
}
The custom Square Layout works fine, until in cases where the custom layout goes out of bound of the screen. What should have automatically adjusted to screen dimensions though, doesn't happen. As seen below, the CustomSquareLayout actually extends below the screen (invisible). What I expect is for the onMeasure to handle this, and give appropriate measurements. But that is not the case. Note of interest here is that even thought the CustomSquareLayout behaves weirdly, its child layouts all fall under a Square shaped layout that is always placed on the Left hand side.
<!-- XML for above image -->
<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"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="300dp"
android:text="Below is the Square Layout"
android:gravity="center"
android:id="#+id/text"
/>
<com.app.application.CustomSquareLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/text"
android:background="#color/colorAccent" #PINK
android:layout_centerInParent="true"
android:id="#+id/square"
android:padding="16dp"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" #Note this
android:background="#color/colorPrimaryDark" #BLUE
>
</RelativeLayout>
</com.app.application.CustomSquareLayout>
</RelativeLayout>
Normal case : (Textview is in Top)
Following are few links I referenced:
Custom Square LinearLayout. How?
Simple way to do dynamic but square layout
Hope to find a solution to this, using onMeasure or any other function when extending the layout (so that even if some extends the Custom Layout, the Square property remains)
Edit 1 : For further clarification, the expected result for 1st case is shown
Edit 2 : I gave a preference to onMeasure() or such functions as the need is for the layout specs (dimensions) to be decided earlier (before rendering). Otherwise changing the dimensions after the component loads is simple, but is not requested.
You can force a square view by checking for "squareness" after layout. Add the following code to onCreate().
final View squareView = findViewById(R.id.square);
squareView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
squareView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
if (squareView.getWidth() != squareView.getHeight()) {
int squareSize = Math.min(squareView.getWidth(), squareView.getHeight());
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) squareView.getLayoutParams();
lp.width = squareSize;
lp.height = squareSize;
squareView.requestLayout();
}
}
});
This will force a remeasurement and layout of the square view with a specified size that replaces MATCH_PARENT. Not incredibly elegant, but it works.
You can also add a PreDraw listener to your custom view.
onPreDraw
boolean onPreDraw ()
Callback method to be invoked when the view tree is about to be drawn. At this point, all views in the tree have been measured and given a frame. Clients can use this to adjust their scroll bounds or even to request a new layout before drawing occurs.
Return true to proceed with the current drawing pass, or false to cancel.
Add a call to an initialization method in each constructor in the custom view:
private void init() {
this.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if (getWidth() != getHeight()) {
int squareSize = Math.min(getWidth(), getHeight());
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams();
lp.width = squareSize;
lp.height = squareSize;
requestLayout();
return false;
}
return true;
}
});
}
The XML can look like the following:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="300dp"
android:gravity="center"
android:text="Below is the Square Layout" />
<com.example.squareview.CustomSquareLayout
android:id="#+id/square"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/text"
android:background="#color/colorAccent"
android:padding="16dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="#color/colorPrimaryDark" />
</com.example.squareview.CustomSquareLayout>
</RelativeLayout>
There is a difference between the view's measured width and the view's width (same for height). onMeasure is only setting the view's measured dimensions. There is still a different part of the drawing process that constrains the view's actual dimensions so that they don't go outside the parent.
If I add this code:
final View square = findViewById(R.id.square);
square.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
System.out.println("measured width: " + square.getMeasuredWidth());
System.out.println("measured height: " + square.getMeasuredHeight());
System.out.println("actual width: " + square.getWidth());
System.out.println("actual height: " + square.getHeight());
}
});
I see this in the logs:
09-05 10:19:25.768 4591 4591 I System.out: measured width: 579
09-05 10:19:25.768 4591 4591 I System.out: measured height: 579
09-05 10:19:25.768 4591 4591 I System.out: actual width: 768
09-05 10:19:25.768 4591 4591 I System.out: actual height: 579
How to solve it by creating a custom view? I don't know; I never learned. But I do know how to solve it without having to write any Java code at all: use ConstraintLayout.
ConstraintLayout supports the idea that children should be able to set their dimensions using an aspect ratio, so you can simply use a ratio of 1 and get a square child. Here's my updated layout (the key piece is the app:layout_constraintDimensionRatio attr):
<android.support.constraint.ConstraintLayout
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">
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="300dp"
android:gravity="center"
android:text="Below is the Square Layout"
app:layout_constraintTop_toTopOf="parent"/>
<RelativeLayout
android:id="#+id/square"
android:layout_width="match_parent"
android:layout_height="0dp"
android:padding="16dp"
android:background="#color/colorAccent"
app:layout_constraintTop_toBottomOf="#+id/text"
app:layout_constraintDimensionRatio="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="#color/colorPrimaryDark">
</RelativeLayout>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
And screenshots:
You cannot compare the two measure specs, as they are not simply a size. You can see a very good explanation in this answer. This answer is for a custom view, but measure specs are the same. You need to get the mode and the size to compute final sizes, and compare the end results for both dimensions.
In the second example you shared, the right question is this one (third answer). Is written for Xamarin in C#, but is easy to understand.
The case that is failing for you is because you're finding an AT_MOST mode (when the view is hitting the bottom of the screen), that's why comparisons are failing in this case.
That should be the final method (can contain typos, I have been unable to test it:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width, height;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
width = Math.min(widthSize, heightSize);
break;
default:
width = 100;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
height = Math.min(widthSize, heightSize);
break;
default:
height = 100;
break;
}
var size = Math.min(width, height);
var newMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
super.onMeasure(newMeasureSpec, newMeasureSpec);
}
I expect the end result to be roughly like this (maybe centered, but this dimensions):
Notice that this is a made up image done with Gimp.
try this. You can use on measure method to make a custom view. Check the link below for more details.
http://codecops.blogspot.in/2017/06/how-to-make-responsive-imageview-in.html

Android: how to configure sizing of a custom layout

I've been trying to create a custom horizontal layout with the goal to have a TextView to the left of an ImageView, containing a icon which depicts a certain status. The ImageView is to kept in a square dimension, with it's height and width equal to the height of the text in the TextView. Issues continue to persist, however, such as the text height not being set as specified in the layout xml file and an unknown padding existing after the ImageView. These problem can be seen in this image, with the red indicating the unknown padding and the blue indicating the text size inconsistency where both where set to 12sp. The font sizing and padding issues need to be fixed so the layout can be properly added to a grid layout, which will contain a grid of these custom layouts.
StatusIcon.java
//This is part of the java class that extends ImageView to resize the Icon
#Override
protected void onMeasure(int width, int height) {
super.onMeasure(width, height);
int measuredHeight = getMeasuredHeight();
setMeasuredDimension(measuredHeight, measuredHeight);
}
StatusIndicator.java
//This is the java class for the custom layout.
public class StatusIndicator extends LinearLayout {
private TextView label;
private StatusIcon statusLed;
private CharSequence labelText;
private float labelTextSize;
public enum Status {
GOOD,
WARNING,
CRITICAL
}
/*
* Removed the basic required class constructors to save space.
*/
private void getAttributes(Context context, AttributeSet attrs){
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StatusIndicator);
labelText = typedArray.getString(R.styleable.StatusIndicator_label);
labelTextSize = typedArray.getDimensionPixelSize(R.styleable.StatusIndicator_labelSize, 0);
typedArray.recycle();
}
private void initializeViews(Context context){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_status_indicator, this);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
//Setup UI elements in layout
label = (TextView) findViewById(R.id.textView_statusIndicatorLabel);
statusLed = (StatusIcon) findViewById(R.id.imageView_statusIndicatorLed);
label.setText(labelText);
if(labelTextSize > 0){
label.setTextSize(TypedValue.COMPLEX_UNIT_SP, labelTextSize);
}
}
public void setStatus(StatusIndicator.Status status){
switch (status){
case GOOD:
statusLed.setImageResource(R.mipmap.ic_status_panel_good);
break;
case WARNING:
statusLed.setImageResource(R.mipmap.ic_status_panel_warning);
break;
case CRITICAL:
statusLed.setImageResource(R.mipmap.ic_status_panel_critical);
break;
}
}
}
view_status_indicator.xml
<?xml version="1.0" encoding="utf-8"?>
<merge 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"
tools:parentTag="LinearLayout"
tools:orientation="horizontal">
<TextView
android:id="#+id/textView_statusIndicatorLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical|start"
android:layout_marginEnd="2dp"
android:text="#string/default_title"
android:textAppearance="#style/TextAppearance.AppCompat.Title"
android:textSize="12sp"/>
<com.css_design.android_quickbridge.ui.home.status_panel.StatusIcon
android:id="#+id/imageView_statusIndicatorLed"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center_vertical|end"
app:srcCompat="#mipmap/ic_status_panel_critical"/>
</merge>
I would solve this problem by using ConstraintLayout instead of creating a custom view implementation.
ConstraintLayout allows you to specify an aspect ratio for its children, which takes care of wanting to make sure your ImageView is always exactly square. ConstraintLayout also allows you to specify height or width based on sibling views (by combining a dimension of 0dp with top and bottom (or left and right) constraints).
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ccf">
<ImageView
android:id="#+id/image"
android:layout_width="0dp"
android:layout_height="0dp"
android:src="#drawable/circle"
app:layout_constraintTop_toTopOf="#+id/text"
app:layout_constraintLeft_toRightOf="#+id/text"
app:layout_constraintBottom_toBottomOf="#+id/text"
app:layout_constraintDimensionRatio="1"/>
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
android:text="hello world"/>
</android.support.constraint.ConstraintLayout>
(Background color added to the ConstraintLayout to show that it's not any larger than its contents).

My custom view is being clipped if its position is not (0,0)

My problem is that if i set the Y-axis of my view to anything but 0 its drawn region gets clipped from the top. Using log traces i've discovered that the view bounds (and its drawable's bounds) are always the correct position. I've tried clipChildren=false, but that gave me a stranger behavior where the drawn region and the views bounds are not in sync. Here's all pertinent code to my view
//onMeasure
#Override
public void onMeasure(int w, int h){
int width = MeasureSpec.getSize(w);
//minHeight is 48dp scaled for density
setMeasuredDimension(width, minHeight);
}
//onDraw
//Note that i've ommited the log statements, however they return the correct
//coordinates for the view Rect
#Override
public void onDraw(Canvas c){
c.drawRect(trackRect, tempPaint);
}
//onLayout
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
trackRect.set(left, top, right, bottom);
mTrack.setBounds(trackRect); //irrelevant atm
}
//XML CODE
//BELOW draws a perfect rectangle with a width of match_parent and a height
//of 48dp
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false" >
<com.bryanstudios.bryan.clock.LocationSwitch
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
//BELOW will cause clipping
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
<com.bryanstudios.bryan.clock.LocationSwitch
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
//BELOW causes the view to disappear entirely
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
<com.bryanstudios.bryan.clock.LocationSwitch
android:align_parent_bottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
My best guess is my onMeasure isn't done properly. Also note again that regardless of where i position the view (via w/ margins or LayoutParam attributes) the logical positioning of the view is accurate, yet the drawn region is not.
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
trackRect.set(left, top, right, bottom);
...
The left/top/etc params here are relative to the parent. If your view's y position == 0, top also == 0. If you change the view's Y position relative to its parent, top is set to that same value. You are then assigning this top to the rect you draw.
public void onDraw(Canvas c){
c.drawRect(trackRect, tempPaint);
...
This will draw a rectangle with the same offsets you have specified in onLayout. This means that it will begin drawing trackRect.top from the top of the view.
If you just want the actual size of the view, view.getWidth() and view.getHeight() are likely all that is needed; if you want to initialise something (like a Path) when the width and height of the view are known, override onSizeChanged() and do it in there instead of in onLayout().

wrap_content height, but limited to half of parent

What I want is to have a layout with 2 views arranged vertically. Let's call the top view A and the bottom one B. I want the amount of height given to B to be its normal height (i.e. wrap content) except that I don't want it to be given more than half of the available space. A gets what is left.
Another way to word it is that A should always get at least 50% of the available height and B should get at most 50%.
I can't seem to find an easy way to achieve that. I can set both layout heights to 0 and give them equal weights which makes them both 50% always, but if B is smaller than 50% it should be given only what it needs.
The only way I can see to do it is use a custom class for A or B and override onMeasure to constrain the height to 50% of the parent, but it seems there should be an easier way.
Ok, I got it now. If I understood correctly you want to have it like this:
if A > B -> do nothing
if B > A & B > parent layout -> 50% to both of them
if B > A & B < parent layout -> A = parent layout - B
I had to do it all in onWindowFocusChanged because otherwise in onCreate the height of the Views would return 0. I did it with 2 LinearLayouts as child layouts, but you can take what ever you want.
My XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/parent_lay"
android:orientation="vertical"
android:layout_height="match_parent" >
//Layout A:
<LinearLayout
android:id="#+id/lay_1"
android:layout_width="match_parent"
android:background="#android:color/background_dark"
android:layout_height="10dp" >
</LinearLayout>
//Layout B:
<LinearLayout
android:id="#+id/lay_2"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#123456" >
</LinearLayout>
</LinearLayout>
MainActivity:
public class MainActivity extends Activity {
LinearLayout parent_lay;
LinearLayout lay_1;
LinearLayout lay_2;
int parent_height;
int lay_1_height;
int lay_2_heigth;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasFocus);
parent_lay = (LinearLayout) findViewById(R.id.parent_lay);
lay_1 = (LinearLayout) findViewById(R.id.lay_1);
lay_2 = (LinearLayout) findViewById(R.id.lay_2);
lay_1_height = lay_1.getHeight();
lay_2_heigth = lay_2.getHeight();
parent_height = parent_lay.getHeight();
if (lay_2.getHeight() > lay_1.getHeight()
&& lay_2.getHeight() > (parent_lay.getHeight() / 2)) {
lay_1.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0, 1));
lay_2.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0, 1));
} else if (lay_2.getHeight() < (parent_lay.getHeight() / 2)) {
lay_1.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, (parent_height - lay_2_heigth)));
}
}
}
Example:
If A is 60dp and B is 40dp:
If A is 60dp and B is 400dp:
You must write your own component to achieve this.
For example, if you use LinearLayout here, you can extends a LinearLayout with overdid onMeasure method. You can implement onMeasure like this:
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
setMeasuredDimension(width, height / 2);
}
This code is not elegant enough. If you really want to do it well, copy the original onMeasure method from Android source code (http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/widget/LinearLayout.java#LinearLayout.onMeasure%28int%2Cint%29), and in measureVertical(int widthMeasureSpec, int heightMeasureSpec), set mTotalLength = mTotalLength / 2.
For detailed information of onMeasure, visit http://developer.android.com/guide/topics/ui/custom-components.html and http://developer.android.com/reference/android/view/View.html#onMeasure(int, int).
Now the desired effect can be achieved with the ConstraintLayout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/containerFrameLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<android.support.constraint.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
<FrameLayout
android:id="#+id/containerFrameLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/guideline"
app:layout_constraintVertical_bias="1">
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
</android.support.constraint.ConstraintLayout>
Create a linear layout with two inner frames, each with .5 weight. Inside those frames, place your views, setting them to wrap_content or match_parent as appropriate.

Categories

Resources