OnTouch ImageView frame animation set default image - android

I've been trying to look for a solution, but it seems like no one tried it, or maybe its too simple that I did not find it.
Anyways, I'm trying to achieve a button press with an ImageView. What I want is, ImageView to display a default image, and if pressed (OnTouch) [which has a MotionEvent.ACTION_DOWN block] to run a frame animation. When you release the touch [which has a MotionEvent.ACTION_UP block], it should stop the animation and return to the default image.
Note: Frame animation is continuous repeating, and should not include the default image as one of the looped through image. (Button up image should not be displayed when touching)
Now the problem is, I have the animation working, but as per the android documentation, the first item in the <animation-list> tag will be displayed by default. But if I add the default image (button un-touched state) as the first item, it will be displayed in the loop. Also, when I release the touch, I use stop() method of the AnimationDrawable, which stops the animation at the current frame (image) and I can't seem to find any way to stop and go to default image state.
Here is my code:
button_anim.xml
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<item android:drawable="#drawable/button1_press1" android:duration="200" />
<item android:drawable="#drawable/button1_press2" android:duration="200" />
<item android:drawable="#drawable/button1_press3" android:duration="200" />
</animation-list>
MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
button = (ImageView)findViewById(R.id.imageView1);
button.setBackgroundResource(R.drawable.button_anim);
buttonAnim = (AnimationDrawable)button.getBackground();
button.setOnTouchListener(buttonTest);
}
private OnTouchListener buttonTest = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
buttonAnim.start();
// Log.d("Test", "Touch down");
} else if (action == MotionEvent.ACTION_UP) {
buttonAnim.stop();
// Log.d("Test", "Touch Stop");
}
return true;
}
};
Default image:- button1_inactive.png

buttonAnim.stop();
buttonAnim.reset();
// Log.d("Test", "Touch Stop");

AnimationDrawable mFrameAnimation = (AnimationDrawable)button.getBackground();
mFrameAnimation.stop();
mFrameAnimation.setVisible(true, true);

Related

Button left in highlighted state with touchListener and clickListener

