Show views currently focused (Even without layout selectors) - android

I'm trying to inspect a code for a very big Android (Amazon Fire TV) activity but i keep loosing the focus in the running app and i don't know what element is being focused.
I'm looking for a way (Wether it's an App, a developer setting - Show Layout Limits gets near - or something i can code inside the activity) to see what view is being focused, without having to change the layout (Selectors) of every single view.
What do you suggest?

Activity has a method called getCurrentFocus().
Maybe you could call hasFocus() on all the Views if the above doesn't work. I imagine the method would look something like this:
public View getFocusedView(View layout)
{
View focusedView = null;
// Note: I'm not sure if FOCUS_DOWN is the right one to use here
// so you may want to see the other constants offered
ArrayList<View> views = layout.getFocusables(View.FOCUS_DOWN)
for(View v: views)
{
if(v.hasFocus())
{
focusedView = v;
}
}
return focusedView;
}

Related

Detect ViewTreeObserver Global Layout last call

I've seen many questions (and answers) regarding ViewTreeObserver but none have completely answer the question/problem i'm facing. So here's what i have:
I have some views being added programmatically inside a scrollview. After all the layout has been set I want to scroll to a specific view that is inside the scrollview. For this I'm using View Tree Observer and the code is as follows (Some pseudo of what I'm doing)
viewInsideScrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public synchronized void onGlobalLayout() {
firstTime++; // Counts how many times I enter the listener
if (firstTime == 1) {
// Here the fully measure of the scroll view has not happened
} else if (firstTime == 2) {
// I still don't have the full measure of the scroll view (Let's say that, for example, paddings are missing/weren't calculated)
} else if (firstTime == 3) {
// Here is when I can safely get the locationOfTheView variable and the scroll down will happen perfectly!
viewInsideScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
scrollView.scrollTo(0, locationOfTheView);
}
}
});
Note: The reason I add firstTime == 3 is because, in this specific case, I noticed that it only calls the global layout 3 times.
So my question is as follows: How can I detect, cleanly, when the Global Layout has finished all the necessary measurements?
Yes I have some work arounds that works like adding a small delay which will give time for the Global Layout to have it's measures done but I was looking for another (cleaner) way to do that.
Any ideas?
Thanks!

Android - onGlobalLayout is not called by ViewTreeObserver

I have a headache in current Android project. I want to detect the changing of the current page. For example, there is a TextView to display device time, which is updated per second. How to detect this change? I searched a lot on SO (thanks SO), but none works for me.
More information: I don't use standard Activity to create page. My way is:
All widgets are created into a View object which is then used to create a container object. After that, I just handle this container to draw on a canvas with a VSYNC callback Choreographer.FrameCallback periodically.
Indeed, it works to draw. All are ok. Except: I want to draw canvas only when the page's content changed. So back to my beginning question, how to detect this "changing" event? I am sure there is some kind of callback to handle this problem. I used the following solution, but onGlobalLayout is not called when textview's text changed.
CanvasAppViewContainer container;//CanvasAppViewContainer extends AbsoluteLayout
LayoutInflater li =(LayoutInflater)getService().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = li.inflate(resourceId, null);//passed a correct the layout id
container = new CanvasAppViewContainer(getService(), view, getWidth(), getHeight(), getSurface());
rootView = (LinearLayout) findViewById(R.id.rootView); //root element of layout
rootView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#SuppressLint("NewApi")
#Override
public void onGlobalLayout() {
Log.d("test", "onGlobalLayout");
}
});
BTW: Even if I register the view tree observer for the textview, onGlobalLayout is still not called.
Thanks
onGlobalLayout() is only called when the layout changes. When you want to detect a change in the text use TextView.addTextChangedListener()

Get on Screen Position of Objects in OnCreate()

I am facing following problem:
I got 4 Buttons over the screen, which can be dragged and move back to their original position when dropping the button.
To do so I want to read out the specific position of the button (on the specific device) by using the getX() and getY() function and writing the results into an array.
buttons[0][0] = button.getX();
buttons[0][1] = button.getY();
As I cannot use these functions in the onCreate or in the onStart (the position is always (0,0) cause the view is not instantiated yet I am thinking about how to read out the original position without any extra coding.
Thanks a lot
In your onCreate—though onResume may make more sense—get a reference to a view that is an ancestor of all the buttons (or just the root of the activity, which may be easiest) and then do this:
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Read values here.
buttons[0][0] = button.getX();
buttons[0][1] = button.getY();
}
});
not sure what you mean by "extra coding" but you could use an OnTouchListener (or modify the one you're already using, if that's the case) to capture the position as soon as they touch the button, but before they drag it anywhere (I think that would be ACTION_DOWN).
This has the advantage of only being set when you need it to, skipping it when they don't actually drag the buttons, and not calling it for every layout pass, etc.

