Button not clickable in custom LinearLayout - android

I have a custom view that contain a framelayout. This framelayout contain two views (LinearLayout) that can be swipe. If I swipe the first one, the second one appears and vice versa. One of those views has a button but I don't know why, this button is like disable. I cannot click on it and the onClick method has no effect.
Here the structure of the layout xml inflated in the custom view :
<FrameLayout>
<LinearLayout
android:id="#+id/frontview"
/>
<LinearLayout
android:id="#+id/backview">
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/ButtonUpdate"
android:text="#string/bUpdate"
android:padding="5dp"
android:clickable="true"
style="?android:attr/borderlessButtonStyle"
/>
</LinearLayout>
</FrameLayout>
Here the code in my custom view :
public class mView extends LinearLayout {
ImageView icon;
TextView current_data;
TextView previous_data;
TextView time ;
Button bUpdate;
EditText TextUpdate;
public mView(Context context) {
super(context);
init(context);
}
public mView(Context context, AttributeSet attrs) {
super(context,attrs);
init(context);
}
public mView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// nothing
}
}
public void init(Context pContext) {
LayoutInflater inflater = (LayoutInflater) pContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View ll = inflater.inflate(R.layout.list_item_data, this, true);
/** We initialize the elements of our UI **/
/**
* First View
*/
icon= (ImageView) ll.findViewById(R.id.ic_icon);
current_data = ll.(TextView) findViewById(R.id.current_data);
previous_data = ll.(TextView) findViewById(R.id.previous_data);
time = (TextView) ll.findViewById(R.id.time);
/**
* Second View
*/
bUpdate = (Button) ll.findViewById(R.id.ButtonUpdate);
TextUpdate= (EditText) ll.findViewById(R.id.TextUpdate);
bUpdate.setOnClickListener(new bUpdateClickListener());
}
private class bUpdateClickListener implements OnClickListener {
#Override
public void onClick(View v) {
// When the button is clicked, the front view re-appears and the backview disappears
frontview
.animate()
.translationX(0);
backview
.animate()
.translationX(-backview.getMeasuredWidth());
}
}
The swipe is correctly handle with onInterceptTouchEvent(MotionEvent ev) and onTouchEvent(MotionEvent ev).
Here the main.xml used in MyActivity :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f6f6f6">
<TextView
android:id="#+id/infoimc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:layout_marginTop="8dp"
android:text="#string/app_name"
android:textColor="#000000"
android:textSize="16dp"/>
<View
android:id="#+id/divider_infoimc"
android:layout_width="match_parent"
android:layout_height="1dip"
android:layout_marginRight="18dp"
android:layout_marginLeft="18dp"
android:background="#99CC00"/>
<com.example.essai.CustomGraph
android:id="#+id/CustomGraph"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_gravity="center"
android:visibility="visible"
android:layout_marginTop="10dp"/>
</LinearLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="80dp"
android:layout_gravity="left|center_vertical">
<com.example.essai.mView
android:id="#+id/CustomView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
</LinearLayout>
And MyActivity.class :
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
I don't know if the button must be handle also in the Activity ?
Thanks for your help !

The root cause
According to API:
FrameLayout is designed to block out an area on the screen to display
a single item.
If there is more items, like in your case, then "unexpected" things will happen:
Child views are drawn in a stack, with the most recently added child
on top.
This means, your frontview is on top of your backview and since the frontview doesn't have android:clickable="true" the click events (on button) are not delegated below.
Solution 1
Reorder the child-layout gravity programmatically.
You can, however, add multiple children to a FrameLayout and control
their position within the FrameLayout by assigning gravity to each
child, using the android:layout_gravity attribute.
Just switch the android:layout_gravity="top" and android:layout_gravity="bottom" whenever you are sliding them.
Solution 2
Control the visibility of the child-layouts programmatically.
When the backview should be displayed, set the visibility of the frontview to View.GONE. And set it to View.VISIBLE in the reversed case.
Solution 3
Change FrameLayout to a different layout type.
Could require more "fiddling" with layout xmls...
See what you're familliar with the most and choose the solution accordingly :)

