Skip to content

feat(android): parity for Ti.UI.View clipMode #14191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft

Conversation

m1ga
Copy link
Contributor

@m1ga m1ga commented Mar 22, 2025

Based on https://github.com/mbender74/clipview/tree/main/ti.clipview

Top: this PR with clipping disabled
bottom: default behavior
Screenshot_20250322-115430

Run the code and click the first view to toggle:

var win = Ti.UI.createWindow();
var view = Ti.UI.createView({
	top: 0,
	backgroundColor: 'yellow',
	height: 300,
	width: 400,
	clipMode: Ti.UI.CLIP_MODE_DISABLED
});
var nonClippingView = Ti.UI.createView({
	backgroundColor: 'red',
	height: 200,
	width: 200,
	clipMode: Ti.UI.CLIP_MODE_DISABLED
});
var insideViewB = Ti.UI.createView({
	backgroundColor: 'blue',
	height: 10,
	width: 300,
});
view.add(nonClippingView)
nonClippingView.add(insideViewB);
win.add(view);

var contentView = Ti.UI.createView({
	top: 330,
	backgroundColor: 'yellow',
	height: 300,
	width: 400,
});
var clippingView = Ti.UI.createView({
	backgroundColor: 'red',
	height: 200,
	width: 200
});
var insideViewA = Ti.UI.createView({
	backgroundColor: 'blue',
	height: 10,
	width: 300
});
contentView.add(clippingView);
clippingView.add(insideViewA);
win.add(contentView);
var toggle = true;
view.addEventListener("click", function() {
	if (toggle) {
		view.clipMode = Ti.UI.CLIP_MODE_ENABLED
		nonClippingView.clipMode = Ti.UI.CLIP_MODE_ENABLED
	} else {
		view.clipMode = Ti.UI.CLIP_MODE_DISABLED
		nonClippingView.clipMode = Ti.UI.CLIP_MODE_DISABLED
	}
	toggle = !toggle;
});
win.open()
  • iOS works now too with Ti.UI.CLIP_MODE_DISABLED and Ti.UI.iOS.CLIP_MODE_DISABLED for backwards compatibility

@m1ga m1ga marked this pull request as ready for review April 6, 2025 15:19
@m1ga m1ga changed the title feat(android): parity for Ti.UI.VIew clipMode feat(android): parity for Ti.UI.View clipMode Apr 6, 2025
@hansemannn
Copy link
Collaborator

cc @prashantsaini1

Copy link
Contributor

@prashantsaini1 prashantsaini1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m1ga I tested the PR and it does not work as intended. It does not allow to change property once set. Also it does not work if we remove the clipMode from the root yellow view despite that the red view has it clipMode: disabled already.

&& nativeView instanceof ViewGroup viewGroup
&& viewGroup.getParent() == null) {
viewGroup.setClipChildren(false);
viewGroup.setClipToPadding(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • setClipToPadding only works if the view has a padding set, which we don't support at this moment.
  • This may even work unexpected inside ListView. setClipToChildren and setClipToPadding serves entirely different purposes.
  • In iOS, clipsToBounds is equivalent to setClipToChildren, but there's no direct equivalent to Android's setClipToPadding.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good to know. I only used both because of the example module. I can remove the padding one

@m1ga
Copy link
Contributor Author

m1ga commented Apr 30, 2025

@m1ga I tested the PR and it does not work as intended. It does not allow to change property once set. Also it does not work if we remove the clipMode from the root yellow view despite that the red view has it clipMode: disabled already.

Correct. Check the readme notes Android: This property is "creation only" and has to be on the top-level view..

@prashantsaini1
Copy link
Contributor

Ok, I didn't check the readme notes, but rather compared this property with iOS where it properly works as expected. I believe we should do more testing around it as Android has a pretty different behaviour to both these properties. And yes, it requires to be on top-level view, which actually we can set ourselves in SDK.

I suggest that we introduce the padding property as well so we can give more flexibilities to use setClipToPadding() which allows to create more immersive scrolling/animation experience. Though a low priority for me 😄

@m1ga
Copy link
Contributor Author

m1ga commented Apr 30, 2025

And yes, it requires to be on top-level view, which actually we can set ourselves in SDK.

very good idea! Yeah, I'll do some more testing and adjust the code but thanks for the check and the feedback 👍

@m1ga m1ga marked this pull request as draft April 30, 2025 07:52
{

if (nativeView instanceof ViewGroup viewGroup) {
if (clipMode == UIModule.CLIP_MODE_DISABLED) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just the setClipChildren(clipMode != UIModule.CLIP_MODE_DISABLED) is fine. Further we can internally set this on top-most view as discussed earlier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks for that 👍

I've put it into draft as I know that parts are still missing especially the parent part. I've added your other suggestions. Thanks for the review 👍

@@ -1197,6 +1207,14 @@ private void applyCustomBackground(boolean reuseCurrentDrawable)
}
}

private void setClipMode(int clipMode)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should work to get the root view (I haven't tested it though).

private void setClipMode(int clipMode)
{
	if (nativeView == null) {
		return;
	}

	View rootView = nativeView;
	while (rootView.getParent() != null && rootView instanceof ViewGroup) {
		rootView = (View) rootView.getParent();
	}

	if (rootView instanceof ViewGroup viewGroup) {
		viewGroup.setClipChildren(clipMode != CLIP_MODE_DISABLED);
	}	
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error: android.view.ViewRootImpl cannot be cast to android.view.View think it's one level to high. But I'll do some more tests at another point

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove the (View) casting, and declare rootView as ViewParent, even or simply keep it as an Object as it's already casted in next if condition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants