Next & Previous Animations - android

I have made a PopupWindow that pops up when a user selects an item in a list. And then when he swipes left and right on the popup window, the data (in the popup) is change to the previous and next items in the list, respectively.
And I have made it so that it looks like a new popup window is sliding in from the left as the current one goes out from the right of the screen when the user slides his finger to the right ("Next" motion). And vice versa for the "Previous" motion.
But the issue is, as I have made 2 styles for the two animations (left-to-middle while middle-to-right and right-to-middle while middle-to-left), and as PopupWindows does not get updated while it is showing, when the user swipes to the right ("Next") and then to the left ("Previous"), the animation looks choppy as the 2 animations overlap. The same happens when the swipes are reversed again.
Now, if I call update() after changing the Animation, then it again slides in to the middle. That's even worse. So is there anything I can do to achieve the behavior I want?
Thanks.
EDIT: Source Code
Here is the code I used to show the popup for the first time:
popup1 = new PopupWindow(popupLayout, popup_width, popup_height, true);
popup1.setAnimationStyle(R.style.AnimationPopup);
popup1.showAtLocation(this.findViewById(R.id.main_container), Gravity.CENTER, 0, 0);
And this is the next() method's code:
if(popup2 != null) popup1 = popup2;
if(popup1 != null) {
popup2 = new PopupWindow(popupLayout, popup_width, popup_height, true);
popup2.setAnimationStyle(R.style.AnimationPopup);
popup1.dismiss();
popup2.showAtLocation(rootLayout, Gravity.CENTER, 0, 0);
}
And this is the prev() method's code:
if(popup2 != null) popup1 = popup2;
if(popup1 != null) {
popup2 = new PopupWindow(popupLayout, popup_width, popup_height, true);
popup2.setAnimationStyle(R.style.AnimationPopupReverse);
popup1.setAnimationStyle(R.style.AnimationPopupReverse);
popup1.dismiss();
popup2.showAtLocation(rootLayout, Gravity.CENTER, 0, 0);
}
That's all for the Java code. The AnimationPopup is a XML with a simple show of slide-from-right-to-center and a hide of slide-from-center-to-left animations. And the AnimationPopupReverse is the reverse of the above animations.
I have provided my first try of the codes. Here, the changed animations take effect after one more popup has been shown.

All animations appear much cleaner if you use android:hardwareAccelerated="true" in the manifest under the application tag. Even regular slide left/right animation will seem choppy without being hardware accelerated.

An easy solution would be to:
Disable swiping until the current animation ends.
Use android:duration="#android:integer/config_mediumAnimTime" making it pretty standard and sort of fast.
You can also implement some sort of dragging:
Catch Touch Events and change the location of the Popup accordingly. When the user removes his finger, either dispatch the swipe and show the next/previous popup window. This would actually involve some head scratching.
The best would be to use a ViewPager that just shows the popup windows with all the functionality of swiping implemented:
Example:
yourViewPager.setAdapter(new PagerAdapter() {
#Override
public Object instantiateItem(ViewGroup container, int position) {
View view;
//intialise view to a PopupWindow or some Layout with a PopupWindow in the location you want
container.addView(view, 0);
return view;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return (view==object);
}
#Override
public int getCount() {
//return the count
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
#Override
public void finishUpdate(ViewGroup arg0) {
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
#Override
public Parcelable saveState() {
return null;
}
#Override
public void startUpdate(ViewGroup arg0) {
}
});

Related

Android: Can I listen for animation updates using TransitionManager.beginDelayedTransition?

I'm using the TransitionManager.beginDelayedTransition outlined here
to animate two views swapping positions within a RelativeLayout. I do this by simply swapping the RelativeLayout.LayoutParams of two the two views.
My question is how do I monitor the animation that is automatically created and executed by TransitionManager without having to create my own custom Transitions. I need to detect when the animation has ended so that I can make a change to the views that have been swapped.
Below is the method that swaps the two views. CollageCanvasAperture is an extension of View and mApertureGroup is the RelativeLayout that holds these views.
private void shuffle(int fromApertureInd, int toApertureInd) {
final CollageCanvasAperture fromV = (CollageCanvasAperture) mApertureGroup.getChildAt(fromApertureInd);
final CollageCanvasAperture toV = (CollageCanvasAperture) mApertureGroup.getChildAt(toApertureInd);
if (null == fromV || null == toV) {
return;
}
TransitionManager.beginDelayedTransition(mApertureGroup);
RelativeLayout.LayoutParams fromLP = (RelativeLayout.LayoutParams) fromV.getLayoutParams();
RelativeLayout.LayoutParams toLP = (RelativeLayout.LayoutParams) toV.getLayoutParams();
fromV.setLayoutParams(toLP);
toV.setLayoutParams(fromLP);
}
I've done a few hours of searching on here and combing through the TransitionManager code but can't see how to detect changes. I'd prefer to be able to detect the animation end within the CollageCanvasAperture but can't see any relevant listeners to apply.
I guess I could provide the view with the destination LayoutParams before the animation is executed and then the view can listen for size & location changes until they match...?
So it turned out it's quite straightforward to add a listener to these 'automatic' transitions.
Instead of using: TransitionManager.beginDelayedTransition(mApertureGroup);
you need to work out which automatic Transition is being using when you invoke beginDelayedTransition and call TransitionManager.go() instead.
My view transitions were using the ChangeBounds Transition. Once that was know all I had to do was:
ChangeBounds mySwapTransition = new ChangeBounds();
mySwapTransition.addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) { }
#Override
public void onTransitionEnd(Transition transition) { }
#Override
public void onTransitionCancel(Transition transition) {}
#Override
public void onTransitionPause(Transition transition) { }
#Override
public void onTransitionResume(Transition transition) { }
});
TransitionManager.go(new Scene(mApertureGroup), mySwapTransition);

