I've been using 2-way databinding for a basic application, it was going pretty well, until i start with custom views and attrs.
I want to create a custom view, with has a TextView and a EditText, and use it inside another layout:
<TextView
android:text="Holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tvTitle"
android:layout_weight="1" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="none"
android:text="Name"
android:ems="10"
android:id="#+id/etAnwser"
android:layout_weight="1" />
And i have the custom attr for it
<resources>
<declare-styleable name="form_item">
<attr name="tvTitle" format="string" />
<attr name="anwserHint" format="string" />
<attr name="anwserText" format="string" />
<attr name="android:enabled" />
</declare-styleable>
In the fragment i do the following:
<rhcloud.com.financialcontrol.tabutil.FormItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="#{state.get()}"
form_item:anwserText='#={expense.description}'
form_item:tvTitle="Description:" />
It works nice has 1-way databind, but whatever i change the text, he don't send me the callback in class
#InverseBindingMethods(value = {
#InverseBindingMethod(type = FormItem.class, attribute = "anwserText"),
})
public class FormItem extends LinearLayout {
private TextView tvTitle;
private EditText etAnwser;
public FormItem(#NonNull Context context) {
super(context);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.form_item, this);
tvTitle = (TextView) findViewById(R.id.tvTitle);
etAnwser = (EditText) findViewById(R.id.etAnwser);
}
public FormItem(#NonNull Context context, #NonNull String title) {
this(context);
setTvTitle(title);
}
public FormItem(#NonNull Context context, #NonNull String title, #NonNull String hint) {
this(context, title);
setAnwserHint(hint);
}
public FormItem(#NonNull Context context, #NonNull String title, #NonNull String hint, #NonNull String anwserText) {
this(context, title, hint);
setAnwserHint(anwserText);
}
public FormItem(#NonNull Context context, #NonNull AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.form_item, this);
tvTitle = (TextView) findViewById(R.id.tvTitle);
etAnwser = (EditText) findViewById(R.id.etAnwser);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.form_item,
0, 0);
try {
setTvTitle(a.getString(R.styleable.form_item_tvTitle));
setAnwserHint(a.getString(R.styleable.form_item_anwserHint));
setAnwserText(a.getString(R.styleable.form_item_anwserText));
String isEnabled = a.getString(R.styleable.form_item_android_enabled);
if (isEnabled != null) {
setEnable(Boolean.parseBoolean(isEnabled));
}
} finally {
a.recycle();
}
}
public void setTvTitle(String title) {
tvTitle.setText(title);
}
public String getTvTitle() {
return tvTitle.getText().toString();
}
public void setAnwserHint(String hint) {
etAnwser.setHint(hint);
}
public String getAnwserHint() {
return etAnwser.getHint().toString();
}
public void setEnable(boolean isEnable) {
tvTitle.setEnabled(isEnable);
etAnwser.setEnabled(isEnable);
}
public void setAnwserText(String anwserText) {
etAnwser.setText(anwserText);
}
public String getAnwserText() {
return etAnwser.getText().toString();
}
#InverseBindingAdapter(attribute = "form_item:anwserText")
public static String setOnAnwserTextAttrChanged(final String value){
Log.d("Test","Calling InverseBindingAdapter: " + value);
return value;
}
#BindingAdapter(value = {"anwserTextAttrChanged"},
requireAll = false)
public static void setOnAnwserTextAttrChanged(final FormItem view,final InverseBindingListener anwserTextAttrChanged){
Log.d("Test","Calling BindingAdapter: " + view.getAnwserText());
if(anwserTextAttrChanged == null){
}else{
Log.d("Test","Calling here");
anwserTextAttrChanged.onChange();
}
}
#BindingAdapter(value = {"android:enabled"})
public static void customEnable(FormItem formItem, boolean isEnable) {
formItem.setEnable(isEnable);
}
}
Does anyone know how to make it work properly?
Fully code can be found at here
This works for me:
#InverseBindingMethods(value = {
#InverseBindingMethod(type = FilterPositionView.class, attribute = "bind:filterStringValue", method = "getFilterValue", event = "android:filterStringValuetAttrChanged")
})
public class FilterPositionView extends LinearLayout {
private FilterPositionBinding mBinding;
public FilterPositionView(Context context) {
super(context);
init(context);
}
public FilterPositionView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public FilterPositionView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public FilterPositionView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context) {
mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.filter_position, this, true);
setOrientation(HORIZONTAL);
mBinding.filterPositionCheck.setOnCheckedChangeListener((buttonView, isChecked) -> {
mBinding.filterPositionValue.setEnabled(isChecked);
if (!isChecked) mBinding.filterPositionValue.setText("");
});
}
/**
* Zwraca wpisywany text
*
* #return wpisane litery tekstu
*/
public String getFilterValue() {
return mBinding.filterPositionValue.getText().toString();
}
#BindingAdapter(value = {"bind:filterTitle", "bind:filterStringValue", "bind:filterDateValue"}, requireAll = false)
public static void setFilterBinding(FilterPositionView positionView, String filterTitle,
String filterStringValue, Long filterDateValue) {
positionView.mBinding.filterPositionTitle.setText(filterTitle);
if (filterStringValue != null)
positionView.mBinding.filterPositionValue.setText(filterStringValue);
if (filterDateValue != null)
positionView.mBinding.filterPositionValue.setText(DateTimeFormatUtil.format(filterDateValue));
}
#BindingAdapter(value = {"android:afterTextChanged", "android:filterStringValuetAttrChanged"}, requireAll = false)
public static void setTextWatcher(FilterPositionView filterPositionView, final TextViewBindingAdapter.AfterTextChanged after,
final InverseBindingListener textAttrChanged) {
TextWatcher newValue = 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 (after != null) {
after.afterTextChanged(s);
}
if (textAttrChanged != null) {
textAttrChanged.onChange();
}
}
};
TextWatcher oldValue = ListenerUtil.trackListener(filterPositionView.mBinding.filterPositionValue, newValue, R.id.textWatcher);
if (oldValue != null) {
filterPositionView.mBinding.filterPositionValue.removeTextChangedListener(oldValue);
}
filterPositionView.mBinding.filterPositionValue.addTextChangedListener(newValue);
}
}
Of course You have to add #={} in your XML layouts like below:
<com.example.customviews.FilterPositionView
style="#style/verticalLabeledValueStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
bind:filterTitle="#{#string/filter_product}"
bind:filterStringValue="#={sfmodel.product}"/>
Related
I have implement a checkable CardView by following https://medium.com/#AlbinPoignot/checkable-cardview-in-all-android-versions-7124ca6df1ab
However, I need to let the user select just one option.
To clarify, if one is already checked, and the user select other, I need to deselect the previous option.
Furthermore, I need to when return the selected CardView keeps the checked state.
Could someone help me with this 2 tasks? Below is my implementation:
public class CheckableCardView extends CardView implements Checkable {
private static final int[] CHECKED_STATE_SET = {
android.R.attr.state_checked
};
private boolean isChecked;
private TextView itemText;
public CheckableCardView(Context context) {
super(context);
init(null);
}
public CheckableCardView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CheckableCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
LayoutInflater.from(getContext()).inflate(R.layout.checkable_card_view, this, true);
setClickable(true);
setChecked(false);
setCardBackgroundColor(ContextCompat.getColorStateList(getContext(), R.color.selector_card_view_background));
if (attrs != null) {
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CheckableCardView, 0, 0);
try {
String text = ta.getString(R.styleable.CheckableCardView_card_text);
itemText = (TextView) findViewById(R.id.text);
if (text != null) {
setText(text);
}
} finally {
ta.recycle();
}
}
}
public void setText(String text){
itemText.setText(text);
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
#Override
public boolean performClick() {
toggle();
return super.performClick();
}
#Override
public void setChecked(boolean checked) {
this.isChecked = checked;
}
#Override
public boolean isChecked() {
return isChecked;
}
#Override
public void toggle() {
setChecked(!this.isChecked);
}
}
You can also use the MaterialCard provided by the Material Components Library.
This card implement a Checkable interface by default.
Just use the android:checkable attribute in the xml:
<com.google.android.material.card.MaterialCardView
android:checkable="true"
..>
or setCheckable(true) in your code.
A way of switching to checked state is:
final MaterialCardView cardView = findViewById(R.id.card);
cardView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
//cardView.setChecked(!cardView.isChecked());
cardView.toggle();
}
});
For those who are looking for an easy solution to this problem, here is the code:
cardONE.setOnClickListener {
cardONE.isChecked = true // set the current card to checked
cardTWO.isChecked = false // set the other card to unchecked
}
cardTWO.setOnClickListener {
cardTWO.isChecked = true
cardONE.isChecked = false
}
Or the function:
fun setChecked(checkCard: MaterialCardView, uncheckCard: MaterialCardView){
checkCard.isChecked = true
uncheckCard.isChecked = false
}
cardONE.setOnClickListener {
setChecked(it as MaterialCardView, cardTWO)
}
It's maybe not the most "elegant" way, but it works like a charm.
Needed Dependencies
implementation "com.google.android.material:material:1.2.0"
XML File
<com.google.android.material.card.MaterialCardView
android:id="#+id/cardONE"
<!-- THIS IS NEEDED -->
android:checkable="true"
android:clickable="true"
android:focusable="true" />
You can do that by declare:
private List<CheckableCardView> checkableCardViewList = new ArrayList<>();
then you can add your cards to your list in "onBindViewHolder"
checkableCardViewList.add(position,holder.cardView);
finally you can add a callback function like "onClick"
holder.cardView.setOnClickListener(new CheckableCardView.OnClickListener() {
#Override
public void onClick(boolean b) {
if (b) {
for(CheckableCardView checkableCardView : checkableCardViewList) {
checkableCardView.setChecked(false);
}
checkableCardViewList.get(position).setChecked(true);
notifyDataSetChanged();
}
}
});
for call back you can add this to your CheckableCardView at bottom
public void setOnClickListener(OnClickListener onClickListener) { this.onClickListener = onClickListener;}
public interface OnClickListener {
void onClick(boolean b);
}
and at the top
private OnClickListener onClickListener;
#Override
public boolean performClick() {
toggle();
onClickListener.onClick(this.isChecked);
return super.performClick();
}
I've developed a custom compound view which has a Button and a ProgressBar in it. I'm using databinding in my app. Compound view is not accepting onClick event, how to sort this issue?
<com.ui.custom.LoadingButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/bg_color"
android:onClick="#{()->viewModel.onNextClick()}"
android:text="#string/next"
android:textColor="#color/white"
app:isLoading="#{viewModel.isLoading}" />
Layout of Loading Button:
<?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>
</data>
<FrameLayout
android:id="#+id/parentFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:textSize="#dimen/btn_text_size" />
<com.wang.avi.AVLoadingIndicatorView
android:id="#+id/loading_indicator"
android:layout_width="#dimen/btn_loading_indicator"
android:layout_height="#dimen/btn_loading_indicator"
android:layout_gravity="center"
android:elevation="#dimen/cardview_default_elevation"
app:indicatorName="LineSpinFadeLoaderIndicator" />
</FrameLayout>
</layout>
Or If I use 'onClick' attribute of android, then how can i get it in TypedArray? So i can set it to view programmatically.
<declare-styleable name="LoadingButton">
<attr name="isLoading" format="boolean" />
<attr name="android:text" />
<attr name="android:textColor" />
<attr name="android:background" />
<attr name="android:onClick" />
</declare-styleable>
LoadingButton java
#BindingMethods({
#BindingMethod(type = LoadingButton.class, attribute = "onLoadingButtonClick", method = "onLoadingButtonClick"),
})
public class LoadingButton extends LinearLayout implements View.OnClickListener {
private Context mContext;
private int background, textColor;
private boolean isLoading;
private String btnText;
private LayoutLoadingBtnBinding itemViewBinding;
private OnLoadingButtonListener listener;
public LoadingButton(Context context) {
super(context);
initializeView(context, null, 0);
}
public LoadingButton(Context context, AttributeSet attrs) {
super(context, attrs);
initializeView(context, attrs, 0);
}
public LoadingButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeView(context, attrs, defStyleAttr);
}
private void initializeView(Context context, AttributeSet attrs, int defStyleAttr) {
mContext = context;
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LoadingButton, defStyleAttr, 0);
try {
background = array.getColor(R.styleable.LoadingButton_android_background, Integer.MAX_VALUE);
textColor = array.getColor(R.styleable.LoadingButton_android_textColor, Integer.MAX_VALUE);
btnText = array.getString(R.styleable.LoadingButton_android_text);
isLoading = array.getBoolean(R.styleable.LoadingButton_isLoading, false);
// Method onClick = array.getValue(R.styleable.LoadingButton_android_onClick);
} finally {
array.recycle();
}
itemViewBinding = LayoutLoadingBtnBinding.inflate(LayoutInflater.from(mContext), this, true);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// setOnClickListener(this::onClick);
setValues();
}
private void setValues() {
try {
if (background != Integer.MAX_VALUE)
itemViewBinding.parentFrame.setBackgroundColor(background);
if (background != Integer.MAX_VALUE) {
itemViewBinding.button.setTextColor(textColor);
itemViewBinding.loadingIndicator.setIndicatorColor(textColor);
}
itemViewBinding.button.setText(btnText);
updateLoadingViews();
} catch (Exception e) {
e.printStackTrace();
}
}
private void updateLoadingViews() {
itemViewBinding.button.setVisibility(isLoading ? INVISIBLE : VISIBLE);
itemViewBinding.loadingIndicator.setVisibility(isLoading ? VISIBLE : GONE);
}
public void setLoading(boolean isLoading) {
if (this.isLoading != isLoading) // update only if loading state is changed
{
this.isLoading = isLoading;
updateLoadingViews();
}
}
public void setOnLoadingButtonClick(OnLoadingButtonListener listener) {
AppLogger.d("usm_loading_btn_0", "setOnLoadingButtonClick is called: listener= " + (listener != null));
this.listener = listener;
}
#Override
public void onClick(View view) {
AppLogger.d("usm_loading_btn_1", "onClick is called");
if (listener != null) {
listener.onLoadingButtonClick();
}
}
public interface OnLoadingButtonListener {
void onLoadingButtonClick();
}
}
You can not using this way
<attr name="android:onClick" />
Try to using BindingMethods
#BindingMethods({
#BindingMethod(type = LoadingButton.class, attribute = "onLoadingButtonClick", method = "onLoadingButtonClick"),
})
public class LoadingButton extends YOUR_ROOT_VIEW implements View.OnClickListener {
// your item view class.
private OnLoadingButtonListener listener;
public void setOnLoadingButtonClick(OnLoadingButtonListener listener) {
this.listener = listener;
}
#Override
public void onClick(View view) {
if (listener != null) {
listener.onLoadingButtonClick();
}
}
public interface OnLoadingButtonListener {
void onLoadingButtonClick();
}
}
and in your layout
<com.ui.custom.LoadingButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/bg_color"
android:onLoadingButtonClick="#{()->viewModel.onNextClick()}"
android:text="#string/next"
android:textColor="#color/white"
app:isLoading="#{viewModel.isLoading}" />
UPDATE 1
If your onClick() not working, remove it and try to using this way
private void initializeView(Context context, AttributeSet attrs, int defStyleAttr) {
mContext = context;
TypedArray array =
context.obtainStyledAttributes(attrs, R.styleable.LoadingButton, defStyleAttr, 0);
try {
background =
array.getColor(R.styleable.LoadingButton_android_background, Integer.MAX_VALUE);
textColor =
array.getColor(R.styleable.LoadingButton_android_textColor, Integer.MAX_VALUE);
btnText = array.getString(R.styleable.LoadingButton_android_text);
isLoading = array.getBoolean(R.styleable.LoadingButton_isLoading, false);
// Method onClick = array.getValue(R.styleable.LoadingButton_android_onClick);
} finally {
array.recycle();
}
itemViewBinding =
LayoutLoadingBtnBinding.inflate(LayoutInflater.from(mContext), this, true);
itemViewBinding.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
listener.onLoadingButtonClick();
}
}
});
}
I'm trying to follow this blog post to try and get two way data binding to work for a custom component (A constraint view with an EditText in it).
I'm able to get two standard EditText components to be in sync (both ways) with my model, but I'm having trouble getting the changes in my custom component to flow into my model (although one way data binding works).
My model:
public class Model extends BaseObservable {
private String value;
#Bindable
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
notifyPropertyChanged(company.com.databinding.BR.value);
}
public Model() {
value = "Value";
}
}
Activity:
#InverseBindingMethods({
#InverseBindingMethod(
type = CustomComponent.class,
attribute = "value",
method = "getValue")
})
public class MainActivity extends AppCompatActivity {
#BindingAdapter("value")
public static void setColor(CustomComponent view, String value) {
if (!value.equals(view.getValue())) {
view.setValue(value);
}
}
#BindingAdapter(
value = {"onValueChange", "valueAttrChanged"},
requireAll = false
)
public static void setListeners(CustomComponent view,
final ValueChangeListener onValueChangeListener,
final InverseBindingListener inverseBindingListener) {
ValueChangeListener newListener;
if (inverseBindingListener == null) {
newListener = onValueChangeListener;
} else {
newListener = new ValueChangeListener() {
#Override
public void onValueChange(CustomComponent view,
String value) {
if (onValueChangeListener != null) {
onValueChangeListener.onValueChange(view,
value);
}
inverseBindingListener.onChange();
}
};
}
ValueChangeListener oldListener =
ListenerUtil.trackListener(view, newListener,
R.id.textWatcher);
if (oldListener != null) {
view.removeListener(oldListener);
}
if (newListener != null) {
view.addListener(newListener);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setModel(new Model());
}
}
Custom component:
public class CustomComponent extends ConstraintLayout {
private String value;
private EditText txt;
private TextWatcher textWatcher;
ValueChangeListener listener;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
if (txt != null) {
txt.setText(value);
}
}
public CustomComponent(Context context) {
super(context);
init(context);
}
public CustomComponent(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CustomComponent(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context) {
}
private void init(Context context, AttributeSet attrs) {
View.inflate(context, R.layout.custom_component, this);
txt = findViewById(R.id.txt_box);
final CustomComponent self = this;
textWatcher = 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) {
if (listener != null) {
listener.onValueChange(self, editable.toString());
}
}
};
txt.addTextChangedListener(textWatcher);
}
public void addListener(ValueChangeListener listener) {
this.listener = listener;
}
public void removeListener(ValueChangeListener listener) {
this.listener = null;
}
}
public interface ValueChangeListener {
public void onValueChange(CustomComponent view, String value);
}
I think the section "Hooking The Event" in that post has gone completely over my head; I really only needed a simple setter and getter for the component, and so couldn't quite understand what was being done in that BindingAdapter. Of all of them I think it's this line that I don't get at all:
ValueChangeListener oldListener =
ListenerUtil.trackListener(view, newListener,
R.id.textWatcher);
Demo at: https://github.com/indgov/data_binding
Sorry that the ListenerUtil was confusing. That's only useful when your component supports multiple listeners. In that case, you can't just set a new listener, you must remove the old one and add the new one. ListenerUtil helps you track the old listener so it can be removed. In your case, it can be simplified:
#BindingAdapter(
value = {"onValueChange", "valueAttrChanged"},
requireAll = false
)
public static void setListeners(CustomComponent view,
final ValueChangeListener onValueChangeListener,
final InverseBindingListener inverseBindingListener) {
ValueChangeListener newListener;
if (inverseBindingListener == null) {
newListener = onValueChangeListener;
} else {
newListener = new ValueChangeListener() {
#Override
public void onValueChange(CustomComponent view,
String value) {
if (onValueChangeListener != null) {
onValueChangeListener.onValueChange(view,
value);
}
inverseBindingListener.onChange();
}
};
}
view.setListener(newListener);
}
and then replace addListener() with setListener() and you don't need the removeListener() because you can always set the listener to null.
The problem you're seeing is in your component:
public String getValue() {
return value;
}
You're returning the value that was last set by the setter and not the value that is in the EditText. To solve this:
public String getValue() {
return txt.getText().toString();
}
I have a custom switch control class MySwitch
public class MySwitch extends RelativeLayout implements CompoundButton.OnCheckedChangeListener {
//private final Context context;
private String swKey;
private Integer swIcoOn, swIcoOff;
Switch mySW;
ImageView swIco;
SharedPreferences preferences;
public MySwitch(Context context) {
super(context, null);
}
public MySwitch(Context context, AttributeSet attrs) {
this(context, attrs, R.layout.my_switch);
}
public MySwitch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
preferences = PreferenceManager.getDefaultSharedPreferences(context);
LayoutInflater.from(context).inflate(defStyleAttr, this, true);
//View.inflate(context, defStyleAttr, this);
swIco = (ImageView) findViewById(R.id.swIcon);
TextView myText = (TextView) findViewById(R.id.swTitle);
TextView mySummary = (TextView) findViewById(R.id.swSummary);
mySW = (Switch) findViewById(R.id.mySW);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MySwitch, 0, 0);
try {
myText.setText(ta.getString(R.styleable.MySwitch_android_title));
mySummary.setText(ta.getString(R.styleable.MySwitch_android_summary));
mySW.setTextOn(ta.getString(R.styleable.MySwitch_android_switchTextOn));
mySW.setTextOff(ta.getString(R.styleable.MySwitch_android_switchTextOff));
mySW.setOnCheckedChangeListener(this);
swIcoOn = ta.getResourceId(R.styleable.MySwitch_icoOn, 0);
swIcoOff = ta.getResourceId(R.styleable.MySwitch_icoOff, 0);
swKey = ta.getString(R.styleable.MySwitch_android_key);
update();
} finally {
ta.recycle();
}
}
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
update(b);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(swKey, b);
editor.commit();
}
public void update(Boolean val) {
mySW.setChecked(val);
if(val)
swIco.setImageResource(swIcoOn);
else
swIco.setImageResource(swIcoOff);
}
public void update() {
update(preferences.getBoolean(swKey, false));
}
}
i want to reuse the code from this class in a SwitchPreference because the code is almost identical, so the class for it is
public class MySwitchPref extends SwitchPreference{
MySwitch obj;
private SharedPreferences preferences;
public MySwitchPref(Context context, AttributeSet attrs) {
super(context, attrs);
//setLayoutResource(R.layout.my_switch);
obj = new MySwitch(context,attrs);
}
#Override
protected void onBindView(View view) {
super.onBindView(view);
obj.update();
}
}
The issue is that on the SwitchPreference the Icon is not shown.
How can i get the icon to show also in the SwitchPreference?
I have a widget View as below:
public class RemoteNumView extends FrameLayout {
how call I use Roboguice just as in RoboActivity? As below:
#InjectView(R.id.btn_remote_control_num_0)
private TextView mText;
Full code is:
/**
* Created by bbcv on 13-12-12.
*/
public class RemoteNumView extends FrameLayout {
private IService mService;
#InjectView(R.id.btn_remote_control_num_0)
private TextView mText;
public RemoteNumView(Context context) {
super(context);
///
addView(LayoutInflater.from(context).inflate(R.layout.v_remote_control_fun,null));
}
public RemoteNumView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RemoteNumView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setService(IService service){
mService = service;
}
}
Anyone can HELP?
Solved it by writing custom code. Roboguice is badly written for this purpose.
protected void injectViews() {
for (Field field : this.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(InjectView.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new UnsupportedOperationException("Views can't be staticaly assigned.");
} else {
if (View.class.isAssignableFrom(field.getType())) {
try {
final InjectView injectView = field.getAnnotation(InjectView.class);
;
final int id = injectView.value();
View view = findViewById(id);
if ((view == null) && Nullable.notNullable(field)) {
throw new NullPointerException(String.format("Can't inject null value into %s.%s when field is not #Nullable", field.getDeclaringClass(), field.getName()));
}
field.setAccessible(true);
field.set(this, view);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
} else {
throw new UnsupportedOperationException("Need view type to assign");
}
}
}
}