Related

Android: How to make Layout clickable when EditText is a child

I have a Relative Layout with an EditText and an ImageView inside it.
Under certain circumstances, I would like to make the whole layout clickable and not any of its children.
I added an OnClickListener on the layout. And I tried the following with the children:
1. setEnabled(false)
2. setClickable(false)
This works for the ImageView but even after the above changes, when I click on the area near the EditText, the keyboard comes up and I can see the cursor in the edit text.
Instead of that, I am hoping that all click/touch events go to the layout.
Could some one help?
Thanks
Yon create a CustomLayout class and override the onInterceptTouchEvent method. If that method returns true, the layout's childrens will not receive the touch event. You can create a member variable and a public setter to change the returning value.
CustomLayout class
public class CustomLayout extends LinearLayout {
//If set to false, the children are clickable. If set to true, they are not.
private boolean mDisableChildrenTouchEvents;
public CustomLayout(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
mDisableChildrenTouchEvents = false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDisableChildrenTouchEvents;
}
public void setDisableChildrenTouchEvents(boolean flag) {
mDisableChildrenTouchEvents = flag;
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CustomLayout layout = findViewById(R.id.mylayout);
//Disable touch events in Children
layout.setDisableChildrenTouchEvents(true);
layout.setOnClickListener(v -> System.out.println("Layout clicked"));
}
}
XML Layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.dglozano.myapplication.CustomLayout
android:id="#+id/mylayout"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#drawable/outline"
android:clipChildren="true"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<EditText
android:id="#+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Enter email"
android:inputType="textEmailAddress"
android:layout_gravity="center"/>
</com.example.dglozano.myapplication.CustomLayout>
</android.support.constraint.ConstraintLayout>

Why is Android Studio designer displaying my custom view nested inside itself, while it isn't

I've got a custom view for my app named AvatarView:
<?xml version="1.0" encoding="utf-8"?>
<com.ulouder.views.AdvancedRelativeLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_gravity="center_horizontal"
android:layout_margin="0dp"
android:padding="0dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CP"
android:id="#+id/initialsView"
android:layout_alignTop="#+id/avatarView"
android:layout_alignLeft="#+id/avatarView"
android:layout_alignBottom="#+id/avatarView"
android:layout_alignRight="#+id/avatarView"
android:background="#drawable/avatar_background"
android:textColor="#color/white"
android:gravity="center"
android:textStyle="bold"
android:textSize="8sp" />
<com.makeramen.roundedimageview.RoundedImageView
app:riv_corner_radius="20dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/avatarView"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginBottom="4dp"
app:riv_border_color="#color/lightGray"
app:riv_border_width="0.2dp" />
</com.uLouder.views.AdvancedRelativeLayout>
AdvancedRelativeLayout is just a superclass of RelativeLayout with a small fix, nothing special there. Then, I've created a view that uses my custom view:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.ulouder.views.AvatarView
android:layout_width="50dp"
android:layout_height="50dp"/>
</LinearLayout>
Nothing fancy either. But in the designer view of the second layout XML, I'm getting this:
The editor displays my view hierarchy like it has a nested instance of itself, while clearly there isn't. If I delete either one, they both get deleted. If I declare attributes on one of them, other also gets it. They are clearly the same instance. The only exception is setting an ID. Then the problem disappears, and only single instance is displayed as expected.
I've rebuilt the project, restarted Android Studio, but it's still the same. What am I doing wrong?
UPDATE: Nope, now, after editing id, the problem still continues again.
UPDATE 2: It's not just a layout so I can't use <include> tag. It's a custom view which has custom logic inside.
UPDATE 3: Here is my custom view's (relevant) code:
public class AvatarView extends FrameLayout {
public AvatarView(Context context) {
super(context);
init();
}
TextView initialsView;
RoundedImageView imageView;
public AvatarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
void init(){
inflate(getContext(), R.layout.view_avatar, this);
initialsView = (TextView) findViewById(R.id.innerInitialsView);
imageView = (RoundedImageView) findViewById(R.id.innerImageView);
}
#SuppressWarnings("SuspiciousNameCombination")
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec); //always square
imageView.setCornerRadius(widthMeasureSpec / 2f);
initialsView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, widthMeasureSpec * 30f);
}
}
UPDATE 4: It appears that this happens wherever I put my custom AvatarView class, not just at one place.
I did not find any reason to inflate the same view inside your class constructor method after checking the custom views documentation. Try to remove the inflate inside your init method.
...
public AvatarView(Context context) {
super(context);
init();
}
...
public AvatarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
void init(){
// inflate(getContext(), R.layout.view_avatar, this);
initialsView = (TextView) findViewById(R.id.innerInitialsView);
imageView = (RoundedImageView) findViewById(R.id.innerImageView);
}
...