Can I partially hide a layout?

As I've a master in MS Paint, I will just upload a picture selfdescripting what I'm trying to achieve.
I've searched, but I'm not really sure what do I've to search. I've found something called Animations. I managed to rotate, fade, etc an element from a View (with this great tutorial http://www.vogella.com/articles/AndroidAnimation/article.html)
But this is a bit limited for what I'm trying to achieve, and now, I'm stuck, because I don't know how is this really called in android development. Tried words like "scrollup layouts" but I didn't get any better results.
Can you give me some tips?
Thank you.
You can see a live example, with this app: https://play.google.com/store/apps/details?id=alexcrusher.just6weeks
Sincerely,
Sergi
Use something like this as your layout (Use Linear, Relative or other layout if you wish):
<LinearLayout
android:id="#+id/lty_parent">
<LinearLayout
android:id="#+id/lyt_first" />
<LinearLayout
android:id="#+id/lyt_second"/>
</LinearLayout>
And then in an onClick method on whatever you want to use to control it, set the Visibility between Visible and Gone.
public void buttonClickListener(){
((Button) findViewById(R.id.your_button))
.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (lyt_second.getVisibility() == View.GONE) {
lyt_second.setVisibility(View.VISIBILE);
}
else {
lyt_second.setVisibility(View.GONE);
}
});
Which is fine if you just want a simple appear/disappear with nothing fancy. Things get a little bit more complicated if you want to animate it, as you need to play around with negative margins in order to make it appear to grow and shrink, like so:
We use the same onClick method that we did before, but this time when we click it starts up a custom SlideAnimation for the hidden/visible view.
#Override
public void onClick(View v) {
SlideAnimation slideAnim = new SlideAnimation(lyt_second, time);
lyt_second.startAnimation(slideAnim);
}
The implementation of the SlideAnimation is based on a general Animation class, which we extend and then Override the transformation.
public SlideAnimation(View view, int duration) {
//Set the duration of the animation to the int we passed in
setDuration(duration);
//Set the view to be animated to the view we passed in
viewToBeAnimated = view;
//Get the Margin Parameters for the view so we can edit them
viewMarginParams = (MarginLayoutParams) view.getLayoutParams();
//If the view is VISIBLE, hide it after. If it's GONE, show it before we start.
hideAfter = (view.getVisibility() == View.VISIBLE);
//First off, start the margin at the bottom margin we've already set.
//You need your layout to have a negative margin for this to work correctly.
marginStart = viewMarginParams.bottomMargin;
//Decide if we're expanding or collapsing
if (marginStart == 0){
marginEnd = 0 - view.getHeight();
}
else {
marginEnd = 0;
}
//Make sure the view is visible for our animation
view.setVisibility(View.VISIBLE);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
// Setting the new bottom margin to the start of the margin
// plus the inbetween bits
viewMarginParams.bottomMargin = marginStart
+ (int) ((marginEnd - marginStart) * interpolatedTime);
// Request the layout as it happens so we can see it redrawing
viewToBeAnimated.requestLayout();
// Make sure we have finished before we mess about with the rest of it
} else if (!alreadyFinished) {
viewMarginParams.bottomMargin = marginEnd;
viewToBeAnimated.requestLayout();
if (hideAfter) {
viewToBeAnimated.setVisibility(View.GONE);
}
alreadyFinished = true;
}
hideAfter = false;
}
}
EDIT: If anyone had used this code before and found that if you click on the button that starts the animation more than once before the animation was finished, it would mess up the animation from then on, causing it to always hide the view after the animation finished. I missed the reset of the hideAfter boolean near the bottom of the code, added it now.
you can do this manually by using setvisibility feature on the event onClick()
or
use this
dynamically adding two views one below other