I'm having a problem with my Button staying in a highlighted state, after doing the following:
public class MainActivity extends AppCompatActivity {
#SuppressLint("ClickableViewAccessibility")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCompatButton button = (AppCompatButton) findViewById(R.id.mybutton);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("Test", "calling onClick");
}
});
button.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
v.invalidate();
break;
}
case MotionEvent.ACTION_UP: {
v.getBackground().clearColorFilter();
v.invalidate();
v.performClick();
Log.d("Test", "Performing click");
return true;
}
}
return false;
}
});
}
}
Concerning the code above, when using it, I'm expecting the button click to be handled by the touch, and by returning "true" the handling should stop at the touchListener.
But this is not the case. The button stays in a highlighted state, even though the click is being called.
What I get is:
Test - calling onClick
Test - Performing click
on the other hand, if I'm using the following code, the button is clicked, same prints, but the button doesn't end up stuck in a highlighted state:
public class MainActivity extends AppCompatActivity {
#SuppressLint("ClickableViewAccessibility")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCompatButton button = (AppCompatButton) findViewById(R.id.mybutton);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("Test", "calling onClick");
}
});
button.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
v.invalidate();
break;
}
case MotionEvent.ACTION_UP: {
v.getBackground().clearColorFilter();
v.invalidate();
// v.performClick();
Log.d("Test", "Performing click");
return false;
}
}
return false;
}
});
}
}
I'm a bit confused as to what's the responder chain to the touch event. My guess is that it's:
1) TouchListener
2) ClickListener
3) ParentViews
Can someone confirm this as well?
Such customizations need no programmatically modifications. You can do it simply in xml files. First of all, delete the setOnTouchListener method that you provide in the onCreate entirely. Next, define a selector color in the res/color directory like the following. (if the directory doesn't exist, create it)
res/color/button_tint_color.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#e0f47521" android:state_pressed="true" />
<item android:color="?attr/colorButtonNormal" android:state_pressed="false" />
</selector>
Now, set it to the button's app:backgroundTint attribute:
<androidx.appcompat.widget.AppCompatButton
android:id="#+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:backgroundTint="#color/button_tint_color" />
Visual Result:
EDITED: (to address touch event issue)
From an overall point of view, the flow of the touch event starts from the Activity, then flows down to the layout (from the parent to the child layouts), and then to the views. (LTR flow in the following picture)
When the touch event reaches the target view, the view can handle the event then decide to pass it to the prior layouts/activity or not (returning false of true in onTouch method). (RTL flow in the above picture)
Now let's take a look at the View's source code to gain a deeper insight into the touch event flows. By taking a look at the implementation of the dispatchTouchEvent, we'd see that if you set an OnTouchListener to the view and then return true in its onTouch method, the onTouchEvent of the view won't be called.
public boolean dispatchTouchEvent(MotionEvent event) {
// removed lines for conciseness...
boolean result = false;
// removed lines for conciseness...
if (onFilterTouchEventForSecurity(event)) {
// removed lines for conciseness...
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) { // <== right here!
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
// removed lines for conciseness...
return result;
}
Now, look at the onTouchEvent method where the event action is MotionEvent.ACTION_UP. We see that perform-click action happens there. So, returning true in the OnTouchListener's onTouch and consequently not calling the onTouchEvent, causes not calling the OnClickListener's onClick.
There is another issue with not calling the onTouchEvent, which is related to the pressed-state and you mentioned in the question. As we can see in the below code block, there is an instance of UnsetPressedState that calls setPressed(false) when it runs. The result of not calling setPressed(false) is that the view gets stuck in the pressed state and its drawable state doesn't change.
public boolean onTouchEvent(MotionEvent event) {
// removed lines for conciseness...
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
// removed lines for conciseness...
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// removed lines for conciseness...
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// removed lines for conciseness...
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
// removed lines for conciseness...
}
// removed lines for conciseness...
break;
// removed lines for conciseness...
}
return true;
}
return false;
}
UnsetPressedState:
private final class UnsetPressedState implements Runnable {
#Override
public void run() {
setPressed(false);
}
}
Regarding the above descriptions, you can change the code by calling setPressed(false) yourself to change the drawable state where the event action is MotionEvent.ACTION_UP:
button.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
v.invalidate();
break;
}
case MotionEvent.ACTION_UP: {
v.getBackground().clearColorFilter();
// v.invalidate();
v.setPressed(false);
v.performClick();
Log.d("Test", "Performing click");
return true;
}
}
return false;
}
});
You are messing around touch and focus events. Let start with understanding behavior with same color. By default, there is Selector assigned as background to the Button in Android. So simply changing background color, make is static (color will not change). But it's not a native behavior.
Selector might look like this one.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_focused="true"
android:state_pressed="true"
android:drawable="#drawable/bgalt" />
<item
android:state_focused="false"
android:state_pressed="true"
android:drawable="#drawable/bgalt" />
<item android:drawable="#drawable/bgnorm" />
</selector>
As you can see above, there is state focused and state pressed. By setting onTouchListener you will handle touch events, which have nothing to do with focus.
Selector of the Button should replace focus event with touch during click event on the button. But in first part of your code you intercepted events for the touch (returning true from callback). Color change cannot proceed further and is freezing with same color . And that is why second variant (without interception) are working fine and that is your confusion.
UPDATE
All you need to do it's to change behavior and color for the Selector. For ex. by using next background for the Button. AND remove onTouchListener from your implementation at all.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="#color/color_pressed" />
<item android:drawable="#color/color_normal" />
</selector>
if you assign a background to the button, it wont change the color on click.
<color name="myColor">#000000</color>
and set it as backgrount to your button
android:background="#color/myColor"
you can just use material Chips for instead of Button view.
refer : https://material.io/develop/android/components/chip
there they handle those hililghted events and you can customize with applying the themes.

Apply Tint on ImageButton

