Hi I have created custom component for TextInputLayout. I want to set hint text to TextInputLayout. I am adding custom class which extends TextInputLayout into my root layout. I tried it in following way:
<android.support.design.widget.TextInputLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColorHint="#color/gray"
android:theme="#style/editTextSelectedTheme"
android:layout_marginTop="#dimen/margin_10"
>
<EditText
android:id="#+id/form_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
public class FormEditText extends TextInputLayout
{
TextInputLayout view;
public FormEditText(Context context) {
super(context);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = (TextInputLayout)inflater.inflate(R.layout.form_edit_text, this, true);
initUI();
}
public FormEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FormEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
And finally in my fragment:
FormEditText gadgetName = new FormEditText(getContext());
gadgetName.setHint(getResources().getString(R.string.gadget_name_hint));
formContainer.addView(gadgetName);
Above code adds new TextInputLayout in my view but not adding hint text for it. Am I doing anything wrong? Need Some help. Thank you.
I did few changes as per comment below.
public class FormEditText extends LinearLayout
view = inflater.inflate(R.layout.form_edit_text, this, true);
Now it si showing hint text as per required.But now it is not showing my edit text value.It is not taking any input value.
Related
I created custom view based on Toolbar:
public class CommonToolbar extends Toolbar {
public CommonToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.common_toolbar, this, true);
}
common_toolbar.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="#color/colorPrimary"
app:contentInsetStart="0dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/backIcon"
style="#style/ToolbarNavigationButton"
app:srcCompat="#drawable/icon_close" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_toEndOf="#+id/backIcon"
android:gravity="center"
android:text="Title"
android:textStyle="bold" />
</RelativeLayout>
</android.support.v7.widget.Toolbar>
As you can see i set attribute contentInsetStart value to 0, but when i use this Toolbar in any layout file, padding is still present (image). I can modify this by changing contentInsetStart value in destination layout file, but I prefer to keep this in source xml. Why is it happening?
Because your custom view extends Toolbar you are inflating your xml toolbar into a Toolbar. If you take a look at your layout with Layout Inspector, your view hierarchy will look like this:
<CommonToolbar> (Subclass of Toolbar)
<Toolbar>
<RelativeLayout>
...
So when you set contentInsetStart via xml, you are setting it on the inner toolbar. The outer toolbar still has a content inset.
To avoid this you can have Common Toolbar extend a ViewGroup i.e.
public class CommonToolbar extends RelativeLayout {
public CommonToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.common_toolbar, this, true);
}
Or inflate the common toolbar using an include tag. i.e.
<include
layout="#layout/common_toolbar"
/>
which allows you to remove the CommonToolbar custom view
Use setContentInsetsAbsolute(0, 0);
public class CustomToolbar extends Toolbar {
#BindView(R.id.txt_screen_title)
TextView txtScreenTitle;
public CustomToolbar(Context context) {
super(context);
initToolbarLayout(context);
}
public CustomToolbar(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initToolbarLayout(context);
}
public CustomToolbar(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initToolbarLayout(context);
}
private void initToolbarLayout(Context context) {
View toolbarView = LayoutInflater.from(context).inflate(R.layout.app_toolbar, null);
this.addView(toolbarView);
ButterKnife.bind(this, toolbarView);
setContentInsetsAbsolute(0, 0);
}
public void setScreenTitle(String title) {
if(txtScreenTitle != null) {
txtScreenTitle.setText(title);
}
}
}
I'm using data binding to bind the layouts in my Android app.
I have set up my layout ( my_custom.xml ) and the binding class is generated (MyCustomBinding), but Android Studio does not seem to find the .inflate(...) method of the Binding class right away, marking it as an error ( red text!).
The code seems to be correct though, since it compiles and builds just fine into an APK.
How do I get Android Studio to update correctly ?
Code example:
This is my custom View code:
public class MyCustomView extends FrameLayout {
public MyCustomView(Context context) {
this(context, null, 0);
}
public MyCustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
MyCustomBinding binding = MyCustomBinding.inflate(inflater, this, true);
binding.aButton.setText("Whatever");
}
}
layout is defined as:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="#+id/a_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click me!"
android:padding="10dp"
android:background="#000"
android:textColor="#fff"
android:layout_gravity="center"
/>
</FrameLayout>
</layout>
And here's the issue: (highlighted red)
Something is not completing in Android Studio because you haven't actually implemented data binding.
Once you add a variable to your layout's data element, the inflate method will be found as you expect. That said, you're really not getting the benefit of databinding by setting the value of the text field directly through the binding. You should instead be setting a View Model in your binding, and then let the binding update the views accordingly. For example:
create a View Model:
public class MyViewModel {
public final ObservableField<String> name;
public MyViewModel(String name) {
this.name = new ObservableField<>(name);
}
}
and use it in your layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="model" type="com.victoriaschocolates.conceirge.MyViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{model.name}"
android:padding="10dp"
android:background="#000"
android:textColor="#fff"
android:layout_gravity="center"
/>
</FrameLayout>
</layout>
(note the variable declared in the data element, and how it is referenced in the TextView's text attribute)
then bind the two in your custom view:
public class MyCustomView extends FrameLayout {
public MyCustomView(Context context) {
this(context, null, 0);
}
public MyCustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
MyCustomBinding binding = MyCustomBinding.inflate(inflater, this, true);
MyViewModel model = new MyViewModel("Whatever");
binding.setModel(model);
}
}
Of course, it would probably be better still to have the data passed in through a setter in the custom view class, or even passed in from the container view (see http://developer.android.com/tools/data-binding/guide.html#includes)
You can try setting a custom class name to your Binding Layout, and referencing it in your custom view to make it clear which layout you using:
<layout>
<data class="MyCustomBinding">
</data>
</layout>
If this does not work, use DataBindingUtil instead of MyCustomBinding, which returns the base DataBinding class and has a inflate() method:
MyCustomBinding binding = DataBindingUtil.inflate(inflater, this, true);
From the docs:
Sometimes the binding cannot be known in advance. In such cases, the binding can be created using the DataBindingUtil class:
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
this is updated answer based on latest databinding and androidx, i come here on this question to solve my own problem, after watching above answers i develop by own working code snippet. hope this helps
public class CustomActionBar1 extends RelativeLayout {
public MutableLiveData<String> title = new MutableLiveData<>("Sample text");
CustomActionBar1Binding binding;
public CustomActionBar1(Context context) {
super(context);
init(context, this);
}
public CustomActionBar1(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, this);
}
public CustomActionBar1(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, this);
}
public CustomActionBar1(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, this);
}
private void init(Context context, ViewGroup viewGroup) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
binding = DataBindingUtil.inflate(inflater, R.layout.custom_action_bar_1, viewGroup, true);
}
// helper to change title
public void changeTitle(String title) {
if (title != null)
this.title.setValue(title);
}
public void setTitleVisibility(Boolean visibile){
binding.titleText.setVisibility(visibile ? View.VISIBLE : View.INVISIBLE);
}
}
Xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="customActionBar1"
type="com.actionBar.CustomActionBar1" />
</data>
<RelativeLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:background="#color/yellow"
android:layout_height="#dimen/action_bar_height"
android:id="#+id/main_header_relative">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="#{customActionBar1.title}"
android:layout_centerInParent="true"
android:gravity="center"
android:id="#+id/titleText"
android:visibility="visible"
android:textColor="#color/black" />
</RelativeLayout>
</layout>
You dont need that type of stuffs, DataBinding includes DataBindingUtils class here we go.
public MyCustomView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
MyCustomBinding binding = DataBindingUtil.inflate(inflater, this, true);
binding.aButton.setText("Whatever");
}
As the title, i want to know if there is a the best way to control a view that added dynamically. (we have to keep reference to the view that was added)
Some time, for a complex request we have to add view in runtime. The is some ways to do that. In my case:
Some time i use a listview/recyclerview and control view via the list/recycleview adapter.
Other way is use a hashmap.
Do you have any other ideas? and how it work?
I prefer way, when I define View both by Java and XML file. View created like this, gives you ability to call your own Java methods, but you don`t need to create whole layout dynamically in Java. Little example:
MyView.java:
public class MyView extends LinearLayout {
TextView textView;
public MyView(Context context) {
super(context);
init();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init() {
inflate(getContext(), R.layout.my_view, this);
setOrientation(VERTICAL);
textView = (TextView) findViewById(R.id.text_view);
}
public MyView setContent(String value) {
textView.setText(value);
return this;
}
}
my_view.xml:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="#+id/text_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</merge>
After that, you can simply add it in you layout through Java:
cont.addView(new MyView(this).setContent("Value"));
or xml:
<com.path.to.your.view.MyView
android:id="#+id/my_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
I am using a TextSwitcher with a TextView factory. I would like to pass the style I set on the TextSwitcher down to the TextViews.
TextSwitcher does not have a 3 arg constructor.
Is it possible to get the style attribute from the attribute set?
Xml
<com.my.TextSwitcher
style="#style/My.TextView.Style"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Java
public class MyTextSwitcher extends TextSwitcher {
public MyTextSwitcher(Context context, AttributeSet attrs) {
super(context, attrs);
int style = attrs.getAttributeIntValue("", "style", 0); // I tried this to no avail
setFactory(new MyTextViewFactory(context, attrs, style));
}
private static class MyTextViewFactory implements ViewFactory {
private final Context context;
private final AttributeSet attrs;
private final int style;
public MyTextViewFactory(Context context, AttributeSet attrs, int style) {
this.context = context;
this.attrs = attrs;
this.style = style;
}
#Override
public View makeView() {
return new TextView(context, attrs, style);
}
}
}
is the only answer to make my own custom attribute of int that will pass the style? I can't use the built in style tag?
You can retrieve the style attribute(and pass it to the inner views) using:
attrs.getStyleAttribute()
or its equivalent(as the docs mention):
getAttributeResourceValue(null, "style")
One alternative is to declare the TextView's in XML, however this gives less flexibility to the amount of child TextView's I can have.
<com.my.TextSwitcher
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/My.TextView.Style"
android:text="#string/some_text" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/My.TextView.Style"
android:text="#string/some_other_text" />
</com.my.TextSwitcher>
I added a custom preference to my project (code below). I added it to my preferences xml with a custom widgetLayout:
<w.PlusOnePreference
android:title="Rate App"
android:key="custom"
android:widgetLayout="#layout/plusone_pref"/>
Preference layout xml:
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.gms.plus.PlusOneButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:plus="http://schemas.android.com/apk/lib/com.google.android.gms.plus"
android:id="#+id/plus_one_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:focusable="false"
plus:size="standard" />
I see the layout and the button in the layout works fine. The only problem is that the preference isn't clickable. Like it's hidden behind something.
Any ideas on how to make it clickable?
If I add a regular Preference (without a widget layout) it works fine.
Thanks.
public class PlusOnePreference extends Preference {
private PlusClient mPlusClient = null;
public PlusOnePreference(Context context) {
super(context);
}
public PlusOnePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PlusOnePreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setPlusClient(PlusClient plusClient) {
mPlusClient = plusClient;
}
#Override
protected void onBindView(View view) {
super.onBindView(view);
//mPlusOneButton =
PlusOneButton plusOneButton = (PlusOneButton)view.findViewById(R.id.plus_one_button);
plusOneButton.initialize(mPlusClient, SettingsActivity.URL, SettingsActivity.PLUS_ONE_REQUEST_CODE);
}
}
in layout/plusone_pref.xml set android:focusable="false" for your Button
Putting Pskink's answer together with Ran's comment:
If your custom preference's layout is a ViewGroup (e.g. a *Layout), use android:descendantFocusability="blocksDescendants"
If it's just one View, use android:focusable="false"
Preferences don't have a clickable attribute, though there is an onClick() method. Tryandroid:selectable.