MotionLayout apply loadLayoutDescription - android-motionlayout

I have 2 different behaviours for MotionLayout. The idea is to change them based on application state. But when i'm setting from code:
if(smth) {
constraintToolbar.loadLayoutDescription(R.xml.layout_description_1)
} else {
constraintToolbar.loadLayoutDescription(R.xml.layout_description_2)
}
Constraints are not applied. But they are applied after drag motion. Is there some workaround or it's because of ConstraintLayout 2.0.0 alpha ?
I'm using:
'com.android.support.constraint:constraint-layout:2.0.0-alpha2'

I have transition in MotionScene.
<Transition
motion:constraintSetStart="#id/start"
motion:constraintSetEnd="#id/end"
motion:duration="1000"
motion:interpolator="linear">
Initial constraint is not setted after "MotionLayout.loadLayoutDescription".
I added:
MotionLayout.setTransition(R.id.start, R.id.end)
and constrains applied correctly.

A bit of a workaround, but does work
if(smth) {
constraintToolbar.loadLayoutDescription(R.xml.layout_description_1)
constraintToolbar.setTransition(R.id.start1, R.id.end1)
} else {
constraintToolbar.loadLayoutDescription(R.xml.layout_description_2)
constraintToolbar.setTransition(R.id.start2, R.id.end2)
}

you must set transactions after load layouts.
if(smth) {
constraintToolbar.loadLayoutDescription(R.xml.layout_description_1)
motionContainer.setTransition(R.id.start1 ,R.id.end1)
} else {
constraintToolbar.loadLayoutDescription(R.xml.layout_description_2)
motionContainer.setTransition(R.id.start1, R.id.end1)
}

loadLayoutDescription I think just use in androidx
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
or if u not using androidx you must change constraintToolbar to motion
val motion = MotionLayout(this)
motion.loadLayoutDescription

aftere called
if(smth) {
constraintToolbar.loadLayoutDescription(R.xml.layout_description_1)
} else {
constraintToolbar.loadLayoutDescription(R.xml.layout_description_2)
}
invoke motionLayout.requestlayout() to let the new MotionScene work.
Note: the first <Transitoin .../> in <MotionScene .../> would be work.

For me this works
doOnPreDraw {
motionLayout.loadLayoutDescription(R.xml.scene)
}

Related

Should the variable value be checked before assigning?

