I am building an android application similar to inshorts. I wanna to slide my cardView up and down. What I am doing yet - I am parsing the data from server using webAPI then using BaseAdapter displaying in cardView. But now I need on swipe up it should move up and next position card should be displayed .
You can do this with ViewPager and Fragments then you need to put slide animation with view pager. For horizontal slide you can see developers reference http://developer.android.com/training/animation/screen-slide.html.
After that you need to modify view pager slightly so that it behave like vertical instead of horizontal and you can achieve this with below code:
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.75f;
#Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 1) { // [0,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame for any child views
return intercepted;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
One way you can do this is by using fragments and ViewPager to load the pages, and you can specify Property Animation to animate the fragments.
If you are new to fragments, try beginning from here
And then you need ViewPager to swipe between pages. here
Property Animation is often used to animate native fragments(Android.App.Fragment) . see this
and see this to swap fragments using Animation
Related
I'm using VerticalViewPager for swiping vertically through images. The vertical view pager is strict when it comes to swiping angle, which means it will proceed with swiping only if it is a perfectly vertical swipe (no change in x coordinate).
But I have observed in the Inshorts app (Inshorts), they allow swiping up even if the swipe is not perfectly vertical. This makes the swiping smoother as users might not always be doing a 100% perfect swipe.
How can we achieve this?
You can use ViewPager.PageTransformer.
A PageTransformer is invoked whenever a visible/attached page is
scrolled. This offers an opportunity for the application to apply a
custom transformation to the page views using animation properties.
private class VerticalPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.75f;
#Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 1) { // [0,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
You can use VerticalViewPager
I'm writing a html page reader, cant load all .html because its huge and performance is bad, so I decided to split it into 3(or more) html and load that inside Web View nested with View Pager (swipe vertically)
My problem is that I should swipe slowly to scroll the web view and swipe fast to change view pager
Slow swiping
fast swipe change the Page on ViewPager
1) Can i expand webview to all its content heigh inside viewpager ?
2) Can change viewpager item only when topScroll or endScroll?
what i've tried so far:
CustomWebView
#Override
public boolean onTouchEvent(MotionEvent event) {
requestDisallowInterceptTouchEvent(true);
return super.onTouchEvent(event);
}
worked but was unable to change viewpager item
so i though about enable/disable it when i get to the top or to the end
removed webview onTouchEvent and added:
#Override
protected void onScrollChanged(final int l, final int t, final int oldl, final int oldt) {
requestDisallowInterceptTouchEvent(true);
int height = (int) Math.floor(this.getContentHeight() * this.getScale());
int webViewHeight = this.getMeasuredHeight();
boolean scrollTop = this.getTop() == t;
boolean scrollEnd = this.getScrollY() + webViewHeight >= height;
if(scrollTop || scrollEnd) {
requestDisallowInterceptTouchEvent(false);
}
worked randomly, the most common thing is that when i change page, i must scroll down and scroll up to trigger the requestDisallow to false so i can change page =[
Vertical CustomViewPage is this one ->
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
#Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame for any child views
return intercepted;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
1st i think it's a bad idea to split your content into 3 different HTML files and putting them on 3 different WebViews.
3 fils can be a good idea but 3 WebView in 3 Views of ViewPager is a bad idea.
Instead, you should detect top and bottom of the webpage using ".js" / "jQuery" and route page/HTML within same WebView.
window.onscroll = function(ev) {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
// you're at the bottom of the page
}};
There are many ways to detect TOP and BOTTOM of your HTML content.
Still, if you want to increase the height of your review to content height use
Getting WebView Content height once its loaded Android it worked for me.
I am designing tab view like feature, but i need one more functionality.
Suppose i have three tabs,
And if press tab2 - Content of second tab will show
tab3 -- Content of Third will show
tab1 - content of tab1 will show
and by default tab1 will be selected. and this is working fine.
Now, i need when i am scrooling the content of tab1, i need to show cotent of second tab too ( But second tab should be selected).. Just like single page application on web..
I don't need Web view. Anybody guide me how to achieve this, or if there is any sample code available on github. Please
Thanks
#AnkitaKashyap sorry mate, I kept forgot, here is your snippet code (in kotlin) if you need help, pls send me a private msg,if i got your idea correctly, here is what you want gif demo:
using a tabbar with recyclerview and set addOnTabSelectedListener:
productTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener{
override fun onTabReselected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabSelected(tab: TabLayout.Tab?) {
productRv.scrollToPosition(tab?.position?:0)
}
})
and set in your recyclerview :
productRv.addOnScrollListener(object : RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
var scrollPosition = layoutManager.findFirstVisibleItemPosition()
productTabs.getTabAt(scrollPosition)?.select()
}
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
}
})
You need to go with VerticalViewPager as shown in this StackOverflow post and use TabLayout along with it.
Code from the given SO link:
/**
* Uses a combination of a PageTransformer and swapping X & Y coordinates
* of touch events to create the illusion of a vertically scrolling ViewPager.
*
* Requires API 11+
*
*/
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
#Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame for any child views
return intercepted;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
Using something similar to the answer of this question, I've tried to disable the ViewPager swipe action for when the user is swiping over a particular item. The view in question is a scrollable chart from the MPAndroidChart library, so naturally I don't want the view pager interfering with the scrolling of the chart.
The issue I am having is that the ViewPager will often have "onInterceptTouch" invoked before the onTouchListener is invoked on my desired view.
In this segment of code, I'm recording when the view is pressed/unpressed:
private long lastDown;
private long lastUp;
...
public void foo(){
barChart.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("AAA");
if(event.getAction() == MotionEvent.ACTION_DOWN){
lastDown = System.currentTimeMillis();
}else if(event.getAction() == MotionEvent.ACTION_UP){
lastUp = System.currentTimeMillis();
}
return false;
}
});
}
In this segment of code, I determine if the view is selected:
public boolean isGraphTouched(){
return lastDown > lastUp;
}
And in this segment of code I'm overriding the onInterceptTouchEvent method:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
System.out.println("BBB");
return isGraphSelected() ? super.onInterceptTouchEvent(ev) : false;
}
And if you take note of the printlines, the onInterceptTouchEvent method is called before...
The only way I can think of getting around this is to make a method which checks if the graph exists at the coordinates of the motion event (although I'm not sure if this is even possible) and then use that to determine if the pager will be swipable or not.
I managed to make it work using the function parent.requestDisallowInterceptTouchEvent(true); being placed inside the child view onTouchEvent(). This way the View does not allow none of his parents to interecpt his touch events in case a scroll happened and was to be handled by the ViewPager.
However in my case I had a ViewPager with a draggable custom views inside it which I wanted to move without that the ViewPager changes page.
My solution in terms of code: (Kotlin)
view.setOnTouchListener { v, event ->
parent.requestDisallowInterceptTouchEvent(true);
//Drag and drop handling is here and the rest of the event logic
}
I hope this will help you as well.
I've managed to solve my problem by incorporating this answer from another post. The code allows you to get the coordinates of a given view and compare them to your own coordinates.
Inside of my CustomViewPager class, I implemented what I mentioned above:
private boolean isPointInsideView(float x, float y, View view) {
int location[] = new int[2];
view.getLocationOnScreen(location);
int viewX = location[0];
int viewY = location[1];
return ((x > viewX && x < (viewX + view.getWidth())) && (y > viewY && y < (viewY + view.getHeight())));
}
I then had a method which returns a boolean, which checks a couple of conditions and returns true/false on if the pager should be able to be swiped:
public boolean canSwipe(float x, float y) {
boolean canSwipe = true;
if (launchActivity.isReady(MainScreenPagerAdapter.STAT_PAGE)) {
FragmentStatistics fragmentStatistics = (FragmentStatistics) launchActivity.getPageAdapter().instantiateItem(this, MainScreenPagerAdapter.STAT_PAGE);
View chart = fragmentStatistics.getView().findViewById(R.id.chart);
canSwipe = !isPointInsideView(x, y, chart) || !fragmentStatistics.isGraphPannable();
}
return canSwipe;
}
And then, of course, I overwrote the onInterceptTouchEvent like so:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return canSwipe(ev.getX(), ev.getY()) ? super.onInterceptTouchEvent(ev) : false;
}
And now, the graph can be fully panned without the Pager interfering with it at all.
Full CustomViewPager code:
public class CustomViewPager extends ViewPager {
/** Reference to the launch activity */
private LaunchActivity launchActivity;
/**
* Constructor to call the super constructor
*
* #param context The application context
* #param attrs The attributes
*/
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Sets object reference for the {#code launchActivity}
*
* #param launchActivity The LaunchActivity to be set
*/
public void set(LaunchActivity launchActivity) {
this.launchActivity = launchActivity;
}
/**
* Determines if the pager can be swiped based off the x and y inputs provided, as well as if the
* barchart can be panned or not.
*
* #param x The x coordinate to check
* #param y The y coordinate to check
* #return True if the ViewPager will continue with normal swiping action.
*/
public boolean canSwipe(float x, float y) {
boolean canSwipe = true;
if (launchActivity.isReady(MainScreenPagerAdapter.STAT_PAGE)) {
FragmentStatistics fragmentStatistics = (FragmentStatistics) launchActivity.getPageAdapter().instantiateItem(this, MainScreenPagerAdapter.STAT_PAGE);
View chart = fragmentStatistics.getView().findViewById(R.id.chart);
canSwipe = !isPointInsideView(x, y, chart) || !fragmentStatistics.isGraphPannable();
}
return canSwipe;
}
/**
* Takes x and y coordinates and compares them to the coordinates of the passed view. Returns true if the passed coordinates
* are within the range of the {#code view}
*
* #param x The x coordinate to compare
* #param y The y coordinate to compare
* #param view The view to check the coordinates of
* #return True if the x and y coordinates match that of the view
*/
private boolean isPointInsideView(float x, float y, View view) {
int location[] = new int[2];
view.getLocationOnScreen(location);
int viewX = location[0];
int viewY = location[1];
// point is inside view bounds
return ((x > viewX && x < (viewX + view.getWidth())) && (y > viewY && y < (viewY + view.getHeight())));
}
/**
* Override of the onInterceptTouchEvent which allows swiping to be disabled when chart is selected
*
* #param ev The MotionEvent object
* #return Call to super if true, otherwise returns false
*/
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return canSwipe(ev.getX(), ev.getY()) ? super.onInterceptTouchEvent(ev) : false;
}
}
I use slidingMenu(from jfeinstein10) in my app, I have created a new Activity which extends from SlidingFragmentActivity, in Activity I use ViewPager from support.v4 to display some fragment, using the way gtRfnkN answered in Question ViewPager inside ViewPager
public class GalleryViewPager extends ViewPager {
/** the last x position */
private float lastX;
/** if the first swipe was from left to right (->), dont listen to swipes from the right */
private boolean slidingLeft;
/** if the first swipe was from right to left (<-), dont listen to swipes from the left */
private boolean slidingRight;
public GalleryViewPager(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public GalleryViewPager(final Context context) {
super(context);
}
#Override
public boolean onTouchEvent(final MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow parent ViewPager to intercept touch events.
this.getParent().requestDisallowInterceptTouchEvent(true);
// save the current x position
this.lastX = ev.getX();
break;
case MotionEvent.ACTION_UP:
// Allow parent ViewPager to intercept touch events.
this.getParent().requestDisallowInterceptTouchEvent(false);
// save the current x position
this.lastX = ev.getX();
// reset swipe actions
this.slidingLeft = false;
this.slidingRight = false;
break;
case MotionEvent.ACTION_MOVE:
/*
* if this is the first item, scrolling from left to
* right should navigate in the surrounding ViewPager
*/
if (this.getCurrentItem() == 0) {
// swiping from left to right (->)?
if (this.lastX <= ev.getX() && !this.slidingRight) {
// make the parent touch interception active -> parent pager can swipe
this.getParent().requestDisallowInterceptTouchEvent(false);
} else {
/*
* if the first swipe was from right to left, dont listen to swipes
* from left to right. this fixes glitches where the user first swipes
* right, then left and the scrolling state gets reset
*/
this.slidingRight = true;
// save the current x position
this.lastX = ev.getX();
this.getParent().requestDisallowInterceptTouchEvent(true);
}
} else
/*
* if this is the last item, scrolling from right to
* left should navigate in the surrounding ViewPager
*/
if (this.getCurrentItem() == this.getAdapter().getCount() - 1) {
// swiping from right to left (<-)?
if (this.lastX >= ev.getX() && !this.slidingLeft) {
// make the parent touch interception active -> parent pager can swipe
this.getParent().requestDisallowInterceptTouchEvent(false);
} else {
/*
* if the first swipe was from left to right, dont listen to swipes
* from right to left. this fixes glitches where the user first swipes
* left, then right and the scrolling state gets reset
*/
this.slidingLeft = true;
// save the current x position
this.lastX = ev.getX();
this.getParent().requestDisallowInterceptTouchEvent(true);
}
}
break;
}
super.onTouchEvent(ev);
return true;
}
}
But when i put a ListView in ViewPager's Fragment, when i moves the ListView horizontally, the ViewPager keeps unmoved and the slidingmenu slides out.Can you tell me how to slove this? thanks a lot.
Try THis Demo for Sliding with listview, map... hope this will help u
:
https://github.com/jfeinstein10/SlidingMenu