The expand animation not working if its in the success block of the api call. But if i put it out from the success block then its working well.
I have to call animation block inside the success block. Because the data is coming from the api.
Here is the sample.
getWeeklyWaterDate method:
public void getWeeklyWaterData(final WaterDataCallBack callBack)
{
// Find last monday
Calendar lastMonday = Calendar.getInstance();
while (lastMonday.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY)
{
lastMonday.add(Calendar.DATE, -1);
}
Calendar now = Calendar.getInstance();
lastMonday.add(Calendar.DAY_OF_YEAR, -1);
lastMonday.set(Calendar.HOUR, 24);
lastMonday.set(Calendar.MINUTE, 0);
lastMonday.set(Calendar.SECOND, 0);
lastMonday.set(Calendar.MILLISECOND, 0);
HashMap<String, String> params = new HashMap<>();
params.put("startDate", Utils.formatYYMMDDDTHHMMSSz(lastMonday.getTimeInMillis()));
params.put("endDate", Utils.formatYYMMDDDTHHMMSSz(now.getTimeInMillis()));
ServiceConnector.groupamaAPI.getMyWaters(params).enqueue(new SuccessCallback<MyWaterResponse>() {
#Override
public void onSuccess(Response<MyWaterResponse> response) {
super.onSuccess(response);
callBack.onWaterDataReceived(response.body());
}
});
}
calling getWeeklyWaterDataMethod from the fragment.
WaterHelper.getCurrent().getWeeklyWaterData(new WaterHelper.WaterDataCallBack() {
#Override
public void onWaterDataReceived(MyWaterResponse waterResponse) {
Collections.reverse(waterResponse.waterLogs);
for (Integer i = 0 ; i < waterResponse.waterLogs.size() ; i++)
{
WaterLog waterLog = waterResponse.waterLogs.get(i);
Calendar calendar = Calendar.getInstance();
calendar.setTime(waterLog.LogDate);
dates.add(calendar);
waterAmounts.add(new WaterAmount(calendar, waterLog.WaterMililiter.intValue()));
}
prepareTotalAndAverage(waterResponse.waterAverage);
prepareGraph2();
}
});
here is the animation code:
final Integer finalGraphMaxY = 6000;
chartContainerLL.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
chartContainerLL.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Animate Bars
Integer totalHeight = chartContainerLL.getHeight();
ExpandHeightAnimation expandHeightAnimation = new ExpandHeightAnimation(barMonday, 55);
expandHeightAnimation.setDuration(800/* animation time */);
barMonday.startAnimation(expandHeightAnimation);
ExpandHeightAnimation expandHeightAnimation2 = new ExpandHeightAnimation(barTuesday, 30);
expandHeightAnimation2.setDuration(800/* animation time */);
barTuesday.startAnimation(expandHeightAnimation2);
ExpandHeightAnimation expandHeightAnimation3 = new ExpandHeightAnimation(barWednesday, 40);
expandHeightAnimation3.setDuration(800/* animation time */);
barWednesday.startAnimation(expandHeightAnimation3);
ExpandHeightAnimation expandHeightAnimation4 = new ExpandHeightAnimation(barThursday, 30);
expandHeightAnimation4.setDuration(800/* animation time */);
barThursday.startAnimation(expandHeightAnimation4);
ExpandHeightAnimation expandHeightAnimation5 = new ExpandHeightAnimation(barFriday, 40);
expandHeightAnimation5.setDuration(800/* animation time */);
barFriday.startAnimation(expandHeightAnimation5);
if(dates.size() >= 6 && dates.get(5).get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY)
{
ExpandHeightAnimation expandHeightAnimation6 = new ExpandHeightAnimation(barSaturday, totalHeight * getNeededDayWaterAmount(Calendar.SATURDAY).mililiter / finalGraphMaxY);
expandHeightAnimation6.setDuration(800/* animation time */);
barSaturday.startAnimation(expandHeightAnimation6);
}
if(dates.size() >= 7 && dates.get(6).get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY)
{
ExpandHeightAnimation expandHeightAnimation7 = new ExpandHeightAnimation(barSunday, totalHeight * getNeededDayWaterAmount(Calendar.SUNDAY).mililiter / finalGraphMaxY);
expandHeightAnimation7.setDuration(800/* animation time */);
barSunday.startAnimation(expandHeightAnimation7);
}
}
});
ExpandHeightAnimation Class
public class ExpandHeightAnimation extends Animation
{
int targetHeight;
View view;
public ExpandHeightAnimation(View view, int targetHeight) {
this.view = view;
this.targetHeight = targetHeight;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().height = (int) (targetHeight * interpolatedTime);
view.requestLayout();
}
#Override
public void initialize(int width, int height, int parentWidth,
int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
You need to add GlobalLayoutListener when onCreate() or onCreateView() called.
First add two flags for animation. If these both are true, your animation will start.
boolean animateFlag1 = false;
boolean animateFlag2 = false;
Secondly, add your global layout listener inside onCreate():
llContainer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
llContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
animateFlag1 = true;
animate();
}
});
You can see that when onGlobalLayout() called, remove listener, set first flag to true and call animate method.
After these, do the same thing for your callback which starts to animation. Set your second flag as true,
#Override
public void success(Result result) {
animateFlag2 = true;
animate();
}
Here is the animate method, which includes your onGlobalLayout() method codes you wrote before.
private void animate(){
if(animateFlag1 && animateFlag2){
// Animate Bars
ExpandHeightAnimation expandHeightAnimation = new ExpandHeightAnimation(view, 500);
expandHeightAnimation.setDuration(800/* animation time */);
view.startAnimation(expandHeightAnimation);
//More your codes to animate
//Set your all flags to false to prevent animating again
animateFlag1 = animateFlag2 = false;
}
}
try replacing this
prepareTotalAndAverage(waterResponse.waterAverage);
prepareGraph2();
with
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
prepareTotalAndAverage(waterResponse.waterAverage);
prepareGraph2();
}
}, 1);
OR
runOnUiThread(new Runnable() {
#Override
public void run() {
prepareTotalAndAverage(waterResponse.waterAverage);
prepareGraph2();
}
});
Its a bit of trick , but worked for me.
Related
I would like to start animation when view is long clicked.
The code is as follows.
public class Example_Dialog extends Dialog implements AnimationListener{
TextView view_Description1 = null;
View view_Button1 = null;
MessageCreateTask task;
public Example_Dialog( Context context , int theme ){
super( context , theme );
setContentView(R.layout.infomation_dialog);
view_Button1 = findViewById(R.id.common_dialog_button_1);
view_Description1 = (TextView)findViewById(R.id.common_dialog_description_1);
view_Description1.setHeight(0);
view_Button1.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
task = new MessageCreateTask(view_Description1);
task.execute();
}
});
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationStart(Animation animation) {
}
class MessageCreateTask extends AsyncTask<Void, Void, String> {
TextView textView;
String messageOutput;
int totalLineNum;
public MessageCreateTask(TextView textView) {
this.textView = textView;
messageOutput = "";
totalLineNum = 0;
}
#Override
protected void onPreExecute() {
}
#Override
protected String doInBackground(Void... params) {
for( int i= 0 ; i< 10 ; i++ ){
messageOutput += "a" + "<br>";
totalLineNum++;
}
return messageOutput;
}
#Override
protected void onPostExecute(String message) {
int dialogWidth = 838;
int dialogHeight = 26*totalLineNum;
textView.setWidth( dialogWidth );
textView.setText(Html.fromHtml(message));
HeightAnimation hanime_open = new HeightAnimation(textView , 0 , dialogHeight );
hanime_open = new HeightAnimation(textView , 0 , dialogHeight );
hanime_open.setDuration(300);
textView.startAnimation(hanime_open);
}
}
}
public class HeightAnimation extends Animation {
int targetHeight;
int startHeight;
TextView textView;
public HeightAnimation(TextView textView,int startHeight, int targetHeight) {
this.textView = textView;
this.targetHeight = targetHeight;
this.startHeight = startHeight;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newHeight = (int)(startHeight + (targetHeight - startHeight)*interpolatedTime);
textView.setHeight(newHeight);
if( (startHeight != 0) && (newHeight == 0) ){
textView.setVisibility(View.GONE);
}
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, ((View)textView.getParent()).getWidth(), parentHeight);
}
}
The animation was not started with the above code, but it was started by replacing onclick with onlongclick like as below.
view_Button1.setOnLongClickListener(new View.OnLongClickListener(){
public boolean onLongClick(View v){
task = new MessageCreateTask(view_Description1);
task.execute();
return true;
}
});
I want to know the reason why animation does not start in case of onclick.
The reason why it is not working with onClickListener() is, that the view hasnĀ“t the Focus. So you Need to press the button twice or you have to use a onlongClickListener(). To make the view clickable the first time, do the following:
enable focusable in touch mode, wether in your XML layout as attribute:
android:focusableInTouchMode="true"
or programmatically after you have initialized the button:
view_button1.setFocusableInTouchMode(true);
Then you can set the Focus to the view simply:
view_button1.requestFocus();
Good day.I have an scenario where this half normal object animator keeps firing over and over causing heap grow and ofcourse out of memory issue at some point.Here is how it go. I have made static method for rainbow animation like this.
public static ObjectAnimator startRainbowAnimation(Context context,
String textToShow,
final TextView textViewToAttach) {
AnimatedColorSpan span = new AnimatedColorSpan(context);
final SpannableString spannableString = new SpannableString(textToShow);
String substring = textToShow;
int start = textToShow.indexOf(substring);
int end = start + substring.length();
spannableString.setSpan(span, start, end, 0);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(
span, ANIMATED_COLOR_SPAN_FLOAT_PROPERTY, 0, 100);
objectAnimator.setEvaluator(new FloatEvaluator());
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
System.gc();
Log.d("Fafasfasfas", "onAnimationUpdate: inside true");
textViewToAttach.setText(spannableString);
}
});
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.setDuration(DURATION);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.start();
return objectAnimator;
}
private static final Property<AnimatedColorSpan, Float> ANIMATED_COLOR_SPAN_FLOAT_PROPERTY
= new Property<AnimatedColorSpan, Float>(Float.class, "ANIMATED_COLOR_SPAN_FLOAT_PROPERTY") {
#Override
public void set(AnimatedColorSpan span, Float value) {
span.setTranslateXPercentage(value);
}
#Override
public Float get(AnimatedColorSpan span) {
return span.getTranslateXPercentage();
}
};
I am calling this method like this inside an recycler view adapter
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
ChatModel chatModel = chatModelList.get(position);
System.gc();
String messageBody = chatModel.getMessage().replaceAll("userid=" + chatModel.getUserId() + ":" + Constants.TYPE_MESSAGE_ATTACHMENT, "").replaceAll("userid=" + chatModel.getOpponentId() + ":" + Constants.TYPE_MESSAGE_ATTACHMENT, "");
holder.message.setText(messageBody);
if (showAsRainbow) {
if (holder.message.getTag() == null) {
objectAnimator = RainbowAnimation.startRainbowAnimation(mContext, messageBody, holder.message);
holder.message.setTag(ANIMATED);
}
} else {
objectAnimator.removeAllUpdateListeners();
objectAnimator.removeAllListeners();
objectAnimator.end();
objectAnimator.cancel();
holder.message.setTag(null);
}
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) holder.chatViewBubble.getLayoutParams();
LinearLayout.LayoutParams deliveryStatusParams = (LinearLayout.LayoutParams) holder.deliveryStatus.getLayoutParams();
LinearLayout.LayoutParams gifChatViewLayoutParams = (LinearLayout.LayoutParams) holder.imageViewWrapper.getLayoutParams();
checkForGif(chatModel, holder);
if (mCurrentUserId.equals(chatModel.getUserId())) {
layoutParams.gravity = Gravity.RIGHT;
layoutParams.rightMargin = Dp.toDps(mContext, 16);
deliveryStatusParams.gravity = Gravity.RIGHT;
gifChatViewLayoutParams.gravity = Gravity.RIGHT;
holder.chatViewBubble.setLayoutParams(layoutParams);
holder.imageView.setLayoutParams(gifChatViewLayoutParams);
holder.chatViewBubble.setBackground(ContextCompat.getDrawable(mContext, R.drawable.outgoing_message_bg));
if (chatModel.getDeliveryStatus().equals(Constants.STATUS_DELIVERED)) {
if (position >= chatModelList.size() - 1) {
holder.deliveryStatus.setVisibility(View.VISIBLE);
} else {
holder.deliveryStatus.setVisibility(View.INVISIBLE);
}
holder.deliveryStatus.setText(mContext.getString(R.string.sentText));
} else if (chatModel.getDeliveryStatus().equals(Constants.STATUS_NOT_DELIVERED)) {
holder.deliveryStatus.setVisibility(View.VISIBLE);
if (updating) {
holder.deliveryStatus.setText(mContext.getString(R.string.sendingNowText) + percentage + " %");
} else {
holder.deliveryStatus.setText(mContext.getString(R.string.sendingNowText));
}
}
} else {
holder.chatViewBubble.setBackground(ContextCompat.getDrawable(mContext, R.drawable.incoming_message_bg));
layoutParams.gravity = Gravity.LEFT;
gifChatViewLayoutParams.gravity = Gravity.LEFT;
holder.chatViewBubble.setLayoutParams(layoutParams);
holder.imageView.setLayoutParams(gifChatViewLayoutParams);
holder.deliveryStatus.setVisibility(View.INVISIBLE);
}
}
Thi issue is that if you noticed the Log.d() keeps firing even after the cancle and end was called on the objectAnimator and yes i have checked that cancel and end is being called.So i have no clue what i have done wrong.Can anyone please help me?
I see lot's of guys been looking at the post,meaning they got this issue too so i got the solution somehow fixed...Issue is that this damned ObjectAnimator when done inside an loop even with static method,each time is being created new reference to it.So you have to do something like this.Have an array list of object animators,on each call add items to array list.Whenever you want to stop it just simply loop through array and stop all object animators.Here is the code
private ArrayList<ObjectAnimator> objectAnimators = new ArrayList<>();
public void startRainbowAnimation(Context context,
final String textToShow,
final TextView textViewToAttach) {
stopCalled = false;
AnimatedColorSpan span = new AnimatedColorSpan(context);
final SpannableString spannableString = new SpannableString(textToShow);
String substring = textToShow;
int start = textToShow.indexOf(substring);
int end = start + substring.length();
spannableString.setSpan(span, start, end, 0);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(
span, animatedColorSpanFloatProperty, 0, 100);
objectAnimator.setEvaluator(new FloatEvaluator());
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
System.gc();
if (!stopCalled) {
textViewToAttach.setText(spannableString);
}
}
});
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.setDuration(DateUtils.MINUTE_IN_MILLIS * 3);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.start();
objectAnimators.add(objectAnimator);
}
objectAnimators here is an array list of object animators like this
And here is how you stop all of them to get ride of infinite update listener call firing.
public void stopRainbowAnimation() {
System.gc();
stopCalled = true;
if (!objectAnimators.isEmpty()) {
for (int i = 0; i < objectAnimators.size(); i++) {
ObjectAnimator eachAnimator = objectAnimators.get(i);
eachAnimator.setRepeatCount(0);
eachAnimator.end();
eachAnimator.cancel();
eachAnimator.removeAllListeners();
eachAnimator.removeAllUpdateListeners();
}
objectAnimators.clear();
}
}
Hope this will help someone
I have to build animation like This.
Sorry I don't have too much reputation to upload image. you can find gif file from the above link.
I have done all this and it works fine on KitKat and LollyPop only but not on the other API version. I am using this library. Can any one please figure out the actual problem
Here is my code. Thanks in advance
public abstract class AbstractSlideExpandableListAdapter extends WrapperListAdapterImpl
{
private View lastOpnUpperView = null;
ArrayList<View> upperViewsList = new ArrayList<View>();
ArrayList<View> lowerViewsList = new ArrayList<View>();
ArrayList<View> circleViewsList = new ArrayList<View>();
private View lastOpen = null;
private int lastOpenPosition = -1;
private int lastOpenItemIndex = 0;
private int animationDuration = 800;
private BitSet openItems = new BitSet();
private final SparseIntArray viewHeights = new SparseIntArray(10);
private ViewGroup parent;
public AbstractSlideExpandableListAdapter(ListAdapter wrapped)
{
super(wrapped);
lastOpenPosition = 0;
openItems.set(lastOpenPosition, true);
}
private OnItemExpandCollapseListener expandCollapseListener;
public void setItemExpandCollapseListener(OnItemExpandCollapseListener listener)
{
expandCollapseListener = listener;
}
public void removeItemExpandCollapseListener()
{
expandCollapseListener = null;
}
public interface OnItemExpandCollapseListener
{
public void onExpand(View itemView, int position);
public void onCollapse(View itemView, int position);
}
private void notifiyExpandCollapseListener(int type, View view, int position)
{
if (expandCollapseListener != null)
{
if (type == ExpandCollapseAnimation.EXPAND)
{
expandCollapseListener.onExpand(view, position);
}
else if (type == ExpandCollapseAnimation.COLLAPSE)
{
expandCollapseListener.onCollapse(view, position);
}
}
}
#Override
public View getView(int position, View view, ViewGroup viewGroup)
{
this.parent = viewGroup;
view = wrapped.getView(position, view, viewGroup);
enableFor(view, position);
return view;
}
public abstract View getExpandToggleButton(View parent);
public abstract View getExpandableView(View parent);
// upperView to expand animation for sequeeze
public abstract View getUpperView(View upperView);
// Lower view to expand and collapse
public abstract View getLowerView(View upperView);
// Get the circle view to hide and show
public abstract View getCircleView(View circleView);
/**
* Gets the duration of the collapse animation in ms. Default is 330ms. Override this method to change the default.
*
* #return the duration of the anim in ms
*/
public int getAnimationDuration()
{
return animationDuration;
}
/**
* Set's the Animation duration for the Expandable animation
*
* #param duration
* The duration as an integer in MS (duration > 0)
* #exception IllegalArgumentException
* if parameter is less than zero
*/
public void setAnimationDuration(int duration)
{
if (duration < 0)
{
throw new IllegalArgumentException("Duration is less than zero");
}
animationDuration = duration;
}
/**
* Check's if any position is currently Expanded To collapse the open item #see collapseLastOpen
*
* #return boolean True if there is currently an item expanded, otherwise false
*/
public boolean isAnyItemExpanded()
{
return (lastOpenPosition != -1) ? true : false;
}
public void enableFor(View parent, int position)
{
View more = getExpandToggleButton(parent);
View itemToolbar = getExpandableView(parent);
View upperView = getUpperView(parent);
View circleView = getCircleView(parent);
View lowerView = getLowerView(parent);
itemToolbar.measure(parent.getWidth(), parent.getHeight());
upperViewsList.add(upperView);
circleViewsList.add(circleView);
lowerViewsList.add(lowerView);
if (position == 0)
{
// lastopenUpperViewTemporary = upperView;
lastOpnUpperView = upperView;
upperView.setVisibility(View.GONE);
lowerView.setVisibility(View.GONE);
}
enableFor(more, upperView, itemToolbar, position);
itemToolbar.requestLayout();
}
private void animateListExpand(final View button, final View target, final int position)
{
target.setAnimation(null);
int type;
if (target.getVisibility() == View.VISIBLE)
{
type = ExpandCollapseAnimation.COLLAPSE;
}
else
{
type = ExpandCollapseAnimation.EXPAND;
}
// remember the state
if (type == ExpandCollapseAnimation.EXPAND)
{
openItems.set(position, true);
}
else
{
openItems.set(position, false);
}
// check if we need to collapse a different view
if (type == ExpandCollapseAnimation.EXPAND)
{
if (lastOpenPosition != -1 && lastOpenPosition != position)
{
if (lastOpen != null)
{
animateWithUpperView(lastOpen, ExpandCollapseAnimation.COLLAPSE, position);
// animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
notifiyExpandCollapseListener(ExpandCollapseAnimation.COLLAPSE, lastOpen, lastOpenPosition);
}
openItems.set(lastOpenPosition, false);
}
lastOpen = target;
lastOpenPosition = position;
}
else if (lastOpenPosition == position)
{
lastOpenPosition = -1;
}
// animateView(target, type);
// Expand the view which was collapse
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
target.startAnimation(anim);
this.notifiyExpandCollapseListener(type, target, position);
// }
}
private void enableFor(final View button, final View upperView, final View target, final int position)
{
// lastopenUpperViewTemporary = upperView;
if (target == lastOpen && position != lastOpenPosition)
{
// lastOpen is recycled, so its reference is false
lastOpen = null;
}
if (position == lastOpenPosition)
{
// re reference to the last view
// so when can animate it when collapsed
// lastOpen = target;
lastOpen = target;
lastOpnUpperView = upperView;
}
int height = viewHeights.get(position, -1);
if (height == -1)
{
viewHeights.put(position, target.getMeasuredHeight());
updateExpandable(target, position);
}
else
{
updateExpandable(target, position);
}
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(final View view)
{
System.out.println("Position: " + position);
if (lastOpenPosition == position)
{
return;
}
System.out.println("Upper View: " + upperView);
Animation anim = new ExpandCollapseUpperViewAnimation(upperViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
anim.setDuration(800);
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
circleViewsList.get(position).setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation)
{
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation)
{
// TODO Auto-generated method stub
// upperViewsList.get(position).setVisibility(View.VISIBLE);
animateListExpand(button, target, position);
}
});
upperViewsList.get(position).startAnimation(anim);
// Lower animation
Animation lowerAnim = new ExpandCollapseUpperViewAnimation(lowerViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
lowerAnim.setDuration(800);
lowerViewsList.get(position).startAnimation(lowerAnim);
}
});
}
private void updateExpandable(View target, int position)
{
final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) target.getLayoutParams();
if (openItems.get(position))
{
target.setVisibility(View.VISIBLE);
params.bottomMargin = 0;
}
else
{
target.setVisibility(View.GONE);
params.bottomMargin = 0 - viewHeights.get(position);
}
}
/**
* Performs either COLLAPSE or EXPAND animation on the target view
*
* #param target
* the view to animate
* #param type
* the animation type, either ExpandCollapseAnimation.COLLAPSE or ExpandCollapseAnimation.EXPAND
*/
private void animateView(final View target, final int type)
{
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
System.out.println("Animation End");
if (type == ExpandCollapseAnimation.EXPAND)
{
if (parent instanceof ListView)
{
ListView listView = (ListView) parent;
int movement = target.getBottom();
Rect r = new Rect();
boolean visible = target.getGlobalVisibleRect(r);
Rect r2 = new Rect();
listView.getGlobalVisibleRect(r2);
if (!visible)
{
listView.smoothScrollBy(movement, getAnimationDuration());
}
else
{
if (r2.bottom == r.bottom)
{
listView.smoothScrollBy(movement, getAnimationDuration());
}
}
}
}
}
});
target.startAnimation(anim);
}
private void animateWithUpperView(final View target, final int type, final int position)
{
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
// upperViewsList.get(lastOpenItemForUpperView).setVisibility(View.VISIBLE);
// lastOpenItemForUpperView = position;
Animation expandItemAniamtion = new ExpandCollapseLowerViewAnimation(upperViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
expandItemAniamtion.setDuration(800);
expandItemAniamtion.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation)
{
circleViewsList.get(lastOpenItemIndex).setVisibility(View.VISIBLE);
lastOpenItemIndex = position;
}
});
// Lower view animation
Animation lowerAnim = new ExpandCollapseLowerViewAnimation(lowerViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
lowerAnim.setDuration(800);
upperViewsList.get(lastOpenItemIndex).startAnimation(expandItemAniamtion);
lowerViewsList.get(lastOpenItemIndex).startAnimation(lowerAnim);
}
});
target.startAnimation(anim);
}
/**
* Closes the current open item. If it is current visible it will be closed with an animation.
*
* #return true if an item was closed, false otherwise
*/
public boolean collapseLastOpen()
{
if (isAnyItemExpanded())
{
// if visible animate it out
if (lastOpen != null)
{
animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
}
openItems.set(lastOpenPosition, false);
lastOpenPosition = -1;
return true;
}
return false;
}
public Parcelable onSaveInstanceState(Parcelable parcelable)
{
SavedState ss = new SavedState(parcelable);
ss.lastOpenPosition = this.lastOpenPosition;
ss.openItems = this.openItems;
return ss;
}
public void onRestoreInstanceState(SavedState state)
{
if (state != null)
{
this.lastOpenPosition = state.lastOpenPosition;
this.openItems = state.openItems;
}
}
/**
* Utility methods to read and write a bitset from and to a Parcel
*/
private static BitSet readBitSet(Parcel src)
{
BitSet set = new BitSet();
if (src == null)
{
return set;
}
int cardinality = src.readInt();
for (int i = 0; i < cardinality; i++)
{
set.set(src.readInt());
}
return set;
}
private static void writeBitSet(Parcel dest, BitSet set)
{
int nextSetBit = -1;
if (dest == null || set == null)
{
return; // at least dont crash
}
dest.writeInt(set.cardinality());
while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1)
{
dest.writeInt(nextSetBit);
}
}
/**
* The actual state class
*/
static class SavedState extends View.BaseSavedState
{
public BitSet openItems = null;
public int lastOpenPosition = -1;
SavedState(Parcelable superState)
{
super(superState);
}
private SavedState(Parcel in)
{
super(in);
lastOpenPosition = in.readInt();
openItems = readBitSet(in);
}
#Override
public void writeToParcel(Parcel out, int flags)
{
super.writeToParcel(out, flags);
out.writeInt(lastOpenPosition);
writeBitSet(out, openItems);
}
// required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>()
{
public SavedState createFromParcel(Parcel in)
{
return new SavedState(in);
}
public SavedState[] newArray(int size)
{
return new SavedState[size];
}
};
}
public static Animation ExpandOrCollapseView(final View v, final boolean expand)
{
try
{
Method m = v.getClass().getDeclaredMethod("onMeasure", int.class, int.class);
m.setAccessible(true);
m.invoke(v, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(((View) v.getParent()).getMeasuredWidth(), MeasureSpec.AT_MOST));
}
catch (Exception e)
{
e.printStackTrace();
}
final int initialHeight = v.getMeasuredHeight();
if (expand)
{
v.getLayoutParams().height = 0;
}
else
{
v.getLayoutParams().height = initialHeight;
}
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
int newHeight = 0;
if (expand)
{
newHeight = (int) (initialHeight * interpolatedTime);
}
else
{
newHeight = (int) (initialHeight * (1 - interpolatedTime));
}
v.getLayoutParams().height = newHeight;
v.requestLayout();
if (interpolatedTime == 1 && !expand)
v.setVisibility(View.GONE);
}
#Override
public boolean willChangeBounds()
{
return true;
}
};
a.setDuration(1000);
return a;
}}
Luckily I have found the solution. On pre-kitkat the upperView on list item which was gone does not forcelly animate but on kitkat and so on it forcelly animate from gone to visible.
So as the solution we must give a layer of view between of list item and upper view (Which we have to animate).
In this way it will work like charm.
I am making horizontal scrollview gallery, and I want to autoscroll it. Now it's scrolling from left to right but when I reach end of list I just simply jump to first one, but it looks really bad, so I want to go scroll around from beginning avoiding just skipping to first one, or if it is not possible just start scrolling to the other side when I reach last view on right (maybe better option). Could someone help me how to do this?
private LinearLayout horizontalOuterLayout;
private HorizontalScrollView horizontalScrollview;
private int scrollMax;
private int scrollPos = 0;
private TimerTask clickSchedule;
private TimerTask scrollerSchedule;
private TimerTask faceAnimationSchedule;
private Timer scrollTimer = null;
private Timer faceTimer = null;
private String[] imageNameArray ={ "sponsors_czarnykot", "sponsors_estradarzeszow","sponsors_klubp","sponsors_kula","sponsors_czarnykot", "sponsors_estradarzeszow","sponsors_klubp","sponsors_kula" };
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.horizontal_layout);
horizontalScrollview = (HorizontalScrollView) findViewById(R.id.horiztonal_scrollview_id);
horizontalOuterLayout = (LinearLayout) findViewById(R.id.horiztonal_outer_layout_id);
horizontalScrollview.setHorizontalScrollBarEnabled(false);
addImagesToView();
ViewTreeObserver vto = horizontalOuterLayout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
horizontalOuterLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
getScrollMaxAmount();
startAutoScrolling();
}
});
}
public void getScrollMaxAmount()
{
int actualWidth = (horizontalOuterLayout.getMeasuredWidth() - 512);
scrollMax = actualWidth;
}
public void startAutoScrolling()
{
if (scrollTimer == null)
{
scrollTimer = new Timer();
final Runnable Timer_Tick = new Runnable()
{
public void run()
{
moveScrollViewRight();
}
};
if (scrollerSchedule != null)
{
scrollerSchedule.cancel();
scrollerSchedule = null;
}
scrollerSchedule = new TimerTask()
{
#Override
public void run()
{
runOnUiThread(Timer_Tick);
}
};
scrollTimer.schedule(scrollerSchedule, 30, 30);
}
}
public void moveScrollViewRight()
{
scrollPos = (int) (horizontalScrollview.getScrollX() + 1.0);
if (scrollPos >= scrollMax)
{
scrollPos = 0;
}
horizontalScrollview.scrollTo(scrollPos, 0);
}
/** Adds the images to view. */
public void addImagesToView()
{
for (int i = 0; i < imageNameArray.length; i++)
{
final ImageView imageView = new ImageView(this);
int imageResourceId = getResources().getIdentifier(imageNameArray[i], "drawable", getPackageName());
Drawable image = this.getResources().getDrawable(imageResourceId);
imageView.setBackgroundDrawable(image);
imageView.setTag(i);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(180, 123);
params.setMargins(0, 25, 0, 25);
imageView.setLayoutParams(params);
horizontalOuterLayout.addView(imageView);
}
}
public void stopAutoScrolling()
{
if (scrollTimer != null)
{
scrollTimer.cancel();
scrollTimer = null;
}
}
public void onBackPressed()
{
super.onBackPressed();
finish();
}
public void onPause()
{
super.onPause();
finish();
}
public void onDestroy()
{
clearTimerTaks(clickSchedule);
clearTimerTaks(scrollerSchedule);
clearTimerTaks(faceAnimationSchedule);
clearTimers(scrollTimer);
clearTimers(faceTimer);
clickSchedule = null;
scrollerSchedule = null;
faceAnimationSchedule = null;
scrollTimer = null;
faceTimer = null;
super.onDestroy();
}
private void clearTimers(Timer timer)
{
if (timer != null)
{
timer.cancel();
timer = null;
}
}
private void clearTimerTaks(TimerTask timerTask)
{
if (timerTask != null)
{
timerTask.cancel();
timerTask = null;
}
}
scrolling back the other way would be easiest.
add in a instance variable that is set to 1.0 (called say scrollDist)
then change this line
scrollPos = (int) (horizontalScrollview.getScrollX() + 1.0);
to
scrollPos = (int) (horizontalScrollview.getScrollX() + scrollDist);
and this line
scrollPos = 0;
to
scrollDist *= -1.0;
this way it will reverse each time it hits the end of the scrollview.
For endless scrolling use the following snnipet
public void getScrollMaxAmount() {
int actualWidth = (horizontalOuterLayout.getMeasuredWidth() - getWindowManager().getDefaultDisplay().getWidth());
scrollMax = actualWidth;
}
public void moveScrollView() {
// ********************* Scrollable Speed ***********************
scrollPos = (int) (horizontalScrollview.getScrollX() + 1.0);
if (scrollPos >= scrollMax) {
Log.v("childCount", ""+scrollMax);
addImagesToView();
getScrollMaxAmount();
}
horizontalScrollview.scrollTo(scrollPos, 0);
}
I have a list of buttons. When I press a button, a View should slide in a downwards motion out of the button, like this:
Start:
Halfway:
End:
How would I go about this? The View that should slide out is bigger than the button, so first hiding the View behind the button and then sliding it downwards causes the View to be visible above the button. That should not happen.
Any ideas or examples on how to approach this?
I believe the simplest approach is to extend Animation class and override applyTransformation() to change the view's height as follows:
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.LinearLayout;
public class MyCustomAnimation extends Animation {
public final static int COLLAPSE = 1;
public final static int EXPAND = 0;
private View mView;
private int mEndHeight;
private int mType;
private LinearLayout.LayoutParams mLayoutParams;
public MyCustomAnimation(View view, int duration, int type) {
setDuration(duration);
mView = view;
mEndHeight = mView.getHeight();
mLayoutParams = ((LinearLayout.LayoutParams) view.getLayoutParams());
mType = type;
if(mType == EXPAND) {
mLayoutParams.height = 0;
} else {
mLayoutParams.height = LayoutParams.WRAP_CONTENT;
}
view.setVisibility(View.VISIBLE);
}
public int getHeight(){
return mView.getHeight();
}
public void setHeight(int height){
mEndHeight = height;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
if(mType == EXPAND) {
mLayoutParams.height = (int)(mEndHeight * interpolatedTime);
} else {
mLayoutParams.height = (int) (mEndHeight * (1 - interpolatedTime));
}
mView.requestLayout();
} else {
if(mType == EXPAND) {
mLayoutParams.height = LayoutParams.WRAP_CONTENT;
mView.requestLayout();
}else{
mView.setVisibility(View.GONE);
}
}
}
}
To use it, set your onclick() as follows:
int height;
#Override
public void onClick(View v) {
if(view2.getVisibility() == View.VISIBLE){
MyCustomAnimation a = new MyCustomAnimation(view2, 1000, MyCustomAnimation.COLLAPSE);
height = a.getHeight();
view2.startAnimation(a);
}else{
MyCustomAnimation a = new MyCustomAnimation(view2, 1000, MyCustomAnimation.EXPAND);
a.setHeight(height);
view2.startAnimation(a);
}
}
Regards.
Use something like:
Animation a = new ScaleAnimation(1, 1, 0, 1, Animation.RELATIVE_TO_SELF, (float) 0.5, Animation.RELATIVE_TO_SELF, (float) 0);
a.setFillAfter(true);
view.setAnimation(a);
a.setDuration(1000);
view.startAnimation(a);
Here is simple example of hand-made animation, that provide what you want. It works in test app, but I'm not sure that there is no bugs:
public class MainActivity extends Activity implements OnClickListener {
private Timer timer;
private TimerTask animationTask;
private View view1;
private View view2;
boolean animating;
boolean increasing = true;
int initHeight = -1;
private LayoutParams params;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer = new Timer();
view1 = findViewById(R.id.view1);// clickable view
view1.setOnClickListener(this);
view2 = findViewById(R.id.view2);// animated view
params = view2.getLayoutParams();
}
#Override
protected void onDestroy() {
super.onDestroy();
timer.cancel();
}
#Override
public void onClick(View v) {
Toast.makeText(this, "start animating...", Toast.LENGTH_SHORT).show();
animationTask = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (animationFinished()) {
animating = false;
cancel();//canceling animating task
return;
}
params.height += increasing ? 1 : -1;
view2.setLayoutParams(params);
}
});
}
private boolean animationFinished() {
int viewHeight = view2.getHeight();
if (increasing && viewHeight >= initHeight) {
return true;
}
if (!increasing && viewHeight <= 0) {
return true;
}
return false;
}
};
//if we already animating - we just change direction of animation
increasing = !increasing;
if (!animating) {
animating = true;
int height = view2.getHeight();
params.height = height;
view2.setLayoutParams(params);//change param "height" from "wrap_conent" to real height
if (initHeight < 0) {//height of view - we setup it only once
initHeight = height;
}
timer.schedule(animationTask, 0, 10);//changing repeat time here will fasten or slow down animation
}
}
}
Maybe you can set the height to 0 and gradually increase the height. But then you will have the problem that you have to be sure your text is aligned at the bottom of the view. And also to know what the maximal height of the view should be.
use a sliding list adapter so much easier than messing around with animations
https://github.com/tjerkw/Android-SlideExpandableListView
Simply pass android:animateLayoutChanges to LinearLayout that holds all the views, you will achieve your desired result.
I would do it like that. First the layout for the whole collapsible panel component: (pseudo xml)
RelativeLayout (id=panel, clip)
LinearLayout (id=content, alignParentBottom=true)
LinearLayout (id=handle, above=content)
This should ensure that the content is always below the handle.
Then when you need to collapse:
Animate the top margin of content from 0 to -content.height
Animate the height of the panel from current to current-content.height