Is it possible to change Canvas Image Automatically Android? - android

I want to change the canvas image automatically.
Means i want that canvas image should be set one after another continuously.
I have written code which sets image only once.
But i don't know the code about changing the canvas image automatically so it creates the effect like Object is running on the road..
So what is the way to create such kind of animation in canvas ?
Please give me some ideas to create such 2D animation.
Thanx in Advance.

i would do this the following way:
create a custom view which contains a bitmap and in onDraw() this bitmap is drawn on the canvas
give this custom view a setter, to give it a new bitmap (maybe as a ressouce-id or something similar)
create a new Thread (with a UI-Handler) which changes the resource of the canvas at least 24times per second and calls customView.invalidate() via handler.post
this worked for me
edit: the code
Activity:
package de.test.animation;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
public class AnimationTestActivity extends Activity {
Button btn;
CustomView customView;
LinearLayout layout;
int[] imageIDs;
private void init(){ //array with my ressouce-IDs
imageIDs = new int[]{
R.drawable.pic1,
R.drawable.pic2,
R.drawable.pic3,
R.drawable.pic4,
R.drawable.pic5,
R.drawable.pic6,
R.drawable.pic7,
R.drawable.pic8,
R.drawable.pic9,
R.drawable.pic10,
R.drawable.pic11,
R.drawable.pic12,
R.drawable.pic13,
R.drawable.pic14,
R.drawable.pic15,
R.drawable.pic16,
R.drawable.pic17,
R.drawable.pic18,
R.drawable.pic19,
R.drawable.pic20,
R.drawable.pic21,
R.drawable.pic22,
R.drawable.pic23,
R.drawable.pic24
};
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
btn = (Button) findViewById(R.id.btnStart);
layout = (LinearLayout)findViewById(R.id.layout);
customView = new CustomView(this);
customView.setNewImage(imageIDs[0]);
layout.addView(customView);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Thread t = new Thread(){
private final int FPS = 24; //How many frames will be dran per second
private final int SLEEPTIME = 1000/FPS; //Time, the thread waits, before drawing the next picture
#Override
public void run() {
super.run();
for(int i=0;i<imageIDs.length;i++){
customView.setNewImage(imageIDs[i]); //set next picture
customView.repaint(); //draw the picture on the canvas
try {
sleep(SLEEPTIME); //wait, until the next picture can be drawn
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
});
}
}
CustomView:
package de.test.animation;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
public class CustomView extends View {
private Bitmap image; //image to be drawn on this view
private Context context;
public CustomView(Context context) { //constructor
super(context);
this.context = context;
}
public void setNewImage(int r_id){ //method to set a new picture (via resouce-id)
image = BitmapFactory.decodeResource(context.getResources(), r_id); //decode the image from the resouces
}
public void repaint(){ //method to repaint this view
this.post(new Runnable(){ //posting via a new runnable (otherwhise you get a "calledByWrongThreadException"
#Override
public void run() {
invalidate(); //Thread initiates UI-Thread to update this view
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(image, 0, 0, new Paint()); //draw the picture in the view
}
}
I hope this helps you.
Good luck then.

Check out this tutorial of frame animation:
http://www.youtube.com/watch?v=iTKtT-R98EE
More info can be found at following links:
Starting Frame-By-Frame Animation
Following is the step by step procedure:
In Frame Animation you will be swapping frames repeatedly, so that it appears continuous to the human eye and we feel that it is animated. Frame is referred to an image. So to implement frame animation one needs to have set of images, which describes a motion.
Step 1- Create a drawable folder.
Within it create an animation_list.xml file.
It includes :
A list of items that has the addresses of the frame images.
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:drawable="#drawable/blank" android:duration="210" />
<item android:drawable="#drawable/logo" android:duration="210" />
<item android:drawable="#drawable/logo1" android:duration="210" />
<item android:drawable="#drawable/logo2" android:duration="210" />
<item android:drawable="#drawable/logo3" android:duration="210" />
<item android:drawable="#drawable/logo4" android:duration="210" />
<item android:drawable="#drawable/logo5" android:duration="210" />
<item android:drawable="#drawable/logo6" android:duration="210" />
<item android:drawable="#drawable/logofinal" android:duration="210" />
</animation-list>
Step 2- Create an activity_main.xml file
It Includes : An Image View
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/imageAnimation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true" />
</RelativeLayout>
Step 3- Outside the onCreate method :
Declare the Image View and Animation Drawable
// Declaring an Image View and an Animation Drawable
ImageView view;
AnimationDrawable frameAnimation;
Step4- Inside the OnCreate method:
Typecast the Image view
Typecast the Animation Drawable
Set the drawable backgroung on the image view
// Typecasting the Image View
view = (ImageView) findViewById(R.id.imageAnimation);
// Setting animation_list.xml as the background of the image view
view.setBackgroundResource(R.drawable.animation_list);
// Typecasting the Animation Drawable
frameAnimation = (AnimationDrawable) view.getBackground();
Step5- After the onCreate method :
The animation should only run when it is in focus that is when it is visible to the user. Hence define this method after the onCreate method.
// Called when Activity becomes visible or invisible to the user
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// Starting the animation when in Focus
frameAnimation.start();
} else {
// Stoping the animation when not in Focus
frameAnimation.stop();
}
}
Source

Related

Moving a button (onClick) to a random position on the screen

everyone. I'm sort of new to android development and I've ran into a bit of a problem.
As is it's written in the title, I'm trying to create an app in which a button moves to a random location on the screen every time it is clicked. A sort of a "runaway button" thingy.
Here is the XML code.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:background="#color/colorPrimary"
tools:context="com.example.a4ld.MainActivity">
<Button
android:id="#+id/button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/button" />
</LinearLayout>
And here is the main activity code.
At first the solution seems pretty simple. Just move the random values into the onClick method. That way new coordinates would get generated every time the button is clicked. But whenever I do that the animationListener class cannot reach those values and move the view accordingly so that the clickable area of the button would match its position on the screen.
package com.example.a4ld;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.LinearLayout;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//get display size.
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
final int height = displayMetrics.heightPixels;
final int width = displayMetrics.widthPixels;
//generates random values within display size range.
//doesn't work as intended. the button still goes out of bounds sometimes.
Random n = new Random();
final int height01 = n.nextInt(height - 0) + 0;
Random m = new Random();
final int width01 = m.nextInt(width - 0) + 0;
final Button button01 = (Button) findViewById(R.id.button01);
button01.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//animates button movement.
TranslateAnimation animation01 = new TranslateAnimation(0, width01, 0, height01);
animation01.setDuration(500);
animation01.setAnimationListener(new animationListener());
button01.startAnimation(animation01);
}
class animationListener implements Animation.AnimationListener {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
//moves the view(probably) to match the new button position.
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(button01.getWidth(), button01.getHeight());
layoutParams.setMargins(width01, height01, 0, 0);
button01.setLayoutParams(layoutParams);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
}
});
}
}
Pardon me for any mistakes. This is my first time asking for help here.
Every bit of help will be greatly appreciated and thank you for your time.
You should rather use ViewPropertyAnimator. This animates the view to its future position and you don't need to force any layout params on the view after the animation ends. And it's rather simple.
myView.animate().x(50f).y(100f);
myView.animate().translateX(pixelInScreen)
Note: This pixel is not relative to the view. This pixel is the pixel position in the screen.