I know this might sound like a silly question but I'm curious should I check my variable value before assigning?
like for example if I'm flipping my skin (Node2D composed of sprite & raycast) based on direction (Vector2) :
func _process(delta):
...
if(direction.x>0):
skin.scale.x=1
elif(direction.x<0):
skin.scale.x=-1
#OR
if(direction.x>0):
if(skin.scale.x!=1):
skin.scale.x=1
elif(direction.x<0):
if(skin.scale.x!=-1):
skin.scale.x=-1
would the skin scale be altered every _process hence consuming more CPU usage
OR
if the value is same will it be ignored?
First of all, given that this is GDScript, so the number of lines will be a performance factor.
We will look at the C++ side…
But before that… Be aware that GDScript does some trickery with properties.
When you say skin.scale Godot will call get_scale on the skin object, which returns a Vector2. And Vector2 is a value type. That Vector2 is not the scale that the object has, but a copy, an snapshot of the value. So, in virtually any other language skin.scale.x=1 is modifying the Vector2 and would have no effect on the scale of the object. Meaning that you should do this:
skin.scale = Vector2(skin.scale.x + 1, skin.scale.y)
Or this:
var skin_scale = skin.scale
skin_scale.x += 1
skin.scale = skin_scale
Which I bet people using C# would find familiar.
But you don't need to do that in GDScript. Godot will call set_scale, which is what most people expect. It is a feature!
So, you set scale, and Godot will call set_scale:
void Node2D::set_scale(const Size2 &p_scale) {
if (_xform_dirty) {
((Node2D *)this)->_update_xform_values();
}
_scale = p_scale;
// Avoid having 0 scale values, can lead to errors in physics and rendering.
if (Math::is_zero_approx(_scale.x)) {
_scale.x = CMP_EPSILON;
}
if (Math::is_zero_approx(_scale.y)) {
_scale.y = CMP_EPSILON;
}
_update_transform();
_change_notify("scale");
}
The method _change_notify only does something in the editor. It is the Godot 3.x instrumentation for undo/redo et.al.
And set_scale will call _update_transform:
void Node2D::_update_transform() {
_mat.set_rotation_and_scale(angle, _scale);
_mat.elements[2] = pos;
VisualServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), _mat);
if (!is_inside_tree()) {
return;
}
_notify_transform();
}
Which, as you can see, will update the Transform2D of the Node2D (_mat). Then it is off to the VisualServer.
And then to _notify_transform. Which is what propagates the change in the scene tree. It is also what calls notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED) if you have enabled it with set_notify_transform. It looks like this (this is from "canvas_item.h"):
_FORCE_INLINE_ void _notify_transform() {
if (!is_inside_tree()) {
return;
}
_notify_transform(this);
if (!block_transform_notify && notify_local_transform) {
notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
}
}
And you can see it delegates to another _notify_transform that looks like this (this is from "canvas_item.cpp"):
void CanvasItem::_notify_transform(CanvasItem *p_node) {
/* This check exists to avoid re-propagating the transform
* notification down the tree on dirty nodes. It provides
* optimization by avoiding redundancy (nodes are dirty, will get the
* notification anyway).
*/
if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) {
return; //nothing to do
}
p_node->global_invalid = true;
if (p_node->notify_transform && !p_node->xform_change.in_list()) {
if (!p_node->block_transform_notify) {
if (p_node->is_inside_tree()) {
get_tree()->xform_change_list.add(&p_node->xform_change);
}
}
}
for (CanvasItem *ci : p_node->children_items) {
if (ci->top_level) {
continue;
}
_notify_transform(ci);
}
}
So, no. There is no check to ignore the change if the value is the same.
However, it is worth noting that Godot invalidates the global transform instead of computing it right away (global_invalid). This is does not make multiple updates to the transform in the same frame free, but it makes them cheaper than otherwise.
I also remind you that looking at the source code is no replacement for using a profiler.
Should you check? Perhaps… If there are many children that need to be updated the extra lines are likely cheap enough. If in doubt: measure with a profiler.

How to create Mapbox Marker Onclick Eventlistener Android Studio?

