How to completely make a view off to focus state? I am adding these parameters in the xml but with the keyboard still able to focus the view.
What I have tried till now;
android:focusable="false"
android:focusableInTouchMode="false"
For instance I got 3 views. I want to set View2 unfocusable so if I use keyboard and focus View1 after switch to another one View3 will be in focus and View2 will be ignored..
How can I do that? Any solutions?
Assuming that you dont want the other views to be used when you are using one .
In the onClickListener of each View , you can set the other two to be unusable .This function could do the trick .
//Kotlin Code
private fun setImageViewStatus(view: View, value: Boolean) {
view.apply {
isEnabled = value
isClickable = value
}
}
You can replace view with anything of your choice .
In your XML for View1 try adding a property for nextFocusDown. For example if the ID of View3 is View3 add the following to the XML for View1:
android:nextFocusDown="#+id/View3"
Related
I have a TextView in a layout whos background is a Selector. And the TextView's text is set to Spanned from HTML.
Then I set the TextView with the LinkMovementMethod.
Now when I tap on the TextView, the click event is not sent to its parent layout to trigger the selector.
How should this be solved?
Declare your TextView not clickable / focusable by using android:clickable="false" and android:focusable="false" or v.setClickable(false) and v.setFocusable(false). The click events should be dispatched to the TextView's parent now.
Note:
In order to achieve this, you have to add click to its direct parent. or set
android:clickable="false" and android:focusable="false" to its direct parent to pass listener to further parent.
I think you need to use one of those methods in order to be able to intercept the event before it gets sent to the appropriate components:
Activity.dispatchTouchEvent(MotionEvent) - This allows your Activity to intercept all touch events before they are dispatched to the window.
ViewGroup.onInterceptTouchEvent(MotionEvent) - This allows a ViewGroup to watch events as they are dispatched to child Views.
ViewParent.requestDisallowInterceptTouchEvent(boolean) - Call this upon a parent View to indicate that it should not intercept touch events with onInterceptTouchEvent(MotionEvent).
More information here.
Hope that helps.
Sometime only this helps:
View child = parent.findViewById(R.id.btnMoreText);
child.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
View parent = (View) v.getParent();
parent.performClick();
}
});
Another variant, works not always:
child.setOnClickListener(null);
Put
android:duplicateParentState="true"
in child then the views get its drawable state (focused, pressed, etc.) from its direct parent rather than from itself.
you can set onclick for parent and it call on child clicked
If your TextView create click issues, than remove android:inputType="" from your xml file.
This answer is similar to Alexander Ukhov's answer, except that it uses touch events rather than click events. Those event allow the parent to display the proper pressed states (e.g., ripple effect). This answer is also in Kotlin instead of Java.
view.setOnTouchListener { view, motionEvent ->
(view.parent as View).onTouchEvent(motionEvent)
}
If you want to both OnTouch and OnClick listener to parent and child view both, please use below trick:
User ScrollView as a Parent view and inside that placed your child view inside Relative/LinearLayout.
Make Parent ScrollView android:fillViewport="true" so View not be scrolled.
Then set OnTouch listener to parent and OnClick listener to Child views.
And enjoy both listener callbacks.
Then I set the TextView with the LinkMovementMethod.
TextView.setMovementMethod() internally calls a private method fixFocusableAndClickableSettings. It is the root of the problem: calls setFocusable(FOCUSABLE); setClickable(true); setLongClickable(true);. So no matter what clickability you think you've set, it'll be all true.
source
Those 3 flags have to be reset back to false in order for the view to become non-clickable.
So suppose I have a textview called tvPlaceholder with the following property android:layout_alignParentEnd="true". How would I go about setting this to be false in the code (Kotlin)? I know you can change things like visibility, text size, e.t.c. in the code but could not find a definitive answer for layout_alignParentEnd. I am also calling tvPlaceholder from a viewholder if that matters at all: viewholder.tvPlaceholder.
When a view is added to a parent, it adds a LayoutParams object that defines any rules for laying it out. You can get this object by calling getLayoutParams(). Each layout parent has their own layout params subclass, I'm assuming this is a relative layout. So for a RelativeLayout, the LayourParams has a function removeRule that can do this.
val params = view.getLayoutParams() as RelativeLayout.LayoutParams
params.removeRule(RelativeLayout.ALIGN_PARENT_END)
I am actually developing an android application which needs to inflates many times the same XML layout.
This layout contains 2 buttons, some textViews and a progressBar which I'll need to update later. I would like to add onClick listeners to the buttons and to set custom tags with setTag() to all of these elements, so I will be able to know which button has been clicked and to modify the right textView (or progressBar).
I inflate the XML with this code :
LinearLayout countersList = (LinearLayout)findViewById(R.id.countersLayout);
View child = getLayoutInflater().inflate(R.layout.counter, null);
countersList.addView(child);
How can I access to the right view to set the tag and to add listeners? Is there a better way to do what I want to do ?
Thank you very much !
As far as how to tag or add onClick listeners to your views: You can add an ID to the views you want to tag and find them using findViewById. For example,
LinearLayout countersList = (LinearLayout)findViewById(R.id.countersLayout);
ViewGroup child = (ViewGroup) getLayoutInflater().inflate(R.layout.counter, null);
child.findViewById(R.id.myButton).setTag("myTagForTheButton");
countersList.addView(child);
On the second question, I'm not sure what your UI looks like, but many repeated views might call for using a ListView.
There is no problem of setting tags and listener
LinearLayout countersList = (LinearLayout)findViewById(R.id.countersLayout);
View child = getLayoutInflater().inflate(R.layout.counter, null);
child.setTag("YourString");
// Similarly
view.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
}
});
countersList.addView(child);
If you want it for some of its child you can also do it using findViewById
you can user view.setTag(key, tag) to many tags.
ex:
view.setTag("right_view", rightView);
I'm having some problems when trying to remove the header from a listView. At first I use addHeaderView() to add it, but when I change to another layout I want it to disappear but removeHeaderView() doesn't work...
I also tried setting visibility to GONE and it doesn't disappear...
What can I do?
Thanks in advance
Try the approach mentioned below..
Android ListView#addHeaderView and ListView#addFooterView methods are strange: you have to add the header and footer Views before you set the ListView's adapter so the ListView can take the headers and footers into consideration -- you get an exception otherwise. Here we add a ProgressBar (spinner) as the headerView:
// spinner is a ProgressBar
listView.addHeaderView(spinner);
We'd like to be able to show and hide that spinner at will, but removing it outright is dangerous because we'd never be able to add it again without destroying the ListView -- remember, we can't addHeaderView after we've it's adapter:
listView.removeHeaderView(spinner); //dangerous!
So let's hide it! Turns out that's hard, too. Just hiding the spinner view itself results in an empty, but still visible, header area.
Now try to hide the spinner:
spinner.setVisibility(View.GONE);
Result: header area still visible with an ugly space:
The solution is to put the progress bar in a LinearLayout that wraps it's content, and hiding the content. That way the wrapping LinearLayout will collapse when its content is hidden, resulting in a headerView that is technically still present, but 0dip high:
<LinearLayout
xmlns:a="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!-- simplified -->
<ProgressBar
android:id="#+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Then, set the layout as the header:
spinnerLayout = getLayoutInflater().inflate(R.layout.header_view_spinner, null);
listView.addHeaderView(spinnerLayout);
And when we need to hide it, hide the layout's content, not the layout:
spinnerLayout.findViewById(R.id.spinner).setVisibility(View.GONE);
Now the header disappears from view. No more ugly space at the top!
Most people don't like to use AddHeaderView, however I sometimes found it very convenient, to avoid modifying complex adapters, or if the headers are not very related to them.
With this easy trick you will be able to seamlessly remove/add headers:
I add an empty LinearLayout with orientation vertical, and height wrap_content, as the only Header View (let mListView be the target listView):
LinearLayout mCustomHeaders=new LinearLayout(context);
mCustomHeaders.setOrientation(LinearLayout.VERTICAL);
mListView.addHeaderView(mCustomHeaders);
mListView.setAdapter (.......)
Thenafter, I can add random stuff, anywhere, to the list as header, even when the list is full:
mCustomHeaders.add(myHeaderView1);
mCustomHeaders.add(myHeaderView2);
mCustomHeaders.add(myHeaderView3);
You can also delete all headers, anytime:
mCustomHeaders.removeAllViews(); // will erase all headers
You get the idea .... Hope it helps !
The problem is that you are always creating a new object when you do:
View headerView = someView
So the new view is not the same as the view already added as listView header, try this:
View headerView = inflater.inflate(R.layout.adapter_datatable_saleitem_header, null, false);
headerView.setTag(this.getClass().getSimpleName() + "header");
if (listView.getHeaderViewsCount() > 0) {
View oldView = listView.findViewWithTag(this.getClass().getSimpleName() + "header");
if (oldView != null) {
listView.removeHeaderView(oldView);
}
}
You can check if header count > 0 then remove the header and add it again.
its works fine for me.
View _headerView;
private void function HandleHeaderView(){
if(listView.getHeaderViewsCount() > 0)
{
listView.removeHeaderView(_headerView);
}
/* Add list view header */
_headerView = GetHeaderView();
listView.addHeaderView(_headerView);
}
private View GetHeaderView()
{
View header = getLayoutInflater().inflate(R.layout.header_layout, null);
// TODO: ...
return header
}
Where drawerLogoView is my headerview, here's what I do:
drawerLogoView = mInflater.inflate(R.layout.navigation_drawer_custom_layout, null);
mDrawerList.addHeaderView(drawerLogoView,null,false);
LinearLayout layOut = ((LinearLayout)drawerLogoView.findViewById(R.id.NavProfilePreviewLayout));
layOut.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 0));
That way, it becomes invisible :D
To show it back, you can use this:
LinearLayout layOut = ((LinearLayout)drawerLogoView.findViewById(R.id.NavProfilePreviewLayout));
layOut.setLayoutParams(newRelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
If you are using addHeaderView(), you can't delete your header after that.
So, don't use addHeaderView(). Rather, create your own adapter that
blends your main roster of Views with your header. While my
MergeAdapter will not handle your specific case, you can use it to see
the concept of blending multiple row sources:
https://github.com/commonsguy/cwac-merge
I encountered this problem in a slightly disguised scenario: the ListView I was dealing with came from PreferenceFragment, and the header represents a PreferenceCategory. So, my freedom of setting up the ListView was severely limited. But there were two approaches (partly inspired by other answers on this page). One was to add a custom layout to my PreferenceCategory (using a class that extends android.preference.PreferenceCategory, see Custom PreferenceCategory Headings). But I found an easier workaround: for the first Preference in this PreferenceCategory, I override onCreateView():
#Override public View onCreateView(ViewGroup parent) {
parent.setTop(-parent.getChildAt(0).getTop());
return super.onCreateView(parent);
}
I have a TextView in a layout whos background is a Selector. And the TextView's text is set to Spanned from HTML.
Then I set the TextView with the LinkMovementMethod.
Now when I tap on the TextView, the click event is not sent to its parent layout to trigger the selector.
How should this be solved?
Declare your TextView not clickable / focusable by using android:clickable="false" and android:focusable="false" or v.setClickable(false) and v.setFocusable(false). The click events should be dispatched to the TextView's parent now.
Note:
In order to achieve this, you have to add click to its direct parent. or set
android:clickable="false" and android:focusable="false" to its direct parent to pass listener to further parent.
I think you need to use one of those methods in order to be able to intercept the event before it gets sent to the appropriate components:
Activity.dispatchTouchEvent(MotionEvent) - This allows your Activity to intercept all touch events before they are dispatched to the window.
ViewGroup.onInterceptTouchEvent(MotionEvent) - This allows a ViewGroup to watch events as they are dispatched to child Views.
ViewParent.requestDisallowInterceptTouchEvent(boolean) - Call this upon a parent View to indicate that it should not intercept touch events with onInterceptTouchEvent(MotionEvent).
More information here.
Hope that helps.
Sometime only this helps:
View child = parent.findViewById(R.id.btnMoreText);
child.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
View parent = (View) v.getParent();
parent.performClick();
}
});
Another variant, works not always:
child.setOnClickListener(null);
Put
android:duplicateParentState="true"
in child then the views get its drawable state (focused, pressed, etc.) from its direct parent rather than from itself.
you can set onclick for parent and it call on child clicked
If your TextView create click issues, than remove android:inputType="" from your xml file.
This answer is similar to Alexander Ukhov's answer, except that it uses touch events rather than click events. Those event allow the parent to display the proper pressed states (e.g., ripple effect). This answer is also in Kotlin instead of Java.
view.setOnTouchListener { view, motionEvent ->
(view.parent as View).onTouchEvent(motionEvent)
}
If you want to both OnTouch and OnClick listener to parent and child view both, please use below trick:
User ScrollView as a Parent view and inside that placed your child view inside Relative/LinearLayout.
Make Parent ScrollView android:fillViewport="true" so View not be scrolled.
Then set OnTouch listener to parent and OnClick listener to Child views.
And enjoy both listener callbacks.
Then I set the TextView with the LinkMovementMethod.
TextView.setMovementMethod() internally calls a private method fixFocusableAndClickableSettings. It is the root of the problem: calls setFocusable(FOCUSABLE); setClickable(true); setLongClickable(true);. So no matter what clickability you think you've set, it'll be all true.
source
Those 3 flags have to be reset back to false in order for the view to become non-clickable.