RecyclerView scrolling glitches when disabling AppBarLayout in a CoordinatorLayout? - java

Background
I have a CoordinatorLayout based view, which has an AppbarLayout and RecyclerView as direct children. The AppBarLayout houses a search bar that collapses when you scroll the RecyclerView up. The XML for this view is:
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/search_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/search_app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
app:elevation="0dp">
<!-- Search bar -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/search_constraint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:focusable="true"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed|snap">
<!-- Abbreviated -->
<EditText .../>
<!-- Abbreviated -->
<ImageView .../>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
<!-- List -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/scroll_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
When you tap on the EditText view in the search bar, that enables what I call "Search Mode". All that search mode does is disable the AppBarLayout from collapsing when scrolling the RecyclerView. The user can then type into the search bar - which filters the items in the RecyclerView - and then they can scroll the list without the search bar collapsing. I hook into the EditText onFocus events to perform this:
searchField.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
// When user taps the search bar, enable search mode
enableSearchMode()
}
}
And the enableSearchMode code is:
private fun enableSearchMode() {
...
itemRecyclerView.isNestedScrollingEnabled = false
...
}
Problem
This setup seems to work perfectly... most of the time. Randomly - maybe 1% of the time - when you touch the EditText to enable search mode, something goes wrong and I'm not able to effectively scroll the RecyclerView anymore. It is like it is stuck at the top of the list. If you try to scroll towards the bottom of the list, the scrolling jerks around, and generally jumps back up to the top of the list. As soon as the search mode is disabled, the problem goes away.
// Disable search mode
itemRecyclerView.isNestedScrollingEnabled = true
Despite an enormous amount of testing, I have not been able to consistently reproduce the issue or determine what progression of actions leads to it. It just randomly happens, as if there is some sort of race condition going on within the CoordinatorLayout.
I have stripped away so much code in my app to isolate the issue that I am confident the issue occurs precisely when I set isNestedScrollingEnabled to false. That said, I have also tried an alternative to disabling the AppBarLayout from moving when the RecyclerView is scrolled, which is to override the behavior of the AppBarLayout as described here. Oddly enough, this leads to the same problem. If I don't disable the AppBarLayout either through this means or via setting isNestedScrollingEnabled false, the issue never appears.
What is happening here?!?

What is happening here?!?
Setting isNestedScrollingEnabled to false, in fact, breaks the communication between your itemRecyclerView as scrolling child and AppBarLayout as it's parent. The normal behaviour is itemRecyclerView notifies it's parent AppBarLayout it's scrolling progress for which the parent is supposed to react to it by calculating it's collapsed height given any scrolling progress and all other stuffs.
I found somewhere that setting isNestedScrollingEnabled to false would cause the RecyclerView to not recycle its views. I can't say exactly if it's true but if it is then, I think it's cause for that glitch.
The solution that I would like to propose is to change the scroll_flags programmatically to NO_SCROLL so that AppBarLayout wouldn't react/scroll in scrolling of its child scrolling view.
Although, it's in java but following code snippet should help you.
// get a reference for your constraint layout
ConstraintLayout constraintLayout = findViewById(R.id.search_constraint);
// get the layout params object to change the scroll flags programmatically
final AppBarLayout.LayoutParams layoutParams = (AppBarLayout.LayoutParams) constraintLayout.getLayoutParams();
// flags variable to switch between
final int noScrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL;
final int defaultScrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
| AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
| AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP;
// now we will set appropriate scroll flags according to the focus
searchField.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
layoutParams.setScrollFlags(hasFocus ? noScrollFlags : defaultScrollFlags);
}
});

Related

How to show progressbar with blurry background and disabled UI

