Reducing code duplication in constructors of subclasses - java

SO here's the situation. I am extending a class in Java and I Need to provide 3 constructors with 1, 2 and 3 parameters respectively.
public class MessageButton extends ImageButton {
private String number;
public MessageButton(Context context) {
super(context);
}
public MessageButton(Context context, AttributeSet attrs) {
super(context, attrs);
if( attrs.getAttributeValue("uk.co.gsteinert.ssbb", "number") != null ) {
this.number = attrs.getAttributeValue("uk.co.gsteinert.ssbb", "number");
}
}
public MessageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if( attrs.getAttributeValue("uk.co.gsteinert.ssbb", "number") != null ) {
this.number = attrs.getAttributeValue("uk.co.gsteinert.ssbb", "number");
}
}
}
Obviously there's a bit of duplication there (in the last two constructors) and I want to reduce this.
I see two options:
Move the code into a setup() function, and call that from each constructor. This reduces the duplicated code, but still requires the call in each constructor.
Use this() in all but the last constructor. The only issue is that the defaults for the optional parameters are not null. I would need to check the source of the superclass to work out what values to use.
SO the way I see it, either way I have to duplicate code (the function call) or make assumptions about the superclass (the default values). I know both of these are less than desirable but which is the greater evil?
Or am I missing something?
Thanks

As you mentioned, I would use this() for all your constructors and check which default values are used in the parent class and supply those instead of null (used in this example). You are using this class for your custom code, so you should know what values you want as default.
public class MessageButton extends ImageButton {
private String number;
public MessageButton(Context context) {
this(context, null);
}
public MessageButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MessageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (attrs.getAttributeValue("uk.co.gsteinert.ssbb", "number") != null) {
this.number = attrs.getAttributeValue("uk.co.gsteinert.ssbb", "number");
}
}
}

You can call one constructor from another (without creating a new instance) with this
public MyClass(String value) {
....
}
public MyClass() {
this(defaultValue);
}

public class MessageButton extends ImageButton {
private String number;
public MessageButton(Context context) {
this(context,null,0);
}
public MessageButton(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MessageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if(attrs!=null){
if( attrs.getAttributeValue("uk.co.gsteinert.ssbb", "number") != null ) {
this.number = attrs.getAttributeValue("uk.co.gsteinert.ssbb", "number");
}
}}
}

Related

How to call class VectorClass_1 from public CustomSeekBar(Context context) in Android?

In my scenario, I am trying to call this.progressAnimate = new VectorClass_1(); from public CustomSeekBar(Context context) and public CustomSeekBar(Context context, AttributeSet attrs) but I am getting error: cannot find symbol this.progressAnimate = new VectorClass_1();. How to resolve it?
class CustomSeekBar extends View {
VectorClass_1 progressAnimate = null;
public CustomSeekBar(Context context) {
super(context);
}
public CustomSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.progressAnimate = new VectorClass_1();
class VectorClass_1 implements Runnable {
}
}
I couldn't see your customview class. You should place the VectorClass_1 class outside the constructor. I think below code will solve your issue:-
class CustomSeekBar extends View {
VectorClass_1 progressAnimate = null;
public CustomSeekBar(Context context) {
super(context);
this.progressAnimate = new VectorClass_1();
}
public CustomSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
this.progressAnimate = new VectorClass_1();
}
public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.progressAnimate = new VectorClass_1();
}
static class VectorClass_1 implements Runnable {
VectorClass_1() {
}
public void run() {
}
}
}

Creating CustomButton Extends Button to pre set conditions which called before onclick