I am creating an android App which has about 15 ImageButtons. The app will have one activity and several fragments. The onclick event handling of some of the button will be done by fragments and rest will be done by activity.
I want to apply the tint on the buttons when it is pressed. I dont want to use different images for Normal and Pressed state but want to apply the tint programatically as it is a cumbersome to maintain two images for every button.
As I searched on Stackoverflow, there can be multiple ways.
1) Handle the onTouchEvent. On Action_Down event apply the tint and on Action_Up cancel the tint effect. The tint can be applied using the setColorFilter on the button.
View.OnTouchListener onTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
ImageButton view = (ImageButton) v;
view.setColorFilter(0x77000000, PorterDuff.Mode.SRC_ATOP);
v.invalidate();
break;
}
case MotionEvent.ACTION_UP
// Your action here on button click
case MotionEvent.ACTION_CANCEL: {
ImageButton view = (ImageButton) v;
view.clearColorFilter();
view.invalidate();
break;
}
}
return true;
}
};
The problem with this approach is that since I am handling the button click on different places(Activity and Fragments) I have to do this change at every place which is difficult to maintain. Also to handle the onClick action, I have put a switch statement in the Action_Up event for every button.
2) Use the Drawables where I create drawable for each button and in the src of the button I specify the drawable file. I have written a drawable file something like this.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="#drawable/btn_pressed_drawable" /><!-- pressed -->
<item android:state_focused="true"
android:drawable="#drawable/button_image" /> <!-- focused -->
<item android:drawable="#drawable/button_image" /> <!-- default -->
</selector>
and the code for btn_pressed_drawable is
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/button_image"
android:tint="#770000"/>
But the problem with this approach is that I need to create two files for each buttons and I have about 15 buttons.
Is there a simple way to do it? I would like to keep the things simple and at a single place.
One more things which i observed is that the result produce by the above two approaches is different. I see the different color while I am applying the same tint value in each case.
You need to write colorfilter code setColorFilter(..) with getBackground().
After that your code look like below:
View.OnTouchListener onTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
ImageButton view = (ImageButton) v;
view.getBackground().setColorFilter(0x77000000, PorterDuff.Mode.SRC_ATOP);//line changed
v.invalidate();
break;
}
case MotionEvent.ACTION_UP
// Your action here on button click
case MotionEvent.ACTION_CANCEL: {
ImageButton view = (ImageButton) v;
view.getBackground().clearColorFilter();//line changed
view.invalidate();
break;
}
}
return true;
}
};
I hope its help you.

Set button click effect in Android

I'm new in Android app developing and I realized that, at the end of the day, what truly matters to the end user is the App's UI.
I always try to do my best when it comes to UI's, but I always end up having some troubles.
In particular, one of the main problems I always have and I don't know how to fix, is that when I set a custom background color or image to some button, the click effect disappears. So you click the button and although it obviously works, it's pretty unpleasant to see how it does nothing graphically.
I'd like to know if there's some way to get the original effect back, or set some click effect myself programmatically.
Thanks in advance.
Save this as a drawable and set as the background:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#212121"/>
<corners android:radius="5dp"/>
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#424242"/>
<corners android:radius="5dp"/>
</shape>
</item>
</selector>
This is just a simple example. You can create more complicated ones yourself.
Lets say you have a button or a TextView or any other view. and you want it to change its color during touch event and also to do something after the click:
button.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE)
{
v.setBackgroundColor(Color.parseColor("#000000"));
}
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL)
{
v.setBackgroundColor(Color.parseColor("#ffffff"));
}
return false;
}
});
and then add click listener:
button .setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
do some stuff...
}
});
Thanks to Amir Horev's answer I could figure out what I really needed. I based my solution on his answer but instead of calling the setBackgroundColor() method, I used the setColorFilter() one instead from the background of the view, to trigger both the ACTION_DOWN and ACTION_UP events. So now I can basically set a color filter to any button. This is how the code finally looked like:
button.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
v.getBackground().setColorFilter(new LightingColorFilter(0xFFFFFFFF, 0xFFAA0000));
}
if (event.getAction() == MotionEvent.ACTION_UP)
{
v.getBackground().setColorFilter(new LightingColorFilter(0xFFFFFFFF, 000000000));
}
return false;
}
});
I also wrote a function like public void triggerActionDownEvent(Button button); so I can call this funciton instead of writing all of the previous code for every button. Now I'll have to ensure that no more than one button is pressed at anytime.
I suggest you to set the color or the image programmatically. You can use:
Button button = (Button)findViewById(R.id.myButton);
button.setImageSource(R.drawable.image);

