Merge "Adding text input mode to TimePicker."

This commit is contained in:
TreeHugger Robot
2017-02-06 21:35:21 +00:00
committed by Android (Google) Code Review
19 changed files with 712 additions and 155 deletions

View File

@@ -144,15 +144,26 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener,
/* do nothing */
}
@Override
public void show() {
super.show();
getButton(BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mTimePicker.validateInput()) {
if (mTimeSetListener != null) {
mTimeSetListener.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
mTimePicker.getCurrentMinute());
}
dismiss();
}
}
});
}
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case BUTTON_POSITIVE:
if (mTimeSetListener != null) {
mTimeSetListener.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
mTimePicker.getCurrentMinute());
}
break;
case BUTTON_NEGATIVE:
cancel();
break;

View File

@@ -0,0 +1,249 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.widget;
import android.content.Context;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.view.View;
import com.android.internal.R;
/**
* View to show text input based time picker with hour and minute fields and an optional AM/PM
* spinner.
*
* @hide
*/
public class TextInputTimePickerView extends RelativeLayout {
public static final int HOURS = 0;
public static final int MINUTES = 1;
public static final int AMPM = 2;
private static final int AM = 0;
private static final int PM = 1;
private final EditText mHourEditText;
private final EditText mMinuteEditText;
private final TextView mInputSeparatorView;
private final Spinner mAmPmSpinner;
private final TextView mErrorLabel;
private final TextView mHourLabel;
private final TextView mMinuteLabel;
private boolean mIs24Hour;
private boolean mHourFormatStartsAtZero;
private OnValueTypedListener mListener;
private boolean mErrorShowing;
interface OnValueTypedListener {
void onValueChanged(int inputType, int newValue);
}
public TextInputTimePickerView(Context context) {
this(context, null);
}
public TextInputTimePickerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TextInputTimePickerView(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs, defStyle, 0);
}
public TextInputTimePickerView(Context context, AttributeSet attrs, int defStyle,
int defStyleRes) {
super(context, attrs, defStyle, defStyleRes);
inflate(context, R.layout.time_picker_text_input_material, this);
mHourEditText = (EditText) findViewById(R.id.input_hour);
mMinuteEditText = (EditText) findViewById(R.id.input_minute);
mInputSeparatorView = (TextView) findViewById(R.id.input_separator);
mErrorLabel = (TextView) findViewById(R.id.label_error);
mHourLabel = (TextView) findViewById(R.id.label_hour);
mMinuteLabel = (TextView) findViewById(R.id.label_minute);
mHourEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void afterTextChanged(Editable editable) {
parseAndSetHourInternal(editable.toString());
}
});
mMinuteEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void afterTextChanged(Editable editable) {
parseAndSetMinuteInternal(editable.toString());
}
});
mAmPmSpinner = (Spinner) findViewById(R.id.am_pm_spinner);
final String[] amPmStrings = TimePicker.getAmPmStrings(context);
ArrayAdapter<CharSequence> adapter =
new ArrayAdapter<CharSequence>(context, R.layout.simple_spinner_dropdown_item);
adapter.add(TimePickerClockDelegate.obtainVerbatim(amPmStrings[0]));
adapter.add(TimePickerClockDelegate.obtainVerbatim(amPmStrings[1]));
mAmPmSpinner.setAdapter(adapter);
mAmPmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position,
long id) {
if (position == 0) {
mListener.onValueChanged(AMPM, AM);
} else {
mListener.onValueChanged(AMPM, PM);
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {}
});
}
void setListener(OnValueTypedListener listener) {
mListener = listener;
}
void setHourFormat(int maxCharLength) {
mHourEditText.setFilters(new InputFilter[] {
new InputFilter.LengthFilter(maxCharLength)});
mMinuteEditText.setFilters(new InputFilter[] {
new InputFilter.LengthFilter(maxCharLength)});
}
boolean validateInput() {
final boolean inputValid = parseAndSetHourInternal(mHourEditText.getText().toString())
&& parseAndSetMinuteInternal(mMinuteEditText.getText().toString());
setError(!inputValid);
return inputValid;
}
void updateSeparator(String separatorText) {
mInputSeparatorView.setText(separatorText);
}
private void setError(boolean enabled) {
mErrorShowing = enabled;
mErrorLabel.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
mHourLabel.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE);
mMinuteLabel.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE);
}
/**
* Computes the display value and updates the text of the view.
* <p>
* This method should be called whenever the current value or display
* properties (leading zeroes, max digits) change.
*/
void updateTextInputValues(int localizedHour, int minute, int amOrPm, boolean is24Hour,
boolean hourFormatStartsAtZero) {
final String format = "%d";
mIs24Hour = is24Hour;
mHourFormatStartsAtZero = hourFormatStartsAtZero;
mAmPmSpinner.setVisibility(is24Hour ? View.INVISIBLE : View.VISIBLE);
mHourEditText.setText(String.format(format, localizedHour));
mMinuteEditText.setText(String.format(format, minute));
if (amOrPm == AM) {
mAmPmSpinner.setSelection(0);
} else {
mAmPmSpinner.setSelection(1);
}
if (mErrorShowing) {
validateInput();
}
}
private boolean parseAndSetHourInternal(String input) {
try {
final int hour = Integer.parseInt(input);
if (!isValidLocalizedHour(hour)) {
final int minHour = mHourFormatStartsAtZero ? 0 : 1;
final int maxHour = mIs24Hour ? 23 : 11 + minHour;
mListener.onValueChanged(HOURS, getHourOfDayFromLocalizedHour(
MathUtils.constrain(hour, minHour, maxHour)));
return false;
}
mListener.onValueChanged(HOURS, getHourOfDayFromLocalizedHour(hour));
return true;
} catch (NumberFormatException e) {
// Do nothing since we cannot parse the input.
return false;
}
}
private boolean parseAndSetMinuteInternal(String input) {
try {
final int minutes = Integer.parseInt(input);
if (minutes < 0 || minutes > 59) {
mListener.onValueChanged(MINUTES, MathUtils.constrain(minutes, 0, 59));
return false;
}
mListener.onValueChanged(MINUTES, minutes);
return true;
} catch (NumberFormatException e) {
// Do nothing since we cannot parse the input.
return false;
}
}
private boolean isValidLocalizedHour(int localizedHour) {
final int minHour = mHourFormatStartsAtZero ? 0 : 1;
final int maxHour = (mIs24Hour ? 23 : 11) + minHour;
return localizedHour >= minHour && localizedHour <= maxHour;
}
private int getHourOfDayFromLocalizedHour(int localizedHour) {
int hourOfDay = localizedHour;
if (mIs24Hour) {
if (!mHourFormatStartsAtZero && localizedHour == 24) {
hourOfDay = 0;
}
} else {
if (!mHourFormatStartsAtZero && localizedHour == 12) {
hourOfDay = 0;
}
if (mAmPmSpinner.getSelectedItemPosition() == 1) {
hourOfDay += 12;
}
}
return hourOfDay;
}
}