Hello Stackoverflow community,
I am trying to develop an Android application with Mapbox.
I followed this guide to create markers on the map.
https://docs.mapbox.com/android/maps/examples/default-point-annotation/
Thus my code is the following:
public fun createMarker(id: String, lon: Double, lat: Double) {
// Create an instance of the Annotation API and get the PointAnnotationManager.
var marker: PointAnnotation? = bitmapFromDrawableRes(
drawercontext,
R.drawable.red_marker
)?.let {
val annotationApi = binding.mapBoxView.mapView?.annotations
val pointAnnotationManager =
annotationApi?.createPointAnnotationManager(binding.mapBoxView.mapView!!)
// Set options for the resulting symbol layer.
val pointAnnotationOptions: PointAnnotationOptions = PointAnnotationOptions()
// Define a geographic coordinate.
.withPoint(Point.fromLngLat(lon, lat))
// Specify the bitmap you assigned to the point annotation
// The bitmap will be added to map style automatically.
.withIconImage(it)
// Add the resulting pointAnnotation to the map.
pointAnnotationManager?.create(pointAnnotationOptions)
}
}
Unfortunately, I can not find any solution to add a click listener to markers (to show extra information outside of the map). In my opinion, this should be an important event, so I don't get why there is so little support. I want to replicate something like this:
https://bl.ocks.org/chriswhong/8977c0d4e869e9eaf06b4e9fda80f3ab
But in Android Studio with Kotlin.
One workaround I have seen is to add a click listener to the map and from there determine the marker with the closest coordinates, but I think that would not be as nice of a solution. Do you know any solutions or workarounds to my problem?
Thanks for the help in advance!
try this:
pointAnnotationManager.apply {
addClickListener(
OnPointAnnotationClickListener {
Toast.makeText(this#MainActivity, "id: ${it.id}", Toast.LENGTH_LONG).show()
false
}
)
}

File or module level 'feature' possible?

Some optimizations/algorithms make code considerably less readable, so it's useful to keep the ability to disable the complex-and-unwieldily functionality within a file/module so any errors introduced when modifying this code can be quickly tested against the simple code.
Currently using const USE_SOME_FEATURE: bool = true; seems a reasonable way, but makes the code read a little strangely, since USE_SOME_FEATURE is being used like an ifdef in C.
For instance, clippy wants you to write:
if foo {
{ ..other code.. }
} else {
// final case
if USE_SOME_FEATURE {
{ ..fancy_code.. }
} else {
{ ..simple_code.. }
}
}
As:
if foo {
{ ..other code.. }
} else if USE_SOME_FEATURE {
// final case
{ ..fancy_code.. }
} else {
// final case
{ ..simple_code.. }
}
Which IMHO hurts readability, and can be ignored - but is caused by using a boolean where a feature might make more sense.
Is there a way to expose a feature within a file without having it listed in the crate?(since this is only for internal debugging and testing changes to code).
You can use a build script to create new cfg conditions. Use println!("cargo:rustc-cfg=whatever") in the build script, and then you can use #[cfg(whatever)] on your functions and statements.

Why do I get a "connot be applied to 'Closure'" Warning in my gradle script?

I have a hard time figuring out, why IntelliJ warns me about this part in my build.gradle file:
apply plugin: 'distribution'
(...)
distributions {
main {
baseName = 'someName'
contents {
from { 'src/readme' }
}
}
}
shot:
I took it straight from the gradle user guide and the build seems to work ok. So, is this a false positive or should I take this serious? If so, what's the problem here and how would one check the API / code to find the expected types and so on?
it is a false positive, but if you want to make it disappear use the following
apply plugin: 'distribution'
distributions.create('someNameButMain', {
baseName = 'someName'
contents {
from { 'src/readme' }
}
})

Catel Auto Register Attributes with multiple instances

we are developing a GUI Plug-In Framework using Catel.MVVM.
Single Plugins should be loaded dynamically using the "ServiceLocatorRegistration" Attribute.
Example:
[ServiceLocatorRegistration(typeof(IInitializable), ServiceLocatorRegistrationMode.SingletonInstantiateWhenRequired, "SamplePlugin")]
In our bootstrapper we load all Plugin assemblies into the default AppDomain:
Catel.Windows.Controls.MVVMProviders.Logic.UserControlLogic.DefaultSkipSearchingForInfoBarMessageControlValue = true;
Catel.Windows.Controls.MVVMProviders.Logic.UserControlLogic.DefaultCreateWarningAndErrorValidatorForViewModelValue = false;
IoCConfiguration.DefaultServiceLocator.AutoRegisterTypesViaAttributes = true;
IoCConfiguration.DefaultServiceLocator.CanResolveNonAbstractTypesWithoutRegistration = true;
foreach (
var file in
BaseDirectory.GetFiles("*.dll", SearchOption.AllDirectories)
.Where(f => IsNetAssembly(f.FullName))
.Where(f => !f.FullName.EndsWith("resources.dll"))
.AsParallel())
{
try
{
var asm = Assembly.ReflectionOnlyLoadFrom(file.FullName);
}
catch { }
}
Then we try to initialize them by calling
var initializables = ServiceLocator.Default.ResolveTypes();
foreach(var initializable in initializables)
initializable.Initialize();
But even if we have the plugin assemblies loaded in the AppDomain, we dont get all Classes with the ServiceLocatorRegistration attribute.
Is there any was to resolve all classes that have the example attribute set as above?
Thanks in advance!
The problem is probably because the assemblies containing the types that use the registration are not loaded into the AppDomain yet. There are a few things you can consider:
1) Use AppDomainExtensions.PreloadAssemblies
2) Use the type somehow (like Console.WriteLine(typeof(TypeFromAssembly).FullName))
I wouldn't recommend the second one because it goes against your plug-in architecture.
Resolved this issue myself. Mistake was probably the linevar asm = Assembly.ReflectionOnlyLoadFrom(file.FullName);
After replacing this line withAppDomain.CurrentDomain.LoadAssemblyIntoAppDomain(file.FullName); everything works as expected.
Thanks Geert for pointing to the right direction!

Resources