Fix the Animation of a Circular ViewPager

Goal
Build a Circular ViewPager.
The first element lets you peak to the last element and swipe to it, and vice versa. You should be able to swipe in either direction forever.
Now this has been accomplished before, but these questions do not work for my implementation. Here are a few for reference:
how to create circular viewpager?
ViewPager as a circular queue / wrapping
https://github.com/antonyt/InfiniteViewPager
How I Tried to Solve the Problem
We will use an array of size 7 as an example. The elements are as follows:
[0][1][2][3][4][5][6]
When you are at element 0, ViewPagers do not let you swipe left! How terrible :(. To get around this, I added 1 element to the front and end.
[0][1][2][3][4][5][6] // Original
[0][1][2][3][4][5][6][7][8] // New mapping
When the ViewPageAdapter asks for (instantiateItem()) element 0, we return element 7. When the ViewPageAdapter asks for element 8 we return element 1.
Likewise in the OnPageChangeListener in the ViewPager, when the onPageSelected is called with 0, we setCurrentItem(7), and when it's called with 8 we setCurrentItem(1).
This works.
The Problem
When you swipe to the left from 1 to 0, and we setCurrentItem(7), it will animate all the way to right by 6 full screens. This doesn't give the appearance of a circular ViewPager, it gives the appearence rushing to the last element in the opposite direction the user requested with their swipe motion!
This is very very jarring.
How I Tried to Solve This
My first inclination was to turn off smooth (ie, all) animations. It's a bit better, but it's now choppy when you move from the last element to the first and vice versa.
I then made my own Scroller.
http://developer.android.com/reference/android/widget/Scroller.html
What I found was that there is always 1 call to startScroll() when moving between elements, except when I move from 1 to 7 and 7 to 1.
The first call is the correct animation in direction and amount.
The second call is the animation that moves everything to the right by multiple pages.
This is where things got really tricky.
I thought the solution was to just skip the second animation. So I did. What happens is a smooth animation from 1 to 7 with 0 hiccups. Perfect! However, if you swipe, or even tap the screen, you are suddenly (with no animation) at element 6! If you had swiped from 7 to 1, you'll actually be at element 2. There is no call to setCurrentItem(2) or even a call to the OnPageChangeListener indicating that you arrived at 2 at any point in time.
But you're not actually at element 2, which is kind of good. You are still at element 1, but the view for element 2 will be shown. And then when you swipe to the left, you go to element 1. Even though you were really at element 1 already.. How about some code to help clear things up:
Animation is broken, but no weird side effects
#Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, duration);
}
Animation works! But everything is strange and scary...
#Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
if (dx > 480 || dx < -480) {
} else {
super.startScroll(startX, startY, dx, dy, duration);
}
}
The ONLY difference is that when the second animation (bigger than the width of the 480 pixel screen) is called, we ignore it.
After reading through the Android Source code for Scroller, I found that startScroll does not start scrolling anything. It sets up all the data to be scrolled, but doesn't initiate anything.
My Hunch
When you do the circular action (1 to 7 or 7 to 1), there are two calls to startScroll(). I think something in between the two calls is causing an issue.
User scrolls from element 1 to element 7 causing a jump from 0 to 7. This should animate to the left.
startScroll() is called indicating a short animation to the left.
STUFF HAPPENS THAT MAKES ME CRY PROBABLY I THINK
startScroll() is called indicating a long animation to the right.
Long animation to the right occurs.
If I comment out 4, then 5 becomes "Short correct animation to the left, things go crazy"
Summary
My implementation of a Circular ViewPager works, but the animation is broken. Upon trying to fix the animation, it breaks the functionality of the ViewPager. I am currently spinning my wheels trying to figure out how to make it work. Help me! :)
If anything is unclear please comment below and I will clarify. I realize I was not very precise with how things are broken. It's difficult to describe because it's not even clear what I'm seeing on the screen. If my explanation is an issue I can work on it, let me know!
Cheers,
Coltin
Code
This code is slightly modified to make it more readable on its own, though the functionality is identical to my current iteration of the code.
OnPageChangeListener.onPageSelected
#Override
public void onPageSelected(int _position) {
boolean animate = true;
if (_position < 1) {
// Swiping left past the first element, go to element (9 - 2)=7
setCurrentItem(getAdapter().getCount() - 2, animate);
} else if (_position >= getAdapter().getCount() - 1) {
// Swiping right past the last element
setCurrentItem(1, animate);
}
}
CircularScroller.startScroll
#Override
public void startScroll(int _startX, int _startY, int _dx, int _dy, int _duration) {
// 480 is the width of the screen
if (dx > 480 || dx < -480) {
// Doing nothing in this block shows the correct animation,
// but it causes the issues mentioned above
// Uncomment to do the big scroll!
// super.startScroll(_startX, _startY, _dx, _dy, _duration);
// lastDX was to attempt to reset the scroll to be the previous
// correct scroll distance; it had no effect
// super.startScroll(_startX, _startY, lastDx, _dy, _duration);
} else {
lastDx = _dx;
super.startScroll(_startX, _startY, _dx, _dy, _duration);
}
}
CircularViewPageAdapter.CircularViewPageAdapter
private static final int m_Length = 7; // For our example only
private static Context m_Context;
private boolean[] created = null; // Not the best practice..
public CircularViewPageAdapter(Context _context) {
m_Context = _context;
created = new boolean[m_Length];
for (int i = 0; i < m_Length; i++) {
// So that we do not create things multiple times
// I thought this was causing my issues, but it was not
created[i] = false;
}
}
CircularViewPageAdapter.getCount
#Override
public int getCount() {
return m_Length + 2;
}
CircularViewPageAdapter.instantiateItem
#Override
public Object instantiateItem(View _collection, int _position) {
int virtualPosition = getVirtualPosition(_position);
if (created[virtualPosition - 1]) {
return null;
}
TextView tv = new TextView(m_Context);
// The first view is element 1 with label 0! :)
tv.setText("Bonjour, merci! " + (virtualPosition - 1));
tv.setTextColor(Color.WHITE);
tv.setTextSize(30);
((ViewPager) _collection).addView(tv, 0);
return tv;
}
CircularViewPageAdapter.destroyItem
#Override
public void destroyItem(ViewGroup container, int position, Object view) {
ViewPager viewPager = (ViewPager) container;
// If the virtual distance is distance 2 away, it should be destroyed.
// If it's not intuitive why this is the case, please comment below
// and I will clarify
int virtualDistance = getVirtualDistance(viewPager.getCurrentItem(), getVirtualPosition(position));
if ((virtualDistance == 2) || ((m_Length - virtualDistance) == 2)) {
((ViewPager) container).removeView((View) view);
created[getVirtualPosition(position) - 1] = false;
}
}
I think the best doable approach would be instead of using a normal list to have a wrapper to the List that when the get(pos) method is executed to obtain the object to create the view, you make something like this get(pos % numberOfViews) and when it ask for the size of the List you put that the List is Integer.MAX_VALUE and you start your List in the middle of it so you can say that is mostly impossible to have an error, unless they actually swipe to the same side until the reach the end of the List. I will try to post a proof of concept later this weak if the time allows me to do so.
EDIT:
I have tried this piece of code, i know is a simple textbox shown on each view, but the fact is that it works perfectly, it might be slower depending on the total amount of views but the proof of concept is here. What i have done is that the MAX_NUMBER_VIEWS represents what is the maximum numbers of times a user can completely give before he is stopped. and as you can see i started the viewpager at the length of my array so that would be the second time it appears so you have one turn extra to the left and right but you can change it as you need it. I hope i do not get more negative points for a solution that in fact does work.
ACTIVITY:
pager = (ViewPager)findViewById(R.id.viewpager);
String[] articles = {"ARTICLE 1","ARTICLE 2","ARTICLE 3","ARTICLE 4"};
pager.setAdapter(new ViewPagerAdapter(this, articles));
pager.setCurrentItem(articles.length);
ADAPTER:
public class ViewPagerAdapter extends PagerAdapter {
private Context ctx;
private String[] articles;
private final int MAX_NUMBER_VIEWS = 3;
public ViewPagerAdapter(Context ctx, String[] articles) {
this.ctx = ctx;
this.articles = articles.clone();
}
#Override
public int getCount() {
return articles.length * this.MAX_NUMBER_VIEWS;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
TextView view = new TextView(ctx);
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
int realPosition = position % articles.length;
view.setText(this.articles[realPosition]);
((ViewPager) container).addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((View) object);
}
#Override
public Parcelable saveState() {
return null;
}
}