View File

@@ -278,6 +278,16 @@ public class TimePicker extends FrameLayout {
return mDelegate.getBaseline();
}
/**
* Validates whether current input by the user is a valid time based on the locale. TimePicker
* will show an error message to the user if the time is not valid.
*
* @return {@code true} if the input is valid, {@code false} otherwise
*/
public boolean validateInput() {
return mDelegate.validateInput();
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
@@ -341,6 +351,8 @@ public class TimePicker extends FrameLayout {
void setIs24Hour(boolean is24Hour);
boolean is24Hour();
boolean validateInput();
void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
void setEnabled(boolean enabled);

View File

@@ -16,12 +16,14 @@
package android.widget;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.icu.text.DecimalFormatSymbols;
import android.os.Parcelable;
import android.text.SpannableStringBuilder;
import android.text.format.DateFormat;
@@ -40,11 +42,14 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.RadialTimePickerView.OnValueSelectedListener;
import android.widget.TextInputTimePickerView.OnValueTypedListener;
import com.android.internal.R;
import com.android.internal.widget.NumericTextView;
import com.android.internal.widget.NumericTextView.OnValueChangedListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Calendar;
/**
@@ -58,6 +63,13 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
*/
private static final long DELAY_COMMIT_MILLIS = 2000;
@IntDef({FROM_EXTERNAL_API, FROM_RADIAL_PICKER, FROM_INPUT_PICKER})
@Retention(RetentionPolicy.SOURCE)
private @interface ChangeSource {}
private static final int FROM_EXTERNAL_API = 0;
private static final int FROM_RADIAL_PICKER = 1;
private static final int FROM_INPUT_PICKER = 2;
// Index used by RadialPickerLayout
private static final int HOUR_INDEX = RadialTimePickerView.HOURS;
private static final int MINUTE_INDEX = RadialTimePickerView.MINUTES;
@@ -78,6 +90,15 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
private final RadialTimePickerView mRadialTimePickerView;
private final TextView mSeparatorView;
private boolean mRadialPickerModeEnabled = true;
private final ImageButton mRadialTimePickerModeButton;
private final String mRadialTimePickerModeEnabledDescription;
private final String mTextInputPickerModeEnabledDescription;
private final View mRadialTimePickerHeader;
private final View mTextInputPickerHeader;
private final TextInputTimePickerView mTextInputPickerView;
private final Calendar mTempCalendar;
// Accessibility strings.
@@ -116,8 +137,8 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout,
R.layout.time_picker_material);
final View mainView = inflater.inflate(layoutResourceId, delegator);
final View headerView = mainView.findViewById(R.id.time_header);
headerView.setOnTouchListener(new NearestTouchDelegate());
mRadialTimePickerHeader = mainView.findViewById(R.id.time_header);
mRadialTimePickerHeader.setOnTouchListener(new NearestTouchDelegate());
// Set up hour/minute labels.
mHourView = (NumericTextView) mainView.findViewById(R.id.hours);
@@ -170,6 +191,8 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
headerTextColor = a.getColorStateList(R.styleable.TimePicker_headerTextColor);
}
mTextInputPickerHeader = mainView.findViewById(R.id.input_header);
if (headerTextColor != null) {
mHourView.setTextColor(headerTextColor);
mSeparatorView.setTextColor(headerTextColor);
@@ -180,7 +203,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
// Set up header background, if available.
if (a.hasValueOrEmpty(R.styleable.TimePicker_headerBackground)) {
headerView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
mRadialTimePickerHeader.setBackground(a.getDrawable(
R.styleable.TimePicker_headerBackground));
mTextInputPickerHeader.setBackground(a.getDrawable(
R.styleable.TimePicker_headerBackground));
}
a.recycle();
@@ -189,6 +215,22 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
mRadialTimePickerView.applyAttributes(attrs, defStyleAttr, defStyleRes);
mRadialTimePickerView.setOnValueSelectedListener(mOnValueSelectedListener);
mTextInputPickerView = (TextInputTimePickerView) mainView.findViewById(R.id.input_mode);
mTextInputPickerView.setListener(mOnValueTypedListener);
mRadialTimePickerModeButton =
(ImageButton) mainView.findViewById(R.id.toggle_mode);
mRadialTimePickerModeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggleRadialPickerMode();
}
});
mRadialTimePickerModeEnabledDescription = context.getResources().getString(
R.string.time_picker_radial_mode_description);
mTextInputPickerModeEnabledDescription = context.getResources().getString(
R.string.time_picker_text_input_mode_description);
mAllowAutoAdvance = true;
updateHourFormat();
@@ -200,6 +242,34 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
initialize(currentHour, currentMinute, mIs24Hour, HOUR_INDEX);
}
private void toggleRadialPickerMode() {
if (mRadialPickerModeEnabled) {
mRadialTimePickerView.setVisibility(View.GONE);
mRadialTimePickerHeader.setVisibility(View.GONE);
mTextInputPickerHeader.setVisibility(View.VISIBLE);
mTextInputPickerView.setVisibility(View.VISIBLE);
mRadialTimePickerModeButton.setImageResource(R.drawable.btn_event_material);
mRadialTimePickerModeButton.setContentDescription(
mRadialTimePickerModeEnabledDescription);
mRadialPickerModeEnabled = false;
} else {
mRadialTimePickerView.setVisibility(View.VISIBLE);
mRadialTimePickerHeader.setVisibility(View.VISIBLE);
mTextInputPickerHeader.setVisibility(View.GONE);
mTextInputPickerView.setVisibility(View.GONE);
mRadialTimePickerModeButton.setImageResource(R.drawable.btn_keyboard_key_material);
mRadialTimePickerModeButton.setContentDescription(
mTextInputPickerModeEnabledDescription);
updateTextInputPicker();
mRadialPickerModeEnabled = true;
}
}
@Override
public boolean validateInput() {
return mTextInputPickerView.validateInput();
}
/**
* Ensures that a TextView is wide enough to contain its text without
* wrapping or clipping. Measures the specified view and sets the minimum
@@ -249,9 +319,16 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
final int maxHour = (mIs24Hour ? 23 : 11) + minHour;
mHourView.setRange(minHour, maxHour);
mHourView.setShowLeadingZeroes(mHourFormatShowLeadingZero);
final String[] digits = DecimalFormatSymbols.getInstance(mLocale).getDigitStrings();
int maxCharLength = 0;
for (int i = 0; i < 10; i++) {
maxCharLength = Math.max(maxCharLength, digits[i].length());
}
mTextInputPickerView.setHourFormat(maxCharLength * 2);
}
private static final CharSequence obtainVerbatim(String text) {
static final CharSequence obtainVerbatim(String text) {
return new SpannableStringBuilder().append(text,
new TtsSpan.VerbatimBuilder(text).build(), 0);
}
@@ -333,10 +410,16 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
updateHeaderSeparator();
updateHeaderMinute(mCurrentMinute, false);
updateRadialPicker(index);
updateTextInputPicker();
mDelegator.invalidate();
}
private void updateTextInputPicker() {
mTextInputPickerView.updateTextInputValues(getLocalizedHour(mCurrentHour), mCurrentMinute,
mCurrentHour < 12 ? AM : PM, mIs24Hour, mHourFormatStartsAtZero);
}
private void updateRadialPicker(int index) {
mRadialTimePickerView.initialize(mCurrentHour, mCurrentMinute, mIs24Hour);
setCurrentItemShowing(index, false, true);
@@ -381,10 +464,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
*/
@Override
public void setHour(int hour) {
setHourInternal(hour, false, true);
setHourInternal(hour, FROM_EXTERNAL_API, true);
}
private void setHourInternal(int hour, boolean isFromPicker, boolean announce) {
private void setHourInternal(int hour, @ChangeSource int source, boolean announce) {
if (mCurrentHour == hour) {
return;
}
@@ -393,10 +476,13 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
updateHeaderHour(hour, announce);
updateHeaderAmPm();
if (!isFromPicker) {
if (source != FROM_RADIAL_PICKER) {
mRadialTimePickerView.setCurrentHour(hour);
mRadialTimePickerView.setAmOrPm(hour < 12 ? AM : PM);
}
if (source != FROM_INPUT_PICKER) {
updateTextInputPicker();
}
mDelegator.invalidate();
onTimeChanged();
@@ -424,10 +510,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
*/
@Override
public void setMinute(int minute) {
setMinuteInternal(minute, false);
setMinuteInternal(minute, FROM_EXTERNAL_API);
}
private void setMinuteInternal(int minute, boolean isFromPicker) {
private void setMinuteInternal(int minute, @ChangeSource int source) {
if (mCurrentMinute == minute) {
return;
}
@@ -435,9 +521,12 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
mCurrentMinute = minute;
updateHeaderMinute(minute, true);
if (!isFromPicker) {
if (source != FROM_RADIAL_PICKER) {
mRadialTimePickerView.setCurrentMinute(minute);
}
if (source != FROM_INPUT_PICKER) {
updateTextInputPicker();
}
mDelegator.invalidate();
onTimeChanged();
@@ -661,6 +750,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1));
}
mSeparatorView.setText(separatorText);
mTextInputPickerView.updateSeparator(separatorText);
}
static private int lastIndexOfAny(String str, char[] any) {
@@ -712,7 +802,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
if (mRadialTimePickerView.setAmOrPm(amOrPm)) {
mCurrentHour = getHour();
updateTextInputPicker();
if (mOnTimeChangedListener != null) {
mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
}
@@ -726,7 +816,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
switch (pickerType) {
case RadialTimePickerView.HOURS:
final boolean isTransition = mAllowAutoAdvance && autoAdvance;
setHourInternal(newValue, true, !isTransition);
setHourInternal(newValue, FROM_RADIAL_PICKER, !isTransition);
if (isTransition) {
setCurrentItemShowing(MINUTE_INDEX, true, false);
@@ -735,7 +825,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
}
break;
case RadialTimePickerView.MINUTES:
setMinuteInternal(newValue, true);
setMinuteInternal(newValue, FROM_RADIAL_PICKER);
break;
}
@@ -745,6 +835,23 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
}
};
private final OnValueTypedListener mOnValueTypedListener = new OnValueTypedListener() {
@Override
public void onValueChanged(int pickerType, int newValue) {
switch (pickerType) {
case TextInputTimePickerView.HOURS:
setHourInternal(newValue, FROM_INPUT_PICKER, false);
break;
case TextInputTimePickerView.MINUTES:
setMinuteInternal(newValue, FROM_INPUT_PICKER);
break;
case TextInputTimePickerView.AMPM:
setAmOrPm(newValue);
break;
}
}
};
/** Listener for keyboard interaction. */
private final OnValueChangedListener mDigitEnteredListener = new OnValueChangedListener() {
@Override

View File

@@ -219,6 +219,11 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
}
}
@Override
public boolean validateInput() {
return true;
}
private void getHourFormatData() {
final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
(mIs24HourView) ? "Hm" : "hm");

View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
/**
* Implementation of {@link android.widget.Space} that uses normal View drawing
* rather than a no-op. Useful for dialogs and other places where the base View
* class is too greedy when measured with AT_MOST.
*/
public final class DrawingSpace extends View {
public DrawingSpace(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public DrawingSpace(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public DrawingSpace(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DrawingSpace(Context context) {
this(context, null);
}
/**
* Compare to: {@link View#getDefaultSize(int, int)}
* <p>
* If mode is AT_MOST, return the child size instead of the parent size
* (unless it is too big).
*/
private static int getDefaultSizeNonGreedy(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
result = Math.min(size, specSize);
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
getDefaultSizeNonGreedy(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSizeNonGreedy(getSuggestedMinimumHeight(), heightMeasureSpec));
}
}