I am making a window to open a dialog window and write a comment.
When you open the dialog window, there is a layout where you can enter your first comment.
Comment can only be written in one line and the following comment layout is created by pressing EnterKey in the input field.
I originally implemented this as a RecyclerView, but now I am trying other methods as well. (using addView())
But when I used addView() I got the following error.
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
The solution to this is shown in the here. (And I would appreciate it if you could explain why this error occurs.)
There is no error when using this.
However, no more items are added.
There is only the first item that existed when the dialog box was opened, and there is no response when pressing the Enter key.
I want to add items dynamically in real time.
What should I do?
fragment_wc_dialog.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context=".fragment.WritingCommentDialogFragment">
</LinearLayout>
wc_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".fragment.WritingCommentDialogFragment">
<EditText
android:id="#+id/comment_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center_vertical"
android:drawableLeft="#drawable/ic_bullet_point"
android:drawablePadding="5dp"
android:layout_marginHorizontal="10dp"
android:background="#null"
android:textSize="15sp"
android:inputType="text"
android:maxLines="1"
android:maxLength="22"
android:imeOptions="actionNone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
WCDialogFragment.java
public class WritingCommentDialogFragment extends DialogFragment {
LinearLayout mContainer; // input layout
View inputView;
EditText editText;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_writing_comment_dialog, container, false);
initViews(view);
mContainer.addView(inputView); // The first item to be present when the dialog is opened
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN) {
mContainer.addView(inputView);
}
return true;
}
});
return view;
}
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
#Override
public void onResume() {
super.onResume();
Toast.makeText(getContext(), "onResume()", Toast.LENGTH_SHORT).show();
setDialogSize();
}
private void initViews(View view) {
mContainer = view.findViewById(R.id.container);
inputView = LayoutInflater.from(getContext()).inflate(R.layout.writing_comment_item, null);
editText = inputView.findViewById(R.id.comment_edit);
}
}
ADDED
Your problem and the linked issue are different, as far as I can tell. The link has two views that they want to swap back and forth between, so the answer is to remove one view before adding the second. You want to build up a list of views, so you must use different instances rather than adding the same view over and over.
If you want the newly-added view to also respond to the Enter key, you can set the same listener to the new view too:
if(event.getAction() == KeyEvent.ACTION_DOWN) {
EditText addedView = (EditText) LayoutInflater.from(getContext()).inflate(R.layout.writing_comment_item, mContainer, false);
addedView.setOnEditorActionListener(this);
mContainer.addView(addedView);
}
Related
I have an activity with some edittexts and some checkboxes, etc.
I have validators set on text entries that get executed when a text entry loses focus. But the problem is when the user clicks/touches a checkbox, edittext doesn't lose focus and therefore the validator is not run.
What is the best way for causing unfocus of text entries when the user touches another input field? I know I can e.g. set a handler on every checkbox to force unfocusing of text fields happen, but I think there is probably a more concise/general method for this purpose; Setting a handler on every checkbox seems too cumbersome and error prone.
thank u.
You can try this way(it only is an example).
your_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sv_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="EditText 1" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="EditText 2" />
..............other views................
</LinearLayout>
</ScrollView>
Using android:focusableInTouchMode="true" in the LinearLayout to make the EditText is focused when touching on it( or when starting this activity there is not auto focus to any EditText also).
In your activity:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
ScrollView svContent = findViewById(R.id.sv_content);
svContent.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
int action = motionEvent.getAction();
if(action == MotionEvent.ACTION_MOVE || action == MotionEvent.ACTION_UP) {
View focusView = getCurrentFocus();
if (focusView != null) {
// The code for unfocus here
return true;
}
}
return false;
}
});
}
I have a dialog with dynamic content, so I've created a listview inside this dialog and then an Adapter which decides what layout each item has,
Adapter chooses from 3 layouts,
one contains checkbox - works fine
second contains spinner - works fine
third contains edittext - SW keyboard will never display
Here is the edittext item layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="?android:listPreferredItemHeight"
android:paddingStart="24dp"
android:paddingEnd="24dp"
>
<TextView
android:id="#+id/name"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Test"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
/>
<EditText
android:id="#+id/value"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:focusable="true"
/>
</LinearLayout>
and the adapter choosing a layout:
class FieldAdapter extends ArrayAdapter<ProfileField> {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ProfileField field = getItem(position);
switch (field.type) {
case ProfileFieldType.CUSTOM_TEXT:
convertView = inflater.inflate(R.layout.scanprofile_dialog_edit_item, parent, false);
break;
case ProfileFieldType.CUSTOM_CHECK:
convertView = inflater.inflate(R.layout.scanprofile_dialog_checkbox_item, parent, false);
break;
default:
ProfileField.ListProfileField listField = (ProfileField.ListProfileField) field;
convertView = inflater.inflate(R.layout.scanprofile_dialog_item, parent, false);
break;
}
return convertView;
}
...
}
I've tried adding this to onCreateDialog
final AlertDialog alertDialog = builder.create();
final Window dialogWindow = alertDialog.getWindow();
dialogWindow.clearFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
return alertDialog;
But it didn't help at all.
Any ideas why the SW keyboard is not shown?
EditText View inside ListView is not supported in android as both need focus to work properly.
In Your case all the event are consume by ListView that's why keyboard is not opening when you are trying to clicking on EditText view.
You can give focus to EditTextView but it will create problem in ListView functionality.
You can either put EditText in header/footer of ListView or if you have
fix number of view like only 3 then directly plot those view on dialog view instead of inflating them inside ListView.
Refer the here if you still want to use EditText inside ListView.
add in xml
<EditText...>
<requestFocus />
add in code
boolean checkFocus=youredittext.requestFocus();
Log.i("CheckFocus", ""+checkFocus);
if(checkFocus==true)
{
InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.showSoftInput(youredittext, InputMethodManager.SHOW_IMPLICIT);
}
you can try this code (help you proactively do it) any time you want)
//edittext demo code
final EditText myEditText = (EditText)findViewById(R.id.editText);
myEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
myEditText.postDelayed(new Runnable() {
#Override
public void run() {
InputMethodManager keyboard = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
keyboard.showSoftInput(myEditText, 0);
}
}, 50); //require system show soft keyboard after 50 miliseconds
}
});
"We already have an EditText, can only have one"
I've built a Fragment for my app (LoginFragment) that handles both 2 of the main authentication modes; namely logging in and signing up a user. There is a button to allow the user to switch between "login mode" and "sign up" mode. Each "mode" has some additional views that aren't required by the other. Therefore it is necessary to add and remove views as the mode is toggled.
I'm using EditText views within TextInputLayout layouts. My application crashes when I do the following:
Add the EditText programmatically
Remove the EditText programmatically
Add the EditText programmatically -> Crash
This is the error that I get is:
java.lang.IllegalArgumentException: We already have an EditText, can only have one
at android.support.design.widget.TextInputLayout.setEditText(TextInputLayout.java:166)
at android.support.design.widget.TextInputLayout.addView(TextInputLayout.java:155)
at android.view.ViewGroup.addView(ViewGroup.java:3985)
at android.view.ViewGroup.addView(ViewGroup.java:3961)
at com.mydomain.myapp.fragments.LoginFragment.showActivateAccountViews(LoginFragment.java:317)
This comes from android.support.design.widget.TextInputLayout which has an internal private EditText variable that is set when the view is added (source below). It appears that when I try to add the view to the TextInputLayout for the 2nd time that the mEditText variable has already set. The class does not have it's own .removeView() method so I don't know how it should be removed?
I suspect that I am removing the EditText view incorrectly but cannot figure out what I am doing wrong. I've also read some other Stack Overflow posts that deal with removing views but these approaches have not resolved the issue either.
Adding a view - https://stackoverflow.com/a/13889257/1500317
Remove a view - Call removeView() on the child's parent first
Does anyone have any ideas on how I can get this to work?
Below is my own code for reference.
LoginFragment.java
...
import android.support.design.widget.TextInputLayout;
import android.widget.EditText;
public class LoginFragment extends Fragment {
private RelativeLayout mContainer;
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_login, container, false);
mContainer = ((RelativeLayout) view.findViewById(R.id.login_container));
showLoginViews();
LayoutTransition layoutTransition = mContainer.getLayoutTransition();
layoutTransition.enableTransitionType(LayoutTransition.CHANGING);
return view;
}
/**
* Show the view elements for Login mode
*/
private void showLoginViews() {
LayoutInflater li = (LayoutInflater)getActivity().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// Configure the button for the primary action
Button loginButton = (Button)mContainer.findViewById(R.id.button_login_fragment_primary_action);
...
// Configure the toggle button to navigate to Activate Account mode
TextView toggleButton = (TextView)mContainer.findViewById(R.id.button_toggle_mode);
toggleButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LoginFragment.this.showActivateAccountViews();
}
});
toggleButton.setText(getResources().getString(R.string.action_activate_account));
// Hide the Member ID EditText
((TextInputLayout)mContainer.findViewById(R.id.member_id_inputlayout)).removeView(mContainer.findViewById(R.id.editText_member_id_field));
}
/**
* Show view elements for Activate Account mode
*/
private void showActivateAccountViews() {
LayoutInflater li = (LayoutInflater)getActivity().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// Configure the primary button for the primary action - Activate Account
Button activateAccountButton = (Button)mContainer.findViewById(R.id.button_login_fragment_primary_action);
...
// Add the Member ID EditText
((TextInputLayout)mContainer.findViewById(R.id.member_id_inputlayout)).addView(li.inflate(R.layout.login_member_id_element_layout, (ViewGroup)mContainer.findViewById(R.id.member_id_inputlayout), false));
// Configure the toggle button to navigate to Login mode
TextView toggleButton = (TextView)mContainer.findViewById(R.id.button_toggle_mode);
toggleButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LoginFragment.this.showLoginViews();
}
});
toggleButton.setText(getResources().getString(R.string.action_login));
}
...
}
login_member_id_element_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/editText_member_id_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/member_id" />
login_fragment.xml
<android.support.design.widget.CoordinatorLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="com.mydomain.myapp.fragments.LoginFragment">
<RelativeLayout
android:id="#+id/login_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<!--placeholder layout with params for activate account elements-->
<android.support.design.widget.TextInputLayout
android:id="#+id/member_id_inputlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- a view can be added here-->
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="#+id/email_inputlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/editText_email_field"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="textEmailAddress" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="#+id/button_login_fragment_primary_action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/password_inputlayout"
android:text="#string/action_login" />
<!-- Toggle button for Login/Activate Account-->
<TextView
android:id="#+id/button_toggle_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/action_activate_account" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
android.support.design.widget.TextInputLayout (from the latest 22.2.1 support library)
public class TextInputLayout extends LinearLayout {
private EditText mEditText;
...
public void addView(View child, int index, LayoutParams params) {
if(child instanceof EditText) {
android.widget.LinearLayout.LayoutParams params1 = this.setEditText((EditText)child, params);
super.addView(child, 0, params1);
} else {
super.addView(child, index, params);
}
}
private android.widget.LinearLayout.LayoutParams setEditText(EditText editText, LayoutParams lp) {
if(this.mEditText != null) {
throw new IllegalArgumentException("We already have an EditText, can only have one");
} else {
this.mEditText = editText;
this.mCollapsingTextHelper.setExpandedTextSize(this.mEditText.getTextSize());
this.mEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
TextInputLayout.this.mHandler.sendEmptyMessage(0);
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
this.mDefaultTextColor = this.mEditText.getHintTextColors().getDefaultColor();
this.mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View view, boolean focused) {
TextInputLayout.this.mHandler.sendEmptyMessage(0);
}
});
if(TextUtils.isEmpty(this.mHint)) {
this.setHint(this.mEditText.getHint());
this.mEditText.setHint((CharSequence)null);
}
if(this.mErrorView != null) {
ViewCompat.setPaddingRelative(this.mErrorView, ViewCompat.getPaddingStart(this.mEditText), 0, ViewCompat.getPaddingEnd(this.mEditText), this.mEditText.getPaddingBottom());
}
this.updateLabelVisibility(false);
android.widget.LinearLayout.LayoutParams newLp = new android.widget.LinearLayout.LayoutParams(lp);
Paint paint = new Paint();
paint.setTextSize(this.mCollapsingTextHelper.getExpandedTextSize());
newLp.topMargin = (int)(-paint.ascent());
return newLp;
}
}
}
There appears to be a limitation in the com.android.support.design library (v22.2.1). You cannot directly remove and then add an EditText to a TextInputLayout at runtime. You can star this bug here.
I've devised a workaround for the problem. I modified the xml layout so that instead of adding/removing EditText views from the TextInputLayout at runtime (which doesn't work) we add/remove the TextInputLayout itself to a LinearLayout holder. With this solution we never need to actually remove the EditText from the TextInputLayout.
The only thing to note about this solution is that it makes your view hierarchy 1 level deeper than it otherwise need to be. So bear this in mind if you already have UI performance problems. if when you are reading this and version of com.android.support.design v22.2.1 is available it may be worth checking to see if this issue has been resolved.
Otherwise see the sample code below for my implementation of the workaround.
LoginFragment.java
import android.support.design.widget.TextInputLayout;
import android.widget.EditText;
public class LoginFragment extends Fragment {
private RelativeLayout mContainer;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_login, container, false);
mContainer = ((RelativeLayout) view.findViewById(R.id.login_container));
showLoginViews();
LayoutTransition layoutTransition = mContainer.getLayoutTransition();
layoutTransition.enableTransitionType(LayoutTransition.CHANGING);
return view;
}
/**
* Show the view elements for Login mode
*/
private void showLoginViews() {
LayoutInflater li = (LayoutInflater)getActivity().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// Configure the toggle button to navigate to Activate Account mode
s TextView toggleButton = (TextView)mContainer.findViewById(R.id.button_toggle_mode);
toggleButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LoginFragment.this.showActivateAccountViews();
}
});
toggleButton.setText(getResources().getString(R.string.action_activate_account));
// Hide the Member ID EditText
((LinearLayout)mContainer.findViewById(R.id.member_id_holderlayout)).removeView(mContainer.findViewById(R.id.member_id_inputlayout));
}
/**
* Show view elements for Activate Account mode
*/
private void showActivateAccountViews() {
LayoutInflater li = (LayoutInflater)getActivity().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// Configure the primary button for the primary action - Activate Account
Button activateAccountButton = (Button)mContainer.findViewById(R.id.button_login_fragment_primary_action);
// Add the Member ID EditText
((LinearLayout)mContainer.findViewById(R.id.member_id_holderlayout)).addView(li.inflate(R.layout.login_member_id_element_layout, (ViewGroup) mContainer.findViewById(R.id.member_id_inputlayout), false));
// Configure the toggle button to navigate to Login mode
TextView toggleButton = (TextView)mContainer.findViewById(R.id.button_toggle_mode);
toggleButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LoginFragment.this.showLoginViews();
}
});
toggleButton.setText(getResources().getString(R.string.action_login));
}
}
login_member_id_element_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.TextInputLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/member_id_inputlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/editText_member_id_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/member_id" />
</android.support.design.widget.TextInputLayout>
login_fragment.xml
<android.support.design.widget.CoordinatorLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
android:id="#+id/login_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--placeholder for TextInputLayout to be dynamically added at runtime-->
<LinearLayout
android:id="#+id/member_id_holderlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- a login_member_id_element_layout can be dynamically added/removed here at runtime-->
</LinearLayout>
<!--TextInputLayout for static fields, the EditText is not removed at runtime-->
<android.support.design.widget.TextInputLayout
android:id="#+id/email_inputlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/member_id_holderlayout">
<EditText
android:id="#+id/editText_email_field"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawablePadding="#dimen/edittext_drawable_padding"
android:drawableStart="?emailIcon"
android:focusable="true"
android:hint="Email"
android:inputType="textEmailAddress" />
</android.support.design.widget.TextInputLayout>
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
Congratulations, you (maybe?) found a possible bug (or should i say non-expected behaviour on removing TextInputLayout's EditText ?)
You can see that removeView() is a method from ViewGroup. It removes your View from the array of children views of ViewGroup, but not the reference that your InputTextLayout have to the EditText.
What should i do, then?
You gotta extend TextInputLayout and create your own method that sets super.mEditText to null. The problem is that you will still need to call these two methods because just setting a Layout reference to null may leave your old layout laying on memory through your application life cycle.
Click events handling does not work.
Click handling works with CheckBox and TextView, but doest not work with frameLayout.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="#drawable/yellow_ripple"
android:clickable="true"
>
<TextView
android:clickable="false"
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
<CheckBox
android:clickable="false"
android:id="#+id/box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
/>
In create view:
View resultView = LayoutInflater.from(context)
.inflate(R.layout.filter_signle_view, parent, true);
selectedBox = (CheckBox) resultView.findViewById(R.id.box);
title = (TextView) resultView.findViewById(R.id.text);
title.setText(m_filter.getTitle());
selectedBox.setChecked(m_state.isSelected());
selectedBox.setOnCheckedChangeListener((compoundButton, b) ->
m_state.setSelected(b)
);
resultView.setClickable(true);
resultView.setOnClickListener(
view -> m_state.setSelected(!m_state.isSelected())
);
);
return resultView;
I want to handle clicks on FrameLayout. It is not happening.
(I use retrolamda)
You are assigning the listener to the parent of your frame layout.
Please change last parameter of your inflation from true to false. For more - LayoutInflater.inflate(int, ViewGroup, boolean)
View resultView = LayoutInflater.from(context).inflate(R.layout.filter_signle_view, parent, false);
1) Add listener to frame layout. You are missing it and its the most important thing here.
2) Add
android:descendantFocusability="blocksDescendants"
to frame layout in xml. This will prevent child items clicks.
Ok, so after fixing numerous compile errors and other Lint issues, I did run your code (slightly modified). And it worked fine, here's what I have.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/selectableItemBackground"
android:clickable="true">
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:clickable="false" />
<CheckBox
android:id="#+id/box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:clickable="false" />
</FrameLayout>
And the Java part...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View resultView = LayoutInflater.from(getActivity()).inflate(R.layout.generator_personalization, container, false);
CheckBox selectedBox = (CheckBox) resultView.findViewById(R.id.box);
TextView title = (TextView) resultView.findViewById(R.id.text);
title.setText("Title!");
selectedBox.setChecked(true);
selectedBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Toast.makeText(getActivity(), "Changed!", Toast.LENGTH_SHORT).show();
}
});
resultView.setClickable(true);
resultView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getActivity(), "CLICKED!", Toast.LENGTH_SHORT).show();
}
});
return resultView;
}
So basically, what I think is the biggest problem here is attaching the view to the parent in your inflate statement. Try changing that last parameter to false.
You should also note that setting click listeners from code also sets the clickable attribute when enabled.
Hope this helps!
P.S. Consider changing that layout to something more maintainable - like using a single CheckBox instead of FrameLayout + TextView + CheckBox without text.
When you infilating a layout, you supply this parameters to the inflator service:
public View inflate(int resource, ViewGroup root, boolean attachToRoot)
If you set attachToRoot true then the returned view is the root view that you suppilied.
In your example you set it to true and it is returning the "parent" view.
You then set a click listener to a parent viewgroup, however, the click event will be consumed on the child FrameLayout because you set the clickable to true. In your case this will do nothing because you set the click listener to the wrong view, and another view consumed the click event.
So you should set the attachToRoot to false, and the returned view will be your FrameLayout, and that will fix your problems.
Background
I have a form-like activity, which has some views that can be created dynamically upon pressing.
I'm using an xml for each field that is inflated upon clicking on a button.
What I need is that upon choosing to add a new item, it will get focus, scroll if needed, and show the keyboard so that the user can type things into it. The keyboard may be shown upon adding the field or when clicking on the EditText.
The problem
For some reason, on some devices (and I don't think it's even an android version issue) when inflating the new view, the editText within it can get focus but it doesn't show the keyboard, even if it has focus and the user clicks on it.
In such a case, the only way to show the keyboard is to click on another EditText.
I've tested it, and noticed that it doesn't occur when I don't use xml at all, meaning when i create the views only in code.
The question
Why does it occur? What can I do in order to fix it?
I've already tried so many possible solutions, but none work on all devices.
Sample Code
the next code has the described issue on xperia j (android 4.0.4) an galaxy mini (android 2.3.6) .
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ViewGroup container = (ViewGroup) findViewById(R.id.LinearLayout1);
final LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
findViewById(R.id.button).setOnClickListener(new OnClickListener() {
#Override
public void onClick(final View v) {
final View view = inflater.inflate(R.layout.field, null);
// using the next code works when using it instead of inflating a layout:
// final EditText editText = new EditText(MainActivity.this);
// editText.setText("item " + container.getChildCount());
container.addView(view);
}
});
}
the field layout file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFffffff"
android:gravity="center_vertical" >
<Spinner
android:id="#+id/typeSpinner"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:background="#null"
android:padding="5dp" />
<include
android:id="#+id/removeItemView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
layout="#layout/remove_item_view" />
<EditText
android:id="#+id/fieldEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/removeItemView"
android:layout_toRightOf="#+id/typeSpinner"
android:ems="10"
android:focusable="true"
android:focusableInTouchMode="true"
android:hint="field"
android:imeOptions="actionDone"
android:inputType="phone"
android:minWidth="200dp"
android:padding="5dp"
android:singleLine="true"
tools:ignore="HardcodedText" >
<requestFocus />
</EditText>
</RelativeLayout>
the main layout file:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="#+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="click"
tools:ignore="HardcodedText" />
</LinearLayout>
</ScrollView>
EDIT: found a partial solution which won't show the keyboard right away, but at least it will be shown when pressing the editText:
public static void forceFocusOnView(final View view) {
if (view == null)
return;
view.post(new Runnable() {
#Override
public void run() {
view.clearFocus();
view.post(new Runnable() {
#Override
public void run() {
view.requestFocus();
}
});
}
});
}
I'm not sure in a reason of that problem. Could be some missmatches when Android sdk were rewritten in order to match some devices.
So I think that the best solution to you is to show your keyboard manually and do not try to move a train, just take the path of the least resistance:
EditText et=(EditText)this.findViewById(R.id.edit_text);
et.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(et, 0);<----------------
}
});
Here you go , man. Wish you luck.
Oh i see...
private EditText et;
private Runnable setFocus;
......
protected void onCreate(...){
.
.
.
setFocus=new Runnable(){
#Override
public void run() {
et.requestFocus();
}
};
}
....
findViewById(R.id.button).setOnClickListener(new OnClickListener() {
#Override
public void onClick(final View v) {
final View view = inflater.inflate(R.layout.field, null);
//try this:
et=(EditText)view.findViewById(r.id.fieldEditText);
et.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(et, 0);<----------------
}
});
container.addView(view);
container.requestLayout();<--------------
et.postDelayed(setFocus,300);
}
});