Android XML Animation horribly lags on bigger images

I encounter a problem to which i got no answer to google at all. So i got an image which is 740x610 pixels, 282.73kb, 32bit color.I simply apply the simplest rotation animation upon this image, thats how the boss wants.The xml code for animation is the next.
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator">
<rotate
android:duration="5000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="360" />
</set>
this is how i apply animation inside the code.(The target to be animated is an ImageView)
levelUpLightingRight.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_right));
Anyways, whenever the animation starts rotation lagging the worst way it could be. I tried to make image smaller, and BOOM, that worked, the animation is never being smoother. Problems are the next. I can't resize image no way,because if i do so,image get rectangle around it and its ugly,so the designer made it look right so i stuck up with this image size. Animation applied to this image lagging. I can't make image smaller because that won't be as boss wants the UI to be seen so i stuck in the point where animation purely lags like i never seen before in my development experience. What i tried so far.
1-applying required parameters to manifest.xml like ...android:hardwareAccelerated="true",android:largeHeap="true"
2-Trying with different types of animations like ValueAnimatior,ObjectAnimator,AnimatorSet and so on.
3-Using third part libraries like 2d renderings.
Ofcourse nothing helped me out.Im thinking of applying animation inside surface view,but i don't see it coming to success..Anyway if anyone encounter such problems and do have solution,please share,or at least guide to right direction.Thank you before hand.
FULL CLASS CODE.
package com.vegasslots.dialogs;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.vegasslots.R;
/**
* Created by vladimirarevhstayna on 12/24/15.
*/
public class LevelUpDialog extends DialogFragment implements View.OnClickListener {
private static final String TAG = "_LevelUpDialog";
private ImageView coin_1;
private ImageView coin_2;
private ImageView coin_3;
private ImageView coin_4;
private ImageView coin_5;
private ImageView coin_6;
private ImageView coin_7;
private ImageView coin_8;
private ImageView coin_9;
private ImageView coin_10;
private ImageView coin_11;
private ImageView coin_12;
private ImageView coin_13;
private ImageView coin_14;
private ImageView coin_15;
private CountDownTimer levelUpCountDownTimer;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//init our viewHolder of .
return inflater.inflate(R.layout.dialog_level_up_view, container);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final RelativeLayout levelUpLightingLeft = (RelativeLayout) view.findViewById(R.id.levelUpLighting_left);
RelativeLayout levelUpLightingRight = (RelativeLayout) view.findViewById(R.id.levelUpLighting_right);
coin_1 = (ImageView) view.findViewById(R.id.imageView8);
coin_2 = (ImageView) view.findViewById(R.id.imageView16);
coin_3 = (ImageView) view.findViewById(R.id.imageView7);
coin_4 = (ImageView) view.findViewById(R.id.imageView12);
coin_5 = (ImageView) view.findViewById(R.id.imageView10);
coin_6 = (ImageView) view.findViewById(R.id.imageView9);
coin_6 = (ImageView) view.findViewById(R.id.imageView9);
coin_7 = (ImageView) view.findViewById(R.id.imageView4);
coin_8 = (ImageView) view.findViewById(R.id.imageView15);
coin_9 = (ImageView) view.findViewById(R.id.imageView14);
coin_10 = (ImageView) view.findViewById(R.id.imageView5);
coin_11 = (ImageView) view.findViewById(R.id.imageView3);
coin_12 = (ImageView) view.findViewById(R.id.imageView5);
coin_13 = (ImageView) view.findViewById(R.id.imageView11);
coin_14 = (ImageView) view.findViewById(R.id.imageView6);
coin_15 = (ImageView) view.findViewById(R.id.imageView13);
view.findViewById(R.id.mainLayout).setOnClickListener(this);
//
levelUpLightingLeft.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_left));
levelUpLightingRight.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_right));
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(coin_1, "translationX", 180, -90),
ObjectAnimator.ofFloat(coin_1, "translationY", 180, -90)
);
set.setDuration(200).start();
levelUpCountDownTimer = new CountDownTimer(4000, 4000) {
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
Log.d(TAG, "ON FINISH");
if (isVisible()) {
dismiss();
}
}
}.start();
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
// getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#aa000000")));
// getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
#Override
public void onDestroyView() {
super.onDestroyView();
levelUpCountDownTimer.cancel();
}
#Override
public void onClick(View v) {
dismiss();
}
}
I reduce the animation duration to half and it worked for some reasons!
android:duration="500"

how to make the animation clickable in android [duplicate]

This question already has an answer here:
image is playing an animation and the image is clickable
(1 answer)
Closed 10 years ago.
I've made a simple animation for an image and I set the event OnClick on the image to make a toast. The problem is that I made the image started doing the animation on the onCreate and I made set the image to be clicked and fire the toast but the problem is that the image isn't clickable, but if I press on the original position of the image, the toast is started (the image is not moving with the animation)
thx for your help
this is the animation code in anim folder (translate.xml)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator">
<translate
android:fromXDelta="-80%p"
android:toXDelta="80%p"
android:duration="20000"
android:repeatCount="100"
android:repeatMode="restart"
/>
</set>
and this is the Activity Class
package com.example.animatest;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
private ImageView image01;
private long aefe;
private ImageView image1;
private ImageView image2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
image01 = (ImageView) findViewById(R.id.imageView1);
final Animation animTranslate1 = AnimationUtils.loadAnimation(this,
R.anim.translate);
image01.startAnimation(animTranslate1);
image01.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "hello", Toast.LENGTH_SHORT)
.show();
}
});
}
}
During the entire animation, your view remains at the old location (location when the animation just started). It is just drawn in another spot. You'd have to move your animated view after your animation ends:
Register a listener to your animation.
http://developer.android.com/reference/android/view/animation/Animation.AnimationListener.html
In your onAnimationEnd implementation, modify your Activity's layout so that it resembles the final state/layout of your animation.
Update after your comment:
The only way I see of doing this is by creating your own custom Animation in Java code and implementing your custom Animation's 'protected void applyTransformation(float interpolatedTime, Transformation t)' method. For example, in our app we have an animation that actually moves a View around instead of just drawing it at a different location. E.g. below is an example of an Animation that increases or decreases the actual height of a View:
public class ViewHeightAnimation extends Animation {
private final View view;
private final float diffHeight;
private final int startHeight;
public ViewHeightAnimation(View view, float diffHeight, int startHeight) {
this.view = view;
this.diffHeight = diffHeight;
this.startHeight = startHeight;
setDuration(200);
setInterpolator(new AccelerateDecelerateInterpolator());
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
android.view.ViewGroup.MarginLayoutParams layoutParams = (android.view.ViewGroup.MarginLayoutParams)view.getLayoutParams();
layoutParams.height = Math.round(startHeight + (diffHeight * interpolatedTime));
view.setLayoutParams(layoutParams);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
Your Animation would be different, but would be using the 'getLayoutParams()' and 'setLayoutParams()' as well to modify the View's (ImageView's) position and change layoutParams.topMargin and layoutParams.leftMargin appropriately.
If you are not concerned about Android 2.x or lower, using the ObjectAnimator (3.0 or higher) or ViewPropertyAnimator (3.1 or higher) is a better solution, as was mentioned in other answer earlier.
Let me know if this helps you.
try like this:
final Animation animTranslate1 = AnimationUtils.loadAnimation(this,R.anim.translate);
animTranslate1.setFillAfter(true);
image01.startAnimation(animTranslate1);
If that does not work then you'll have to use the newer Property Animation framework (which was pointed out in the answer to your previous duplicate question)
See here to learn about it
The way you animate your imageView is only move its appearance, so actually your imageView is still at the old position. There's not an easy way to do what you want, with this type animations. You should consider to use ObjectAnimators ...

Android bringToFront causes flicker

I am animating three views that are stacked on top of each other. When I tap one that is not the front view, one or two views will slide up or down to uncover the tapped view, bring the tapped view to front, and then return everything to their original position. Most of these work fine. Only when I bring a view to front that I just animated away I get a noticeable flicker.
I have read at least a hundred posts but none contains the solution.
I am posting this to consolidate every suggested solution in one place and to hopefully find a solution.
I know that the animation does not animate the view itself, but just an image. The view stays at its original position. It is definitely related to that. It only happens when bringing a view to front that just moved.
Moving the view to the animation end position before starting the animation or after the animation is finished does not help one bit.
It also is not related to the AnimationListener.onAnimationEnd bug, since I derived my own views and intercept onAnimationEnd there.
I am using Animation.setFillAfter and Animation.setFillEnabled to keep the final image at the animation end location.
I tried using Animation.setZAdjustment but that one only works for entire screens, not views within a screen.
From what I have learned I suspect that the problem is bringToFront() itself, which does a removeChild()/addChild() on the parent view. Maybe the removeChild causes the redraw showing the view without the removed child briefly.
So my questions: Does anyone see anything I missed that could fix this?
Does Android maybe have a command to temporarily stop drawing and resume drawing later. Something like a setUpdateScreen(false) / setUpdateScreen(true) pair?
That would allow me to skip the flicker stage.
Minimal code to demo the effect follows. Tap white to see red move up and back down behind white without flicker (white comes to front but does not move). Then tap red to see red move back up from behind white and the flicker when it is brought to front just before it slides back down over white. Weird thing is that the same thing does not always happen when using blue instead of red.
MainActivity.java
package com.example.testapp;
import com.example.testapp.ImagePanel.AnimationEndListener;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.TranslateAnimation;
public class MainActivity extends Activity
{
private static final int ANIMATION_TIME = 1000;
private ImagePanel mRed;
private ImagePanel mWhite;
private ImagePanel mBlue;
private int mFrontPanelId;
private void animate(final ImagePanel panel, final int yFrom, final int yTo,
final AnimationEndListener animationListener)
{
final TranslateAnimation anim = new TranslateAnimation(0, 0, 0, 0, 0, yFrom, 0, yTo);
anim.setDuration(ANIMATION_TIME);
anim.setFillAfter(true);
anim.setFillEnabled(true);
if (animationListener != null)
{
panel.setAnimListener(animationListener);
}
panel.startAnimation(anim);
}
public void onClick(final View v)
{
final int panelId = v.getId();
if (mFrontPanelId == panelId)
{
return;
}
final ImagePanel panel = (ImagePanel) v;
final int yTop = mWhite.getTop() - mRed.getBottom();
final int yBot = mWhite.getBottom() - mBlue.getTop();
final boolean moveRed = panelId == R.id.red || mFrontPanelId == R.id.red;
final boolean moveBlue = panelId == R.id.blue || mFrontPanelId == R.id.blue;
animate(mBlue, 0, moveBlue ? yBot : 0, null);
animate(mRed, 0, moveRed ? yTop : 0, new AnimationEndListener()
{
public void onBegin()
{
}
public void onEnd()
{
// make sure middle panel always stays visible
if (moveRed && moveBlue)
{
mWhite.bringToFront();
}
panel.bringToFront();
animate(mBlue, moveBlue ? yBot : 0, 0, null);
animate(mRed, moveRed ? yTop : 0, 0, new AnimationEndListener()
{
public void onBegin()
{
}
public void onEnd()
{
}
});
mFrontPanelId = panelId;
}
});
}
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRed = (ImagePanel) findViewById(R.id.red);
mWhite = (ImagePanel) findViewById(R.id.white);
mBlue = (ImagePanel) findViewById(R.id.blue);
mFrontPanelId = R.id.red;
}
}
ImagePanel.java
package com.example.testapp;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
public class ImagePanel extends ImageView
{
public interface AnimationEndListener
{
public void onBegin();
public void onEnd();
}
private AnimationEndListener mAnim = null;
public ImagePanel(final Context context)
{
super(context);
}
public ImagePanel(final Context context, final AttributeSet attrs)
{
super(context, attrs);
}
public ImagePanel(final Context context, final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
}
#Override
protected void onAnimationEnd()
{
super.onAnimationEnd();
clearAnimation();
if (mAnim != null)
{
final AnimationEndListener anim = mAnim;
mAnim = null;
anim.onEnd();
}
}
#Override
protected void onAnimationStart()
{
super.onAnimationStart();
if (mAnim != null)
{
mAnim.onBegin();
}
}
public void setAnimListener(final AnimationEndListener anim)
{
mAnim = anim;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.example.testapp.ImagePanel
android:id="#+id/blue"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="#000080"
android:src="#drawable/testpattern"
android:onClick="onClick" />
<com.example.testapp.ImagePanel
android:id="#+id/white"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="#808080"
android:src="#drawable/testpattern"
android:onClick="onClick" />
<com.example.testapp.ImagePanel
android:id="#+id/red"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:adjustViewBounds="true"
android:background="#800000"
android:src="#drawable/testpattern"
android:onClick="onClick" />
</RelativeLayout>
testpattern.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<gradient
android:startColor="#00000000"
android:endColor="#ffffffff" />
</shape>
Do you need all the images be visible at any time? you can set their visibility as invisible so they will not disturb you. you make them visible again once you need them.
Try calling animation.cancel() in onAnimationEnd. I remember a while back I had a similar issue with animation flickering after executing code in onAnimationEndand that did the trick.
I was observing the same problems when calling bringToFront() during an animation.
I could solve my problem by using setChildrenDrawingOrderEnabled(boolean enabled) and getChildDrawingOrder(int childCount, int i) on the ViewGroup that contained the children I was animating.

Android onClick method doesn't work on a custom view

I've started working on an app. I build the menu yesterday but the onClick method doesn't work!
I created a class that extends View and called her MainMenuObject - that class is for any object in the main menu (buttons, logos etc). I've created a special class for them because I'm doing an animation when the menu starts. After I've built the MainMenuObject class I've built another class (OpeningTimesView) that extends View and will have all the buttons of the main menu in it, and will function as the main activity's layout.
Everything was good, the animation went very well and I wanted to put listeners on my buttons, so I've added an implemention of onClickListener to the OpeningTimesView class, and overrided the onClick method. Then I've added the listener to the buttons with setOnClickListener(this) and setClickable(true), but it doesn't work! I've tried everything! Please help me figure out what I'm doing wrong. I've added a toast to the onClick method that doesn't depend on any "if" but it's won't show neither.
(BTW is there any way to define the screen width and height as variable that all classes can access? it can't be static because you get the height and width from a display object but there must be another way)
this is the code:
public class OpeningTimesView extends View implements OnClickListener{
private MainMenuObjectView searchButton;
private MainMenuObjectView supportButton;
private MainMenuObjectView aboutButton;
private int screenWidth;
private int screenHeight;
public OpeningTimesView(Context context, Display dis) {
super(context);
this.screenWidth = dis.getWidth();
this.screenHeight = dis.getHeight();
searchButton = new MainMenuObjectView(context, 200, MovingMode.RIGHT, R.drawable.search, dis);
supportButton = new MainMenuObjectView(context, 400, MovingMode.LEFT, R.drawable.support, dis);
aboutButton = new MainMenuObjectView(context, 600, MovingMode.RIGHT, R.drawable.about, dis);
searchButton.setClickable(true);
supportButton.setClickable(true);
aboutButton.setClickable(true);
searchButton.setOnClickListener(this);
supportButton.setOnClickListener(this);
aboutButton.setOnClickListener(this);
}
#Override
public void onClick(View view){
Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
if(view == searchButton){
Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
}
else if(view == supportButton){
Toast.makeText(getContext(), "Support button pressed", Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getContext(), "About button pressed", Toast.LENGTH_SHORT).show();
}
#Override
public void onDraw(Canvas canvas)
{
// Drawing the buttons
this.searchButton.onDraw(canvas);
this.aboutButton.onDraw(canvas);
this.supportButton.onDraw(canvas);
}
Thanks in advance, Elad!
I just had the same Problem - I created a custom view and when I registered a new Listener for it in the activity by calling v.setOnClickListener(new OnClickListener() {...}); the listener just did not get called.
In my custom view I also overwrote the public boolean onTouchEvent(MotionEvent event) {...} method. The problem was that I did not call the method of the View class - super.onTouchEvent(event). That solved the problem. So if you are wondering why your listener does not get called you have probably forgotten to call the superclass'es onTouchEvent method
Here is a simple example:
private static class CustomView extends View implements View.OnClickListener {
public CustomView(Context context) {
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event); // this super call is important !!!
// YOUR LOGIC HERE
return true;
}
#Override
public void onClick(View v) {
// DO SOMETHING HERE
}
}
Creating custom controls in Android can be tricky if you aren't comfortable with how the UI Framework operates. If you haven't already, I would recommend reading these:
http://developer.android.com/guide/topics/ui/declaring-layout.html
http://developer.android.com/guide/topics/ui/custom-components.html
http://developer.android.com/guide/topics/ui/layout-objects.html
Notice that when layouts are declared in XML the elements are nested. This creates a layout hierarchy that you must create your self when customizing a component using only Java code.
Most likely you are getting caught up in Android's touch hierarchy. Unlike some other popular mobile platforms, Android delivers touch events starting at the top of the View hierarchy and works its way down. The classes that traditionally occupy the higher levels of the hierarchy (Activity and Layouts) have logic in them to forward touches they don't themselves consume.
So, what I would recommend doing is changing your OpeningTimesView to extend a ViewGroup (the superclass of all Android layouts) or a specific layout (LinearLayout, RelativeLayout, etc.) and add your buttons as children. Right now, there does not seem to be a defined hierarchy (the buttons aren't really "contained" in the container, they're just members) which may be confusing the issue as to where events are really going.
The touches should more naturally flow down to the buttons, allowing your click events to trigger
You can take advantage of Android's layout mechanisms to draw your view instead of relying on drawing code to do all of that.
Pick a layout class to start with that will help you place your buttons in their FINAL locations. You can use the animation framework in Android or custom drawing code (like you have now) to animate them anyway you like up to that point. The location of a button and where that button is currently drawn are allowed to be very different if necessary, and that's how the current Animation Framework works in Android (prior to 3.0)...but that's a separate issue. You also have AbsoluteLayout, which allows you to place and replace objects anywhere you like...but be careful of how your app looks on all Android devices with this one (given the different screen sizes).
As to your second point about display info.
The simplest method is probably just to use Context.getResources().getDisplayMetrics() wherever you need it. Activity inherits from Context, so they can call this method directly. Views always have a Context you can access with getContext(). Any other classes you can just pass the Context as a parameter in construction (this is a common pattern in Android, you'll see many objects require a Context, mainly to access Resources).
Here's a skeleton example to jump start things. This just lines the three up horizontally once as a final location:
Public class OpeningTimesView extends LinearLayout implements OnClickListener {
private MainMenuObjectView searchButton;
private MainMenuObjectView supportButton;
private MainMenuObjectView aboutButton;
private int screenWidth;
private int screenHeight;
public OpeningTimesView(Context context) {
this(context, null);
}
//Thus constructor gets used if you ever instantiate your component from XML
public OpeningTimesView(Context context, AttributeSet attrs) {
super(context, attrs);
/* This is a better way to obtain your screen info
DisplayMetrics display = context.getResources().getDisplayMetrics();
screenWidth = display.widthPixels;
screenHeight = display.heightPixels;
*/
//This way works also, without needing to customize the constructor
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Display dis = wm.getDefaultDisplay();
screenWidth = dis.getWidth();
screenHeight = dis.getHeight();
searchButton = new MainMenuObjectView(context, 200, MovingMode.RIGHT, R.drawable.search, dis);
supportButton = new MainMenuObjectView(context, 400, MovingMode.LEFT, R.drawable.support, dis);
aboutButton = new MainMenuObjectView(context, 600, MovingMode.RIGHT, R.drawable.about, dis);
//Even if they don't extend button, if MainMenuObjectView is always clickable
// this should probably be brought into that class's constructor
searchButton.setClickable(true);
supportButton.setClickable(true);
aboutButton.setClickable(true);
searchButton.setOnClickListener(this);
supportButton.setOnClickListener(this);
aboutButton.setOnClickListener(this);
//Add the buttons to the layout (the buttons are now children of the container)
setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
addView(searchButton, params);
addView(supportButton, params);
addView(aboutButton, params);
}
#Override
public void onClick(View view){
Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
if(view == searchButton){
Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
}
else if(view == supportButton){
Toast.makeText(getContext(), "Support button pressed", Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getContext(), "About button pressed", Toast.LENGTH_SHORT).show();
}
#Override
public void onDraw(Canvas canvas)
{
//Drawing the buttons
// This may only be necessary until they are in place, then just call super.onDraw(canvas)
this.searchButton.onDraw(canvas);
this.aboutButton.onDraw(canvas);
this.supportButton.onDraw(canvas);
}
}
You can customize this from there. Perhaps starting the buttons with visibility set to View.INVISIBLE until you animate them in with your drawing code or a custom Animation object, then making them visibile in their final resting place.
The key here, though, is the the layout is smart enough to know that when it receives a touch event it is supposed to forward it to the corresponding child. You can create a custom view without this, but you will have to intercept all touches on the container and do the math to determine which subview to manually forward the event to. If you truly can't make any layout manager work, this is your recourse.
Hope that Helps!
You can just call performClick() in onTouchEvent of your custom view.
Use this in you custom view:
#Override
public boolean onTouchEvent(final MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP){
return performClick();
}
return true;
}
I do this so:
public class YourView extends LinearLayout implements OnClickListener {
OnClickListener listener;
//... constructors
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
#Override
public void onClick(View v) {
if (listener != null)
listener.onClick(v);
}
}
You have to call setOnClickListener(this) in contructor(s) and implement View.OnClickListener on self.
In this way:
public class MyView extends View implements View.OnClickListener {
public MyView(Context context) {
super(context);
setOnClickListener(this);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
setOnClickListener(this);
}
#Override
public void onClick(View v) {
Toast.makeText(getContext(), "On click.", Toast.LENGTH_SHORT).show();
}
}
I had the same problem. In my case I had a LinearLayout as a root element of my custom view, with clickable and focusable set to true, and the custom view tag itself (used in a fragment's layout) was also set to be clickable and focusable. Turns out that the only thing I had to do to get it working was to remove all the clickable and focusable attributes from within the XML :) Counter-intuitive, but it worked.
Implement the onClickListener in the MainMenuObjectView class, since those are the objects that will respond to clicks.
Another alternative would be to extend Button instead of View, because you are using only buttons in there
Update: Full example
This is the idea to implement it directly into the clickable views. There is a TestView class that extends View and overrides onDraw, as you need it to, and also responds to clicks. I left out any animation implementation as you have that part and it's not relevant to the ClickListener discussion.
I tested it in an Eclair emulator and it works as expected (a Toast message after a click).
file: Test.java
package com.aleadam.test;
import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;
public class Test extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
TextView label = new TextView(this);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
label.setText("Click the circle!");
TestView testView = new TestView(this);
ll.addView(label, layoutParams);
ll.addView(testView, layoutParams);
setContentView(ll);
}
}
file: TestView.java
package com.aleadam.test;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
public class TestView extends View implements OnClickListener {
Context context;
public TestView(Context context) {
super(context);
this.context = context;
setOnClickListener(this);
}
public void onClick(View arg0) {
Toast.makeText(context, "View clicked.", Toast.LENGTH_SHORT).show();
}
#Override
public void onDraw (Canvas canvas) {
super.onDraw(canvas);
this.setBackgroundColor(Color.LTGRAY);
Paint paint = new Paint (Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.RED);
canvas.drawCircle(20, 20, 20, paint);
}
}
If you need some clickable and some not clickable, you can add a constructor with a
boolean argument to determine whether the ClickListener is attached or not to the View:
public TestView(Context context, boolean clickable) {
super(context);
this.context = context;
if (clickable)
setOnClickListener(this);
}
I've got a solution!
It's not really a solution for this specific issue, but a whole new approach.
I sent this thread to somebody I know and he told me to use the Animation SDK the android has (like Wireless Designs mentioned), so instead of doing the main menu page with 4 classes, I'm doing it only with one class that extends Activity, and the Animation class offers many animation options.
I want to thank you both for helping me, you are great.
I'm adding the code if someone will encounter this thread with the same problem or something:
package elad.openapp;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.Toast;
public class OpeningTimes extends Activity implements OnClickListener{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Disabling the title bar..
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
// Create the buttons and title objects
ImageView title = (ImageView)findViewById(R.id.title_main);
ImageView search = (ImageView)findViewById(R.id.search_button_main);
ImageView support = (ImageView)findViewById(R.id.support_button_main);
ImageView about = (ImageView)findViewById(R.id.about_button_main);
// Setting the onClick listeners
search.setOnClickListener(this);
support.setOnClickListener(this);
about.setOnClickListener(this);
setButtonsAnimation(title, search, support, about);
}
#Override
public void onClick(View v) {
if(v.getId()==R.id.search_button_main){
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
startActivity(new Intent(this,SearchPage.class));
}
else if(v.getId()==R.id.support_button_main){
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Toast.makeText(this, "Coming soon...", Toast.LENGTH_LONG).show();
}
else if(v.getId()==R.id.about_button_main){
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Toast.makeText(this, "Coming soon...", Toast.LENGTH_LONG).show();
}
}
// Setting the animation on the buttons
public void setButtonsAnimation(ImageView title, ImageView search, ImageView support, ImageView about){
// Title animation (two animations - scale and translate)
AnimationSet animSet = new AnimationSet(true);
Animation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f);
anim.setDuration(750);
animSet.addAnimation(anim);
anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f);
anim.setDuration(750);
animSet.addAnimation(anim);
title.startAnimation(animSet);
// Search button animation
anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -1.5f, Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f);
anim.setDuration(750);
search.startAnimation(anim);
// Support button animation
anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 1.5f, Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f);
anim.setDuration(750);
support.startAnimation(anim);
// About button animation
anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, 3f, Animation.RELATIVE_TO_SELF, 0.0f);
anim.setDuration(750);
about.startAnimation(anim);
}
}
In my case I had a RelativeLayout as a parent in my custom view and the only way to make it work was to set focusable and clickable to true in the RelativeLayout and in the constructor of the custom view, after inflating the layout, add this:
View view = inflate(getContext(), R.layout.layout_my_custom_view, this);
view.findViewById(R.id.theparent).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
performClick();
}
});
It is this easy:
public class FancyButton
extends FrameLayout
implements View.OnClickListener { ..
void yourSetupFunction(Context context, #Nullable AttributeSet attrs) {
..
super.setOnClickListener(this); // NOTE THE SUPER
}
OnClickListener consumerListener = null;
#Override
public void setOnClickListener(#Nullable OnClickListener l) {
consumerListener = l;
// DO NOT CALL SUPER HERE
}
#Override
public void onClick(View v) {
Log.i("dev","perform my custom functions, and then ...");
if (consumerListener != null) { consumerListener.onClick(v); }
}
implement View.OnClickListener, and hence have an
onClick(View v)
in setOnClickListener, "remember" the listener set from the outside world
set the actual listener to be us (using "super." ...)
in onClick do you custom stuff, and then call the outside world's onClick
just add callOnClick() to your onTouchEvent() method
public boolean onTouchEvent(MotionEvent event) {
.....YOURCODE.....
callOnClick();
return boolean;
}

Categories

Resources