Why are View animations sometimes clipped? - android

I have a FrameLayout containing a subclassed SurfaceView and an ImageView. I want to do a TranslateAnimation on the ImageView. The only way I can make it work is to add a vacuous View to the FrameLayout. Otherwise, the ImageView gets clipped (to the bounds of the ImageView's position at the start of the animation) during the animation.
I'm curious as to why the empty sibling View allows the ImageView to animate correctly. The line that makes it work is marked with a comment in the code below.
public class Test5 extends Activity {
private static final String TAG = "Test5";
private MySurfaceView mMSV;
private ImageView mRectView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMSV = new MySurfaceView(this);
mRectView = new ImageView(this);
ShapeDrawable sd = new ShapeDrawable(new RectShape());
sd.getPaint().setColor(Color.RED);
sd.setIntrinsicWidth(300);
sd.setIntrinsicHeight(100);
mRectView.setImageDrawable(sd);
FrameLayout frameLayout = new FrameLayout(this);
frameLayout.addView(mMSV);
frameLayout.addView(mRectView, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT));
// I don't know why the following line prevents clipping during
// animation. It simply adds a vacuous View.
frameLayout.addView(new View(this));
setContentView(frameLayout);
} // onCreate
public class MySurfaceView extends SurfaceView implements
SurfaceHolder.Callback {
public MySurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas can = mMSV.getHolder().lockCanvas();
can.drawColor(Color.GRAY);
mMSV.getHolder().unlockCanvasAndPost(can);
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
int arg3) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_UP) {
Log.v(TAG, String.format("ACTION_UP, w=%d, h=%d", mRectView
.getWidth(), mRectView.getHeight()));
Animation an = new TranslateAnimation(0f, 200f, 0f, 200f);
// Animation an = new RotateAnimation(0f, 90f);
an.setStartOffset(200);
an.setDuration(1000);
mRectView.startAnimation(an);
}
return true;
}
} // MySurfaceView
} // Test5

This is interesting... I guess that the size of the FrameLayout is changed when a vacuous view is added. Your frame layout does not fill the parent, I wonder if you change the layout params to fill parent, what would happen?
I simplified your code to this:
FrameLayout frameLayout = new FrameLayout(this);
frameLayout.addView(mMSV);
frameLayout.addView(mRectView, 50, 50);
frameLayout.addView(new View(this)); //expands frame layout
It seems that the FrameLayout size itself equal to the last added child view. If you set addView(new View(this)) before adding a rectangle then it reduces to 50 x 50 and animation is clipped. I assume that addView(new View(this)); expands FrameLayout to the full screen.

I don't know how you figured that out, but it seemed to work for me, too. I had just switched to using a SurfaceView, and noticed that the animations were getting clipped. Adding an empty View stopped the clipping.

the trick was setting setClipChildren to the
layout that enclosed the view.

Related

How to create explode animation for linear recyclerView

I am trying to make an explode animation for my linear layout Recycle view. So that when I press the screen, all the items nicely fly away. But for some reason as soon as the animation is initiated, it only happens on a very narrow portion of the screen and everything else is sliced off.
Here is the main activity code:
package com.stanislav.disturber;
import...
public class MainActivity extends AppCompatActivity {
List<Alarm> alarms;
RecyclerView recyclerView;
private final int DND_OFF = 0;
private final int DND_ON = 1;
#SuppressLint("ClickableViewAccessibility")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window w = getWindow(); // in Activity's onCreate() for instance
w.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
alarms = new ArrayList<>();
Alarm firstAlarm = new Alarm("This is the on alarm\n\n");
firstAlarm.setAlarmKind(DND_ON);
alarms.add(firstAlarm);
Alarm thAlarm = new Alarm("And this one is the off alarm\n\n");
secondAlarm.setAlarmKind(DND_OFF);
alarms.add(thAlarm);
...
Alarm niAlarm = new Alarm("And this one is the off alarm\n\n");
secondAlarm.setAlarmKind(DND_OFF);
alarms.add(niAlarm);
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
DisturberAdapter disturberAdapter = new DisturberAdapter(alarms, this);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true);
linearLayoutManager.scrollToPosition(alarms.size()-1);
RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator();
recyclerView.setAdapter(disturberAdapter);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setItemAnimator(itemAnimator);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// save rect of view in screen coordinates
final Rect viewRect = new Rect();
v.getGlobalVisibleRect(viewRect);
// create Explode transition with epicenter
Transition explode = new Explode();
explode.setEpicenterCallback(new Transition.EpicenterCallback() {
#Override
public Rect onGetEpicenter(Transition transition) {
return viewRect;
}
});
explode.setDuration(7000);
TransitionManager.beginDelayedTransition(recyclerView, explode);
// remove all views from Recycler View
recyclerView.setAdapter(null);
return false;
}
});
}
}
Figured out that the problem shows up when the height of recyclerview is set to "wrap content". But without "wrap content" how on Earth to have cards in the middle if there are only few of them?
Also when the height is set to "wrap content", what causes problems - padding in recyclerview!! Weird!
A temporary solution is: in the animation routine, add the following line, to give recyclerview enormously ridiculous padding. Then everything will work. Just don't forget to return the previous padding back afterwards!! The code:
recyclerView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
//Set enormous padding
recyclerView.setPadding(0,2000,0,2000);
// save rect of view in screen coordinates
final Rect viewRect = new Rect();
//v.getGlobalVisibleRect(viewRect);
//create Explode transition with epicenter
Transition explode = new Explode();
explode.setEpicenterCallback(new Transition.EpicenterCallback() {
#Override
public Rect onGetEpicenter(Transition transition) {
return viewRect;
}
});
explode.setDuration(600);
TransitionManager.beginDelayedTransition(recyclerView, explode);
// remove all views from Recycler View
recyclerView.setAdapter(null);
return false;
}
});