so I am making an app which has a lot of connections to the database, so there is a "waiting" time everywhere.
I want to put a progress bar everywhere where is a connection to the database. It should look like this:
-The progress bar is shown after clicking the Login button with the blurry background.
In short - Show progress bar, blur the background, deactivate UI controls while progressbar is activated.
I'll try to show you the pseudo code here:
loginBtn.setOnClickListener {
progressBar.visibility = View.VISIBLE
BlurTheBackground()
getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
DoTheLoginStuff()
}
And after Login I want to disable progress bar and reactivate fully UI.
PS: After Login the activity changes to another,but after hitting back button on the smartphone it comes back without refresh
You can try https://android-arsenal.com/details/1/4409 this library. I think it can help You.
<RelativeLayout
android:id="#+id/progressBar_blurLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.armboldmind.natalipharm.view.customViews.RealtimeBlurView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:realtimeBlurRadius="15dp"
app:realtimeOverlayColor="#99FFFFFF" />
<ProgressBar
android:id="#+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>
Set it on top of your layout, and on login button click change visibility of progress bar layout.

bottomsheet stops between EXPANDED and HIDDEN

I'm trying to create a bottomsheet that's either completely expanded or completely out of view - I don't want it to be anywhere in the middle or peeking.
Here's the xml:
<LinearLayout
android:id="#+id/bottom_sheet"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_gravity="center_horizontal"
android:background="#android:color/holo_blue_bright"
android:clipToPadding="true"
android:orientation="vertical"
android:elevation="10dp"
app:behavior_peekHeight="0dp"
app:behavior_hideable="true"
app:behavior_skipCollapsed="true"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
<TextView
android:id="#+id/delete_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#color/cinza3"
android:text="clear/delete Q"/>
</LinearLayout>
In my code I have the following methods:
private void showHideBottomSheet() {
if (mBSBehavior.getState() != BottomSheetBehavior.STATE_EXPANDED) {
showBottomSheet();
} else {
hideBottomSheet();
}
}
private void showBottomSheet() {
mBSBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
private void hideBottomSheet() {
mBSBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
And in my layout there's a button that calls showHideBottomSheet() when clicked.
When I click the button, everything works fine and the bottomsheet is shown/hidden. But if it's EXPANDED and I click on a textview elsewhere in the code (outside the bottomsheet), for example, the bottomsheet moves down a little, but not completely - it's top half is visible, but if I log it's state, it's STATE_EXPANDED.
what's the difference between STATE_HIDDEN and STATE_COLLAPSED? I've searched everywhere for a visual explanation but couldn't find it. Is this 'intermediate' state the collapsed state? Even if I set peekHeight="0" in the xml and skipCollapsed="true"?
what does peekHeight and skipCollapsed in the xml actually do?
how can I make it to be fully visible or fully hidden at all times and avoid this 'intermediate' state?
EDIT: There's a TextView inside the BottomSheet, and and OnClickListener on it. When I click it, the BottomSheet goes to that 'intermediate' state too, even though the OnclickListener does not call setState or anything related to the BottomSheet.
Updated my support:design library to 25.3.1 and it started working as expected.

Android AutoCompleteTextView popup moving after displaying

When I use the autocompletetextview everything works fine except it keeps switching between two positions: the correct one right below the textview and quite a ways lower. It starts wrong, but almost immediately moves to the correct position. However this is very annoying when typing or backspacing as it happens for every letter. I am using android studio.
It appears as if two events are simultaneously trying to decide the layout. Sometimes it will stick in one position or the other.
**I slowed down the filtering process with a custom adapter and it looks like when text is entered it moves into the incorrect position, and then when the filtering is done it moves back into the correct position.
Incorrect
Correct:
java (in OnCreate())-
String[] drugs = new String[]{"Nexium","Amoxicillin","LEVOCETIRIZINE DIHYDROCHLORIDE", "Advil", "Advair Diskus", "Daraprim"};
AutoCompleteTextView drugNameAutoComplete = ((AutoCompleteTextView) findViewById(R.id.drugNameEditText));
drugNameAutoComplete.setAnimation(null);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,drugs);
drugNameAutoComplete.setAdapter(adapter);
And the layout code-
<AutoCompleteTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/drugNameEditText"
android:enabled="true"
android:singleLine="true"
android:layout_below="#+id/lookingForView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:dropDownVerticalOffset="50dp"
android:hint="#string/drug_name" />
If I remove the dropDownVeticalOffset I get flickering between the correct value and this-
To change this position use dropDownAnchor attribute and reference another view id.
(view under autocomplete)
android:dropDownAnchor
View to anchor the auto-complete dropdown to. If not specified, the text view itself is used.
and you have many attributes for dropdown..
You better to use implement Filter to autocomplete adapter and pass the entered text from OnQueryTextListener of autocomplete, and set the selected text by calling adapter.getitem.
Change your xml to-
<AutoCompleteTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/drugNameEditText"
android:enabled="true"
android:singleLine="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
**android:dropDownVerticalOffset="0dp"**
**android:dropDownAnchor="#id/drugNameEditText"**
android:hint="drug" />
Did you by any chance use the same id on the other AutoCompleteTextView? I can reproduce the behaviour you describe by including two views with the same id.
I had the same problem on my end with the newest API for some reason. I solved the problem by explicitly setting dropDownAnchor, dropDownVerticalOffset, and dropDownHeight in both the XML and the onTextChanged event.
Edit: After some checking, it seems this behavior is INCONSISTENT BETWEEN APIs starting with Marshmallow (API 23). Try changing your code to the following:
final float scale = context.getResources().getDisplayMetrics().density;
int verticalOffsetDP;
int dropDownHeightDP = (int) (200 * scale + 0.5f);
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
if (currentApiVersion < Build.VERSION_CODES.M)
{
// Prior to Marshmallow DropDownVerticalOffset works as expected
verticalOffset = 0;
}
else
{
// Marshmallow has some weird DropDownVerticalOffset behavior so we have to compensate
verticalOffset = (int) (50 * scale + 0.5f);
}
AutoCompleteTextView drugNameAutoComplete = ((AutoCompleteTextView)findViewById(R.id.drugNameEditText));
drugNameAutoComplete.setAnimation(null);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,drugs);
drugNameAutoComplete.setAdapter(adapter);
drugNameAutoComplete.setDropDownAnchor(R.id.drugNameEditText);
drugNameAutoComplete.setDropDownVerticalOffset(verticalOffsetDP);
drugNameAutoComplete.setDropDownHeight(dropDownHeightDP);
And your XML to the following:
<AutoCompleteTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/drugNameEditText"
android:enabled="true"
android:singleLine="true"
android:layout_below="#+id/lookingForView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:dropDownVerticalOffset="50dp"
android:dropDownAnchor="#id/drugNameEditText"
android:dropDownHeight="200dp"
android:hint="#string/drug_name" />
Note that you have to calculate DP based on screen dimensions in code. I got the code example from the excellent accepted response in this thread: Android and setting width and height programmatically in dp units
The reason you have to set dropDownHeight as well is because otherwise Android has a behavior to push it up to the top of the screen if the list extends beyond the height boundaries of the view, further complicating the moving-around issues. If you actually don't mind this (some people don't, depends on your view setup), you can remove dropDownHeight from both programmatic and XML.
I should note that these changes still don't make things EXACTLY the same between Marshmallow and other APIs. Looks like there is still a few pixels difference. But it gets pretty close.
I had the same problem and too many hours later I've found the solution.
You should change:
android:layout_width="wrap_content" to android:layout_width="match_parent"
I hope it works for you too.

