I have an AR Screen where I placed some 3d objects. I am using google AR Core with android SDK.
I need to remove those objects from the scene on clicking of a clear button.
I was able to remove the last placed object. But the other objects were not removing.
This is the code i am using.
if (newAnchor != null) {
arFragment.getArSceneView().getScene().removeChild(newAnchor);
newAnchor.getAnchor().detach();
you can detach android model from scene form using below code
List<Node> children = new ArrayList<>(arFragment.getArSceneView().getScene().getChildren());
for (Node node : children) {
if (node instanceof AnchorNode) {
if (((AnchorNode) node).getAnchor() != null) {
((AnchorNode) node).getAnchor().detach();
}
}
if (!(node instanceof Camera) && !(node instanceof Sun)) {
node.setParent(null);
}
}
You can detect if a user has tapped on a particular node like this - in this example the check is to see if the hit node is an 'Andy' renderable, i.e. the default example renderable with Sceneform:
private void handleOnTouch(HitTestResult hitTestResult, MotionEvent motionEvent) {
Log.d(TAG,"handleOnTouch");
// First call ArFragment's listener to handle TransformableNodes.
arFragment.onPeekTouch(hitTestResult, motionEvent);
// Check for touching a Sceneform node
if (hitTestResult.getNode() != null) {
Log.d(TAG,"handleOnTouch hitTestResult.getNode() != null");
Node hitNode = hitTestResult.getNode();
if (hitNode.getRenderable() == andyRenderable) {
//DO whatever you need to here...
}
return;
And you can delete a node like this:
private void removeAnchorNode(AnchorNode nodeToremove) {
//Remove an anchor node
if (nodeToremove != null) {
arFragment.getArSceneView().getScene().removeChild(nodeToremove);
anchorNodeList.remove(nodeToremove);
nodeToremove.getAnchor().detach();
nodeToremove.setParent(null);
nodeToremove = null;
} else {
//Handle error case here
}
}
Full code available here: https://github.com/mickod/LineView - the above are edited excerpts.
If you need to cleanup the scene, you can do the following:
arSceneView.getScene().callOnHierarchy(node -> {
node.setParent(null);
if (node instanceof AnchorNode) {
((AnchorNode) node).getAnchor().detach();
}
}
Related
I have a project that use Android to display Unity Player. So I export Untiy project as Android module which implemented by Android Application.
I create buttons in Android Activity which contains UnityPlayer, And when I click button, it send a message to Unity Player to invoke C# function, just like this:
findViewById(R.id.btnChange).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mUnityPlayer.UnitySendMessage("ScriptHolder", "ChangeSkin", "");
}
});
And the function named "ChangeSkin" is just to change some GameObjects' active. Just like this:
void ChangeSkin()
{
int prefab;
if (_currentPrefab == PREFAB_DEFAULT)
{
prefab = PREFAB_PRINCESS;
}
else
{
prefab = PREFAB_DEFAULT;
}
ShowSkin(prefab);
}
private void ShowSkin(int prefab)
{
_currentPrefab = prefab;
foreach (var item in _defaultDressList)
{
item.SetActive(prefab == PREFAB_DEFAULT);
}
foreach (var item in _princessDressList)
{
item.SetActive(prefab == PREFAB_PRINCESS);
}
}
And something weird happening: when I click button to change the person's cloth in Unity, the GameObjects which called SetActive(true) show at the position above the right position for a frame and become normal, it looks like they flash. Here is the gif of the project demo:
It looks like the position offset is equal to the height of status bar. If I create a button on Unity Scene and call "ChangeSkin" function, everything will be OK.
I tried all I can to fix this but not succeed. So I hope you will help me, thx.
You should check your Unity layout. If you have a layout group that has content size fitter component, and the child objects also have it (content size fitter), this could cause these glitches.
I fixed this problem by using a flag (or trigger). Just like this:
// set value true to trigger ChangeSkin() in Update(),
// instead of invoke ChangeSkin() directly
private bool _changingSkinTrigger = false;
void ChangeSkin()
{
if (_currentPrefab == PREFAB_DEFAULT)
{
_currentPrefab = PREFAB_PRINCESS;
}
else
{
_currentPrefab = PREFAB_DEFAULT;
}
_changingSkinTrigger = true;
}
void Update()
{
if (_changingSkinTrigger)
{
_changingSkinTrigger = false;
ShowSkin();
}
}
private void ShowSkin()
{
foreach (var item in _defaultDressList)
{
item.SetActive(_currentPrefab == PREFAB_DEFAULT);
}
foreach (var item in _princessDressList)
{
item.SetActive(_currentPrefab == PREFAB_PRINCESS);
}
}
Thank #AndriyMarshalek for enlightening me.
I have RecyclerView which contains user bookmarks. The plan is bookmarked item will be marked with certain icon. This is my code in onBindViewHolder():
// ...
if (bookmarks != null) {
for (BookmarkModel bookmarkData : bookmarks) {
if (bookmarkData.getLetterId() == letter && bookmarkData.getEntryId() == entry) {
holder.imgBookmark.setVisibility(View.VISIBLE);
} else {
holder.imgBookmark.setVisibility(View.INVISIBLE);
}
}
}
However, the RecyclerView is not showing all bookmark icons, only a few of them. Currently I have 3 bookmarks yet it only shows 1 of them. I have debug it and verified that holder.imgBookmark.setVisibility(View.VISIBLE) have been called 3 times. How to update the image properly?
I forgot to put break when the letter and entry match. Because of it, only the last match would show the icon.
if (bookmarks != null) {
for (BookmarkModel bookmarkData : bookmarks) {
if (bookmarkData.getLetterId() == letter && bookmarkData.getEntryId() == entry) {
holder.imgBookmark.setVisibility(View.VISIBLE);
break;
} else {
holder.imgBookmark.setVisibility(View.INVISIBLE);
}
}
}
I'm working in Accessibility Service. i need to get the app name using Accessibility Service. i have studied the documentation of the Accessibility Service in developer's of Android website. but there is no mention about getting the app name using Accessibility.
I also want to extract text from "TextViews" of the other apps(Activities) running in background. How i can do this..
I'm assuming you know how to implement an AccessibilityService.
Retrieving window content:
First register for TYPE_WINDOW_CONTENT_CHANGED events.
#Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent){
int eventType = accessibilityEvent.getEventType();
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
ArrayList<AccessibilityNodeInfo> textViewNodes = new ArrayList<AccessibilityNodeInfo>();
findChildViews(rootNode);
for(AccessibilityNodeInfo mNode : textViewNodes){
if(mNode.getText()==null){
return;
}
String tv1Text = mNode.getText().toString();
//do whatever you want with the text content...
}
break;
}
}
Method findChildViews() :
private void findChildViews(AccessibilityNodeInfo parentView) {
if (parentView == null || parentView.getClassName() == null || ) {
return;
}
if (childCount == 0 && (parentView.getClassName().toString().contentEquals("android.widget.TextView"))) {
textViewNodes.add(parentView);
} else {
for (int i = 0; i < childCount; i++) {
findChildViews(parentView.getChild(i));
}
}
}
}
As far as i know, there's no assured way to get the app name, but you can try fetching the text content from the event object you get from TYPE_WINDOW_STATE_CHANGED events.
Try dumping accessibilityEvent.toString() & you'll know what i'm talking about.
Also, accessibilityEvent.getPackageName() is a simple way to get package name of that app; in case you find it useful!
previous answer is missing definition of childCount
int childCount = parentView.getChildCount();
I have a written a multi-touch system that works correctly with objects that have collider2d (use them as buttons) and i can move the player in my android phone.
But when i use an image from the UI canvas system and add this code to the image, it doesn't detect any thing ?!
here is my code :
using UnityEngine;
using System.Collections;
public class Jump : MonoBehaviour {
public float jumpForce;
private GameObject hero;
void Start () {
hero = GameObject.Find("hero");
}
void Update () {
if (Input.touchCount > 0)
{
Touch[] myTouches = Input.touches;
for(int i = 0; i < Input.touchCount; i++)
{
if(Input.GetTouch(i).phase == TouchPhase.Began)
{
CheckTouch(Input.GetTouch(i).position, "began");
}
else if (Input.GetTouch(i).phase == TouchPhase.Ended)
{
CheckTouch(Input.GetTouch(i).position, "ended");
}
}
}
}
void CheckTouch (Vector3 pos, string phase)
{
Vector3 wp = Camera.main.ScreenToWorldPoint (pos);
Vector2 touchPos = new Vector2 (wp.x, wp.y);
Collider2D hit = Physics2D.OverlapPoint(touchPos);
if(hit.gameObject.name == "JumpButton" && hit && phase == "began")
{
hero.rigidbody2D.AddForce(new Vector2(0f, jumpForce)); //Add jump force to hero
audio.Play ();
}
}
}
any help?
For Graphics items in canvas you need to use GraphicRaycaster instead of Physics2D using UnityEngine.UI
You don't have to work with touches yourself anymore. Just implement IPointerClickHandler interface and make sure you have EventSystem and an appropriate raycaster present in the scene.
I am trying to implement an AccessibilityService that records the user's actions (only click events at this point) and stores them, such that they can be replayed at a later point in time.
For this I register for AccessibilityEvent.TYPE_VIEW_CLICKED events and check if I could somehow recover them later on (when all I have is a reference to the window / root node of the activity) by using two strategies:
Get the clicked view's id and look for this id in the root node's tree
Get the clicked view's text and look for this text in the root node's tree
I have tested this in various applications and different parts of the Android system and the results have been very confusing. About half of the views were not recoverable by any of the two strategies, and some views were sometimes reported as being recoverable and sometimes not. I found out that the latter was due to a race condition, since the accessibility service runs in a different process than the application to which the clicked views belong.
My question now is whether there is a better way to get a handle to a view in an accessibility and find this view again in a later execution of the application.
Below you find the code of my AccessiblityService class:
public class RecorderService extends AccessibilityService {
private static final String TAG = "RecorderService";
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
switch (event.getEventType()) {
case AccessibilityEvent.TYPE_VIEW_CLICKED:
AccessibilityNodeInfo node = event.getSource();
if (node == null) {
Log.i(TAG, "node is null");
return;
}
AccessibilityNodeInfo root = getRootInActiveWindow();
if (root == null) {
Log.i(TAG, "root is null");
return;
}
// Strategy #1: locate node via its id
String id = node.getViewIdResourceName();
if (id == null) {
Log.i(TAG, "id is null");
} else {
List<AccessibilityNodeInfo> rootNodes = root.findAccessibilityNodeInfosByViewId(id);
if (rootNodes.size() == 1) {
Log.i(TAG, "success (via id)");
return;
} else {
Log.i(TAG, "multiple nodes with that id");
}
}
// Strategy #2: locate node via its text
CharSequence text = node.getText();
if (text == null) {
Log.i(TAG, "text is null");
} else {
List<AccessibilityNodeInfo> rootNodes = root.findAccessibilityNodeInfosByText(text.toString());
if (rootNodes.size() == 1) {
Log.i(TAG, "success (via text)");
return;
}
}
Log.i(TAG, "failed, node was not recoverable");
}
}
#Override
protected boolean onKeyEvent(KeyEvent event) {
Log.i("Key", event.getKeyCode() + "");
return true;
// return super.onKeyEvent(event);
}
#Override
public void onInterrupt() {
}
}
I am developing this on SDK Version 21 (Lollipop) and testing it on a HTC Nexus M8 and a Samsung Galaxy Note2, both showing similar results.