Android View.GONE not working as intended on rare occasions - android

I've been seeing an issue in an app I'm working on relating to views being visible when they shouldn't be and I was wondering if anyone had seen similar.
I have a fragment viewed from a viewPager, that fragment has a base layout, in that base layout I include a banner style area with some info in it and a button, the layout is defaulted to GONE in the XML, like this.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:animateLayoutChanges="true" >
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/usage_swipe_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:animateLayoutChanges="true"
android:layout_height="match_parent">
<ScrollView
android:id="#+id/usage_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:background="#color/background_colour">
<include layout="#layout/layout_in_question"
android:id="#+id/layout_in_question_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"/>
.... Normal Stuff in layout
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
This view is only set to VISIBLE in very select circumstances. However some users are seeing it outside of those circumstances based on a response from a server call, its a very low percentage of users who are seeing it when they shouldn't ( in the region of 0.001% ), the server team insist its not them so I'm trying to ascertain if there are any known android issue or "hacks" that allow this sort of thing. The App supports API 15 and up and we're currently using support libs 25.3.0.
Has anyone seen behavior like this before? are there developer options on some devices that "show all views" or Modded OS's that allow it?
Edit: While I cant share full code snippets due to NDA, I have 2 locations the view can be enabled, both call the same method.
private View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
setupBanner();
}
/**
* OTTO event based on server response
*/
#Subscribe
public void receiveEvent(BannerEvent event)
{
setupBanner();
}
public void setupBanner() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (shouldShowBanner()) {
findViewById(R.id.layout_in_question_id).setVisibility(View.VISIBLE);
}
}
});
}
shouldShowBanner returns a boolean which is either true/false depending on what the server responded with.

Related

Can Android layout be loaded in the C language?

This link says that to make the app display a layout, you create a main_layout.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a Button" />
</LinearLayout>
and then load it in onCreate method:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// main_layout.xml is compiled into the R.layout.main_layout object
setContentView(R.layout.main_layout);
}
My problem is that I am coding in C, and have the C version of "onCreate" method:
JNIEXPORT
void ANativeActivity_onCreate(ANativeActivity *activity, void *savedState,
size_t savedStateSize) {
....
}
is it possible to load the layout and make the app display it in the C language?
C uses "ANativeActivity" that it doesn't has any of Java methods and resources/objects. Even if you manually parse that XML you should MANUALLY implement all Widgets/Components in C.....so it's near impossible due to high amount of work involved in it.
Usually someone chooses C on Android to do "some special work" that is not available on normal Java or due to performance issues on it.

How can I work around strange S Pen bugs with the spinner?

When using a Spinner with all default settings, I found some weird bugs when using the S Pen:
If you open the spinner and hover the S Pen at the bottom of the list, so it scrolls to the bottom, and then lift the S Pen up (so that the "hover circle" disappears), the list jumps back to the top
If you open the spinner and hover the S Pen at the bottom of the list, so it scrolls to the bottom, and then select an option, sometimes (intermittently - maybe about 1 in 4 times) it ignores the selected option and just jumps back to the top
I am testing on a tablet device in landscape orientation, the device is running Android 9 but I have done some tests on other devices and seems to be the same.
For comparison I tried using an app I use regularly which makes heavy use of spinners - "Packing List" by dotnetideas. This app was last updated in 2019 and has target SDK 27. You can easily test the spinners in the app by going into the settings and adding multiple items of luggage, then trying to edit the luggage on any packing list item. I found the S Pen works fine with this app, and it doesn't have the bugs described above, so there must be some way to work around it. That's not an open source app so I can't get ideas from their source code. I tried changing my target SDK to 27, but didn't make any difference.
Here is my code for what seems to me to be a completely vanilla implementation of spinner with all default settings - and still has these same "S Pen bugs" which are not reproducible in Packing List.
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.penpoc">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.SPenControlExperiment">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow();
}
}
}
main_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" />
MainFragment.java:
public class MainFragment extends Fragment {
public static MainFragment newInstance() {
return new MainFragment();
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.main_fragment, container, false);
Spinner spDefault = root.findViewById(R.id.spDefault);
ArrayAdapter<String> defaultSpinnerAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item);
defaultSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spDefault.setAdapter(defaultSpinnerAdapter);
defaultSpinnerAdapter.addAll(getExampleList());
defaultSpinnerAdapter.notifyDataSetChanged();
return root;
}
private List<String> getExampleList() {
List<String> list = new ArrayList<>();
list.add("Alpha");
list.add("Bravo");
list.add("Charlie");
list.add("Delta");
list.add("Echo");
list.add("Foxtrot");
list.add("Golf");
list.add("Hotel");
list.add("India");
list.add("Juliet");
list.add("Kilo");
list.add("Lima");
list.add("Mike");
list.add("November");
list.add("Oscar");
list.add("Papa");
list.add("Quebec");
list.add("Romeo");
return list;
}
}
main_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainFragment">
<!-- Add some text views to push the spinner further down the page, this is not strictly necessary -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="one" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="two" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="three" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="four" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="five" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="six" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="seven" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="eight" />
<Spinner
android:layout_width="350dp"
android:layout_height="wrap_content"
android:id="#+id/spDefault" />
</LinearLayout>
Gradle deps:
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
I exactly faced the same problem with the S Pen. For an old app, API 25 Spinners were working great, but not any more with API 30. The only way I can resolve it was to replace Spinners by AutoCompleteTextViews. The drop down it generates seems to work correctly with the S Pen.
A few adaptations are needed to make it look like a Spinner:
1. Make AutoCompleteTextView not editable
From the documentation, you can make it by adding android:inputType="none". But, as said in MaterialAutoCompleteTextView code:
// Due to a framework bug, setting android:inputType="none" on xml has no effect. Therefore,
// we check it here in case the autoCompleteTextView should be non-editable.
So you have two options:
Set the input type programmatically autoComplete.setInputType(InputType.TYPE_NULL)
Use MaterialAutoCompleteTextView having a workaround for this
2. Handle clicks to show the drop down list
The default behavior with AutoCompleteTextView is to show suggestions by typing a few characters. So you have to force the drop down show when view gets focused.
autoComplete.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
autoComplete.showDropDown();
}
}
});
autoComplete.setOnDismissListener(new AutoCompleteTextView.OnDismissListener() {
#Override
void onDismiss() {
// Force focus change after selection
autoComplete.clearFocus()
}
});
3. Style the drop down list
Contrary to Spinner calling both getView() and getDropDownView() from Adapter, AutoCompleteTextView only calls getView(). So, to provide the correct view, you need to check the parent type in getView() call:
#Override
public #NonNull View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
// Workaround for AutoCompleteTextView using only this method
if (parent instanceof ListView) {
return getDropDownView(position, convertView, parent);
} else {
return super.getView(position, convertView, parent);
}
}
4. Style the AutoCompleteTextView
One last thing is to add the little caret at the end of the AutoCompleteTextView to make it look like a Spinner. You can use the android:drawableEnd attribute in your XML layout file to handle this.
5. Other tips
AutoCompleteTextView drop down list selection triggers AdapterView.OnItemClickListener instead of AdapterView.OnItemSelectedListener as Spinners do. Methods signatures are almost the same.
There is no proper way to trigger this listener for initial values. As a workaround, you can set the initial value, then attach listener and put your first validation logic in its constructor.

iOS equivalent for Android's include control

I am designing an cross-platform app for iOS aswell Android with a shared logic using MVVMCross (5.1.1).
Throughout my app I have a fixed toolbar at the top displaying the current view's title aswell a button. Below the bar the interface is changing from view to view
The Android part:
On Android I created a reuseable layout which I embed in my current layout using include.
In my portable project I have a BaseViewModel which has the properties the reuseable toolbar layout binds to. Every other ViewModel derives from this base class. This way I can have all bindable properties of a displayed screen in one ViewModel without the need of nesting but see for yourself:
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar_login" />
<RelativeLayout
android:id="#+id/parentLoginLayout"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/toolbar">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
app:MvxBind="Text Pin"
/>
</RelativeLayout>
</RelativeLayout>
toolbar_login.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar_login"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="0dp"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
android:minWidth="25px"
android:minHeight="25px">
<TextView
app:MvxBind="Click ToolbarMenuCommand"
/>
<!-- some other -->
</android.support.v7.widget.Toolbar>
ViewModels.cs
using System.Threading.Tasks;
using Mobile.Helpers;
using ViewModels.Base;
using MvvmCross.Core.Navigation;
using MvvmCross.Core.ViewModels;
using Plugin.MessageBox;
namespace Mobile.ViewModels
{
public abstract class BaseViewModel : MvxViewModel
{
protected void NavigateToMainView()
{
NavigateTo<MainViewModel>();
}
private readonly IMvxNavigationService _navigationService;
protected BaseViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
public IMvxCommand ToolbarMenuCommand => new MvxCommand(OnMenuButtonClick);
protected abstract void OnMenuButtonClick();
}
public class LoginViewModel : BaseViewModel
{
private bool _menuVisibility;
private string _pin;
public LoginViewModel(IMvxNavigationService navigationService) : base(navigationService)
{
}
public bool MenuVisibility
{
get => _menuVisibility;
set => SetProperty(ref _menuVisibility, value);
}
public string Pin
{
get => _pin;
set => SetProperty(ref _pin, value);
}
protected override void OnMenuButtonClick()
{
MenuVisibility = !MenuVisibility;
}
}
}
The iOS part:
I am not entirely sure how to realise above behavior on iOS. I hope someone has a good idea or a good example project for me which I can take a look at. In general it is no problem to refactorise the ViewModels incase my idea is just not possible at iOS.
A few facts about the iOS project:
I am not using storyboards but single .xib's being independent
from each other
In my .xib's files I use autolayout constraints for positioning and
sizing entirely
A few ideas I already had (cant test them right now):
1. idea:
Create a base .xib with the above bar, the constraints aswell the
outlets
Create each new xib Design based on the previously created file
This would mean I need to adjust every view incase I decide to change something about the toolbar but so far I found no other way to embed a .xib in another .xib without having two different ViewControllers. Also I read that inheritance cause problems with outlets.
2. idea
Each .xib has an empty view at the top which acts as a container for
the toolbar
Have a Base ViewController which constructs the toolbar from code and
adds it as a child to the container view, and binds the properties
from the BaseViewModel
In a previous iOS project I noticed that adding views to the layout can cause problems with autolayout. Probably also a not that good solution?
3. idea
Create a xib with the toolbar and a container below and use it as a master page which would probably mean having a MasterViewModel with the toolbar properties and a nested ChildViewModel.
This is probably the way to go but I have to admit that I have no clue what is the best way to approach it (stil pretty new to iOS and MVVMCross).
Does someone have a few useful hints for me? Thanks a lot!
From what I understood I think you should try to use ScrollView for iOS part and try to imitate the ViewPager's behavior from Android, an example.