onTouch event on an animated view

I want to set a touch event on an animated view and then stop the animation on touching view.
Here is my activity code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touch_me = (TextView) findViewById(R.id.touch_me);
touch_me.setOnTouchListener(new OnTouchMeListener());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int fromX = size.x;
int fromY = size.y;
int toX = (-1) * view.getWidth();
TranslateAnimation animation = new TranslateAnimation(fromX, -100, 0, 0);
animation.setDuration(6000);
animation.setRepeatCount(5);
animation.setFillAfter(true);
touch_me.startAnimation(animation);
}
Here is my class listener:
public class OnTouchMeListener implements View.OnTouchListener{
#Override
public boolean onTouch(View v, MotionEvent event) {
touch_me.clearAnimation();
return true;
}
}
The problem is that the onTouch is detected only on the initial position(in XML file) of the view
Can anyone help me please ?
ok, here's the problem.
the view animation you are using is from older system and it only simulates the animation, not move the positioning of the view. so when you touch the area where the view has moved, there is no reading on your touch so it gets ignored. but if you touch the area of the original position of the view, it will fire the touchevent callback method and cancel the animation per your code.
there are several approach to solve this.
one is this by calling onlayout and relocating the view each time there is a change in the animation by calling the view's onlayout method.
Button is not clickable after TranslateAnimation
or another is to change the view's touch area by using touchdelegate class
http://cyrilmottier.com/2012/02/16/listview-tips-tricks-5-enlarged-touchable-areas/
but i think these two options aren't very good approaches to your problem.
i would go with this third option:
use propertyanimation instead. it's different from older system in that it updates the values needed for changes instead of doing the actual animation. the actual animation is done inside the update method with your own algorithm.
http://developer.android.com/guide/topics/graphics/prop-animation.html
here is example:
ValueAnimator animTranslation = ValueAnimator.ofFloat(valueStart, valueEnd);
animTranslation.setDuration(300);
animTranslation.removeAllUpdateListeners();
animTranslation.removeAllListeners();
animTranslation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
myView.setRotation(currValue - (Float) animation.getAnimatedValue());
}
}
});
animRotation2.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});

How to keep ball moving

I'm trying to create a simple app with a ball moving around reflecting off the screen edges.
I've found out how to create my ball. However i'm not able to move it around.
Here's my code till now.
protected void onCreate(Bundle savedInstanceState) {
initial_x=100;
initial_y=100;
speed_x=1;
speed_y=1;
x=initial_x;
y=initial_y;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
draw();
}
private void draw() {
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
height = displaymetrics.heightPixels;
width = displaymetrics.widthPixels;
Log.d("dimensions",String.valueOf(width));
Log.d("dimensions",String.valueOf(height));
Bitmap bitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
g = new Canvas(bitmap);
g.drawColor(Color.WHITE);
paint = new Paint();
paint.setColor(Color.RED);
g.drawCircle(50,50, 20, paint); //Put your values
// In order to display this image, we need to create a new ImageView that we can display.
ImageView imageView = new ImageView(this);
// Set this ImageView's bitmap to ours
imageView.setImageBitmap(bitmap);
// Create a simple layout and add imageview to it.
RelativeLayout layout = new RelativeLayout(this);
RelativeLayout.LayoutParams params = new
RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
layout.addView(imageView, params);
layout.setBackgroundColor(Color.BLACK);
// Show layout in activity.
setContentView(layout);
layout.setOnClickListener(l);
}
I've set an onclick listener to basically be able to move the ball when i touch the screen. How do I make this a continuous process?
Edit:
I changed the onclicklistener to an ontouchlistener to keep the ball moving while i touch the screen but it still works like an onclick listener. here is the code
OnTouchListener l = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
g.drawColor(Color.WHITE);
x+=speed_x;
y+=speed_y;
g.drawCircle(x, y, 20, paint);
if(x==0)
speed_x=-1;
if(x==height)
speed_x=1;
if(y==0)
speed_y=-1;
if(y==width)
speed_y=1;
v.invalidate();
break;
}
return false;
}
};
Use a TimerTask to periodically update the position continuously. Probably 10 to 30 times a second should suffice.
Put your drawing and position update logic in the run() method of TimerTask since creating another thread won't work. (Drawing must occur on GUI thread).
Hope this helps.
Edit:
TimerTask myTimerTask = new TimerTask() {
#Override
public void run() {
// Update logic here
runOnUiThread(new Runnable() {
#Override
public void run() {
// Drawing logic here
}
});
}
}
Timer timer = new Timer();
timer.schedule(myTimerTask, 50, 50);

add view to scrollview in android

I have a relative layout inside a scrollview, then i created a new view with this code:
public class DrawView extends View {
Paint paint = new Paint();
public DrawView(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
paint.setColor(Color.BLACK);
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(5, 5, 140, 140, paint);
}
}
Then i try to use layout.addView(DrawView) to add the new view to the relative layout, so it can be scrollable with the rest of the content, but is doesn't work, nothing shows up..
Am i missing something ?
Edit:
DrawView formas;
GifMovieView gif;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final RelativeLayout layout = new RelativeLayout(this);
final ScrollView scrollView = new ScrollView(this);
//gif = new GifMovieView(this, t_img);
formas = new DrawView(this);
layout.addView(formas);
scrollView.addView(layout);
this.setContentView(scrollView);
inicializar();
load(layout);
}
You will probably want to override onMeasure() in your DrawView. Otherwise your view will have size of 0x0 pixels.
You can start with something as simple as
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(200, 200);
}
just to prove it works, but you will need to do some more meaningful, based on what are contents of your view.
Start from this article to see help on overriding onDraw() and onMeasure()

Customize view does not appear

When I comment out setContentView(boardView); in my Game.java my custom view in BoardView works fine and displays everything nicely... but onSizeChanged never gets called in BoardView.java... so I can't read the device width and height at runtime. If I leave setContentView uncommented onSizeChanged works... but the screen is blank!
I want to be able to read the screen width and height at runtime and set the sizes of my ImageViews at creation so they are the optimal size.
public class Game extends Activity implements OnClickListener{
private BoardView boardView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boardView = new BoardView(this);
setContentView(boardView); // when this line disabled, it looks ok
boardView.requestFocus();
}
public class BoardView extends View {
private final Game game;
private float width; // width of one unit
private float height; // height of one unit
public BoardView(Context context){
super(context);
this.game = (Game)context;
setFocusable(true);
setFocusableInTouchMode(true);
LinearLayout maincontainer = new LinearLayout(game);
maincontainer.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
maincontainer.setGravity(Gravity.CENTER);
maincontainer.setOrientation(LinearLayout.VERTICAL);
maincontainer.setBackgroundColor(Color.BLACK);
LinearLayout innercontainer = new LinearLayout(game);
innercontainer.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
innercontainer.setGravity(Gravity.CENTER);
innercontainer.setOrientation(LinearLayout.HORIZONTAL);
// declare a new table
TableLayout layout = new TableLayout(game);
layout.setLayoutParams(new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
// build a grid of ImageViews in a TableLayout
for (int f=1; f<=7; f++) {
TableRow tr = new TableRow(game);
for (int c=1; c<=7; c++) {
ImageView b = new ImageView(game);
b.setImageResource(R.drawable.neworb);
b.setOnClickListener(game);
tr.addView(b, 30,30); // I'd like to not use fixed values here
} // for
layout.addView(tr);
} // for
innercontainer.addView(layout);
maincontainer.addView(innercontainer);
game.setContentView(maincontainer);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh){
width = w/9f;
height = width;
super.onSizeChanged(w, h, oldw, oldh);
}
}
}
Thought I solved my own problem. I was neglecting to place my my view building code withing the Overriden onDraw method. i.e.:
#Override
protected void onDraw(Canvas canvas)
{
LinearLayout maincontainer = new LinearLayout(this.game);
// etc..
I was just including it within the main class and not Overriding onDraw... which you gotta do when you extend View.

Categories

Resources