I have a CustomView which contains a LinearLayout that holds an EditText & another custom view element. However, when I run my app and touch the EditText it does not behave as expected (doesn't appear to receive focus & the soft keyboard doesn't open). I've tried setting duplicateParentState="true" on both the EditText & the LinearLayout. I also attached an onTouchEventListener to the EditText that calls a simple toast & the toast did show up.
Here's the code for my CustomView.
form_field.xml
<?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="horizontal"
android:layout_marginBottom="15dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/grey_rounded_borders">
<EditText
android:id="@+id/edit_text"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:inputType="text"
android:padding="10dp"
android:textColor="#2d6169"
android:textSize="18sp"
android:background="@color/transparent" />
<RelativeLayout
android:layout_marginStart="5dp"
android:layout_marginEnd="10dp"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<com.example.app.ValidationIcon
android:id="@+id/validation_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
tools:ignore="ContentDescription" />
</RelativeLayout>
</LinearLayout>
FormField.java
package com.example.app;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import java.util.ArrayList;
public class FormField extends LinearLayout {
private EditText editText;
private ValidationIcon validationIcon;
private Integer fieldInputType;
private String fieldInputHint;
public FormField(Context context)
{
super(context);
}
public FormField(Context context, AttributeSet attributeSet)
{
super(context, attributeSet);
TypedArray attrs = context.obtainStyledAttributes(attributeSet, R.styleable.FormField, 0, 0);
fieldInputType = attrs.getInt(
R.styleable.FormField_fieldInputType,
InputType.TYPE_TEXT_VARIATION_NORMAL
);
fieldInputHint = attrs.getString(
R.styleable.FormField_fieldInputHint
);
attrs.recycle();
inflate(getContext(), R.layout.form_field, this);
this.setFocusable(true);
this.setFocusableInTouchMode(true);
editText = (EditText)findViewById(R.id.edit_text);
editText.setInputType(fieldInputType);
editText.setHint(fieldInputHint);
editText.setFocusableInTouchMode(true);
validationIcon = (ValidationIcon)findViewById(R.id.validation_icon);
validationIcon.setValid(true);
ArrayList<View> touchables = new ArrayList<View>();
touchables.add(this);
touchables.add(editText);
addTouchables(touchables);
}
public FormField(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
}
registration_layout.xml
<RelativeLayout android:id="@+id/container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp">
<RelativeLayout android:id="@+id/account_details"
android:layout_marginBottom="15dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.example.app.FormField
android:id="@+id/name_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:fieldInputHint="@string/name_field_hint" />
<com.example.app.FormField
android:id="@+id/email_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:fieldInputHint="@string/email_field_hint" />
<com.example.app.FormField
android:id="@+id/password_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:fieldInputHint="@string/password_field_hint" />
<com.example.app.FormField
android:id="@+id/re_password_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:fieldInputHint="@string/re_password_field_hint" />
</RelativeLayout>
</RelativeLayout>
Min SDK Version is set at 14, target is set at 20. Any help would be greatly appreciated!
EDIT:
I got this logcat message today while testing long presses on my FormFields.
W/TextView﹕ TextView does not support text selection. Action mode cancelled.
I double checked my code and the EditText element is only ever cast as an EditText. If the EditText is being cast to a TextView that would explain my original issues but now I'm confused as to why or how it was cast as a TextView.
It looks like you are extending LinearLayout but never using it in your layout. From what you've posted, you aren't using the FormField at all and the only custom View I see is the ValidationIcon view. So hooking the touch events into EditText and ValidationIcon aren't occurring because you aren't using FormField in your layout.xml.
Rather than extending LinearLayout, why not just use the predefined methods and attributes to handle the behaviour you are trying to achieve. For example, displaying keyboard, requesting focus, and displaying hint:
<EditText
android:id="@+id/edit_text"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
**android:inputType="text"**
**android:hint="@styleable/FormField_fieldInputHint"**
android:padding="10dp"
android:textColor="#2d6169"
android:textSize="18sp"
android:background="@color/transparent" />
The keyboard should display when the edit text receives focus, if not you could programmatically do this by requesting focus and handling the IME manager call. For example, from the Activity/Fragment:
//call this from onViewCreated to grab focus and begin the flow
editText.requestFocus();
//set focus listener to handle keyboard display
editText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
InputMethodManager imm = (InputMethodManager)getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(myEditText.getWindowToken(), 0);
} else {
InputMethodManager imm = (InputMethodManager)getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.showSoftInputFromWindow(myEditText.getWindowToken(), 0);
}
});