Button state change

I'm rather new to android programming and I hit a bump. I want to make a button that changes it's state when pressed, so it will have to states pressed and not pressed. I managed to make the button change state to pressed and keep it like that but I don't know how to make it go back to the not pressed state when clicked again.
here is my code.
<Button
android:id="#+id/scaunstg"
android:layout_width="170dp"
android:layout_height="70dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:background="#drawable/scaunstg"
android:baselineAlignBottom="true"
android:clickable="true" />
the button has an xml file that controls the image displayed.
scaunstg.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// show interest in events resulting from ACTION_DOWN
if(event.getAction()==MotionEvent.ACTION_DOWN) return true;
// don't handle event unless its ACTION_UP so "doSomething()" only runs once.
if(event.getAction()!=MotionEvent.ACTION_UP) return false;
// doSomething();
scaunstg.setPressed(true);
return true;
}
});
Any ideas?
Thank you.
Instead of calling setPressed(true) ever time, we need to get the current state of the button and then call setPressed to the opposite. So if the button is pressed, we want to call setPressed(false), and if the button is unpressed then setPressed(true).
We can use the button's isPressed() method for this, and then use the not operator (!) to get the opposite.
scaunstg.setOnTouchListener(new OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
// show interest in events resulting from ACTION_DOWN
if (event.getAction() == MotionEvent.ACTION_DOWN)
return true;
// don't handle event unless its ACTION_UP so "doSomething()" only runs once.
if (event.getAction() != MotionEvent.ACTION_UP)
return false;
// doSomething();
scaunstg.setPressed( !scaunstg.isPressed() );
return true;
}
});
So, you can do this via XML.To be more exact ,create a xml file in drawable folder (create the folder too, if you don't have it yet)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_enabled="false"
android:drawable="#drawable/btn_sendemail_disable" />
<item
android:state_pressed="true"
android:state_enabled="true"
android:drawable="#drawable/btn_send_email_click" />
<item
android:state_focused="true"
android:state_enabled="true"
android:drawable="#drawable/btn_sendemail_roll" />
<item
android:state_enabled="true"
android:drawable="#drawable/btn_sendemail" />
</selector>
So, you just give your button a link to this xml file as resource.

Custom Imageview for irregular shape of drawable or image

This is original images and combine from 29 pieces of other small images and final result like this.
For example, when I click the particular area,the small piece will change to another colour.
Is there any solution that I can detect the particular area touch and know which piece of image should change?
set implements OnTouchListener for your class add the unimplement method then put all your image view in array and set setOnTouchListener
ImageView[] image = new ImageView[9];//for exapmle 9
image[i].setOnTouchListener(this);
#Override
public boolean onTouch(View v, MotionEvent event) {
boolean eventConsumed = true;
int x = (int)event.getX();
int y = (int)event.getY();
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
for (int i=0;i<=8;i++)//change the loop size to image array size
{
if (v == image[i]) {
point=i;
// here i'm getting which image i have touched
System.out.println(" image"+i+i+i+i+i+i+i+i+i+i+"is clicked");
}
}else if (action == MotionEvent.ACTION_UP) {
//here wright the condition for which image what color have to give
}
If you only want to show the touched part by changing its color then maybe you should use a selector for each image.
selector.xml -> drawable folder.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/colored_image"
android:state_pressed="true"/>
<item android:drawable="#drawable/normal_image" >
</selector>
ImageView:
<ImageView
android:id="#+id/change_city_small_down_ImageView"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/changeCityRelativeLayout"
android:layout_marginLeft="5dp"
android:src="#drawable/selector"
</ImageView>
It works, I just used it by overlapping imageviews:

Categories

Resources