I would like to smoothly animate some views Y position based on the user scrolling a bottom sheet.
http://imgur.com/dVBlh83
However the animation is slightly jerky.
This is probably because scrolling a bottomsheet gives me a slideOffset at some interval, which is too infrequent to setTranlationY like I'm currently doing.
Is there a better way to do this.
Bottom sheet callback
BottomSheetBehavior.BottomSheetCallback() {
#Override public void onSlide(#NonNull View bottomSheetView, float slideOffset) {
int offset = (int) (slideOffset * 100);
if (offset > 0) {
scrollingHeaderView.animate(offset);
}
}
});
scrollingHeaderView
public void animate(int offset) {
position = -(offset / 2);
arrow.setTranslationY(offset * ANIMATION_RATIO_ARROW);
detailView.setTranslationY(offset * ANIMATION_RATIO);
}
I have tried using ObjectAnimator to create lots of small animations at each step.
ObjectAnimator.ofFloat(arrow, "translationY", arrow.getTranslationY(), position * ANIMATION_RATIO_ARROW).start();
in case it helps anyone else. I got it looking much better.
First i set the ration by pixel * range of values i'd get. So offset will be 0 -> 100. Mutliplied by the dp i want the view to move
private static int ANIMATION_RANGE = 100;
arrowScale = -(getResources().getDimensionPixelOffset(R.dimen.arrow_distance) / ANIMATION_RANGE);
containerScale = -(getResources().getDimensionPixelOffset(R.dimen.detail_distance) / ANIMATION_RANGE);
then as the value changes i set this using translationY
arrow.setTranslationY(position * arrowScale);
detailView.setTranslationY(position * containerScale);
I also needed a reset method, which animate back to the original position
public void reset() {
arrow.animate().setDuration(100).translationY(0);
detailView.animate().setDuration(100).translationY(0);
}
Related
I need to set up a ViewPager with the following requirements:
- at any given time, 3 pages must be visible in the screen
- the center page isn't scaled and the behind pages are 60% of the center page;
- center page, must overlap a bit the behind pages.
From what I saw in other questions I've tried to do this:
viewPager.setClipToPadding(false);
int padding = (metrics.widthPixels - centerViewSize) / 2;
viewPager.setPadding(padding, 0, padding, 0);
viewPager.setPageMargin(-(padding / 2));
viewPager.setCurrentItem(10);
viewPager.setOffscreenPageLimit(3);
Doing this got me here:
This is what I was expecting. Just need to bring to front the current selected item.
I've then tried to scale the view with:
public static final float SCALED_SIZE = 0.6f;
viewPager.setPageTransformer(false, new PageTransformer() {
#Override
public void transformPage(final View page, final float position) {
if (position < -1) {
page.setScaleX(SCALED_SIZE);
page.setScaleY(SCALED_SIZE);
} else if (position <= 1) {
float scaleFactor = Math.max(SCALED_SIZE, 1 - Math.abs(position));
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
} else {
page.setScaleX(SCALED_SIZE);
page.setScaleY(SCALED_SIZE);
}
}});
But that gives me some problems.
The first item that should be selected is not the correct one.
Animation chopy.
At some points, there are more than 3 pages on the screen.
Scale is from the bottom and I would like to be from the center.
Any idea how I can solve my issues?
use Library UltraViewPager. It have very nice effects.
Actually I don't know how it properly called - overlay, parallax or slideUP, whatever, I have an Activity called "cafe details" which presents a Container(LinearLayout) with header information (name, min price, delivery time min e.t.c) and other container (ViewPager) which contains a ExpandableListView with something information (menus&dishes) and all I want to do is slide up my Viewpager when scrolls listview to scpecific Y position to cover(or overlay) header information.
A similar effect (but with parallax that I don't need to use) looks like this
I can detect when user scrolling listview down or up but how I can move container with ViewPager to overlay other container? Please give me ideas, regards.
UPD
I have tried a huge number of ways how to implement it and all of them unfortunately are not suitable. So now I have come to next variant - add scroll listener to ListView, calculate scrollY position of view and then based on that move the viewpager on y axis by calling setTranslationY();
Here is some code
1) ViewPager's fragment
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView absListView, int i) {
}
#Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
if (getActivity() != null) {
((MainActivity) getActivity()).resizePagerContainer(absListView);
}
}
});
2) MainActivity
//object fields
int previousPos;
private float minTranslation;
private float minHeight;
<--------somewhere in onCreate
minTranslation = - (llVendorDescHeaders.getMeasuredHeight()+llVendorDescNote.getMeasuredHeight());
//llVendorDescHeaders is linearLayout with headers that should be hidden
//lVendorDescNote is a textView on green background;
minHeight = llVendorDescriptionPagerContainer.getMeasuredHeight();
//llVendorDescriptionPagerContainer is a container which contains ViewPager
--------->
public void resizePagerContainer(AbsListView absListView){
final int scrollY = getScrollY(absListView);
if (scrollY != previousPos) {
final float translationY = Math.max(-scrollY, minTranslation);
llVendorDescriptionPagerContainer.setTranslationY(translationY);
previousPos = scrollY;
}
}
private int getScrollY(AbsListView view) {
View child = view.getChildAt(0);
if (child == null) {
return 0;
}
int firstVisiblePosition = view.getFirstVisiblePosition();
int top = child.getTop();
return -top + firstVisiblePosition * child.getHeight() ;
}
This simple solution unfortunately has a problem - it is blinking and twitching (I don't know how to call it right) when scrolls slowly. So instead setTranslationY() I've used an objectAnimator:
public void resizePagerContainer(AbsListView absListView){
.............
ObjectAnimator moveAnim = ObjectAnimator.ofFloat(llVendorDescriptionPagerContainer, "translationY", translationY);
moveAnim.start();
..............
}
I don't like this solution because 1) anyway it does resize viewpager with delay, not instantly 2) I don't think that is good idea to create many ObjectAnimator's objects every time when I scroll my listView.
Need your help and fresh ideas. Regards.
I'm assuming that you are scrolling the top header (the ImageView is a child of the header) based on the scrollY of the ListView/ScrollView, as shown below:
float translationY = Math.max(-scrollY, mMinHeaderTranslation);
mHeader.setTranslationY(translationY);
mTopImage.setTranslationY(-translationY / 3); // For parallax effect
If you want to stick the header/image to a certain dimension and continue the scrolling without moving it anymore, then you can change the value of mMinHeaderTranslation to achieve that effect.
//change this value to increase the dimension of header stuck on the top
int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height);
mHeaderHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
mMinHeaderTranslation = -mHeaderHeight + tabHeight;
The code snippets above are in reference to my demo but I think it's still general enough for you.
If you're interested you can check out my demo
https://github.com/boxme/ParallaxHeaderViewPager
Have you tried CoordinatorLayout from this new android's design support library? It looks like it's what you need. Check this video from 3:40 or this blog post.
I am trying to set my EditTest on specific position in RelativeLayout. So i am getting position of Ediitext and set according what i want. It's working fine with me. But problem is occur when Android 2.2 and android 2.3 devices didn't find getY() method. because i am trying to get the position of Ediitext.
Now i need to find another method for this because it's not working in 2.2 and 2.3.
Here is my code for reference.
ViewTreeObserver autoCompleteObserver = myAutoComplete.getViewTreeObserver();
autoCompleteObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
relativeLayoutHeight = ((int) myAutoComplete.getY()-12);
}
});
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
Log.i("TEST", "GLOBAL LAYOUT "+activityRootView.getHeight());
Log.i("TEST", "GLOBAL LAYOUT rel"+relativeLayoutHeight);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
Log.i("TEsT","Keyboard visible");
myRelativLayout.scrollTo(0, relativeLayoutHeight);
}
else
{
Log.i("TEsT","Keyboard not visible");
myRelativLayout.scrollTo(0, 0);
myAutoComplete.setDropDownHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
});
}
This code working fine in 4.1 but i need to find alternate method for getY() which return me position of EditText.
Give me any reference or hint.
Have a look at the getY() method from View class in android 4.1: Link
/**
* The visual y position of this view, in pixels. This is equivalent to the
* 'translationY' property plus the current
* 'top' property.
*
* #return The visual y position of this view, in pixels.
*/
public float getY() {
return mTop +
(mTransformationInfo != null ? mTransformationInfo.mTranslationY : 0);
}
On Android 2.2 & 2.3, there's a similar method called getTop(): Link
/**
* Top position of this view relative to its parent.
*
* #return The top of this view, in pixels.
*/
public final int getTop() {
return mTop;
}
So, if you are not applying any translations to your EditText (using View#setTranslationY(float) etc.), getY() and getTop() will provide essentially the same result.
Add this code inside your onGlobalLayout
int[] locations = new int[2];
yourView.getLocationOnScreen(locations);
int x = locations[0];
int y = locations[1];//returns Y position
With a positive offset, startScroll would scroll to the right. So with a negative offset, I'm assuming it should scroll to the left—the documentation doesn't say anything about this.
However this does not work as expected. The right scroll doesn't work the same, and the left scroll doesn't work at all.
Here's my code.
#Override
public void setSelection(int position) {
int scrollx = mScroller.getCurrX();
int offsetToScroll = position * childWidth;
if(offsetToScroll > scrollx) {
mScroller.startScroll(scrollx, 0, offsetToScroll, 0);
} else {
mScroller.startScroll(scrollx, 0, -offsetToScroll, 0);
}
requestLayout();
}
Scroller has nothing to do with the UI - it's just a helper class that helps to compute position based on initial position and initial velocity, simulating inertia effect - see computeScrollOffset() & getCurrX()/getCurrY()
am trying to figure out the max scroll position that the WebView can reach, i've tried the webView.pageDown(true) but the result is delayed ( i cant scroll it down, then up infront of the user, and this method doesn't work every time), i've tried also webView.getContentHeight() and the height isn't correct for Arabic content.
Please Advice
ok, i figured out the answer
you can get the real content height using
(int) Math.floor(webView.getContentHeight() * webView.getScale());
when you get the real height, then just override the scroll method in webview to listen to scroll event, if the scroll reach the real height, your webview is in the bottom of the scroll.
#Override
public void onScroll(int l, int t) {
int height = (int) Math.floor(webView.getContentHeight() * webView.getScale());
int webViewHeight = webView.getHeight();
int cutoff = height - webViewHeight - 10; // Don't be too strict on the cutoff point
if (t >= cutoff) {
setDisclaimerButtonEnabled(true);
}
}
The non-strictness is required, is because I found on the Samsung S5 the bottom most scroll value was only 1 pixel value away from the bottom most value!
Loading / Visible button only when webview reached / scrolled to bottom.
Create JavaScript class :
public class JavaScriptInterface {
#android.webkit.JavascriptInterface
public void didScrollToBottom() {
Log.d(TAG, "Scroll to Bottom");
myHandler.post(new Runnable() {
#Override
public void run() {
btnAccept.setVisibility(View.VISIBLE);
}
});
}
}
In onCreate() :
final JavaScriptInterface jsInterface = new JavaScriptInterface();
myWebView.addJavascriptInterface(jsInterface, "AndroidFunction");