Adding items into listview dynamically [duplicate]

In iOS, there is a very easy and powerful facility to animate the addition and removal of UITableView rows, here's a clip from a youtube video showing the default animation. Note how the surrounding rows collapse onto the deleted row. This animation helps users keep track of what changed in a list and where in the list they were looking at when the data changed.
Since I've been developing on Android I've found no equivalent facility to animate individual rows in a TableView. Calling notifyDataSetChanged() on my Adapter causes the ListView to immediately update its content with new information. I'd like to show a simple animation of a new row pushing in or sliding out when the data changes, but I can't find any documented way to do this. It looks like LayoutAnimationController might hold a key to getting this to work, but when I set a LayoutAnimationController on my ListView (similar to ApiDemo's LayoutAnimation2) and remove elements from my adapter after the list has displayed, the elements disappear immediately instead of getting animated out.
I've also tried things like the following to animate an individual item when it is removed:
#Override
protected void onListItemClick(ListView l, View v, final int position, long id) {
Animation animation = new ScaleAnimation(1, 1, 1, 0);
animation.setDuration(100);
getListView().getChildAt(position).startAnimation(animation);
l.postDelayed(new Runnable() {
public void run() {
mStringList.remove(position);
mAdapter.notifyDataSetChanged();
}
}, 100);
}
However, the rows surrounding the animated row don't move position until they jump to their new positions when notifyDataSetChanged() is called. It appears ListView doesn't update its layout once its elements have been placed.
While writing my own implementation/fork of ListView has crossed my mind, this seems like something that shouldn't be so difficult.
Thanks!
Animation anim = AnimationUtils.loadAnimation(
GoTransitApp.this, android.R.anim.slide_out_right
);
anim.setDuration(500);
listView.getChildAt(index).startAnimation(anim );
new Handler().postDelayed(new Runnable() {
public void run() {
FavouritesManager.getInstance().remove(
FavouritesManager.getInstance().getTripManagerAtIndex(index)
);
populateList();
adapter.notifyDataSetChanged();
}
}, anim.getDuration());
for top-to-down animation use :
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="20%p" android:toYDelta="-20"
android:duration="#android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
The RecyclerView takes care of adding, removing, and re-ordering animations!
This simple AndroidStudio project features a RecyclerView. take a look at the commits:
commit of the classic Hello World Android app
commit, adding a RecyclerView to the project (content not dynamic)
commit, adding functionality to modify content of RecyclerView at runtime (but no animations)
and finally...commit adding animations to the RecyclerView
Take a look at the Google solution. Here is a deletion method only.
ListViewRemovalAnimation project code and Video demonstration
It needs Android 4.1+ (API 16). But we have 2014 outside.
Since ListViews are highly optimized i think this is not possible to accieve. Have you tried to create your "ListView" by code (ie by inflating your rows from xml and appending them to a LinearLayout) and animate them?
Have you considered animating a sweep to the right? You could do something like drawing a progressively larger white bar across the top of the list item, then removing it from the list. The other cells would still jerk into place, but it'd better than nothing.
call
listView.scheduleLayoutAnimation();
before changing the list
I hacked together another way to do it without having to manipulate list view. Unfortunately, regular Android Animations seem to manipulate the contents of the row, but are ineffectual at actually shrinking the view. So, first consider this handler:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
Bundle bundle = message.getData();
View view = listView.getChildAt(bundle.getInt("viewPosition") -
listView.getFirstVisiblePosition());
int heightToSet;
if(!bundle.containsKey("viewHeight")) {
Rect rect = new Rect();
view.getDrawingRect(rect);
heightToSet = rect.height() - 1;
} else {
heightToSet = bundle.getInt("viewHeight");
}
setViewHeight(view, heightToSet);
if(heightToSet == 1)
return;
Message nextMessage = obtainMessage();
bundle.putInt("viewHeight", (heightToSet - 5 > 0) ? heightToSet - 5 : 1);
nextMessage.setData(bundle);
sendMessage(nextMessage);
}
Add this collection to your List adapter:
private Collection<Integer> disabledViews = new ArrayList<Integer>();
and add
public boolean isEnabled(int position) {
return !disabledViews.contains(position);
}
Next, wherever it is that you want to hide a row, add this:
Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt("viewPosition", listView.getPositionForView(view));
message.setData(bundle);
handler.sendMessage(message);
disabledViews.add(listView.getPositionForView(view));
That's it! You can change the speed of the animation by altering the number of pixels that it shrinks the height at once. Not real sophisticated, but it works!
After inserting new row to ListView, I just scroll the ListView to new position.
ListView.smoothScrollToPosition(position);
I haven't tried it but it looks like animateLayoutChanges should do what you're looking for. I see it in the ImageSwitcher class, I assume it's in the ViewSwitcher class as well?
Since Android is open source, you don't actually need to reimplement ListView's optimizations. You can grab ListView's code and try to find a way to hack in the animation, you can also open a feature request in android bug tracker (and if you decided to implement it, don't forget to contribute a patch).
FYI, the ListView source code is here.
Here's the source code to let you delete rows and reorder them.
A demo APK file is also available. Deleting rows is done more along the lines of Google's Gmail app that reveals a bottom view after swiping a top view. The bottom view can have an Undo button or whatever you want.
As i had explained my approach in my site i shared the link.Anyways the idea is create bitmaps
by getdrawingcache .have two bitmap and animate the lower bitmap to create the moving effect
Please see the following code:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View rowView, int positon, long id)
{
listView.setDrawingCacheEnabled(true);
//listView.buildDrawingCache(true);
bitmap = listView.getDrawingCache();
myBitmap1 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), rowView.getBottom());
myBitmap2 = Bitmap.createBitmap(bitmap, 0, rowView.getBottom(), bitmap.getWidth(), bitmap.getHeight() - myBitmap1.getHeight());
listView.setDrawingCacheEnabled(false);
imgView1.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap1));
imgView2.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap2));
imgView1.setVisibility(View.VISIBLE);
imgView2.setVisibility(View.VISIBLE);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(0, rowView.getBottom(), 0, 0);
imgView2.setLayoutParams(lp);
TranslateAnimation transanim = new TranslateAnimation(0, 0, 0, -rowView.getHeight());
transanim.setDuration(400);
transanim.setAnimationListener(new Animation.AnimationListener()
{
public void onAnimationStart(Animation animation)
{
}
public void onAnimationRepeat(Animation animation)
{
}
public void onAnimationEnd(Animation animation)
{
imgView1.setVisibility(View.GONE);
imgView2.setVisibility(View.GONE);
}
});
array.remove(positon);
adapter.notifyDataSetChanged();
imgView2.startAnimation(transanim);
}
});
For understanding with images see this
Thanks.
I have done something similar to this. One approach is to interpolate over the animation time the height of the view over time inside the rows onMeasure while issuing requestLayout() for the listView. Yes it may be be better to do inside the listView code directly but it was a quick solution (that looked good!)
Just sharing another approach:
First set the list view's android:animateLayoutChanges to true:
<ListView
android:id="#+id/items_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"/>
Then I use a handler to add items and update the listview with delay:
Handler mHandler = new Handler();
//delay in milliseconds
private int mInitialDelay = 1000;
private final int DELAY_OFFSET = 1000;
public void addItem(final Integer item) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
new Thread(new Runnable() {
#Override
public void run() {
mDataSet.add(item);
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
}
}).start();
}
}, mInitialDelay);
mInitialDelay += DELAY_OFFSET;
}

