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?
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 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}"/>
Hello I am new programmer and I have a custom view in my app that i try to make to redraw itself after i give him new data i tried using invalidate but it seem to not work I'm not sure if the problem is the way i update the data or the way i try to make the view redraw itself. any help will be appreciated.
view:
public class BlackJackView extends View {
private PlayerView playerView, computerView;
private BlackJackGame game;
private Context contex;
public void setPlayer(BlackJackPlayer player) {
playerView = new PlayerView(contex, player);
}
public void setComputer(BlackJackPlayer computer) {
computerView = new PlayerView(contex, computer);
}
public BlackJackView(Context context) {
super(context);
init(context);
}
public BlackJackView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public BlackJackView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
this.contex = context;
game = new BlackJackGame();
playerView = new PlayerView(contex, game.getPlayer());
computerView = new PlayerView(contex, game.getComputer());
}
public void init(BlackJackGame game) {
this.game = new BlackJackGame(game.getPlayer().getName(),game.getPlayer().getMoney());
playerView = new PlayerView(contex, game.getPlayer());
computerView = new PlayerView(contex, game.getComputer());
this.invalidate();
}
#Override
public void onDraw(Canvas canvas) {
playerView.onDraw(canvas);
computerView.onDraw(canvas);
}
}
activity:
public class BlackJackActivity extends Activity {
private UsersDataSource dataSource;
private List<User> values;
private BlackJackView view;
private BlackJackGame game;
private int userID;
private int playerMoney;
private String playerName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
game = new BlackJackGame();
view = new BlackJackView(this);
view.setPlayer(game.getPlayer());
setContentView(R.layout.black_jack_activity);
GetId(savedInstanceState);
GetPlayerData();
view = (BlackJackView) findViewById(R.id.blackJackView);
view.init(game);
}
private void GetId(Bundle savedInstanceState) {
if (savedInstanceState == null) {
Bundle extras = getIntent().getExtras();
if (extras == null) {
userID = -1;
} else {
userID = extras.getInt("ID");
}
} else {
userID = (int) savedInstanceState.getSerializable("ID");
}
}
private void GetPlayerData() {
//data base open
dataSource = new UsersDataSource(this);
dataSource.open();
//get list of database
values = dataSource.getAllUsers();
playerName = values.get(userID).getUsername();
playerMoney = (int) values.get(userID).getTokens();
}
#Override
protected void onResume() {
dataSource.open();
super.onResume();
}
#Override
protected void onPause() {
dataSource.close();
super.onPause();
}
}
I have a extended EditText and I can not call any standard function with the following situation:
MainActivity
private Editor sEditor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ex);
sEditor = (Editor)findViewById(R.id.s_editor);
}
private String retText() {
return sEditor.getText.toString();
}
Editor
public Editor(Context context) {
super(context);
init();
}
public Editor(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Editor(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
}
When retText() is called:
Attempt to invoke virtual method 'android.text.Editable com.paulo.ex.Editor.getText()' on a null object reference
I also tried to initialize Editor as sEditor = new Editor(MainActivity.this); but the return value of retText() is always null. What is the appropriate way to resolve this problem?
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");
}
}
}
}