Android - programmatically set background selector state

I have a linear layout containing an EditText and an ImageView. I've given the EditText a #null background and have given the LinearLayout a background of:
?android:attr/editTextBackground
to make it look like the whole thing is one widget. When the EditText gets focus/is selected I'd like to update the background drawable of the linear layout to show that the whole thing is selected.
My layout XML for the Linear Layout:
<LinearLayout
android:id="#+id/search_plate"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/editTextBackground">
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="#+id/edit_text"
android:layout_weight="1"
android:background="#null"
android:height="36dp"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/image_view_close"
android:src="#drawable/ic_clear"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
This is the code I'm using to try and change the background state of the LinearLayout when the EditText is focused:
public class IconEditText extends LinearLayout implements View.OnFocusChangeListener {
private static final String LOG_TAG = "IconEditText";
private View mSearchPlate; // Linear Layout
private EditText mEditTextSearch;
public IconEditText(Context context) {
this(context, null);
}
public IconEditText(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.icon_edit_text, this, true);
mSearchPlate = findViewById(R.id.search_plate);
mEditTextSearch = (EditText) findViewById(R.id.editText);
mEditTextSearch.setOnFocusChangeListener(this);
}
#Override
public void onFocusChange(View view, boolean focused) {
mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
}
}
As well as using FOCUSED_STATE_SET, I've also tried the following:
ENABLED_FOCUSED_SELECTED_STATE_SET
FOCUSED_SELECTED_STATE_SET
ENABLED_SELECTED_STATE_SET
SELECTED_STATE_SET
None of the above seemed to change the background of the LinearLayout to the blue underline. Any help would be appreciated!
Seems as though: ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET works... if anyone could explain that, that would be great!

How to implement Custom View as part of MVC?