List view snap to item

I'm creating a list of pictures using a ListView and the photos are of a size that would fit 2 to 3 photos on the screen.
The problem that I'm having is that I would like to when the user stops scrolling that the first item of the visible list would snap to the top of screen, for example, if the scroll ends and small part of the first picture displayed, we scroll the list down so the picture is always fully displayed, if mostly of the picture is displayed, we scroll the list up so the next picture is fully visible.
Is there a way to achieve this in android with the listview?
I've found a way to do this just listening to scroll and change the position when the scroll ended by implementing ListView.OnScrollListener
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:
if (scrolling){
// get first visible item
View itemView = view.getChildAt(0);
int top = Math.abs(itemView.getTop()); // top is a negative value
int bottom = Math.abs(itemView.getBottom());
if (top >= bottom){
((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition()+1, 0);
} else {
((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition(), 0);
}
}
scrolling = false;
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
case OnScrollListener.SCROLL_STATE_FLING:
Log.i("TEST", "SCROLLING");
scrolling = true;
break;
}
}
The change is not so smooth but it works.
Utilizing a couple ideas from #nininho's solution, I got my listview to snap to the item with a smooth scroll instead of abruptly going to it. One caveat is that I've only tested this solution on a Moto X in a basic ListView with text, but it works very well on the device. Nevertheless, I'm confident about this solution, and encourage you to provide feedback.
listview.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
if (scrollState == SCROLL_STATE_IDLE) {
View itemView = view.getChildAt(0);
int top = Math.abs(itemView.getTop());
int bottom = Math.abs(itemView.getBottom());
int scrollBy = top >= bottom ? bottom : -top;
if (scrollBy == 0) {
return;
}
smoothScrollDeferred(scrollBy, (ListView)view);
}
}
private void smoothScrollDeferred(final int scrollByF,
final ListView viewF) {
final Handler h = new Handler();
h.post(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
viewF.smoothScrollBy(scrollByF, 200);
}
});
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
});
The reason I defer the smooth scrolling is because in my testing, directly calling the smoothScrollBy method in the state changed callback had problems actually scrolling. Also, I don't foresee a fully-tested, robust solution holding very much state, and in my solution below, I hold no state at all. This solution is not yet in the Google Play Store, but should serve as a good starting point.
Using #nininho 's solution,
In the onScrollStateChanged when the state changes to SCROLL_STATE_IDLE, remember the position to snap and raise a flag:
snapTo = view.getFirstVisiblePosition();
shouldSnap = true;
Then, override the computeScroll() method:
#Override
public void computeScroll() {
super.computeScroll();
if(shouldSnap){
this.smoothScrollToPositionFromTop(snapTo, 0);
shouldSnap = false;
}
}
You can do a much more smooth scrolling if you use RecyclerView. The OnScrollListener is way better.
I have made an example here: https://github.com/plattysoft/SnappingList
Well.. I know 10 years have past since this question was asked, but now we can use LinearSnapHelper:
new LinearSnapHelper().attachToRecyclerView(recyclerView);
Source:
https://proandroiddev.com/android-recyclerview-snaphelper-19eaa9598da6
Apart from trying the code above one thing you should make sure of is that your listView have a height that can fit exact number of items you want to be displayed.
e.g
If you want 4 items to be displayed after snap effect and your row height (defined in its layout) should be 1/4 of the total height of the list.
Note that after the smoothScrollBy() call, getFirstVisiblePosition() may point to the list item ABOVE the topmost one in the listview. This is especially true when view.getChildAt(0).getBottom() == 0. I had to call view.setSelection(view.getFirstVisiblePosition() + 1) to remedy this odd behavior.

Categories

Resources