Android TextView.setText() is not changing on recent Samsung phones with SurfaceView Overlay

In my app I have a TextView and an ImageView that I update based on sensor information. This works perfectly on emulators and most of the handsets I have tried it on, however on Samsung handsets from the S7 to the S9 for some reason neither of them changes when they should be updated.
They are part of a Fragment overlaying a SurfaceView with a camera preview in it, there is a second SurfaceView overlay above them:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="myApp.CameraViewFragment">
<FrameLayout
android:id="#+id/control"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true">
<myApp.CameraSurfaceView
android:id="#+id/camera_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<TextView
android:id="#+id/direction"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:background="#color/black_overlay"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:layout_centerInParent="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:textSize="30dp"
android:text="" />
<ImageButton
android:id="#+id/gpsNotification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/gps_unfixed"
android:layout_gravity="right"
android:tint="#color/gps_unfixed"
android:background="#null" />
</FrameLayout>
<com.myapp.OverlaySurfaceView
android:id="#+id/overlay_view"
android:layout_width="200dp"
android:layout_height="200dp"
android:gravity="center"
android:layout_gravity="center_horizontal|center_vertical"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" />
</FrameLayout>
As far as I know, this is the relevant part of the Fragment code:
public class CameraViewFragment extends Fragment
{
private TextView directionView;
private CameraSurfaceView cameraView;
private OverlaySurfaceView overlay;
#Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
cameraView = (CameraSurfaceView) view.findViewById(R.id.camera);
directionView = (TextView) view.findViewById(R.id.direction);
overlay = (OverlaySurfaceview) view.findViewById(R.id.overlay_view);
}
private void setDirection(AccurateDirection direction) {
final String directionString = direction.getDirection();
overlay.update();
Activity activity = getActivity();
if ( activity != null ) {
activity.runOnUiThread(new Runnable() {
public void run() {
directionView.setText(directionString);
directionView.invalidate();
}
});
}
}
}
When I run this on a regular handset, the directionView text updates as new direction data arrives. On a Samsung S7 the value gets set once, maybe updated once right at the start and then it seems to stick and not change any more. If I pause in the debugger and call directionView.getText() from the immediate window, it gives me the result I am expecting, not what I see on the screen. If I call directionView.setText('ABC') in the immediate window, the value returned by directionView.getText() changes but again the value on the screen does not. The ImageView also seems to get stuck in its initial state regardless of changes that should adjust it.
Previous questions on this topic seem to involve updates not being triggered from the UI thread or failures to call invalidate on the view component, so I have fixed both of those already but the problem persists.
I have only seen this on recent Samsung handsets ( my old Samsung S4 appears to be fine ) which makes it hard to troubleshoot. Why are my view components not changing on the Samsung devices and what do I need to do to get them to update?
Edit: Apologies to anyone previously attempting to answer this as I had ommited the OverlaySurfaceView and I now realise that the other components update correctly if the OverlaySurfaceView is removed, so apparently having that second SurfaceView in the view hierarchy is interfering, but only on Samsung. Switching on and off hardware acceleration doesn't seem to make a difference, but there may be a specific place or way that I need to do it. On a working handset hardware acceleration is marked as disabled for the `OverlaySurfaceView.
I had the same issue with Samsung S7 device,
eventually I realized i was not handling the setZOrderOnTop correctly
check out this answer :
Button on top of SurfaceView with setZOrderOnTop set to true in Android

Two Layout in one Activity, searching for solution

First Basically I need to have layout when application starts.
Second I am getting data from views i have in this layout.
Third Im setting next layout and doing hard code work with data, that I got in step 2.
I can't figure out how to do this.
You can re-use the framework class ViewAnimator without any animations. It's not the prettiest solution, but should get the job done.
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ViewAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:id="#+id/viewFlipper" />
</LinearLayout>
MyActivity.java:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
viewAnimator = (ViewAnimator)findViewById(R.id.viewFlipper);
viewOne = createViewOne();
viewTwo = createViewTwo();
viewAnimator.addView(viewOne);
viewAnimator.addView(viewTwo);
...
}
private void someMethod() {
... do my background task ...
viewAnimator.setDisplayedChild(1); // viewTwo
}
have you gone through the google notepad tutorials? They explain using a list screen and loading a edit/add screen used to modify you sqlite database

Categories

Resources