Android OnLongClickListener strange / unreliable behaviour - android

I'm currently fighting against the OnLongClickListener on Android Api Lvl 8.
Take this code:
this.webView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
System.out.println("long click");
return true;
}
});
It works perfectly. I can press anywhere on the WebView and the event triggers every time.
Now take a look at this one:
this.webView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
final EditText editText = getUrlTextField();
switch (editText.getVisibility()) {
case View.VISIBLE:
editText.setVisibility(View.GONE);
return true;
case View.GONE:
editText.setVisibility(View.VISIBLE);
return true;
default:
return false;
}
}
});
Assuming the URL EditText components is currently visible, it gets gone from the display and should be shown again when another long click event is triggered.
But if you run this, the event just works once (!) when one performs a long click on any position on the WebView. To make things complicated, the long click works again when it is performed on a link on the website...
Can anyone explain if it is a bug in the sdk and/or if there is a mistake in my thinking how the OnLongClickListener is working?!? :/
EDIT:
I've run now several different scenarios on a Nexus One and come to following conclussion: Changing the layout on runtime more or less kills the OnLongClickListener... I haven't found a way to get it work reliably at all...
I would really appreciate if anyone could give me a hint... I'm at my wits end :(

Personnally, I ended up by re-setting the listener after each relayout.

I've run into this issue as well. It seems that if the view layout changes in a way that child view bounds need to be modified (i.e. TextView is wrap_content width and you set its text to something longer/shorter than it was before), views in the hierarchy will have their onStartTemporaryDetach method called (most likely due to a layout pass, although I haven't dug deep enough to find out for sure). If you look at the source for View that onStartTemporaryDetach ultimately unsets the pressed state of the view.
Changing the views in your layout that will be updated periodically to have bounds that will not change regardless of the value you set, will fix the issue. Although, that is still not awesome.

Related

NavigationView menu does not update

I am writing here about an issue that was introduced when we migrated from the AppCompat library to the AndroidX library. While doing so, we switched from android.support.design.widget.NavigationView to com.google.android.material.navigation.NavigationView and that’s when the following issue started.
In our NavigationView design, in order to save space, we implemented an expandable menu, so that when users clicks on the “more” button, the menu expands to show more options. It starts off with only some options visible, and the rest are not visible, as follows;
Option 1
Option 2
More…
Upon clicking on the “More...” button, the menu expands to;
Option 1
Option 2
Option 3
Option 4
Option 5
Option 6
To do this we used following code;
#Override
public boolean onNavigationItemSelected(MenuItem item)
{
....
if (item.getItemId() == R.id.nav_more)
{
item.setVisible(false); // hide the “More” item
getMenu().findItem(R.id.nav_option_3).setVisible(true);
getMenu().findItem(R.id.nav_option_4).setVisible(true);
getMenu().findItem(R.id.nav_option_5).setVisible(true);
getMenu().findItem(R.id.nav_option_6).setVisible(true);
return true;
}
.......
return false;
}
Well, this code has worked in the past, but when we migrated to using the androidx library, poof, it stopped working. Well, it did work a bit. The “More...” button got hidden, but the previously hidden options, were not being displayed.
As, it took me many hours to solve this issue, and to save others this headache, I will explain the issue and the solution.
The first thing to do in such cases, is to look at the source code. As the code is open source, I was able to get it at github. At first glance I didn’t get smarter. I found that the NavigationView has a NavigationMenuPresenter object field (called presenter), that has a method called updateMenuView() which calls adapter.update(), which calls prepareMenuItems() and notifyDataSetChanged(). This sounded like the needed fix, so using reflection, we accessed and called the updateMenuView() method, but surprisingly, it did not help!
So, I decided to take it to the extreme, and see what happens if I call getMenu().clear(), and believe it or not, nothing happened. It seems that any changes made to Menu after the NavigationView is shown, are ignored. But a quick look through source code, I could not see any reason for that.
So how do I solve this issue? I tried using the latest alpha version of the library, but I still have the same issue.
Well, after much work, I found the solution. It's actually simple. Just hold on for the answer.
Lionscribe
So I was back to the source code, searching for some clue, when I fell upon a method called setUpdateSuspended(boolean updateSuspended). Well, that sounded suspicious! I searched for usage of this method, and found it being called in the onClick callback. Here is a minimized version of the code;
#Override
public void onClick(View view) {
NavigationMenuItemView itemView = (NavigationMenuItemView) view;
setUpdateSuspended(true);
MenuItemImpl item = itemView.getItemData();
boolean result = menu.performItemAction(item, NavigationMenuPresenter.this, 0);
setUpdateSuspended(false);
}
Bingo! It seems that while handling clicks, the NavigationView suspends and will not recognize any changes done to menu. I am not sure the reason for this, but as we were updating the menu in the onNavigationItemSelected callback, which is called by the onClick method, the menu updates are ignored.
Well, once I understood the issue, the solution was simple and clean. I just wrapped the code in a Runnable, and posted it, so that it runs after the onClick method returns, and setUpdateSuspended is set back to false. Here is the updated code;
#Override
public boolean onNavigationItemSelected(MenuItem item)
{
....
if (item.getItemId() == R.id.nav_more)
{
final MenuItem itemFinal = item;
post(new Runnable()
{
#Override
public void run()
{
getMenu().findItem(R.id.nav_option_3).setVisible(true);
getMenu().findItem(R.id.nav_option_4).setVisible(true);
getMenu().findItem(R.id.nav_option_5).setVisible(true);
getMenu().findItem(R.id.nav_option_6).setVisible(true);
itemFinal.setVisible(false); // hide the “More” item
}
});
return true;
}
.......
return false;
}
Viola! The expandable menu now works like it used to, the hidden items are now being shown!
I hope this will be of help to others with same issue.
Lionscribe

