I am following this guide to create a dropdown menu:
https://material.io/components/menus/android#exposed-dropdown-menus
So I have a TextInputLayout containing a custom subclass of MaterialAutoCompleteTextView.
I override convertSelectionToString to show a shorter version of the selected item:
#Override
protected CharSequence convertSelectionToString(Object selectedItem) {
if (selectedItem instanceof CountryInfo) {
CountryInfo country = (CountryInfo) selectedItem;
return country.toShortString();
}
return super.convertSelectionToString(selectedItem);
}
You can see below what it looks like. However what I want is for the upper text input box to wrap only the shortened text, while the dropdown expands wide enough to show its contents.
What I have
What I want (mock)
You need to compute the width of the view based on the selection of the item in it.
I guess your custom view has some method like setText, right?
And after that you can set the width to wrap content of the currently selected view. So something like this:
CharSequence selectedText = convertSelectionToString(selectedObject);
yourView.setText(selectedText);
// ConstraintLayout is mentioned here, but it can be any layout
yourView.setLayoutParams(new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
yourView.invalidate(); // might be needed to use
Related
I have a form in Android where the user has to enter their height. However, I have a spinner where the user can select the measurement units (feet/inches, and centimeters), and then an EditText where they can enter the actual value
If the user selects cm as their units, I want a single EditText. If they select feet/inches, I want to display 2 EditText fields
The way I'm currently handling this seems inefficient. I have 3 EditText fields: 1 for cm, 1 for feet, and 1 for inches
I then attach an onItemSelected listener to the spinner. If the user selects the cm option, I setVisibility(view.VISIBLE) on the cm EditText, and setVisibility(view.INVISIBLE) on both the feet and inches EditText fields. If they select the feet/inches option, I just do the visibility in reverse and show/hide the corresponding fields.
Is there a more appropriate way to handle multiple unit types that require a different number of EditText fields?
Note: my EditText are actually wrapped up in TextInputLayouts so I can get Material animations on hints, and its on the TextInputLayouts that I'm setting visibility.
The code looks something like this:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//Height unit spinner
String [] values = {"cm","ft"};
Spinner spinner = (Spinner) view.findViewById(R.id.input_height_spinner);
spinner.setOnItemSelectedListener(this);
ArrayAdapter<String> LTRadapter = new ArrayAdapter<>(this.getActivity(), android.R.layout.simple_spinner_item, values);
LTRadapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);
spinner.setAdapter(LTRadapter);
heightCMWrapper = (TextInputLayout) view.findViewById(R.id.input_height_cm_wrapper);
heightFTWrapper = (TextInputLayout) view.findViewById(R.id.input_height_ft_wrapper);
heightINWrapper = (TextInputLayout) view.findViewById(R.id.input_height_in_wrapper);
//Some other code for inflating layout etc.
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
heightCMWrapper.setVisibility(view.VISIBLE);
heightCMWrapper.setHint("Height (cm)");
heightCMWrapper.requestFocus();
heightFTWrapper.setVisibility(view.INVISIBLE);
heightFTWrapper.setHint("");
heightINWrapper.setVisibility(view.INVISIBLE);
heightINWrapper.setHint("");
break;
case 1:
heightCMWrapper.setVisibility(view.INVISIBLE);
heightCMWrapper.setHint("");
heightFTWrapper.setVisibility(view.VISIBLE);
heightFTWrapper.setHint("Feet");
heightFTWrapper.requestFocus();
heightINWrapper.setVisibility(view.VISIBLE);
heightINWrapper.setHint("Inches");
break;
}
}
You could do it using just 2 EditTexts instead of 3. Have a primary EditText for both feet and cms and a secondary one for inches if they choose feet and inches. Then you just have to set the hint of the primary EditText and toggle the secondary EditText visibility.
If you want to optimise your current solution with 3 EditText theres a few things you can do. You shouldn't have to set the hint every time they toggle the units, you should be able to just set it once in onCreateView since each unit has its own EditText. This will drop 6 lines from your method. You could take it further and make a method toggleVisibility(EditText editText, boolean visible, boolean giveFocus) but thats up to you.
As a side note you might want to use View.GONE instead of View.INVISIBLE. View.INVISIBLE just makes it not visible but it still consumes its space in the layout. So if this is your layout:
(input1) (input 2)
and you set (input1) to invisible, this is what the layout would look like:
(blank) (input2)
instead if you set (input1) to View.GONE this is what it would look like
(input 2)
I have an activity that is basically a long form of entry fields.
On each row, I want to show a TextView to serve as hint text just below each EditText and I want the TextView to remain visible at all times when the user is entering data. Unfortunately, the soft keyboard obscures the hint text and always positions itself immediately below the EditText. Is there any technique that will allow the TextView below the EditText to also be visible when the soft keyboard appears and the contents are adjusted (via windowSoftInputMode=adjustResize|adjustPan), without having the user scroll ?
Vishavjeet got me on the right track in suggesting I scrolldown to reveal the view that may be overlapped by the keyboard. Below is a function similar to what I used to solve the problem. It can be called when the EditText above the TextView receives focus:
// View targetView; // View that may be hidden by keyboard
// ScrollView scrollContainerView; // Scrollview containing hiddenView
//
void assureViewVisible (View targetView, ScrollView, scrollContainerView) {
Window rootWindow = activity.getWindow();
Rect rMyView = new Rect();
View rootview = rootWindow.getDecorView();
rootview.getWindowVisibleDisplayFrame(rMyView); // Area not taken up by keyboard
int subTextPos[] = new int[2];
targetView.getLocationInWindow(subTextPos); // Get position of targetView
int subTextHt = targetView.getHeight(); // Get bottom of target view
if ((subTextPos[1]+subTextHt) > rMyView.bottom) { // Is targetView at all obscured?
int scrollBy = (subTextPos[1]+subTextHt) - rMyView.bottom + 10; // add a small bottom margin
mMeasurementViewScrollView.smoothScrollBy(0, scrollBy); // Scroll to subtext
}
}
EDIT:
By understanding the problem more deeply, I think that you should add scroll programatically when user clicks on the Edittext. Here is the code to do that:
private final void focusOnView()
{
new Handler().post(new Runnable()
{
#Override
public void run()
{
your_scrollview.scrollTo(0, your_EditBox.getBottom());
}});
}
From my personal experience I think there is not such way to do that. The thing you can do is place the hint textview toRightOf the editext. Or Use modern Approach by using a Hint Placeholder on Edittext:
In XML, it's simply android:hint="someText"
Programatically you can use edittext.setHint(int);
pass R.string.somestring in above method.
Im using a textSwither for hide/show a part of text like this way :
txtDescription.setInAnimation(in);
txtDescription.setOutAnimation(out);
txtDescription.setFactory(new ViewSwitcher.ViewFactory() {
#Override
public View makeView() {
TextView textView = new TextView(ShopContentDetailActivity.this);
textView.setTextColor(getResources().getColor(R.color.NightDark));
return textView;
}
});
And call this method for change text :
private void changeDescription() {
if (displayShort)
txtDescription.setText(Html.fromHtml(shortDescription));
else txtDescription.setText(fullDescription);
displayShort = !displayShort;
}
My problem is when user touch textSwitcher to display text and touch again to hide it ,the height of parent view (a linear layout ) of textSwitcher remain as when text is displayed and dont change its height.
How can i update the parent view when text change ? (its ok when touch for display full text)
I resolve my problem in this way : (I think TextSwutcher change visibility of TextViews to INVISIBLE not GONE then i use this trck)
private void changeDescription() {
if (displayShort) {
txtDescription.setCurrentText(null); // this line change invisible textView visible but without text and make it height 0 and then set the second textView text
txtDescription.setText(Html.fromHtml(shortDescription));
} else txtDescription.setText(shopContent.getDescription());
displayShort = !displayShort;
}
But there is a problem that is not so important for my case: when you hide a part of text its blink.
if i resolve it i will update my answer.
I have extended EditTextPreference, but the Dialog Message won't display. This happens if I add the dialogMessage programatically or in the the preferences.xml.
Here is my onBindDialogView:
AutoCompleteTextView editText = mEditText;
editText.setText(getText());
ViewParent oldParent = editText.getParent();
if (oldParent != view) {
if (oldParent != null) {
((ViewGroup) oldParent).removeView(editText);
}
onAddEditTextToDialogView(view, editText);
}
Is the dialog message really absent? It's probably there but its text color might make it less (or not) visible. (Or try to dismiss software keyboard). Try experimenting with dialog messages having a number of "\n" characters and see if that affects dialog layout. If so, it means the dialog message is actually there but camouflaged too well.
EditTextPreference brings a text view (in the preference_dialog_edittext.xml) that replaces the existing one (in the alert_dialog.xml) for the dialog message, but unfortunately with different text style, which might cause a visibility problem under certain themes. Even their sizes are different.
One solution might be to obtain the text color and size from the original text view to be replaced and apply them to the new one, but I would suggest retaining the original text view instead, because it's more likely to be visually consistent if there are any future UI changes. Try adding the following overrides
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
builder.setMessage(getDialogMessage()); // bring back the original text view
}
protected void onAddEditTextToDialogView(View dialogView, EditText editText) {
int id = getContext().getResources().getIdentifier("edittext_container", "id", "android");
ViewGroup container = (ViewGroup) dialogView.findViewById(id);
container.removeAllViews(); // remove the new text view
super.onAddEditTextToDialogView(dialogView, editText);
}
If you think the dialog message and the edittext view is too far apart, they can be brought together a little closer by adding another override:
protected void showDialog(Bundle state) {
super.showDialog(state);
int id = getContext().getResources().getIdentifier("message", "id", "android");
TextView message = (TextView) getDialog().findViewById(id);
message.setPadding(message.getPaddingLeft(), message.getPaddingTop(), message.getPaddingRight(), 0);
}
and add the following line in the onAddEditTextToDialogView method after calling removeAllViews:
container.setPadding(container.getPaddingLeft(), 0, container.getPaddingRight(), container.getPaddingBottom());
I have a problem... have been thinking about it for a while now and been looking on line and still haven't come up with a clear explanation...
I have a number of textviews and have set onClickListeners to each of them.. and when the user clicks on one of them I want them to have the ability to change the text to another set of string array options which I have created progammatically. When the user selects an option the text should change to the option they choose. (I.e. TextView was A now it is B. hope this makes sense.. anyway... )
The current solution was to set a OnClickListener to every TextView and when someone pressed it an individual dialog showed. But I found that if I do this the code would be so long it would take an eternity to code so am hoping someone has a more elegant way of coding such a long process =(
So I guess my question would be... 1) is there a way I can find out which text view was pressed and then change the text of that TextView being pressed within a single method? to save me having to code 1000 alert dialogs...
http://i.stack.imgur.com/LRJGz.png
I would advise you to use a grid view.
You can see which textview was pressed like this:
gridView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v, int position,
long id) {
//get id
switch (v.getId()) {
case R.id.textView1: ...
}
});
One of the ways to do what you want is to use the text view setTag() and getTag() methods.
On init of a text view use the setTag() to set some value to identify the view.
In the on click event use the getTag() on the view argument to know which view was clicked.
I would suggest holding the textviews in an array, like so:
TextView[] textViewArray = new TextView[textViewCount];
Then using a for loop assign each one a tag of integer - it's position
textViewArray.setTag(i)
And add an onClickListener to each one, again using a for loop:
textviewArray[i].setOnClickListener(etc...)
Then when one is clicked, you can use get the position of view that was clicked. This will require a custom method inside of your:
textviewArray.setOnClickListener(new customOnClickListener())
Where your customOnClickListner is like this:
private class customOnClickListener implements CompoundButton.{
public void OnClick(View view){
int position = (Integer) view.getTag()
///Do more code here - your processing
}
}
Hope that makes sense :)
For your for loops, you could use for(i = 0, i
Use set id for all text, where set the id positive integer(distinct), and then have one on view click listener(set it all) where u catch all text view clicks(downcast view with textview) and in side it put a switch case where you handle clicks on which text view is clicked.
You have to set "onClickListner" on all of of your textview.
For Saving some length of code i would suggest you create a function of your dialogbox, and give some int parameter to it, which would be directly called by the clickListener of textview,
Like ,
int i=0;
......
textView1 = (TextView)findViewById(R.id.yourtextview1);
textView2 = (TextView)findViewById(R.id.yourtextview2);
......
......
// and so on, for your all textviews
#Override
public void onClick(View view) {
if (view.equals(textView1)) {
i = 1;
CustomDialog(i);
}
//Similarly for all your textViews..
..........
Make A function CustomDialog Like
public void CustomDialog(int i){
if(i==1){
//Do something
}
}