Another Fragment on top op a Mapview (SupportMapFragment)

Im adding another Fragment to the screen. This screen already has en MapView in a SupportMapFragment so the added Fragment should be on top of the MapView. This new view cover like 2/3 of the screen and a big part of the Map but when I scroll on the View it scrolls the MapView underneath. This is not what i expected because the new View is added on top of the MapView. The new view exist of a relative layout containing a ImageView which wraps it contents. So when scrolling on the imageview (which has totally no functionality) it scrolls the MapView. Can anyone explain why this happens and if possible, provide me with a solution?
EDIT:
basically i have a MapView which fills the whole screen. This code sets up my mapview:
public void setUpMapIfNeeded(int satelliteMode, LatLng startPoint, float zoomLevel) {
// Do a null check to confirm that we have not already instantiated the map.
if (mapView == null) {
// Try to obtain the map from the SupportMapFragment.
mapView = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapview))
.getMap();
// Check if we were successful in obtaining the map.
if (mapView != null) {
setUpMap(satelliteMode, startPoint, zoomLevel);
}
}
}
private void setUpMap(int satelliteMode, LatLng startPoint, float zoomLevel) {
// more settings
}
Then when a user clicks a marker a fragment must be shown on top of the mapview.
public void createDetailView() {
detailView = new DetailViewFragment();
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.viewLayout, detailView).commit();
fragmentManager.executePendingTransactions();
}
This all works fine. The view is shown (which is about 2/3 of the whole screen) but user can scroll the map when swiping the detailview. The detailview consist of a relative layout with a lot of text and buttons:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginTop="45sp">
<!-- a lot of text and buttons here -->
</RelativeLayout>
So I still havent found a proper way to deal with this. Instead I just disabled all touches and clicks on the mapview when another view is on top of the mapview which btw works ok.
Just in case it might still be relevant, the way I've handled this kinds of layouts (one view appearing "on top of" another) is to use LinearLayout instead of a RelativeLayout in the following way:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<SomeView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<PopOutView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:visibility="gone" />
</LinearLayout>
In your case the map would be that SomeView and the relative layout the PopOutView. Then in the code you would just call popOutView.setVisibility = View.VISIBLE whenever you would like it to show up. It shrinks the first view instead of being drawn on top (thus having a bit different visual effect) but at least this way the touch events should go to the right view.
Set the fragment's layout clickable property to true. It will stop the touch events from propagating down.
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF"
android:clickable="true">
</FrameLayout>