Android scrolling makes whole screen blue

I have not been able to replicate this, but is anyone aware of what might cause the entire screen on an Android device to go light blue? It seems related to selecting a radio buttons and then scrolling. It happens on Nexus 5x with Android 8.
Here is what it looks like:
I have only heard of one other instance of this occurring. Could it be device specific? Strangely enough, once it happens it seem to stay this way, though the user says it is somewhat intermittent.
Update:
This only seems to happen on Android 8, if that helps anyone...
So, I eventually found the offending code. I verified this is only happening on Android 8 devices, maybe only Samsung? The offending code was:
mFormScrollView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
mFormScrollView.setFocusable(true);
mFormScrollView.setFocusableInTouchMode(true);
// Hide the keyboard when moving the screen up or down. This should only
// be an issue when
// on a text edit field. Also disable focus jump using
// "requestFocusFromTouch"
mFormScrollView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View view, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_DOWN) {
Utilities.hideKeyBoard(FormActivity.this, view);
}
// Keeps screen from jumping to nearest EditText
// view.requestFocusFromTouch();
return false;
}
});
The offending line is commented out - the view.requestFocusFromTouch() method, which was meant to keep the screen from auto jumping to the next text field when the keyboard was hidden and focus lost. On Android 8 this is not happening, but I need to verify with older versions.

floating View displaying on hover in android

I am trying to implement some hints when a user is hovering a button or another view, I see that android have support for onHoverListener but I don't understand how it really works. However I did try to find a solution how to make a floating editText on a button hover but I didn't find any ideas.
I am thinking that hover in android is the same think with long click because you can't hover with finger without clicking the view.
OnHoverListener is only implemented in 2 specific situations: a) when a Bluetooth or USB mouse is plugged into the device, or b) on Galaxy Note devices, when the S-Pen (stylus) is hovering over the object.
In specific setups/situations this can be a very neat feature, but unfortunately, most users will never even know it exists. For your situation, you may want to implement an OnLongClickListener for showing hints/tips as that is pretty standard in Android.
This example will show a TextView or ImageView hint for 5 seconds when a long-click is initiated:
btn.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
findViewById(R.id.hint).setVisibility(View.VISIBLE);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
findViewById(R.id.hint).setVisibility(View.GONE);
}
}, 5000);
return true;
}
});
Hope this helps,
I want to add up on Aaron's answer.
Some devices with sensitive screens - Samsung S5 comes to mind - don't even need an s-pen.
Just need to hold your finger 1/2 inch over the screen and it would trigger it.

Disable parts of the touch screen in android

So I was trying to disable the screen for an app I am making for a brief period using this
#Override
public boolean onTouchEvent(MotionEvent pMotioneEvent) {
if(pMotioneEvent.getY() < TestSprite.getY()){
return false;
}else{
return true;
}
}
but this seems to have no effect. I read around and it seems like in general its a bad idea to disable the touch screen, but I'm still curious to know if there is a way.
Thanks
You could try
requestDisallowInterceptTouchEvent
If you want to check for touchevents in a certain area of your screen, you might want to put this in a View and set a touchEvent Listener to it.

Stop ListView scroll animation

I have a ListView with about 100 entries. When the user does the "fling" from bottom to top it starts scrolling and keeps on scrolling even when the finger does not touch the display any more.
Is there a way to stop the scrolling animation at this point?
and we lookup the android source code (AbsListView), give it a ACTION_CANCEL touchEvent, can stop the fling. it is easy.
listView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, 0, 0, 0));
I didn't try the solution of Pompe de velo but since smoothScrollToPosition() is not available for API level less than 8 this didnt work for me.
I agree, changing default behaviour is not a good Idea, but sometimes you need to. So here is my (dirty) solution which uses reflection. This is by far not the recommended way since it's a hack but it works for me. There might be a better solution but I didn't found it.
class StopListFling {
private static Field mFlingEndField = null;
private static Method mFlingEndMethod = null;
static {
try {
mFlingEndField = AbsListView.class.getDeclaredField("mFlingRunnable");
mFlingEndField.setAccessible(true);
mFlingEndMethod = mFlingEndField.getType().getDeclaredMethod("endFling");
mFlingEndMethod.setAccessible(true);
} catch (Exception e) {
mFlingEndMethod = null;
}
}
public static void stop(ListView list) {
if (mFlingEndMethod != null) {
try {
mFlingEndMethod.invoke(mFlingEndField.get(list));
} catch (Exception e) {
}
}
}
}
Well there surely is a way to do it. But the point is more whether or not it is advisable to do it, in my opinion.
The list is a standard Android control that behaves constistently across all applications. So I would be surprised if I found a list that did not behave the same in your application. You can stop the fling by putting your finger back on the screen at any time.
That said, if you want to do extra work, you could subclass the list view and override its on touch method. Best way to know what to do is to get the source code of ListView (ListView in Android 1.6).
You can prevent flinging for ListViews in API 8 by overriding onTouchEvent and calling smoothScrollBy.
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
this.smoothScrollBy(0, 0);
break;
}
return super.onTouchEvent(ev);
}
This takes over from the fling scrolling and scrolls 0px instead.
My opinion is that you shouldn't modify this behaviour, since the fling behaviour is what the user expects.
However, to your question. I haven't tried this but in theory it should work.
Implement an OnScrollListener to your ListView and use the onScrollStateChanged() method to check if the current state is SCROLL_STATE_FLING. After you've determined that the scrolling perfoms by a fling you can get your ListView's first visible position by using the getFirstVisiblePosition() method and from there you can use smoothScrollToPosition() where you put in your getFirstVisiblePosition() value as an argument.
if you what disable default animation from list view just need set id for root (main) layout in xml and call void onClickListener in class for root layout

Categories

Resources