Inflating Android list subviews -> duplicate IDs

I have some content in an Android app which is shown in a list. Each list entry has similar fields - let's say a picture, some text, and a text box. However, some list entries are different than others. The order of the content is based on the result of a server call.
The list itself needs to be fairly dynamic, and I'm currently using a linearlayout rather than a listview for a few reasons. My code looks something like this:
LinearLayout list = findViewById(android.R.id.list);
while (more content to add) {
switch (content type) {
case A:
View v = layoutInflater.inflate(R.layout.list_item_type_a, list, false);
EditText editText = (EditText)v.findViewById(android.R.id.edit);
// Do stuff with editText
list.addView(v);
break;
case B:
View v = layoutInflater.inflate(R.layout.list_item_type_b, list, false);
....
}
}
This works great. Except - when I put this in a fragment, and rotate the screen, now my app crashes because I have multiple fields with the same android.R.id.edit identifier.
I had thought this was a fairly elegant solution and the Android gods seem to disagree. Do I need to rip out the ID for all of my xml sublayouts? If I go this route, how should I grab references to the content?
So obviously, ListView or RecyclerView would be preferable to use here, but since you've stated you have reasons not to, I'd suggest that you disable automatic state saving for each of the views.
You can just call editText.setSaveEnabled(false), which will fix the issue, but have the side effect of not automatically retaining the view's state (e.g. input data will be lost). If you're maintaining this data yourself and restoring it on configuration changes or state restoration, this should be a totally workable solution.
I believe you could also just call setSaveFromParentEnabled(false) on the containing LinearLayout (although I haven't used that flag myself), which should disable state saving for any view in the sub-hierarchy. Same caveat applies.
I would suggest using a ListView instead of the LinearLayout and creating a custom adapter to fill the ListView. You could still have the list_item layouts that you have and then add them to the list in the newView method of your adapter. Pass your content type through a method, say getItemViewType(). Something like this:
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// Choose the layout type
int contentType = getItemViewType();
int layoutId = -1;
switch (contentType) {
case A: {
View v = layoutInflater.inflate(R.layout.list_item_type_a, list, false);
EditText editText = (EditText)v.findViewById(android.R.id.edit);
// Do stuff with editText
list.addView(v);
break;
}
case B: {
View v = layoutInflater.inflate(R.layout.list_item_type_b, list, false);
....
}
}
I think this problem occur due to device screen rotation
When the phone rotates and the screen changes orientation, Android usually destroys your application’s existing Activities and Fragments and recreates them. Android does this so that your application can reload resources based on the new configuration.
The most important aspect of handling orientation changes is saving state. In most cases this involves implementing the onSaveInstanceState method (this could be in your Activity, Fragment or both) and placing the values you need to save in the Bundle argument that gets passed to the method.
For more details and code examples...
Please read this article
If you have a dynamic list, you should be using a ListView or RecyclerView.

onTouchEvent for whole app, without an activity

I am developing an android app which tracks touch events for the whole application. For that purpose, I want to override onTouchEvent() without an activity; i.e., inside a simple Java class, I don't whether it's possible. Any ideas are welcome.
No, you cannot arbitrarily override onTouchEvent, particularly in a "simple Java class", i.e. one that does not extend a View.
Solution 1
If you want to capture all touches to your app, override onInterceptTouchEvent() in your main layout. So, if your main layout is a LinearLayout, do something like:
public class MyTouchLayout extends LinearLayout {
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Do whatever with your touch event
return false; // Do not prevent the rest of the layout from being touched
}
}
Then, in your XML, you'd use:
<com.example.MyTouchLayout
...
>
<!-- The rest of your layout -->
</com.example.MyTouchLayout>
Solution 2
This is by far the simpler solution, though I've had varying luck with it in the past and would never suggest that you reply upon it.
You can simply get the View that contains all your content (the root View, a LinearLayout above), then give it a listener.
View v = findViewById(android.R.id.content); // Find the root View; you may have to give it your own ID in the XML, and use that
v.setOnTouchListener(new OnTouchListener {
// ...
}

Categories

Resources