I have been facing a problem with a custom view I coded. Basically, I have a custom view which handles different part of a seat, with different states. I managed to code it, and it was working perfectly.
Since the app was getting a bit slow, I decided to crop the images: instead of taking the whole screen with transparency, it only takes a part of the screen (I didn’t do the graphics).
But since I have cropped the images, they are messing up my layout. And I have no idea why, because when I replace my custom view with an ImageView, it’s fitting perfectly!
It looks like my view is ignoring “wrap_content”, and takes more than what it needs. Setting the value to “match_parent” didn’t work either.
I tried cropping my images again, I thought it was because of the size, but it is not. I tried overriding the onMeasure method, no change. And I don’t want to hardcode the size, because I feel like it’s a bad thing to do.
Why is this happening, and how do I fix it?
Here are some of my code snippets.
fragment_lumbar.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- Main screen -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background_logo">
<!-- Left menu with control buttons -->
<RelativeLayout
android:id="@+id/lumbar_control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="61dp">
<ImageButton
android:id="@+id/button_arrowup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:background="@drawable/arrowup_button"
android:contentDescription="@string/moveup_lumbar" />
<!-- Plus/minus buttons -->
<ImageButton
android:id="@+id/button_plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/button_arrowup"
android:layout_centerVertical="true"
android:background="@drawable/plus_button"
android:contentDescription="@string/inflate_lumbar" />
<ImageButton
android:id="@+id/button_minus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/button_arrowup"
android:layout_toRightOf="@id/button_plus"
android:layout_marginLeft="90dp"
android:background="@drawable/minus_button"
android:contentDescription="@string/deflate_back" />
<!-- Arrown down button -->
<ImageButton
android:id="@+id/button_arrowdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/button_plus"
android:layout_centerHorizontal="true"
android:background="@drawable/arrowdown_button"
android:contentDescription="@string/movedown_lumbar" />
<!-- Home button -->
</RelativeLayout>
<!-- Right panel -->
<!-- Screen -->
<FrameLayout
android:id="@+id/center_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="7dp"
android:layout_toRightOf="@id/lumbar_control"
android:background="@drawable/screen" >
<TextView
android:id="@+id/text_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_margin="10dp"
android:textSize="32sp"
android:gravity="center"
android:textColor="@color/white"
android:text="hello"/>
<!-- Seat screen -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/seat"
android:src="@drawable/lumbar_control_seat"/>
<com.SeatStateView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/lumbar_seat_state"
android:id="@+id/lumbar_seat_state" />
</FrameLayout>
<!-- Home button -->
<Button
android:id="@+id/button_home"
android:layout_width="332dp"
android:layout_height="44dp"
android:layout_centerHorizontal="true"
android:background="@android:color/transparent"
android:layout_alignParentBottom="true"
android:contentDescription="@string/home_button"/>
</RelativeLayout>
lumbar_seat_state:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/lumbar_seat_state_1" />
<item android:drawable="@drawable/lumbar_seat_state_2" />
<item android:drawable="@drawable/lumbar_seat_state_3" />
</layer-list>
lumbar_seat_state_1:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com">
<item android:drawable="@drawable/lumbar_inflate_1"
app:state_inflating="true" />
<item android:drawable="@drawable/lumbar_deflate_1"
app:state_deflating="true" />
<item android:drawable="@drawable/lumbar_idle_1" />
</selector>
SeatStateView.java:
public class SeatStateView extends View {
/* Custom states */
private static final int[] INFLATING_STATE_SET = {
R.attr.state_inflating
};
private static final int[] DEFLATING_STATE_SET = {
R.attr.state_deflating
};
private static final int[] HEATING_STATE_SET = {
R.attr.state_heating
};
private static final int[] COOLING_STATE_SET = {
R.attr.state_cooling
};
/* Inflate/Deflate state */
private boolean isInflating = false;
private boolean isDeflating = false;
/* Heating state */
private boolean isHeating = false;
private boolean isCooling = false;
public SeatStateView(Context context, AttributeSet attrs) {
super(context, attrs);
}
// Overrides the onCreateDrawable to add our custom states
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 2);
// Checking the states
if(isInflating) {
mergeDrawableStates(drawableState, INFLATING_STATE_SET);
}
if(isDeflating) {
mergeDrawableStates(drawableState, DEFLATING_STATE_SET);
}
if(isHeating) {
mergeDrawableStates(drawableState, HEATING_STATE_SET);
}
if(isCooling) {
mergeDrawableStates(drawableState, COOLING_STATE_SET);
}
return drawableState;
}
@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth, parentHeight);
}
/**
* Clears all the states and set to idle.
*/
public void setIdle() {
isInflating = isDeflating = false;
// Refresh view
refreshDrawableState();
}
public void setInflating(boolean state) {
if(isInflating != state) {
// Update all the states
isInflating = state;
if(isInflating) {
isDeflating = false;
}
// Refresh view
refreshDrawableState();
}
}
public void setDeflating(boolean state) {
if(isDeflating != state) {
// Update all the states
isDeflating = state;
if(isDeflating) {
isInflating = false;
}
// Refresh view
refreshDrawableState();
}
}
public void setHeating(boolean state) {
if(isHeating != state) {
isHeating = state;
if(state) {
isCooling = false;
}
// Refresh view
refreshDrawableState();
}
}
public void setCooling(boolean state) {
if(isCooling != state) {
isCooling = state;
if(state) {
isHeating = false;
}
// Refresh view
refreshDrawableState();
}
}
}
Well, just searched more. Found out that I didn’t override my onMeasure method correctly. Here is my new implementation if someone stumbles upon this question:
SeatStateView.java:
@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec){
// Background image’s size
int desiredWidth = getBackground().getIntrinsicWidth();
int desiredHeight = getBackground().getIntrinsicHeight();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// Final size
int width, height;
// Set width depending on mode
if(widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
}
else if(widthMode == MeasureSpec.AT_MOST) {
width = Math.min(desiredWidth, widthSize);
}
else {
width = desiredWidth;
}
// Set height depending on mode
if(heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
}
else if(widthMode == MeasureSpec.AT_MOST) {
height = Math.min(desiredHeight, heightSize);
}
else {
height = desiredHeight;
}
// Finally, set dimension
setMeasuredDimension(width, height);
}
For details, check this page: onMeasure custom view explanation.
Sorry for bothering you.