Issue
I want to change the background of a button while being pressed by filling it a color from left to right just like a horizontal progress bar.
This is the code for detecting press-hold:
lateinit var buttonView: View
val cdt = object : CountDownTimer(1000, 100) {
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
//do something after being press by 1 sec.
}
}
val onTouchListener = View.OnTouchListener { v, event ->
buttonView = v
when (event.action) {
MotionEvent.ACTION_DOWN -> {
(v.background as AnimationDrawable).start()
cdt.start()
}
MotionEvent.ACTION_UP -> {
(v.background as AnimationDrawable).stop()
cdt.cancel()
}
}
false
}
This is the background I used for the button:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
...items with drawables which colors are in different position. Frame by frame....
</animation-list>
This works but it ain't smooth. Is there a way to do this efficiently? Just like this? https://codepen.io/kay8/pen/azKbjN
(It's okay to answer in Java)
Solution
Update: The following will not work as it is coded for Material buttons, so make sure that your button is either a framework button (android.widget.Button) or an AppCompat button (androidx.appcompat.widget.AppCompatButton).
For the drawable, I would go with a ClipDrawable
as the top layer in a LayerDrawable
. This will visually work like a progress bar.
In the following drawable, the clip drawable overlays a bottom layer that is just a red rectangle.
button_background.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@android:color/holo_red_light" />
</shape>
</item>
<item android:id="@+id/clip_drawable">
<clip
android:clipOrientation="horizontal"
android:gravity="start">
<shape
android:shape="rectangle">
<solid android:color="@android:color/black" />
</shape>
</clip>
</item>
</layer-list>
The black of the clip drawable will eclipse the red rectangle as the level of the clip drawable increases from 0 (not shown) to 10,000 (100% covering the red rectangle.)
TimeAnimator
can be used to animate the level of the clip drawable.
This class provides a simple callback mechanism to listeners that is synchronized with all other animators in the system.
Here is a quick demo. You can change onTimeUpdate()
to be sensitive to total and elapsed time if you want to set the duration of the animation.
MainActivity.java
public class MainActivity extends AppCompatActivity implements TimeAnimator.TimeListener {
private TimeAnimator mAnimator;
private int mCurrentLevel = 0;
private ClipDrawable mClipDrawable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get a handle on the ClipDrawable that we will animate.
LayerDrawable layerDrawable = (LayerDrawable) findViewById(R.id.button).getBackground();
mClipDrawable = (ClipDrawable) layerDrawable.findDrawableByLayerId(R.id.clip_drawable);
// Set up TimeAnimator to fire off on button click.
mAnimator = new TimeAnimator();
mAnimator.setTimeListener(this);
}
@Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
mClipDrawable.setLevel(mCurrentLevel);
if (mCurrentLevel >= MAX_LEVEL) {
mAnimator.cancel();
} else {
mCurrentLevel = Math.min(MAX_LEVEL, mCurrentLevel + LEVEL_INCREMENT);
}
}
public void animateButton(View view) {
if (!mAnimator.isRunning()) {
mCurrentLevel = 0;
mAnimator.start();
}
}
private static final int LEVEL_INCREMENT = 400;
private static final int MAX_LEVEL = 10000;
}
activity_main.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:background="@drawable/button_background"
android:text="Button"
android:onClick="animateButton"
android:textColor="@android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Answered By - Cheticamp
Answer Checked By - Clifford M. (JavaFixing Volunteer)