I'm building a custom layout for a phone dialer. For each number in the dialer i'm using a LinearLayout with some TextViews inside it.
Problem: When an onClickListener is set to the custom LinearLayout and it is subsequently pressed by the user, it gets stuck in the pressed state and does not revert to the default item in the state list when released. If I don't set an onClickListener to the LinearLayout, the state correctly changes to pressed and then unpressed.
Debugging via Android Studio's layout inspector shows that the LinearLayout still has isPressed() == true after the user has released. I've also tried an ImageButton and Button instead of the LinearLayout and it exhibits similar behavior. The app's theme inherits from Theme.MaterialComponents.Light.Bridge.
The layout of the fragment containing the dialer buttons:
<TableLayout
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_gravity="center_horizontal"
android:layout_weight="2">
<TableRow
android:layout_height="0px"
android:layout_weight="1"
android:gravity="center">
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#e2e2e2"/>
<DialerButton
android:id="#+id/keypad2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"/>
...
The LinearLayout used for the DialerButton class (which is a basic class that just overrides LinearLayout and inflates the following layout) looks like this:
<?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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="#drawable/dialpad_btn_background"
android:clickable="true"
android:focusable="true">
<TextView
android:id="#+id/tvNumeral"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="2"/>
<TextView
android:id="#+id/tvLettering"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="ABC"/>
</LinearLayout>
The dialpad_btn_background.xml looks like this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#color/dark_grey"/>
<item android:drawable="#color/white"/>
</selector>
The code to attach the OnClickListener to the LinearLayout is standard:
keypad1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
/// ... Do work ...
}
});
It is better to use ImageButton instead of a whole LinearLayout as ImageButton will automatically have this feature or if you are concerned about the design you should use the card layout.This is a calculator where I've used Imagebutton for the keys.
I wasn't able to find a way to fix this using layouts, but I was able to get around it programatically. This doesn't address the root of why the LinearLayouts or the ImageButtons were getting stuck in the pressed state though, or why the state list was not working as expected.
First, I went back to using ImageButton instead of a custom LinearLayout since that made no difference with regards to the pressed state. By attaching a touch listener and programmatically setting the background of the ImageButtons on MotionEvent.ACTION_UP and MotionEvent.ACTION_DOWN, I got the results I need. Here's the snippet for how I did this:
View.OnTouchListener touchListener = (v, event) -> {
if (event.getAction() == MotionEvent.ACTION_UP) {
v.setBackground(getResources().getDrawable(R.drawable.img_dialpad_bg, null));
}
else if (event.getAction() == MotionEvent.ACTION_DOWN) {
v.setBackground(getResources().getDrawable(R.drawable.img_dialpad_bg_pressed, null));
}
return false;
};
keypad0.setOnTouchListener(touchListener);
keypad1.setOnTouchListener(touchListener);
...
Related
I have two views one is a button and the other is LinearLayout.
When i set View.GONE and then to View.VISIBLE the view wont get visible again.
This mechanism was working in the past.
android:id="#+id/selector_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="#drawable/shape_round_white_1"/>
<include
android:id="#+id/actions_container"
android:layout_width="match_parent"
android:layout_height="48dp"
layout="#layout/wait_request_accept_panel"
android:visibility="visible"
android:layout_alignParentBottom="true"
android:layout_centerInParent="true" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="#id/selector_controls"
android:layout_alignEnd="#id/selector_controls"
android:layout_alignLeft="#id/selector_controls">
Now... what i want is to toggle is the elements inside wait_request_accept_panel this is the layout file, I want to toggle elements inside it..
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<RelativeLayout
android:id="#+id/wait_container"
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="#drawable/shape_round_white_1">
<!-- Other view elements -->
</RelativeLayout>
<Button
android:id="#+id/btnAccept"
android:layout_width="match_parent"
android:layout_height="48dp"
style="#style/H18b"
android:visibility="visible"
android:background="#drawable/shape_round_white_1"
android:text="#string/str_accept"
android:layout_alignParentTop="true"/>
</RelativeLayout>
As you can se here there is two elements basically a wait_container which shows up when user is waiting, and a Button btnAccept it only change state once: one in the original state which is, wait_container is GONE and button Button which is visible at first time. When I hit btnAcceptthe btn changes to GONE and the wait container changes to VISIBLE
Here is the programatic impementation:
switch (req.getType()) {
case REQ: // this is the initial flow
waitContainer.setVisibility(View.GONE);
acceptBtn.setVisibility(View.VISIBLE);
break;
case ACCEPT: // after hit the accept btn it toggles the two views
acceptBtn.setVisibility(View.GONE);
waitContainer.setVisibility(View.VISIBLE);
break;
}
Init details
waitContainer = (RelativeLayout) view.findViewById(R.id.wait_container);
acceptBtn = (Button) view.findViewById(R.id.btnAccept);
cancelBtn = (Button) view.findViewById(R.id.btnCancel);
Something to take in count is the fact the waitContainer and acceptBtn are included, they came from another xml file, I did that because I wanted to reuse code, but in this moment that's not so important since the current screen is the only that uses the wait_request_accept_panel.xml file.
SOLUTION
The view were always there, but it's alpha channel was modified by an animation when the fragment was starting, I sent by mistake the viewContainer as a parameter to the animation method which i turns animates its alpha channel.
You can try with getVisibility ().
Returns the visibility status for this view.
if(waitContainer.getVisibility()== View.GONE)
{
acceptBtn.setVisibility(View.GONE);
waitContainer.setVisibility(View.VISIBLE);
}
Check the visibility of view after that apply ViSIBLE/GONE have look
if(waitContainer.getVisibility()== View.GONE)
{
waitContainer.setVisibility(View.VISIBLE);
}else{
waitContainer.setVisibility(View.GONE);
}
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()));
I have one layout which includes other layout inside. I tried to set onClikeListener on ImageView inside the included layout, but it's not working. But when I set background drawable it works. I don't know why. Here is my code:
//custom header
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sticky_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/iv_header_close"
android:layout_width="60dp"
android:layout_height="55dp"
android:background="#drawable/big_cross_icon" />
</LinearLayout>
//activity_detail
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f1f2f6">
<include
android:id="#+id/header"
layout="#layout/custom_header"/>
</RelativeLayout>
//in Activity
View header = findViewById(R.id.header);
iv_header_close = (ImageView)header.findViewById(R.id.iv_header_close);
iv_header_close.setBackgroundDrawable(getResources().getDrawable(R.drawable.big_edit_icon));
iv_header_close.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish(); //not working
}
});
I wonder why I can access to child view inside included layout but can't set OnClickListner. Thank you so much :)
Create your iv_header_close like this:
ImageView iv_header_close = (ImageView) findViewById(R.id.iv_header_close);
Instead of calling finish() Try to call YourActivity.this.finish()
What's happening is that, inside the onClick of your imageview, you don't have access to your activity so you have to access your activity using YourActivity.this if you want to access the methods of your activity.
In case anybody is having the same issue five years later...
Using the onClick attribute on the view (ImageView, Button... mine was a button) inside the included layout file fixes the problem. However, note that the onClick attribute is now deprecated
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.
Okay, so I've done some looking around and I see how you are SUPPOSED to do it, but for me, it is just not working.
I need to be able to set the alpha of a RelativeLayout both in XML and in code. For my XML, I have the following
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/player_controls"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:alpha="0.0">
<RelativeLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/player_controls_touch_me"
>
</RelativeLayout>
</RelativeLayout>
I get the error: no resource identifier found for attribute 'alpha' in package 'android'
Also, based on the Android documentation, I should be able to call setAlpha(double) on any View object, but when I try to make that call on a RelativeLayout it tells me that this method is not defined for this object.
Why am I not able to control the alpha transparency for a RelativeLayout object in Android? Am i missing something? Thanks!
Update
Although using the visibility property works, it prevents me from be able to click on the ViewGroup. This is important for me because I am utilizing the OnTouchListener of the ViewGroup.
What I am trying to do is to have a layer with media controls, initially hidden. when the user taps anywere on the screen, I want the controls to fade in and when they tap the screen again I want the controls to fade out. I have this part already working. I am using a viewgroup that sits over-top my entire application with an OnTouchListener attached that can determine if it has or hasn't been touched. My problem is that after the animation runs to fade out the controls, they re-appear. If I use #Hydrangea suggestion, I can have it fade out and immediately made invisible. This gives me the desired effect, but then the ViewGroup is unclickable and the user cannot get the controls to come back (or go away, depending on what we decide to do first).
I hope this makes sense.
You'll want to use a alpha animation to fade things in and out. This will maintain your touch events for your layouts. Here's an example
public class Main extends Activity {
/** Called when the activity is first created. */
private boolean mShowing = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.textview).setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
if(mShowing){
Animation animation = new AlphaAnimation(1.0f, 0.0f);
animation.setFillAfter(true);
arg0.startAnimation(animation);
} else {
Animation animation = new AlphaAnimation(0.0f, 1.0f);
animation.setFillAfter(true);
arg0.startAnimation(animation);
}
mShowing = !mShowing;
}
});
}
}
Here's the accompanying 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"
>
<TextView
android:id="#+id/textview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
android:clickable="true"
/>
</LinearLayout>
Unless you need levels of alpha between 0 and 1, I'd suggest, if you truly want to make this item invisible, to use setVisibility();
android:visibility="invisible"
I checked out the android:alpha line, and my ide doesn't find it either. I can't guess why, though... the documentation seems pretty clear.
The alpha property is new in Android 3.0, and it's not the most efficient way to hide a view. Use View.setVisibility() or android:visibility to achieve what you want.
You can set alpha by setting the (background) color i guess. Color values can be in the format of #aarrggbb (alpha, red, green, blue).
You can add to the right answer the following option:
animation.setDuration(xxx);
To each animation instance. In this way your animation will look better.
Based on your discription, you should be able to create a view that contains only the relative layout and have the onClickListener set to it. This way you can set the visibility of the relative layout to invisible, but still register a click.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/clickable_layout"
android:layout_height="match_parent"
android:layout_width="match_parent" >
<RelativeLayout
android:id="#+id/player_controls"
android:layout_height="match_parent"
android:layout_width="match_parent" >
<RelativeLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/player_controls_touch_me"
>
</RelativeLayout>
</RelativeLayout>
</FrameLayout>
Use onTouchEvent in Activity, and then you could get touch event to control to your RelativeLayout even if it is "invisible".