Setting PopupWindow to have a maximum height - android

I inflate a ListView inside a PopupWindow and I want the popup to behave like this:
wrap the listview when its height is < x
set the popup's height = x when the listiew's height > x (the listview being scrollable)
the popup is showed by a TextWatcher attached to an EditText, as it is meant to show search suggestions.
The adapter underlying the ListView is managed by a custom Loader, started whenever the user has typed something in the EditText.
I tried to override onMeasure() on the listview and pass the measured height to a listener which calls PopupWindow.update(), but this creates a loop since the latter would end up calling the first.
With the following code, the popup wraps the contained ListView as wanted, but the height is not restricted to any value. I need a solution to restrict the height to a maximum value, let's say 300dp.
etFiltro.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
/...
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
Log.i("loader","text changed");
filtro = String.valueOf(charSequence);
getSupportLoaderManager().restartLoader(0,null,loaderCallbacks);
if (popupWindow.isShowing()) popupWindow.dismiss();
}
#Override
public void afterTextChanged(Editable editable) {
popupWindow.showAsDropDown(etFiltro);
}
});
private LoaderManager.LoaderCallbacks<Cursor> loaderCallbacks = new LoaderManager.LoaderCallbacks<Cursor>() {
MyListView suggestionList;
SimpleCursorAdapter adapter;
int mHeight;
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
SuggestionLoader loader = new SuggestionLoader(MainActivity.this, databaseConnector, filtro);
loader.setUpdateThrottle(500);
View view = ((LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE)).inflate(R.layout.popup_window,null);
suggestionList = (MyListView) view.findViewById(R.id.suggestionList);
adapter = new SimpleCursorAdapter(MainActivity.this,android.R.layout.simple_list_item_1,null,
new String[]{"note"},new int[]{android.R.id.text1},0);
suggestionList.setAdapter(adapter);
suggestionList.setEmptyView(view.findViewById(R.id.tvNoSuggestions));
//ensure previous popup is dismissed
if (popupWindow!=null) popupWindow.dismiss();
popupWindow = new PopupWindow(view,etFiltro.getWidth(),0);
popupWindow.setWindowLayoutMode(0,WindowManager.LayoutParams.WRAP_CONTENT);
popupWindow.setAnimationStyle(0);//0 = no animation; -1 = default animation
Log.i("loader","onCreateLoader");
return loader;
}
#Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
adapter.changeCursor(cursor);
Log.i("loader","onLoadFinished");
}
#Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
Log.i("loader", "onLoaderReset");
adapter.changeCursor(null);
}
};

I have found the solution by myself. For anyone who will ever stumble upon this problem, the answer is to override the method onMeasure() of the ListView like this:
public class MyListView extends ListView {
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//set your custom height. AT_MOST means it can be as tall as needed,
//up to the specified size.
int height = MeasureSpec.makeMeasureSpec(300,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec,height);
}
}

Related

How to create a a expandable view in android?

I want to expand the view on clicking the + button and this + sign changes to - when the view expanded.Again when i click the - button the view should be shrink.
Inside the view i have some TextView field.Please anyone suggest me.I am new to android.
This answer solves the question.
public class ExpandableTextView extends TextView implements OnClickListener
{
private static final int MAX_LINES = 5;
private int currentMaxLines = Integer.MAX_VALUE;
public ExpandableTextView(Context context)
{
super(context);
setOnClickListener(this);
}
public ExpandableTextView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
setOnClickListener(this);
}
public ExpandableTextView(Context context, AttributeSet attrs)
{
super(context, attrs);
setOnClickListener(this);
}
#Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter)
{
/* If text longer than MAX_LINES set DrawableBottom - I'm using '...' icon */
post(new Runnable()
{
public void run()
{
if (getLineCount()>MAX_LINES)
setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, R.drawable.icon_more_text);
else
setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
setMaxLines(MAX_LINES);
}
});
}
#Override
public void setMaxLines(int maxLines)
{
currentMaxLines = maxLines;
super.setMaxLines(maxLines);
}
/* Custom method because standard getMaxLines() requires API > 16 */
public int getMyMaxLines()
{
return currentMaxLines;
}
#Override
public void onClick(View v)
{
/* Toggle between expanded collapsed states */
if (getMyMaxLines() == Integer.MAX_VALUE)
setMaxLines(MAX_LINES);
else
setMaxLines(Integer.MAX_VALUE);
}
}
You can visible or gone particular layout on button click event at runtime like below code:
findViewById(R.id.yourButtonId).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mainView.setVisibility(View.GONE);
}
});

