Reputation: 839
I'm trying to extend the Button
class in Android. I have a flicker.java
and a MainActivity.java
. I want to extend the Button class and add a method like public void flicker_with_current_color(){}
to it so that I can use flicker.flicker_with_current_color();
in the MainActivity.java
. Unfortunately, Android Studio gives me the error (as a hint):
This custom view should extend androidx.appcompat.widget.AppCompatButton instead
What I want to do is add a custom method so I can use it everywhere. I know it can be done by creating a method in MainActivity.java
but I'm trying to make a custom Button class that is the same as Android's Button
class but with more methods. I have no idea where I'm wrong.
flicker.java
:
package com.its.me;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.provider.CalendarContract;
import android.util.AttributeSet;
import android.widget.Button;
import android.widget.TextView;
import org.w3c.dom.Text;
import java.util.Timer;
public class flicker extends Button{
public flicker(Context context) {
super(context);
}
public void flicker_with_current_color(Button btn, String color_to_flicker, String color_to_switch_back_to, int time_interval, int times_to_flicker){
for(int i=0; i<times_to_flicker; i++){
btn.setTextColor(Color.parseColor(color_to_flicker));
Timer t = new java.util.Timer();
t.schedule(
new java.util.TimerTask() {
@Override
public void run() {
btn.setTextColor(Color.parseColor(color_to_switch_back_to));
t.cancel();
}
},
time_interval
);
}
}
public void flicker_with_current_color(String color_to_flicker, String color_to_switch_back_to, int time_interval, int times_to_flicker){
for(int i=0; i<times_to_flicker; i++){
this.setTextColor(Color.parseColor(color_to_flicker));
Timer t = new java.util.Timer();
t.schedule(
new java.util.TimerTask() {
@Override
public void run() {
//this.setTextColor(Color.parseColor(color_to_switch_back_to));
t.cancel();
}
},
time_interval
);
}
}
public void flicker_with_current_color(Button btn, int total_time, int times_to_flicker, String color_to_flicker, String color_to_switch_back_to){
int time_interval = total_time/times_to_flicker;
for(int i=0; i<times_to_flicker; i++){
btn.setTextColor(Color.parseColor(color_to_flicker));
Timer t = new java.util.Timer();
t.schedule(
new java.util.TimerTask() {
@Override
public void run() {
btn.setTextColor(Color.parseColor(color_to_switch_back_to));
t.cancel();
}
},
time_interval
);
}
}
}
Upvotes: 1
Views: 780
Reputation: 1970
First You have to keep in mind, that You will need additional, overloaded constructors. The most important one is the one in which You provide XML attributes attrs
- without it the button won't be even possible to create. You can use this set of constructors in Your code and it would be fine:
public FlickerButton(Context context) {
super(context, null, android.R.attr.buttonStyle);
}
public FlickerButton(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.buttonStyle);
}
public FlickerButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
Second issue that might arise in Your code is CalledFromWrongThreadException
. As You set a timer with its task in a different thread, Android will complain that the view, in this case our FlickerButton
, was being manipulated on a different thread than the one that created it:
Only the original thread that created a view hierarchy can touch its views.
For that I would change the approach of that flickering animation. Instead of using the timer I would use ValueAnimator
to animate the color flickering. Analyze the code carefully:
private int buttonTextColor = Color.BLACK; // Initially we will set the color to black
private ValueAnimator flickerAnimation; // Animator that will animate between the colors
public void flicker_with_current_color(String colorToFlicker, String colorToSwitchBackTo, int timeInterval, int timesToFlicker) {
// Animator will animate from the colorToSwitchBackTo
// through colorToFlicker and back to the colorToSwitchBackTo
flickerAnimation = ValueAnimator.ofArgb(
Color.parseColor(colorToSwitchBackTo),
Color.parseColor(colorToFlicker),
Color.parseColor(colorToSwitchBackTo)
);
// The duration of one animation interval and how many times teh animation will repeat
flickerAnimation.setDuration(timeInterval).setRepeatCount(timesToFlicker);
// Specify what will happen when the animated value will change
flickerAnimation.addUpdateListener(valueAnimator -> {
buttonTextColor = (int) valueAnimator.getAnimatedValue();
// invalidate() will invalidate the view and in turn call the onDraw() method
invalidate();
});
flickerAnimation.start(); // start the animation
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setTextColor(buttonTextColor);
}
With all of that code inside a subclass of AppCompatButton
for backwards compatibility You will be ready to use the button:
public class FlickerButton extends AppCompatButton {
// Insert all the code from above here
}
In the XML file:
<com.example.flickerbutton.FlickerButton
android:id="@+id/flicker_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Flicker Button" />
In the parent of the FlickerButton
:
((FlickerButton) findViewById(R.id.flicker_button)).setOnClickListener(view -> {
((FlickerButton) view).flicker_with_current_color(
"white", "black", 500, 10);
});
With all that code the button's text color will flicker for half a second 10 times through black and white color.
Now I won't leave You with just this code - I recommend You read this guide on custom views in Android to get even better understanding on what is happening in the above code and to improve Your existing projects. Good Luck :)
Upvotes: 2