So I'm experimenting with implementing an MVC pattern in Android where my views are subclassed from RelativeLayout, LinearLayout, ScrollView, etc... It's working until I try to get a hold of a view within my view. I get an NPE. I've tried accessing the view in order to set the onClickListener in the constructor and also in onAttachedToWindow(), but I get the NPE in both places.
For example, here's a view class:
public class ViewAchievements extends LinearLayout
{
private RelativeLayout mRelativeLayoutAchievement1;
public ViewAchievements(Context context, AttributeSet attrs)
{
super(context, attrs);
mRelativeLayoutAchievement1 = (RelativeLayout) findViewById(R.id.relativeLayout_achievement1);
mRelativeLayoutAchievement1.setOnClickListener((OnClickListener) context); //NPE on this line
}
#Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
mRelativeLayoutAchievement1.setOnClickListener(mOnClickListener); //Also get NPE on this line
}
}
Can someone please tell me the proper way to get a hold of my subviews, in this case mRelativeLayoutAchievement1?
Here's an XML snippet:
<com.beachbody.p90x.achievements.ViewAchievements xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/gray_very_dark"
android:orientation="vertical" >
<!-- kv Row 1 -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="1"
android:baselineAligned="false">
<RelativeLayout
android:id="#+id/relativeLayout_achievement1"
style="#style/linearLayout_achievement"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_margin="#dimen/margin_sm"
android:layout_weight="1" >
<TextView
android:id="#+id/textView_achievement1"
style="#style/text_small_bold_gray"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="#dimen/margin_large"
android:text="1/20" />
</RelativeLayout>
...
And here's how I'm creating the view from my Activity:
public class ActivityAchievements extends ActivitySlidingMenu
{
private ViewAchievements mViewAchievements;
#Override
public void onCreate(Bundle savedInstanceState)
{
mViewAchievements = (ViewAchievements) View.inflate(this, R.layout.view_achievements, null);
setContentView(mViewAchievements);
...
You're trying to get the child views during the view's constructor. Since they are child views, they haven't been inflated yet. Can you move this code out of the constructor, possibly into View.onAttachedToWindow()?
http://developer.android.com/reference/android/view/View.html#onAttachedToWindow()

Dynamically adding views to mixed xml/code compound layout

Sorry if this redundant with the ton of questions/answers on inflate, but I could not get a solution to my problem.
I have a compound view (LinearLayout) that has a fixed part defined in XML and additional functionalities in code. I want to dynamically add views to it.
Here is the XML part (compound.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/compoundView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView android:id="#+id/myTextView"
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="000" />
</LinearLayout>
I have defined in code a LinearLayout to refer to the XML:
public class CompoundControlClass extends LinearLayout {
public CompoundControlClass (Context context) {
super(context);
LayoutInflater li;
li = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
li.inflate(R.layout.compound_xml,*ROOT*, *ATTACH*);
}
public void addAView(){
Button dynBut = new Button();
// buttoin def+layout info stripped for brevity
addView(dynBut);
}
}
I tried to programmatically add a view with addAView.
If ROOT is null and ATTACH is false, I have the following hierarchy (per HierarchyViewer):
CompoundControlClass>dynBut
The original TextView in the XML is gone.
If ROOT is this and ATTACH is true, I have the following hierarchy:
CompoundControlClass>compoundView>myTextView
CompoundControlClass>dynBut
I would like to have
CompoundControlClass>myTextView
CompoundControlClass>dynBut
where basically the code and XML are only one unique View.
What have I grossly missed?
ANSWER BASED on feedback from D Yao ----------------------
The trick is to INCLUDE the compound component in the main layout instead of referencing it directly.
activity_main.xml
<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">
<include layout="#layout/comound"
android:id="#+id/compoundView"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
mainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CompoundControlClass c = (CompoundControlClass) this.findViewById(R.id.compoundView);
c.addAView(this);
}
}
CompoundControlClass.java
public class CompoundControlClass extends LinearLayout {
public CompoundControlClass(Context context) {
super(context);
}
public CompoundControlClass(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CompoundControlClass(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void addAView(Context context){
ImageView iv = new ImageView(context);
iv.setImageResource(R.drawable.airhorn);
addView(iv);
}
}
compound.xml
<com.sounddisplaymodule.CompoundControlClass xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/compoundView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_width="110dp"
android:layout_height="wrap_content"
android:gravity="right"
android:textSize="40sp"
android:textStyle="bold"
android:text="0:00" />
</com.sounddisplaymodule.CompoundControlClass>
Why not just call addView on the linearlayout? I don't see the need for CompoundControlClass based on the needs you have listed.
LinearLayout v = (LinearLayout)findViewById(R.id.compoundView);
v.addView(dynBut);
In this case, v will contain myTextView, then dynBut.
if you wish to have other functions added and thus really feel a need for creating the compound control class, just leave the constructor as super(etc) and remove the rest
Then your xml would look like this:
<com.yourpackage.CompoundControlClass xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/compoundView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView android:id="#+id/myTextView"
android:layout_width="110dp"
android:layout_height="wrap_content"
android:text="000" />
</com.yourpackage.CompoundControlClass>
you will also have to ensure your CompoundControlClass.java contains the appropriate Constructor which takes both a Context and an attribute set.
Then, in your java, after you've called setContentView, you can do the following:
CompoundControlClass c = (CompoundControlClass)findViewById(R.id.compoundView);
Button b = new Button(context);
//setup b here or inflate your button with inflater
c.addView(b);
this would give you your desired heirarchy.

Categories

Resources