RecyclerView childs, how to connect 2 or more views

Hello I have a RecyclerView, and I use HorizontalScrollView in children of theRecyclerView`. I need to scroll all of them when I scrolling one. Anyone can tell me How to make that,thanks!
I'm going to assume you're doing something similar to what I did where you have some sort of tabular view and the HorizontalScrollViews are all the same width.
This is how I did it:
First I made a customization to the HorizontalScrollView so I could get event notifications when the view was swiped:
public class HorizontalScrollView extends android.widget.HorizontalScrollView {
private OnScrollListener mListener;
public HorizontalScrollView(Context context) {
super(context);
}
public HorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setOnScrollListener(OnScrollListener listener) {
mListener = listener;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mListener != null) {
mListener.onScrollChanged(this, l);
}
}
public interface OnScrollListener {
public void onScrollChanged(View view, int scrollX);
}
}
Then when I create the ViewHolders I add a listener that will set all the views to the same scrollX:
view.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollChanged(View scrollView, int scrollX) {
for (int i = 0; i < recyclerView.getChildCount(); i++) {
View child = recyclerView.getChildAt(i);
if (child instanceof HorizontalScrollView && child != scrollView) {
HorizontalScrollView scrollView2 = (HorizontalScrollView) child;
if (scrollView2.getScrollX() != scrollX) {
scrollView2.setScrollX(scrollX);
}
}
}
}
});
This code is just for illustration purposes; don't expect to copy/paste this and have it work.
I'm assuming that your ViewHolder can get a reference to your RecyclerView to access all the current list items.
This code had some problems, when you swiped one view then swiped another view while everything was still moving from the first swipe, things could get out of sync. But this is a basic idea to get you started in a positive direction.

Android ListView full scroll to the top while using clipToPadding="false"

I have this litle custom ListView witch is actually a 'ListView' with some call backs. When i dynamically change padding when starting the Activity (Height of the Toolbar), Items dose not respond to padding, and i have to scroll to the top some how.
This is my ListWiew.
<somesustom.cutom.ObservableListView
android:id="#+id/listview"
android:clipToPadding="false"
android:theme="#style/ListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
So the question is How to scroll fully to the top from code?
So i have found the solution
first i deleted android:clipToPadding="false" from xml.
then i configured my ObservableListView like this
public class ObservableListView extends ListView {
private boolean clipPadding = true;
public ObservableListView(Context context) {
this(context, null, 0);
setScrollListner();
}
public ObservableListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
setScrollListner();
}
public ObservableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setScrollListner();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(clipPadding) /// this is the trick, if clip to padding is true, then set selection 0
setSelection(0);
}
public void setScrollListner()
{
clipPadding = true;
super.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(clipPadding && scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
setClipToPadding(false);
clipPadding = false; // if ii begin to scroll clipPadding
// turns to false and onMeasure set selection not being called.
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
}
Not sure if this method wouldn't work if i change padding on button click i guess. But in my case, where i set padding from ViewTreeObserver.OnGlobalLayoutListener this works quite well.
You can try one of two things or both:
listview.smoothScrollToPosition(0);
or
listview.setSelection(0):

Spinner - focus to first item

I use dropdown spinner with cursor adapter. It contains e.g 1 - 100 items.
I select e.g. item 50. Item is selected. Next time when I open spinner first visible row is item 50. How can I achieve that when I open spinner it will focus to first item/first visible item will be item 1?
I mean like autoscroll up in the list, so first visible item in dropdown is 1st one and not selected one.
You can make the Spinner do what you want by extending it and overriding the two methods that are responsible for setup/showing the list of values:
public class CustomSpinnerSelection extends Spinner {
private boolean mToggleFlag = true;
public CustomSpinnerSelection(Context context, AttributeSet attrs,
int defStyle, int mode) {
super(context, attrs, defStyle, mode);
}
public CustomSpinnerSelection(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public CustomSpinnerSelection(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSpinnerSelection(Context context, int mode) {
super(context, mode);
}
public CustomSpinnerSelection(Context context) {
super(context);
}
#Override
public int getSelectedItemPosition() {
// this toggle is required because this method will get called in other
// places too, the most important being called for the
// OnItemSelectedListener
if (!mToggleFlag) {
return 0; // get us to the first element
}
return super.getSelectedItemPosition();
}
#Override
public boolean performClick() {
// this method shows the list of elements from which to select one.
// we have to make the getSelectedItemPosition to return 0 so you can
// fool the Spinner and let it think that the selected item is the first
// element
mToggleFlag = false;
boolean result = super.performClick();
mToggleFlag = true;
return result;
}
}
It should work just fine for what you want to do.
You can set the selection of a Spinner to the first item like this:
yourspinner.setSelection(0);
You might want to do this in the onStart() method.
This short of code will do the work for you.
int prevSelection=0;
spSunFrom = (Spinner) findViewById(R.id.spTimeFromSun);
spSunFrom.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
prevSelection = spSunFrom.getSelectedItemPosition();
spSunFrom.setSelection(0);
return false;
}
});
spSunFrom.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
if(arg2==0)
spSunFrom.setSelection(prevSelection);
prevSelection = arg2;
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
spSunFrom.setSelection(prevSelection);
}
});

Android: AutoCompleteTextView show suggestions when no text entered

I am using AutoCompleteTextView, when the user clicks on it, I want to show suggestions even if it has no text - but setThreshold(0) works exactly the same as setThreshold(1) - so the user has to enter at least 1 character to show the suggestions.
This is documented behavior:
When threshold is less than or equals 0, a threshold of 1 is
applied.
You can manually show the drop-down via showDropDown(), so perhaps you can arrange to show it when you want. Or, subclass AutoCompleteTextView and override enoughToFilter(), returning true all of time.
Here is my class InstantAutoComplete. It's something between AutoCompleteTextView and Spinner.
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.AutoCompleteTextView;
public class InstantAutoComplete extends AutoCompleteTextView {
public InstantAutoComplete(Context context) {
super(context);
}
public InstantAutoComplete(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
public InstantAutoComplete(Context arg0, AttributeSet arg1, int arg2) {
super(arg0, arg1, arg2);
}
#Override
public boolean enoughToFilter() {
return true;
}
#Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (focused && getAdapter() != null) {
performFiltering(getText(), 0);
}
}
}
Use it in your xml like this:
<your.namespace.InstantAutoComplete ... />
Easiest way:
Just use setOnTouchListener and showDropDown()
AutoCompleteTextView text;
.....
.....
text.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event){
text.showDropDown();
return false;
}
});
Destil's code works just great when there is only one InstantAutoComplete object. It didn't work with two though - no idea why. But when I put showDropDown() (just like CommonsWare advised) into onFocusChanged() like this:
#Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (focused) {
performFiltering(getText(), 0);
showDropDown();
}
}
it solved the problem.
It is just the two answers properly combined, but I hope it may save somebody some time.
The adapter does not perform filtering initially.
When the filtering is not performed, the dropdown list is empty.
so you might have to get the filtering going initially.
To do so, you can invoke filter() after you finish adding the entries:
adapter.add("a1");
adapter.add("a2");
adapter.add("a3");
adapter.getFilter().filter(null);
You can use onFocusChangeListener;
TCKimlikNo.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
TCKimlikNo.showDropDown();
}
}
});
Destil's answer above almost works, but has one subtle bug. When the user first gives focus to the field it works, however if they leave and then return to the field it will not show the drop down because the value of mPopupCanBeUpdated will still be false from when it was hidden. The fix is to change the onFocusChanged method to:
#Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (focused) {
if (getText().toString().length() == 0) {
// We want to trigger the drop down, replace the text.
setText("");
}
}
}
Just call this method on touch or click event of autoCompleteTextView or where you want.
autoCompleteTextView.showDropDown()
To make CustomAutoCompleteTextView.
1. override setThreshold,enoughToFilter,onFocusChanged method
public class CustomAutoCompleteTextView extends AutoCompleteTextView {
private int myThreshold;
public CustomAutoCompleteTextView (Context context) {
super(context);
}
public CustomAutoCompleteTextView (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomAutoCompleteTextView (Context context, AttributeSet attrs) {
super(context, attrs);
}
//set threshold 0.
public void setThreshold(int threshold) {
if (threshold < 0) {
threshold = 0;
}
myThreshold = threshold;
}
//if threshold is 0 than return true
public boolean enoughToFilter() {
return true;
}
//invoke on focus
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
//skip space and backspace
super.performFiltering("", 67);
// TODO Auto-generated method stub
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
protected void performFiltering(CharSequence text, int keyCode) {
// TODO Auto-generated method stub
super.performFiltering(text, keyCode);
}
public int getThreshold() {
return myThreshold;
}
}
try it
searchAutoComplete.setThreshold(0);
searchAutoComplete.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) {//cut last probel
if (charSequence.length() > 1) {
if (charSequence.charAt(charSequence.length() - 1) == ' ') {
searchAutoComplete.setText(charSequence.subSequence(0, charSequence.length() - 1));
searchAutoComplete.setSelection(charSequence.length() - 1);
}
}
}
#Override
public void afterTextChanged(Editable editable) {
}
});
//when clicked in autocomplete text view
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.header_search_etv:
if (searchAutoComplete.getText().toString().length() == 0) {
searchAutoComplete.setText(" ");
}
break;
}
}):
Seven years later, guys, the problem stays the same. Here's a class with a function which forces that stupid pop-up to show itself in any conditions. All you need to do is to set an adapter to your AutoCompleteTextView, add some data into it, and call showDropdownNow() function anytime.
Credits to #David Vávra. It's based on his code.
import android.content.Context
import android.util.AttributeSet
import android.widget.AutoCompleteTextView
class InstantAutoCompleteTextView : AutoCompleteTextView {
constructor(context: Context) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun enoughToFilter(): Boolean {
return true
}
fun showDropdownNow() {
if (adapter != null) {
// Remember a current text
val savedText = text
// Set empty text and perform filtering. As the result we restore all items inside of
// a filter's internal item collection.
setText(null, true)
// Set back the saved text and DO NOT perform filtering. As the result of these steps
// we have a text shown in UI, and what is more important we have items not filtered
setText(savedText, false)
// Move cursor to the end of a text
setSelection(text.length)
// Now we can show a dropdown with full list of options not filtered by displayed text
performFiltering(null, 0)
}
}
}
on FocusChangeListener, check
if (hasFocus) {
tvAutoComplete.setText(" ")
in your filter, just trim this value:
filter { it.contains(constraint.trim(), true) }
and it will show all suggestion when you focus on this view.
This worked for me, pseudo code:
public class CustomAutoCompleteTextView extends AutoCompleteTextView {
public CustomAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean enoughToFilter() {
return true;
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (focused) {
performFiltering(getText(), 0);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
this.showDropDown();
return super.onTouchEvent(event);
}
}
Just paste this to your onCreate Method in Java
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(
this, android.R.layout.simple_spinner_dropdown_item,
getResources().getStringArray(R.array.Loc_names));
textView1 =(AutoCompleteTextView) findViewById(R.id.acT1);
textView1.setAdapter(arrayAdapter);
textView1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View arg0) {
textView1.setMaxLines(5);
textView1.showDropDown();
}
});
And this to your Xml file...
<AutoCompleteTextView
android:layout_width="200dp"
android:layout_height="30dp"
android:hint="#string/select_location"
android:id="#+id/acT1"
android:textAlignment="center"/>
And create an Array in string.xml under Values...
<string-array name="Loc_names">
<item>Pakistan</item>
<item>Germany</item>
<item>Russia/NCR</item>
<item>China</item>
<item>India</item>
<item>Sweden</item>
<item>Australia</item>
</string-array>
And you are good to go.
For me, It is :
autoCompleteText.setThreshold(0);
do the trick.

Categories

Resources