I have extended the Button class and I want to run methods like isInternetConnected and isUserLoggedIn when user click to this button and after perform onclick if all condition satisfied.
For Example if I created simple form where user name, email, phone no. and Submit Button placed. When user click on submit first it checks isInternetConnected and isUserLoggedIn if this satisfies then perform final operation saves user data to server or anywhere.
public class CustomButton extends Button {
private static final String TAG = "CustomButton";
public CustomButton(Context context) {
super(context);
}
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
If this is possible then I don't need to check every time
`mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!isInternetConnected) {
//show dialog or snackbar
} else if (!isUserLoggedIn) {
//show dialog or snackbar
}else {
// save data
}
}
});`
Now i understand what you are asking. Ideal solution for your problem would be AOP (aspect oriented programming).
These should help you:
https://medium.com/#jdvp/aspect-oriented-programming-in-android-159054d52757
https://fernandocejas.com/2014/08/03/aspect-oriented-programming-in-android/
This lib should help you:
https://www.eclipse.org/aspectj/

SimpleDraweeView cannot be cast to custom View that extends ImageView

I need to save a number inside an ImageView object so I can access it later in my code. That's why I created a custom class that extends ImageView:
public class SliderView extends ImageView {
private int position;
public SliderView(Context context) {
super(context);
}
public SliderView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public SliderView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setViewPosition(int pos){
this.position = pos;
}
public int getPosition(){
return position;
}
}
This is my SimpleDraweeView object in .xml :
<com.facebook.drawee.view.SimpleDraweeView
android:id="#+id/bottomslider"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
And what I am trying to achieve is this:
final SliderView mediaImage = (SliderView) convertView.findViewById(R.id.bottomslider);
When I try to run the above code, I get this error:
java.lang.ClassCastException:
com.facebook.drawee.view.SimpleDraweeView cannot be cast to
android.custom.SliderView
If I use ImageView instead of SliderView everything works fine, but I don't have the functionality that I need. Is there any workaround?
public class SliderView extends ImageView {
private int position;
public SliderView(Context context) {
super(context);
}
public SliderView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public SliderView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public SliderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void setViewPosition(int pos){
this.position = pos;
}
public int getPosition(){
return position;
}
}
Try this, and let me know if this works

java construct method overload with multi match

Refer to the code below:
public class ExpandableTextView extends TextView {
public ExpandableTextView(Context context) {
this(context, null, null);
}
public ExpandableTextView(Context context, AttributeSet attrs) {
this(context, attrs, null);
}
public ExpandableTextView(Context context, AttributeSet attrs, Runnable runnable) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
this.trimLength = typedArray.getInt(R.styleable.ExpandableTextView_trimLength, DEFAULT_TRIM_LENGTH);
typedArray.recycle();
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
trim = !trim;
setText();
requestFocusFromTouch();
}
});
}
public ExpandableTextView(Context context, AttributeSet attrs, Activity activity) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
this.trimLength = typedArray.getInt(R.styleable.ExpandableTextView_trimLength, DEFAULT_TRIM_LENGTH);
typedArray.recycle();
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
trim = !trim;
setText();
requestFocusFromTouch();
}
});
}
}
method like this(context, null, null); could refer to two other constructor methods, is there any way that i can specify which one it refers to rather than changing the signature or the "null"?
thanks
Sure, just cast the null into the signature type!
new ExpandableTextView(context, (AttributeSet)null, (Runnable)null)
Your code should not work as Compiler will throw error mentioning ambiguous method. Because Java will always try to use the most specific applicable version of a method that's available (see JLS ยง15.12.2). And null is a valid value for the types Context, AttributeSet and Runnable. Therefore all 3 version are applicable.

Custom spinner: setSelection scrolling down

I have a custom spinner
I have a Hint label that is in the last position of my array(spinner), so to display it I set selection to the last position, like this:
ArrayAdapter myAdapter = new MySpinnerAdapter(this,R.layout.spinner_item,createMyList());
myAdapter.setDropDownViewResource(spinner_item);
mySpinner.setAdapter(subCategoryAdapter);
mySpinner.setSelection(myList.size() - 1);
It's working perfectly, but when I touch on Spinner to select a item, the scroll it's "focusing" the bottom of spinner, because of my setSelection.
How can I focus on the first item of the spinner OR "scroll" to the top of it?
Thanks!
You can achieve this by extending Spinner and overriding methods that are responsible for setup and showing the list of values in the drop down:
public class CustomSpinner extends Spinner {
private boolean mToggleFlag = true;
public CustomSpinner(Context context, AttributeSet attrs, int defStyle, int mode) {
super(context, attrs, defStyle, mode);
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSpinner(Context context, int mode) {
super(context, mode);
}
public CustomSpinner(Context context) {
super(context);
}
#Override
public int getSelectedItemPosition() {
if (!mToggleFlag) {
return 0; // Gets to the first element
}
return super.getSelectedItemPosition();
}
#Override
public boolean performClick() {
mToggleFlag = false;
boolean result = super.performClick();
mToggleFlag = true;
return result;
}
}

Categories

Resources