Android - propagating click from parent to child - android

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.

Related

RecyclerView not clickable

I have a LinearLayout with a nested RecyclerView showing a list of items. I'd like to open a popup when RecyclerView is clicked (either one of the items or the background white area), but the usual setOnClickListener is not working.
Of course I can put a click listener on each of the items, but the white area between them remains unclickable.
Is there a way to make the entire RecyclerView area clickable?
EDIT: I've added some sample code. I'd like to have the entire layout clickable to open a popup, but while the first three views behave properly, the RecyclerView does not.
<LinearLayout
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="#dimen/spacing_half"
android:background="#color/color_item_margin_divider"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/fragment_tags_title"
style="#style/ItemFragmentHeader"/>
<View
android:layout_width="match_parent"
android:layout_height="#dimen/spacing_line"
android:background="#color/color_line_divider"/>
<RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/spacing_half"/>
</LinearLayout>
Add onClickListener in the viewHolder . Below is a snippet of my project where I had implemented Listener
public class MyViewHolder extends RecyclerView.ViewHolder {
public ImageView shotThumbnail;
public MyViewHolder(View view) {
super(view);
shotThumbnail = (ImageView) view.findViewById(R.id.shotThumbnail);
shotThumbnail.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Put here stuff that you want the onclickListener should do
}
});
Check if this link helps:
Detect click on RecyclerView outside of items
You should use padding instead of margin.
<RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_padding="#dimen/spacing_half"/>
Cause whenever you use padding it will crop your RecyclerView. But whenever you use padding it will just shorten your inside functions. hence you need padding. Then you should use an onClickListener in MainActivity.
recyclerView.setOnClickListener(v->{
//Do whatever you want
});

How to make EditText's parent clickable?

Suppose I have this pink box:
It consists of LinearLayout with its children: TextView as field name and an EditText. EditText is intentionally disabled. What I want is that, user can click wherever user wants on that pink box. By the way, please just ignore any UI/UX things that you found weird.
I've tried, but user can't tap the area that EditText occupies. User have to tap in TextView or blank area on pink box so that the apps got the 'click'. But if user taps on EditText's area, nothing will happen.
I've tried playing with some things in xml's properties, such as set LinearLayout's clickable to true, and all children or just EditText's properties of clickable, focusable, and focusableInTouchMode to false, all to no avail. EditText area still cannot be clicked.
Any idea? Can't it be reached just through xml? Should it be done programmatically just to turn bypass EditText's click?
You can simply add onTouch Listener instead of click Listener.
You need to request the parent layout (LinearLayout, whatever) and loop through the views if you dont want to bind them all. If you use databinding its easier. Anyway, here is a solution (a small piece of code is required!).
Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="out of focus"/>
<LinearLayout
android:id="#+id/linearTest"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="test for clicking"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="another clicking test"
/>
<EditText
android:id="#+id/editTest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="focus edittext when clicking linearlayout or other elements inside"
/>
</LinearLayout>
</LinearLayout>
Code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.linear_focus_test);
final EditText editText = (EditText) findViewById(R.id.editTest);
LinearLayout linearTest = (LinearLayout) findViewById(R.id.linearTest);
for (int i = 0; i < linearTest.getChildCount(); i++)
View v = linearTest.getChildAt(i);
v.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
editText.requestFocus();
}
});
}
}
If you dont like boilerplate you may also use lambda (use 1.8 features)
for (int i = 0; i < linearTest.getChildCount(); i++)
linearTest.getChildAt(i).setOnClickListener(v1 -> editText.requestFocus());
If you use at least API 24 you can even make it shorter:
IntStream.range(0, linearTest.getChildCount()).forEach(i -> linearTest.getChildAt(i).setOnClickListener(v1 -> editText.requestFocus()));

android which layout for elements side by side

I'm new on android programmation. I want to achieve a simple layout with elements side by side inside a scrollview. The idea is to process a single element per time with image and text, letting the layout choose when its the right time for the carriage return, in dependence of the screen resolution. I tried every type of layout, but no one seems to be suitable for my purpose. Particulary with Relative Layout elements are overlapped, instead what I need is an spatial append. Before to try a workaroud (for example adding more element in a row inside a linear layout) i would to know if exists a more natural solution.
(source: youth-stories.com)
I create an example activity to try the solutions:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final RelativeLayout container = (RelativeLayout)findViewById(R.id.container);
for(int i=0; i < 100; i++)
{
final Button button = new Button(this);
button.setText("sda"+i);
button.setId(i);
container.addView(button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
container.removeView(button);
}
});
}
}
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="0dp"
android:id="#+id/outer"
android:tileMode="disabled" android:gravity="top">
<ImageView android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/background"
android:scaleType="centerCrop"/>
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/scrollView" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/container"></RelativeLayout>
</ScrollView>
</RelativeLayout>
From the above fig given, you can use GridView to draw UI as given. You can specify spacing between items also how many columns each row consists.
For reference check developer doc.
Check here for GridView example and doc
For the diagram shown you can go for a Grid Layout, you can customize grid layout for spacing between cells.
If it still doesn't fit your need then, my suggestion would be Linear layout with layout weights,however nested weights are a performance overhead.
http://developer.android.com/reference/android/widget/GridLayout.html
http://androidexample.com/Custom_Grid_Layout_-_Android_Example/index.php?view=article_discription&aid=76&aaid=100
Why are nested weights bad for performance? Alternatives?
Hope this help!!