Android: Context menu doesn't show up for ListView with members defined by LinearLayout?

I've got a ListActivity and ListView and I've bound some data to it. The data shows up fine, and I've also registered a context menu for the view. When I display the list items as just a simple TextView, it works fine:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/nametext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
However when I try something a bit more complex, like show the name and a CheckBox, the menu never shows up:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:id="#+id/nametext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox
android:id="#+id/namecheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
Can long-presses work on more complex elements? I'm building on 2.1.
(edit)
Registering with this on the ListActivity:
registerForContextMenu(getListView());
The code I posted is the item template for the list.
Your CheckBox may be interfering with matters. Consider using a CheckedTextView instead of a LinearLayout, CheckBox, and TextView combination, since CheckedTextView is what Android expects for a CHOICE_MODE_MULTIPLE list.
Check out $ANDROID_HOME/platforms/$VERSION/data/res/layout/simple_list_item_multiple_choice.xml, where $ANDROID_HOME is wherever you installed the SDK and $VERSION is some Android version (e.g., android-2.1). This resource is the standard resource you should use for CHOICE_MODE_MULTIPLE lists. Feel free to copy it into your project and adjust the styling of the CheckedTextView as needed.
set checkbox property
focusable = false;
and run project again..
Found at this place: http://www.anddev.org/view-layout-resource-problems-f27/custom-list-view-row-item-and-context-menu-t52431.html
Setting the checkbox to not be focusable fixes the problem.
Not sure if it would cause issues when navigating the UI with something else than a touchscreen (with a wheel or arrow keys), but it fixed my problem (my layout was a bit more complicated than just a TextView and a Checkbox...)
Context menu's can only be registered to subclasses of View. I don't know how you registered the LinearLayout with a context menu, did you package it in some type of View? if so, you should post that code.
Anyways why not just register the TextView of each list item? Who would long press a checkbox...
This should from a regular ListView as well. But if you're starting from scratch on a new list I would consider using the CheckedTextView:
checkBox.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View v) {
// return false to let list's context menu show
return false;
}
});

Categories