I want to create a control in android where user can input through keyboard or enter through drop down list(spinner).
Actually the values I hard code in array in spinner is not exhaustive, so user should have the option to input through virtual keyboard also.
So Either user can enter through keyboard or select from list?
How can i achieve this in android?
You can build your own view:
public class ServerPreference extends DialogPreference {
private String value;
private EditText editText;
private Spinner spinner;
private final int server_list_id;
ArrayList<String> servers;
public ServerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setPersistent(false);
setDialogLayoutResource(R.layout.server_preference_layout);
servers = new ArrayList<String>();
Resources r = context.getResources();
if (MySettings.KEY_ASR_SERVER_HOST.equals(getKey())) {
server_list_id = R.array.asrServers;
} else if (MySettings.KEY_LOG_SERVER_HOST.equals(getKey())) {
server_list_id = R.array.logServers;
} else {
server_list_id = R.array.servicesServers;
}
Collections.addAll(servers, r.getStringArray(server_list_id));
}
#Override
protected void onBindDialogView(View view) {
editText = (EditText)view.findViewById(R.id.server_preference_text);
spinner = (Spinner)view.findViewById(R.id.server_preference_spinner);
SharedPreferences pref = getSharedPreferences();
value = pref.getString(getKey(), "");
editText.setText(value);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
view.getContext(), server_list_id, android.R.layout.simple_spinner_item );
adapter.setDropDownViewResource( android.R.layout.simple_dropdown_item_1line );
spinner.setAdapter( adapter );
updateSpinner();
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if ( position != 0 ) {
value = servers.get(position);
editText.setText(value);
}
}
public void onNothingSelected(AdapterView<?> arg0) {
}
});
editText.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
updateSpinner();
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
}
});
super.onBindDialogView(view);
}
void updateSpinner() {
value = editText.getText().toString();
int index = servers.indexOf(value);
if ( index == -1 )
index = 0;
spinner.setSelection(index);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
if ( positiveResult ) {
SharedPreferences.Editor editor = getEditor();
editor.putString(getKey(),value);
editor.commit();
}
super.onDialogClosed(positiveResult);
}
public String getValue() {
return value;
}
void setValue(String value) {
this.value = value;
}
}
Layout of custom view:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText android:layout_height="wrap_content" android:layout_width="match_parent" android:id="#+id/server_preference_text" android:inputType="text">
<requestFocus></requestFocus>
</EditText>
<Spinner android:layout_height="wrap_content" android:id="#+id/server_preference_spinner" android:layout_width="match_parent"></Spinner>
</LinearLayout>
When you want to use this view, just define in XML file:
<xxx.xxxxxx.xxxxx.settings.debug.ServerPreference
android:title="ASR/VVS Server"
android:persistent="true"
android:positiveButtonText="OK"
android:dialogTitle="ASR/VVS Server"
android:key="SERVER_NAME"
android:negativeButtonText="Cancel"
android:entryValues="#array/asrServers"
android:entries="#array/asrServers"/>
Related
I have one linear layout in that I have two views for an email address, one is Textview and another one is Edittext.
When there is empty or not proper email address then Textview's color should change to red,
if the user gets focus on edit text Textview's color should change to blue.
if the user lost focus on EditText then TextView's color should change to black.
To check how to achieve with customview, I have created a custom view of its parent view which is LinearLayout which is below
CustomLinearLayout.java file
public class CustomLinearLayout extends LinearLayout {
int editTextResourceId, textViewResourceId;
EditText editText;
TextView textView;
Context mContext;
public CustomLinearLayout(Context context) {
super(context);
mContext = context;
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
inflate(context, R.layout.activity_register_account_new, this);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyTextView, defStyleAttr, 0);
editTextResourceId = a.getResourceId(R.styleable.MyTextView_supportedEditText, NO_ID);
textViewResourceId = a.getResourceId(R.styleable.MyTextView_supportedTextView, NO_ID);
a.recycle();
}
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
if (editTextResourceId != 0 && textViewResourceId != 0) {
editText = (EditText)findViewById(editTextResourceId);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {}
#Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
if(s.length() != 0){
textView.setText(s);
}
}
});
}
}
}
activity_main.xml file looks like
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/grey"
android:orientation="vertical">
<mobile.android.view.CustomLinearLayout
style="#style/RegisterItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:supportedEditText="#id/edUserName"
custom:supportedTextView="#id/tvUserName"
android:orientation="horizontal">
<TextView
android:id="#+id/tvUserName"
style="#style/RegisterTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/username" />
<EditText
android:id="#+id/edUserName"
style="#style/RegisterEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="#string/hint_username"
android:inputType="textPersonName" />
</mobile.android.view.CustomLinearLayout>
attris.xml file
<declare-styleable name="MyTextView">
<attr name="supportedEditText" />
<attr name="supportedTextView" />
</declare-styleable>
But whenever I debug code, it shows me 0 (resource id) instead of proper id or NO_ID.
Can anyone help me how to resolve this? thanks,
Try this code inside CustomLinearLayout.java :
private EditText findEditText() {
int i = 0;
for (; i < getChildCount(); i++) {
if (getChildAt(i) instanceof EditText) {
return (EditText) getChildAt(i);
}
}
return null;
}
private TextView findTextView() {
int i = 0;
for (; i < getChildCount(); i++) {
if (getChildAt(i) instanceof TextView) {
return (TextView) getChildAt(i);
}
}
return null;
}
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
TextView textView = findTextView();
EditText editText = findEditText();
if (editText != null) {
editText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
}
#Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
if (s.length() != 0) {
if (textView != null) {
textView.setText(s);
}
}
}
});
}
}
I built one thing like you want and I call it : EditTextComponent
This is java source code for EditTextComponent:
public class EdittextComponent extends InputComponent {
protected TextView tvTitle;
protected EditText edtInput;
protected int mInputType = InputType.TYPE_CLASS_TEXT;
protected int mMaxLine = 1;
protected InputFilter[] mInputFilter;
protected boolean hasFirstChange = false;
protected TextView tvWarning;
protected int mIDLayout = R.layout.component_edittext;
protected boolean isAlwaysShowWarning = false;
#Override
public View createView() {
LayoutInflater inflater = LayoutInflater.from(mContext);
rootView = inflater.inflate(mIDLayout, null, false);
tvTitle = rootView.findViewById(R.id.tv_title);
if (Utils.validateString(mTitle)) {
tvTitle.setText(mTitle);
}
edtInput = rootView.findViewById(R.id.edt_input);
if (Utils.validateString(mPlaceHolder)) {
edtInput.setHint(mPlaceHolder);
}
edtInput.setInputType(mInputType);
edtInput.setMaxLines(mMaxLine);
if (null != mInputFilter && mInputFilter.length > 0) {
edtInput.setFilters(mInputFilter);
}
if (Utils.validateString(mValue)) {
edtInput.setText(mValue);
}
if(null != mValueSelected){
isCompleted = true;
String value = (String) mValueSelected;
if(Utils.validateString(value)) {
edtInput.setText(value);
}
}
edtInput.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if (null != s) {
hasFirstChange = true;
checkChange(s.toString().trim());
}
}
});
tvWarning = rootView.findViewById(R.id.tv_warning);
return rootView;
}
protected void checkChange(String source) {
if (null != mValidator) {
boolean newState = mValidator.validate(source);
if (isCompleted != newState) {
notifyStateChanged(newState, source);
}
} else {
if (source.length() == 0) {
if (isCompleted) {
notifyStateChanged(false, source);
}
} else if (!isCompleted) {
notifyStateChanged(true, source);
}
}
if(isCompleted){
edtInput.setBackgroundResource(R.drawable.bg_input);
tvWarning.setVisibility(View.INVISIBLE);
}
else{
showWarning(true);
}
}
protected void notifyStateChanged(boolean is_complete, String source) {
isCompleted = is_complete;
if (null != mCallBack) {
mCallBack.onChanged(isCompleted, source);
}
}
public void updateValue(String value){
mValue = value;
if(null != edtInput){
edtInput.setText(value);
}
checkChange(value);
}
#Override
public boolean validate() {
if(!isVisible){
return true;
}
if (!isCompleted ) {
showWarning(false);
}
return isCompleted;
}
protected void showWarning(boolean isCallByItSelf) {
String message = "Cannot be empty";
if (null != mValidator) {
message = mValidator.getMessage();
}
if(null != tvWarning) {
tvWarning.setText(message);
tvWarning.setVisibility(View.VISIBLE);
}
if(null != edtInput) {
edtInput.setBackgroundResource(R.drawable.bg_input_warning);
}
}
#Override
public String getValue() {
mValue = edtInput.getText().toString();
return mValue;
}
#Override
public Object getData() {
return getValue();
}
public void setInputType(int inputType) {
mInputType = inputType;
}
public void setMaxLine(int maxLine) {
mMaxLine = maxLine;
}
public void setInputFilter(InputFilter[] inputFilter) {
mInputFilter = inputFilter;
}
public void setIDLayout(int IDLayout) {
mIDLayout = IDLayout;
}
public void setAlwaysShowWarning(boolean alwaysShowWarning) {
isAlwaysShowWarning = alwaysShowWarning;
}}
And here is component_edittext.xml
<?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">
<TextView
android:id="#+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:text="Last name"
android:textColor="#333333"
android:textSize="14sp"/>
<EditText
android:id="#+id/edt_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/tv_title"
android:layout_marginStart="10dp"
android:layout_marginTop="9dp"
android:layout_marginEnd="10dp"
android:background="#drawable/bg_input_inactive"
android:paddingStart="19dp"
android:paddingTop="16dp"
android:paddingBottom="15dp"
android:textColor="#333333"
android:textSize="14sp"/>
<TextView
android:id="#+id/tv_warning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/edt_input"
android:layout_alignParentEnd="true"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:text="Cannot be empty"
android:textColor="#DD4033"
android:textSize="12sp"
android:visibility="invisible"/>
</RelativeLayout>
InputComponent.java
public class InputComponent extends BaseComponent {
protected String mTitle;
protected String mValue;
protected String mKey;
protected String mPlaceHolder;
protected Validator mValidator;
protected boolean isCompleted = false;
protected InputCallBack mCallBack;
protected float mWeight = 1;
public boolean validate(){
return false;
}
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
mTitle = title;
}
public String getValue() {
return mValue;
}
public void setValue(String value) {
mValue = value;
}
public String getKey() {
return mKey;
}
public void setKey(String key) {
mKey = key;
}
public void setCallBack(InputCallBack callBack) {
mCallBack = callBack;
}
public void setPlaceHolder(String placeHolder) {
mPlaceHolder = placeHolder;
}
public void setValidator(Validator validator) {
mValidator = validator;
}
public float getWeight() {
return mWeight;
}
public void setWeight(float weight) {
mWeight = weight;
}
}
BaseComponet.java
public class BaseComponent implements Comparable<BaseComponent>{
protected View rootView;
protected String mIDComponent;
protected Context mContext;
protected Object mValueSelected;
protected int mPosition;
protected boolean isVisible = true;
public BaseComponent() {
mContext = Manager.getInstance().getCurrentActivity();
}
public View createView() {
return rootView;
}
public Object getValueSelected() {
return mValueSelected;
}
public void setValueSelected(Object valueSelected) {
mValueSelected = valueSelected;
}
public String getIDComponent() {
return mIDComponent;
}
public void setIDComponent(String IDComponent) {
mIDComponent = IDComponent;
}
public void setRootView(View rootView) {
this.rootView = rootView;
}
public int getPosition() {
return mPosition;
}
public void setPosition(int position) {
mPosition = position;
}
public Object getData(){
return null;
}
public void removeAllView(){
}
public boolean isVisible() {
return isVisible;
}
public void setVisible(boolean visible) {
isVisible = visible;
}
public void updateView(){
if(null != rootView){
if(isVisible){
rootView.setVisibility(View.VISIBLE);
}
else{
rootView.setVisibility(View.GONE);
}
}
}
public View getRootView() {
return rootView;
}
#Override
public int compareTo( BaseComponent otherComponent) {
if (null != otherComponent) {
return (otherComponent.getPosition() - getPosition());
}
return 0;
}
}
And this is how you can use EditTextComponent:
EdittextComponent lastNameComponent = new EdittextComponent();
lastNameComponent.setPlaceHolder("Enter last name");
lastNameComponent.setTitle("Last name");
lastNameComponent.setKey("lastname");
lastNameComponent.setCallBack(new InputCallBack() {
#Override
public void onChanged(boolean isSelected, Object data) {
checkComplete();
}
});
listInputComponents.add(lastNameComponent);
You can use this component to create email, last name, phonecode .... and add them to listInputComponents ( an array)
Then you can add them to a linearlaylout like this:
LinearLayout llBody = findViewById(...)
for(BaseComponent component: listInputComponents)
{ llBody.addView(component.createView()
}
I write this code for search in recyclerview with edit text but, when I run the application and input a text that I need to search about it on the edit text in the first letter the recycler content not changed and when I input the second Letter the RecyclerView become empty.
how can I filter the recycler? what is the wrong in my code ?
xml code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".FamilyActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/search"
android:layout_width="294dp"
android:layout_height="70dp"
android:layout_marginTop="-20dp"
android:hint="Search ..."
android:drawableLeft="#drawable/ic_search_black_24dp"
/>
<Button
android:id="#+id/add"
android:layout_width="54dp"
android:layout_height="match_parent"
android:layout_marginLeft="35dp"
android:drawableTop="#drawable/ic_person_add_black_24dp"
android:onClick="add_new_family"
tools:ignore="OnClick" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginStart="0dp"
android:layout_marginLeft="0dp"
android:layout_marginTop="50dp"></android.support.v7.widget.RecyclerView>
</RelativeLayout>
code in main activity:
public class FamilyActivity extends AppCompatActivity {
RecyclerView recyclerView;
FamilyAdapter familyAdapter;
Button add_family;
EditText search;
List<Family> familyList;
String patientID = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_family);
add_family=(Button)findViewById(R.id.add);
search =(EditText)findViewById(R.id.search);
familyList = new ArrayList<>();
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
Bundle extras = getIntent().getExtras();
if (extras != null) {
patientID = extras.getString("ID");
}
loadFamilyList();
//adding some items to our list
//creating recyclerView adapter
FamilyAdapter adapter = new FamilyAdapter(this, familyList);
//setting adapter to recyclerView
recyclerView.setAdapter(adapter);
addTextListener();
}
public void addTextListener(){
search.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence query, int start, int before, int count) {
query = query.toString().toLowerCase();
final List<Family> filteredList = new ArrayList<>();
for (int i = 0; i < familyList.size(); i++) {
final String text = familyList.get(i).toString().toLowerCase();
if (text.contains(query)) {
filteredList.add(familyList.get(i));
}
}
recyclerView.setLayoutManager(new LinearLayoutManager(FamilyActivity.this));
FamilyAdapter fadapter = new FamilyAdapter(FamilyActivity.this,filteredList);
recyclerView.setAdapter(fadapter);
fadapter.notifyDataSetChanged(); // data set changed
}
});
}
code in adapter:
public class FamilyAdapter extends RecyclerView.Adapter<FamilyAdapter.FamilyViewHolder> {
private Context context;
private List<Family> familyList;
private List<Family> familyListFull;
public FamilyAdapter(Context context, List<Family> familyList) {
this.context = context;
this.familyList = familyList;
familyListFull=new ArrayList<>(familyList);
}
#NonNull
#Override
public FamilyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.list_layout, null);
return new FamilyViewHolder(view);
}
#Override
public void onBindViewHolder(FamilyViewHolder familyViewHolder, final int position) {
Family family = familyList.get(position);
familyViewHolder.textViewTitle.setText(family.getName());
familyViewHolder.familyLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "onClick: clicked on : " + familyList.get(position));
String name = familyList.get(position).getName();
String num = familyList.get(position).getNum();
Intent intent = new Intent(context, Chatting.class);
intent.putExtra("num", num);
intent.putExtra("name", name);
context.startActivity(intent);
}
});
familyViewHolder.deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final AlertDialog.Builder builder = new AlertDialog.Builder(context );
builder.setTitle("Delete");
builder.setMessage("Are you sure you want to delete this one ")
.setPositiveButton("YES", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
RemoveFamilyMember(position);
}
}).setNegativeButton("NO", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
builder.setCancelable(true);
}
});
AlertDialog alert=builder.create();
alert.show();
}
});
}
you can try something like this I think this might work, I haven't tested it but I think that making a new adapter everytime that the edittext has something in it is a bad idea. and if you get to the point back to where the query is equal to "" empty string then you should make the fileterd list back into the whole list which I didn't put in there
public class FamilyActivity extends AppCompatActivity {
RecyclerView recyclerView;
FamilyAdapter familyAdapter;
Button add_family;
EditText search;
List<Family> familyList;
List<Family> filteredList;
String patientID = "";
FamilyAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_family);
add_family=(Button)findViewById(R.id.add);
search =(EditText)findViewById(R.id.search);
familyList = new ArrayList<>();
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
Bundle extras = getIntent().getExtras();
if (extras != null) {
patientID = extras.getString("ID");
}
loadFamilyList();
filteredList = new ArrayList<>(familyList);
//adding some items to our list
//creating recyclerView adapter
adapter = new FamilyAdapter(this, filteredList);
//setting adapter to recyclerView
recyclerView.setAdapter(adapter);
addTextListener();
}
public void addTextListener(){
search.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence query, int start, int before, int count) {
query = query.toString().toLowerCase();
filteredList = new ArrayList<>();
for (int i = 0; i < familyList.size(); i++) {
final String text = familyList.get(i).toString().toLowerCase();
if (text.contains(query)) {
filteredList.add(familyList.get(i));
}
}
recyclerView.removeAllViews();;
adapter.notifyDataSetChanged(); // data set changed
}
});
}
editTextSearch.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
filter(s.toString());
}
});
private void filter(String text) {
ArrayList<Model> newList = new ArrayList<>();
for (Model item : mModelList) {
if (item.getTitle().contains(text)){
newList.add(item);
}
}
yourAdapter.setFilter(newList);
}
I have a componed view in android contains several textViews and one EditText. I defined an attribute for my custom view called text and getText, setText methods. Now I want to add a 2-way data binding for my custom view in a way its bind to inner edit text so if my data gets updated edit text should be updated as well (that's works now) and when my edit text gets updated my data should be updated as well.
My binding class looks like this
#InverseBindingMethods({
#InverseBindingMethod(type = ErrorInputLayout.class, attribute = "text"),
})
public class ErrorInputBinding {
#BindingAdapter(value = "text")
public static void setListener(ErrorInputLayout errorInputLayout, final InverseBindingListener textAttrChanged) {
if (textAttrChanged != null) {
errorInputLayout.getInputET().addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
textAttrChanged.onChange();
}
});
}
}
}
I tried to bind text with the code below. userInfo is an observable class.
<ir.avalinejad.pasargadinsurance.component.ErrorInputLayout
android:id="#+id/one_first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="#string/first_name"
app:text="#={vm.userInfo.firstName}"
/>
When I run the project I get this error
Error:(20, 13) Could not find event 'textAttrChanged' on View type 'ir.avalinejad.pasargadinsurance.component.ErrorInputLayout'
And my custom view looks like this
public class ErrorInputLayout extends LinearLayoutCompat implements TextWatcher {
protected EditText inputET;
protected TextView errorTV;
protected TextView titleTV;
protected TextView descriptionTV;
private int defaultGravity;
private String title;
private String description;
private String hint;
private int inputType = -1;
private int lines;
private String text;
private Subject<Boolean> isValidObservable = PublishSubject.create();
private Map<Validation, String> validationMap;
public ErrorInputLayout(Context context) {
super(context);
init();
}
public ErrorInputLayout(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
readAttrs(attrs);
init();
}
public ErrorInputLayout(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
readAttrs(attrs);
init();
}
private void readAttrs(AttributeSet attrs){
TypedArray a = getContext().getTheme().obtainStyledAttributes(
attrs,
R.styleable.ErrorInputLayout,
0, 0);
try {
title = a.getString(R.styleable.ErrorInputLayout_title);
description = a.getString(R.styleable.ErrorInputLayout_description);
hint = a.getString(R.styleable.ErrorInputLayout_hint);
inputType = a.getInt(R.styleable.ErrorInputLayout_android_inputType, -1);
lines = a.getInt(R.styleable.ErrorInputLayout_android_lines, 1);
text = a.getString(R.styleable.ErrorInputLayout_text);
} finally {
a.recycle();
}
}
private void init(){
validationMap = new HashMap<>();
setOrientation(VERTICAL);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
titleTV = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.error_layout_default_title_textview, null, false);
addView(titleTV);
descriptionTV = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.error_layout_default_description_textview, null, false);
addView(descriptionTV);
readInputFromLayout();
if(inputET == null) {
inputET = (EditText) LayoutInflater.from(getContext()).inflate(R.layout.error_layout_defult_edittext, this, false);
addView(inputET);
}
errorTV = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.error_layout_default_error_textview, null, false);
addView(errorTV);
inputET.addTextChangedListener(this);
defaultGravity = inputET.getGravity();
//set values
titleTV.setText(title);
if(description != null && !description.trim().isEmpty()){
descriptionTV.setVisibility(VISIBLE);
descriptionTV.setText(description);
}
if(inputType != -1)
inputET.setInputType(inputType);
if(hint != null)
inputET.setHint(hint);
else
inputET.setHint(title);
inputET.setLines(lines);
inputET.setText(text);
}
private void readInputFromLayout() {
if(getChildCount() > 3){
throw new IllegalStateException("Only one or zero view is allow in layout");
}
if(getChildCount() == 3){
View view = getChildAt(2);
if(view instanceof EditText)
inputET = (EditText) view;
else
throw new IllegalStateException("only EditText is allow as child view");
}
}
public void setText(String text){
inputET.setText(text);
}
public String getText() {
return text;
}
public void addValidation(#NonNull Validation validation, #StringRes int errorResourceId){
addValidation(validation, getContext().getString(errorResourceId));
}
public void addValidation(#NonNull Validation validation, #NonNull String error){
if(!validationMap.containsKey(validation))
validationMap.put(validation, error);
}
public void remoteValidation(#NonNull Validation validation){
if(validationMap.containsKey(validation))
validationMap.remove(validation);
}
public EditText getInputET() {
return inputET;
}
public TextView getErrorTV() {
return errorTV;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
checkValidity();
if(editable.toString().length() == 0) //if hint
inputET.setGravity(Gravity.RIGHT);
else
inputET.setGravity(defaultGravity);
}
public Subject<Boolean> getIsValidObservable() {
return isValidObservable;
}
private void checkValidity(){
//this function only shows the first matched error.
errorTV.setVisibility(INVISIBLE);
for(Validation validation: validationMap.keySet()){
if(!validation.isValid(inputET.getText().toString())) {
errorTV.setText(validationMap.get(validation));
errorTV.setVisibility(VISIBLE);
isValidObservable.onNext(false);
return;
}
}
isValidObservable.onNext(true);
}
}
After hours of debugging, I found the solution. I changed my Binding class like this.
#InverseBindingMethods({
#InverseBindingMethod(type = ErrorInputLayout.class, attribute = "text"),
})
public class ErrorInputBinding {
#BindingAdapter(value = "textAttrChanged")
public static void setListener(ErrorInputLayout errorInputLayout, final InverseBindingListener textAttrChanged) {
if (textAttrChanged != null) {
errorInputLayout.getInputET().addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
textAttrChanged.onChange();
}
});
}
}
#BindingAdapter("text")
public static void setText(ErrorInputLayout view, String value) {
if(value != null && !value.equals(view.getText()))
view.setText(value);
}
#InverseBindingAdapter(attribute = "text")
public static String getText(ErrorInputLayout errorInputLayout) {
return errorInputLayout.getText();
}
First, I added AttrChanged after the text like this #BindingAdapter(value = "textAttrChanged") which is the default name for the listener and then I added getter and setter methods here as well.
event = "android:textAttrChanged" works for me:
object DataBindingUtil {
#BindingAdapter("emptyIfZeroText") //replace "android:text" on EditText
#JvmStatic
fun setText(editText: EditText, text: String?) {
if (text == "0" || text == "0.0") editText.setText("") else editText.setText(text)
}
#InverseBindingAdapter(attribute = "emptyIfZeroText", event = "android:textAttrChanged")
#JvmStatic
fun getText(editText: EditText): String {
return editText.text.toString()
}
}
you need add one more function
#BindingAdapter("app:textAttrChanged")
fun ErrorInputLayout.bindTextAttrChanged(listener: InverseBindingListener) {
}
Here is my code:
public class MainActivity extends AppCompatActivity {
ListView listView;
List all;
SharedPreferences sharedPref;
SharedPreferences.Editor editor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sharedPref = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
editor = sharedPref.edit();
setListAdapter();
}
private void setListAdapter(){
final String[] data = displayData().toString().split(",");
ListAdapter listAdapter = new CustomAdapter(this, data);
listView = (ListView) findViewById(R.id.listView);
listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
listView.setAdapter(listAdapter);
}
public List displayData(){
all = new ArrayList<>();
try {
Map<String, ?> allMap = sharedPref.getAll();
for (Map.Entry<String, ?> entry : allMap.entrySet()) {
all.add(entry.getKey().toString() + ":" + entry.getValue().toString());
}
} catch(NullPointerException npe){
all.add(" : ");
}
Log.i("understandCode", "displayData: " + all.toString());
return all;
}
public void add(View view){
editor.putString(" ", " ");
editor.apply();
setListAdapter();
}
private class CustomAdapter extends ArrayAdapter<String> {
SharedPreferences.Editor editor;
String type;
String content;
public CustomAdapter(Context context, String[] resource) {
super(context, R.layout.custom_row, resource);
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences
(MainActivity.this);
editor = sharedPref.edit();
}
EditText typeET;
EditText contentET;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
View customView = layoutInflater.inflate(R.layout.custom_row, parent, false);
String singleItem = getItem(position);
typeET = (EditText) customView.findViewById(R.id.type);
contentET = (EditText) customView.findViewById(R.id.content);
String[] givenStrings = singleItem.split(":");
try {
typeET.setText(givenStrings[0]);
contentET.setText(givenStrings[1]);
}catch(ArrayIndexOutOfBoundsException aioobe){
typeET.setText(" ");
contentET.setText(" ");
}
typeET.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
type = s.toString();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
type = s.toString();
save();
}
#Override
public void afterTextChanged(Editable s) {
}
});
contentET.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
content = s.toString();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
content = s.toString();
save();
}
#Override
public void afterTextChanged(Editable s) {
}
});
return customView;
}
private void save() {
editor.putString(type, content);
editor.apply();
Log.i("understandCode", type + ":" + content);
}
}
}
Here is my log when I am editing my key, value:
05-04 17:52:52.107 11146-11146/com.x.xI/understandCode: [ my mom!a said:y ]
05-04 17:52:52.462 11146-11146/com.valerdaita.myinfo I/understandCode: [ my mom!a said:yo ]
05-04 17:52:52.698 11146-11146/com.valerdaita.myinfo I/understandCode: [ my mom!a said:yol ]
05-04 17:52:52.897 11146-11146/com.x.x I/understandCode: [ my mom!a said:yolo ]
When I restart the Activity:
05-04 17:53:05.085 12056-12056/com.x.x I/understandCode: displayData: [ : ]
So, it is not showing the data that I saved. I really am having trouble identifying the problem because I know that my save functionality is working well because of the Log, but I cannot identify any problem with the displayer.
You will definitely get NPE as you have not saved any values in your preferences yet.
Code flow is setListAdapter() ->displayData() ->sharedPref.getAll().
In between this you are not adding any key-value pair to preferences.
So why do you expect you'll not get NPE ?
Save some values before using getAll() if you don't want to get NPE.
I've created an Activity where I've got an "Add subject" button. When I press it, it creates an item in a ListView, which is formed by an EditText where the user enters a number.
What I want to do is to add the numbers inside the EditTexts of each item created, depending if the user has created 3, 4, 5, etc. items in the ListView, via button.
Here is the code of the Activity:
public class PersActivity extends Activity {
Button start, calcaverage1;
private SubjectAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.subject_list_view);
setupListViewAdapter();
setupAddMarkButton();
// Accept button
Button acceptbn= (Button)findViewById(R.id.start1);
acceptbn.setOnClickListener(new OnClickListener()
{ public void onClick(View v)
{
Intent intent = new Intent(PersActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
});
}
public void removeClick(View v) {
Mark itemToRemove = (Mark)v.getTag();
adapter.remove(itemToRemove);
}
private void setupListViewAdapter() {
adapter = new SubjectAdapter(PersActivity.this, R.layout.subject_list_item, new ArrayList<Mark>());
ListView atomPaysListView = (ListView)findViewById(R.id.subject_list_item);
atomPaysListView.setAdapter(adapter);
}
private void setupAddMarkButton() {
findViewById(R.id.addsubject).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
adapter.insert(new Mark("", 0), 0);
}
});
}
}
Here is the code of the adapter:
public class SubjectAdapter extends ArrayAdapter<Mark> {
protected static final String LOG_TAG = SubjectAdapter.class.getSimpleName();
private List<Mark> items;
private int layoutResourceId;
private Context context;
public SubjectAdapter(Context context, int layoutResourceId, List<Mark> items) {
super(context, layoutResourceId, items);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
MarkHolder holder = null;
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new MarkHolder();
holder.Mark = items.get(position);
holder.removePaymentButton = (ImageButton)row.findViewById(R.id.remove);
holder.removePaymentButton.setTag(holder.Mark);
holder.name = (TextView)row.findViewById(R.id.subjectname);
setNameTextChangeListener(holder);
holder.value = (TextView)row.findViewById(R.id.subjectmark);
setValueTextListeners(holder);
row.setTag(holder);
setupItem(holder);
return row;
}
private void setupItem(MarkHolder holder) {
holder.name.setText(holder.Mark.getName());
holder.value.setText(String.valueOf(holder.Mark.getValue()));
}
public static class MarkHolder {
Mark Mark;
TextView name;
TextView value;
ImageButton removePaymentButton;
}
private void setNameTextChangeListener(final MarkHolder holder) {
holder.name.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
holder.Mark.setName(s.toString());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
#Override
public void afterTextChanged(Editable s) { }
});
}
private void setValueTextListeners(final MarkHolder holder) {
holder.value.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
try{
holder.Mark.setValue(Double.parseDouble(s.toString()));
}catch (NumberFormatException e) {
Log.e(LOG_TAG, "error reading double value: " + s.toString());
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
#Override
public void afterTextChanged(Editable s) { }
});
}
}
I've implemented serializable to pass data through the adapter:
public class Mark implements Serializable {
private static final long serialVersionUID = -5435670920302756945L;
private String name = "";
private double value = 0;
public Mark(String name, double value) {
this.setName(name);
this.setValue(value);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
}
}
Hope there's a solution. Thanks!
Here is how you would do it
addNumberFromText()
{
int total=0;
for(int i=0;i<listView.getChildCount();i++)
{
View wantedView = listView.getChildAt(i);
EditText edtText=view.findViewById(R.id.specificEditTextId);
//not checking wheter integer valid or not, Please do so
int value=Integer.parseInt(edtText.toString());
total+=value;
}
Log.d(TAG,"total sum is "+total);
}
update your activity from following code
public class PersActivity extends Activity
{
Button start, calcaverage1;
private SubjectAdapter adapter;
ListView atomPaysListView;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.subject_list_view);
atomPaysListView = (ListView)findViewById(R.id.subject_list_item);
setupListViewAdapter();
setupAddMarkButton();
// Accept button
Button acceptbn= (Button)findViewById(R.id.start1);
acceptbn.setOnClickListener(new OnClickListener()
{ public void onClick(View v)
{
Intent intent = new Intent(PersActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
});
}
public void removeClick(View v) {
Mark itemToRemove = (Mark)v.getTag();
adapter.remove(itemToRemove);
}
private void setupListViewAdapter() {
adapter = new SubjectAdapter(PersActivity.this, R.layout.subject_list_item, new ArrayList<Mark>());
atomPaysListView.setAdapter(adapter);
}
private void setupAddMarkButton() {
findViewById(R.id.addsubject).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
adapter.insert(new Mark("", 0), 0);
}
});
}
addNumberFromText()
{
double total=0;
for(int i=0;i<atomPaysListView.getChildCount();i++)
{
View wantedView = atomPaysListView.getChildAt(i);
/*
// if edit text
EditText edt=(EditText)view.findViewById(R.id.editText);
//not checking wheter valid or not, Please do so
double value=Double.parseDouble(edt.toString());
*/
//you say edittext, but its a textview or so it seems
TextView txv=(TextView)view.findViewById(R.id.subjectmark);
//not checking wheter valid or not, Please do so
double value=Double.parseDouble(txv.toString());
total+=value;
}
Log.d(TAG,"total sum is "+total);
}
}