ViewFlipper's onTouch event is NOT fired while its child Button's onClick event is

I've been struggling to find a solution in StackOverflow or elsewhere, but I couldn't find any direct post talking about this particular issue, maybe except for the possibly closest one that I can no longer find anymore. However, sorry in advance if this's simply because I overlook something or I'm just such a big noob.
Anyway, I'm trying to set OnTouchListener to a ViewFlipper (parents) and setOnClickListener to a Button (child) which fills the parent layout-wise. I wanted ViewFlipper's onTouch() event to be called first. Thought if ViewFlipper's onTouch() returned false, Button's onClick() was fired. However, only the Button's onClick() was called. Why? Is there any defined priority?
Alternatively, I could set an onTouchListener to the Button, since the Button has the 'match_parent' attribute, so touching this viewflipper was virtually the same as touching this button, even if I did so, it just makes Button's onClick() event unfired....
Below is my simplified Activity;
public class TestActivity extends Activity implements OnTouchListener, View.OnClickListener {
#Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ViewFlipper vf = (ViewFlipper) findViewById(R.id.viewFlipper1);
Button btn = (Button) this.findViewById(R.id.btnTest1);
vf.setOnTouchListener(this);
btn.setOnClickListener(this);
}
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ViewFlipper
android:id="#+id/viewFlipper1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<include
android:id="#+id/button"
layout="#layout/test" />
</ViewFlipper>
</LinearLayout>
test.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/testLinear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:orientation="vertical" >
<Button
android:id="#+id/btnTest1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Button" />
</LinearLayout>
I still don't grasp why ViewFlipper's onTouch event wasn't called first, but I found a way to kinda escape from this issue. We can set both onClickListener and onTouchListener to the button.
btn.setOnClickListener(this);
btn.setOnTouchListener(this);
As talked in the original post it prevents the onClick() event from happening, but whatsoever? If it's not called by android system, then we should call it manually.
#Override public boolean onTouch (View v, MotionEvent event) {
// walls of codes
this.onClick(v);
I know I need some adjustments. I wouldn't call it 'Click' if user takes more than 0.5 second to get his finger off the screen.

View.layout() works until next UI update

My launching activity consists of Linear layout with two buttons. Both buttons have listeners: the first one (b) moves itself upon click: 30px to the left and 30px back upon the next click.
The second one (b2) changes its text upon click. Here is the code:
public class TestActivity extends Activity {
public final String TAG="TestActivity";
boolean toTop=true;
boolean setInitialText=false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b=(Button)findViewById(R.id.button);
Button b2=(Button)findViewById(R.id.button2);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
int modifier;
if(toTop) modifier=-30;
else modifier=30;
v.layout(v.getLeft()+modifier,v.getTop(),v.getRight()+modifier,v.getBottom());
toTop=!toTop;
}
});
b2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String currentText;
if(setInitialText)currentText="Press to change text";
else currentText="Press to change back";
((Button)v).setText(currentText);
setInitialText=!setInitialText;
}
});
}
}
XML layout-file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press to begin animation" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press to change text" />
My problem: when b is moved to the left and I press b2, b moves to its initial position. Why? I don't want it to move back and didn't specify it anywhere.
It looks like View.layout looses its effect. Why it happens? I tested that in other situations and it seems that any UI update makes all invoked View.layout methods loose their effect.
In my main project there is a ListView which is populated with images from background - all view moves loose effect when new image appears. Besides, if I add EditText and try to enter something (as user), view moves loose their effect as well. Can anyone explain to me what's going on and why do views move back?
Looks like parent layout repositions it's siblings after you set a new text for the button2, because as describe in xml, button2 wraps it's content by width and height.
As you changed button's content, it requests it's parent layout to get a new position for it. In this case parent layout will recalculate position values for all of it's siblings. That's why button1 also comes back to it's previous position.
Keep in mind, that you also set the gravity value for parent layout as center, that means that when layout will position it's siblings it will position them in it's center.
Try to experiment with some ohter layout classes like FrameLayout, which has absolute manner of positioning it's siblings and RelativeLayout which and also try your case getting rid of layout's gravity.
Here says that this issue may be solved, using view.setLayoutParams() instead of view.layout()

Categories

Resources