I have a linear layout existing of an EditText and an ImageView.
When I click anywhere inside the linear layout I need a touch event to be dispatched to this layer, but the EditText is blocking/consuming the touch event.
I want to use onInterceptTouchEvent(ev MotionEvent) but I can't figure out how to set it on the linear layout.
<LinearLayout
android:id="#+id/consultCv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:layout_width="#dimen/grid_3x"
android:layout_height="#dimen/grid_3x"
android:layout_gravity="center"
android:src="#drawable/img_document" />
<EditText
style="#style/TextStyle.EditDisabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="#dimen/grid_1x"
android:text="#string/document" />
</LinearLayout>
class ArchiveActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
}
}
I tried to override onInterceptorTouchEvent() in this activity but it can only be overridden in a view.
Is there a way to treat my linear layout as a view and then implement this as a lambda expression?
Do I need to tear apart my XML file and then rebuild every separate view group in code?
My linear layout is inside a layout with several layers, so I can't just make a view out of it alone.
Help me Obi Wan Kenobi, you'r my only hope!
Related
I decided to finally jump into Kotlin this month as a hobby project. I'm working through a Udacity course (Android app dev with Kotlin).
I was just trying to replace the viewById with View binding (as a test for me). Which works fine.
But why if I use setContentView(view) from the binding does my layout no longer respect the gravity?
My layout file for my main activity is linear, with center-vertical layout_gravity
<<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center_vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
android:textSize="30sp"
android:layout_gravity="center_horizontal"/>
<Button
android:id="#+id/roll_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/roll"
android:layout_gravity="center_horizontal"
/>
</LinearLayout>
When I use the old setContentView(R.layout.main_activity) in my activity, this displays as expected in the center of the screen
class MainActivity : AppCompatActivity() {
#RequiresApi(Build.VERSION_CODES.P)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Let's do it the trad way without binding
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.roll_button)
Image with central gravity
If I swap this out for view binding instead and replace the setContentView as shown below
var binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
The app still runs, but now the text and button are at the top of the screen.
image top aligned
I was just testing that I could make this work in general, as I'd prefer to use in real life (anything to reduce nullPointer exceptions). But if I can't even get a simple example to work properly I'm stuffed.
Does anyone know what I am doing wrong? Or what concept I'm missing?
Try to change android:layout_gravity="center_horizontal" to android:layout_gravity="center" or maybe try to use RelativeLayout I heard that it's one of the best layouts.
I'm having trouble with this same problem. I was looking at the documentation for View Binding and it states:
"View binding doesn't support layout variables or layout expressions,
so it can't be used to declare dynamic UI content straight from XML
layout files."
I'm wondering if it's related to that? I'm still very new to this, but it's the only thing I can find so far that makes some sense to me.
UPDATE: Found another question asking the same thing (How to use View Binding with Linear Layout?)
Changed android:layout_gravity="center_vertical" to android:gravity="center", but then had an issue with the preview in Layout Editor not showing it centered; to remedy that I added tools:layout_gravity="center_vertical" so the activity_main.xml shows this now at the top:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
tools:layout_gravity="center_vertical"
android:orientation="vertical"
tools:context=".MainActivity">
Might not be the best solution, but works to show you the preview and then when you actually run it!
XML layout_-prefixed attributes work with the parent view. When you inflate a view without a parent, the layout_* attributes have no effect.
An activity's setContentView(int) normally delegates to PhoneWindow#setContentView(int) that implicitly uses a parent container layout when inflating the XML layout. That's why the 1-arg inflation works in an activity.
View binding does not implicitly supply any parent layouts. You need to explicitly supply it with the three-arg inflate(int, View, boolean) method call where the first arg is the layout id, second one is the parent and the third controls whether the inflated layout should be added to the parent when inflating.
The usual an easy use case is to use fragments where the onCreateView() callback supplies you with a parent container layout and you can just return inflate(id, container, false).
The PhoneWindow content layout is lazily generated, so you kinda have a chicken-and-egg problem. Calling setContentView() generates the content layout but when calling it you kinda want to already have inflated the view binding with a parent content layout.
My suggestion is to move the layout code away from your activity and instead use a fragment for it. Then you can use view binding in your fragment.
The ViewBinding implementation is unable to render the layout configuration for LinearLayout used in my code, while the same layout works with the older technique of findViewById()
I have setup the gradle to use ViewBinding
android {
...
buildFeatures {
viewBinding = true
}
Below is the activity which uses a LinearLayout
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
... />
<Button
... />
</LinearLayout>
The corresponding Kotlin class includes the binding instance with its root view passed to setContentView()
MainActivity.kt
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
...
setContentView(binding.root)
}
While the Layout editor renders a correct alignment of the app, once it is launched onto the emulator the layout configuration such as center_vertical alignment set using layout_gravity of LinearLayout is ignored.
Is something incorrect or missing the way ViewBinding is implemented?
in your activity_main.xml give id to your LinearLayout
There are one possibility is that your LinearLayout have not any id.
When you are doing binding.root it keeps a reference of root view which is LinearLayout in your case.
As per the official document:
If view binding is enabled for a module, a binding class is generated
for each XML layout file that the module contains. Each binding class
contains references to the root view and all views that have an ID.
Give id to your LinearLayout android:id="rootView, rebuild your project and try to run.
I hope, it will solve your issue.
Thanks & Happy coding..!
Had the same issue, Solved with adding android:gravity="center_vertical" to LinearLayout in activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:orientation="vertical"
android:id="#+id/root_view"
tools:context=".MainActivity">
</LinearLayout>
I had the same problem doing an exercise in the "Developing Android Apps with Kotlin" course.
I did not find a solution because I suppose that without using view binding, a view above the one in main_activity.xml must be taken as root (probably the same full screen), so when using layout_gravity without view binding, it works, because it positions the LinearLayout relative to the screen. However, when using view binding, I assume that it takes the LinearLayout as its root, so layout_gravity does nothing (again, I'm not sure, I don't know very well how view binding works).
The solution I used was to simply try to achieve the same view with something different. For this in activity_main.xml I used:
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"
tools:context=".MainActivity">
I have a LinearLayout with a couple of TextViews stacked vertically. I want to attach a click listener to the parent and make the click event propagate to one of the TextViews (child views of the containing LinearLayout).
I understand that I can attach click listener's to the individual TextViews and achieve the same result but I'd like to do otherwise.
I came across the
android:duplicateParentState="true"
attribute, but this doesn't seem to solve the issue either. What should I be doing to get the child views to consume the containing parent's click event. Also each text view should perform a different action.
Here is the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/linear_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text_1"
android:duplicateParentState="true"
android:layout_marginBottom="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:text="Text_2" />
</LinearLayout>
And here is the Activity:
public class MainActivity extends AppCompatActivity {
LinearLayout mLinearLayout;
TextView mTextView1;
TextView mTextView2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLinearLayout = (LinearLayout) findViewById(R.id.linear_layout);
mLinearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.v("view",view.toString());
}
});
}
}
The log always prints the parent view:
V/view: android.widget.LinearLayout{3ea32ab0 V.E...C. ...PH... 0,0-1080,1533 #7f0c0050 app:id/linear_layout}
V/view: android.widget.LinearLayout{3ea32ab0 V.E...C. ...PH... 0,0-1080,1533 #7f0c0050 app:id/linear_layout}
I am obviously missing something here and hopefully the solution doesn't involve navigating down the view stack. What am I doing wrong, any help would be most appreciated.
You may use setOnTouchListener for your LinearLayout. Inside this OnTouchListener you need to detect a click (GestureDetector can help) in this point you will have MotionEvent, you can get x,y coordinates from this event and compare them with your TextViews coordinates. So you can detect which TextView was clicked. In such a way you can do what you want, but it is complicated, and I believe you can avoid it.
I have a Relativelayout that is clickable. I have an element inside it (an ImageButton) that I also want to be clickable.
The layout was clickable just fine until I added the ImageButton; now only that is clickable.
I have tried all combinations of focusable and focusableInTouchMode = true and false in both elements (I tried only in xml layout). How can I make them BOTH clickable?
My code; note this is inside a ListView and each row has this; there is a non-clickable LinearLayout surrounding this:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/row_selector"
android:duplicateParentState="true"
android:paddingBottom="10dp" >
// several TextViews edited out
<ImageButton
android:id="#+id/ibMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:background="#color/row_overflow_state"
android:contentDescription="menu"
android:src="#drawable/ic_action_overflow" />
</RelativeLayout>
Use this to your parent
android:addStatesFromChildren="true"
if used this then don't use the following otherwise you will get stuck overflow exception
what about setting this to your child
android:duplicateParentState="true"
I had faced the same problem. I had a rectangular view which had to receive clicks for some other functionality, as well as an ImageView inside the rectangle, had to receive clicks for some other functionality.
Used Relative Layout for this purpose where Relative Layout was clickable=true and having the property
android:descendantFocusability="afterDescendants"
which means that the layout will receive focus only if none of its descendants want.
So parent, as well as the child, was clickable for two different functionalities.
Set android:descendantsFocusability="blocksDescendants" on the
RelativeLayout.
Quick question: at runtime I do a boolean check, if it returns true I would like to have two buttons in a relative layout on my MainActivity class. If its false I want to instead have two other widgets where those buttons would be (or near enough). How do I do that?
you could also implement a ViewSwitcher where a more complicated set of buttons/widgets can be switched out very easily with a single call to
ViewSwitcher mViewSwitcher = (ViewSwitcher) findViewById(R.id.viewswitcher);
if (some_logic == true) {
mViewSwitcher.showNext();
}
Set up your XML like this and the above will switch between the two LinearLayouts:
<ViewSwitcher
android:id="#+id/viewswitcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
--- buttons, Views, whatever---
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
--- buttons, Views, whatever---
</LinearLayout>
If you have just those two alternatives put them both in your layout and hide / show the one you want. View#setVisibility()
If you want it more dynamic you can add and remove widgets programmatically. ViewGroup#addView()
Modifying a RelativeLayout during runtime is quite complicated since you need to set all those layout parameters so you could add a simple layout like a FrameLayout in the place where the buttons should go and put them inside the frame. Has the advantage that you can setup all the relative layout parameters for the frame in xml.