I'd like to use ImageViewZoom with Universal Image Loader in ImagePagerActivity.
I am able to zoom the image, Swipe to the next image. But i am facing problem when image is in Zoom position.
if an image is zoomed and i am at center position, if i want to see the right side part of the same image and swipe left it is going to the next image. Please help me in doing this.
here is my XML code:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="1dip" >
<it.sephiroth.android.library.imagezoom.ImageViewTouch
android:layout_weight="1"
android:id="#+id/image"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter" />
<ProgressBar
android:id="#+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
EDIT:
Thank you NOSTRA, Now i am able to control the zoom and slide with the below code. But the problem with Multi touch zoom. I am not able to zoom with two fingers.
Here is my previous code(working fine with two finger):
public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale( ScaleGestureDetector detector ) {
Log.d( LOG_TAG, "onScale" );
float span = detector.getCurrentSpan() - detector.getPreviousSpan();
float targetScale = mCurrentScaleFactor * detector.getScaleFactor();
if ( mScaleEnabled ) {
targetScale = Math.min( getMaxZoom(), Math.max( targetScale, getMinZoom()-0.1f ) );
zoomTo( targetScale, detector.getFocusX(), detector.getFocusY() );
mCurrentScaleFactor = Math.min( getMaxZoom(), Math.max( targetScale, getMinZoom()-1.0f ) );
mDoubleTapDirection = 1;
invalidate();
return true;
}
return false;
}
}
And here is the Modified one:
public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
externalScaleListener.onScaleBegin();
return super.onScaleBegin(detector);
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
externalScaleListener.onScaleEnd(mCurrentScaleFactor);
}
}
First you must be able to listen scaling of image. ImageViewTouch doesn't support this feature (e.g gesture-imageview support it) so you should implement it yourself.
Change ImageViewTouch.java like this:
public class ImageViewTouch extends ImageViewTouchBase {
...
private OnPageScaleListener externalScaleListener;
public void setOnScaleListener(OnPageScaleListener onScaleListener) {
this.externalScaleListener = onScaleListener;
}
public interface OnPageScaleListener {
void onScaleBegin();
void onScaleEnd(float scale);
}
#Override
protected void onZoom(float scale) {
super.onZoom(scale);
if (!mScaleDetector.isInProgress()) {
mCurrentScaleFactor = scale;
externalScaleListener.onScaleEnd(scale);
}
}
public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
externalScaleListener.onScaleBegin();
return super.onScaleBegin(detector);
}
...
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
externalScaleListener.onScaleEnd(mCurrentScaleFactor);
}
}
...
}
Then use next implementation of ViewPager in your application:
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* {#link ViewPager} which can be deactivated (ignore touch events)
*
* #author Sergey Tarasevich
* #created 19.07.2012
*/
public class DeactivableViewPager extends ViewPager {
private boolean activated = true;
public DeactivableViewPager(Context context) {
super(context);
}
public DeactivableViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void activate() {
activated = true;
}
public void deactivate() {
activated = false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (activated) {
try {
return super.onInterceptTouchEvent(event);
} catch (Exception e) { // sometimes happens
return true;
}
} else {
return false;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
try {
return super.onTouchEvent(event);
} catch (Exception e) {
return true;
}
}
}
Then in your ViewPager's adapter you should set next scale listener for your ImageViewTouch:
ImageViewTouch imageView = ...;
imageView.setOnScaleListener(new OnPageScaleListener() {
#Override
public void onScaleBegin() {
viewPager.deactivate();
}
#Override
public void onScaleEnd(float scale) {
if (scale > 1.0) {
viewPager.deactivate();
} else {
viewPager.activate();
}
}
});
imageLoader.displayImage(url, imageView, ...);
And don't forget set big values for maxImage***ForMemoryCache in ImageLoader's configuration so UIL won't downscale original images during decoding to Bitmaps:
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
...
.memoryCacheExtraOptions(3000, 3000)
...
.build();
I was having trouble with pre-zoomed images in viewPager, but now its alright and solved the problem.
You have to search the ImageViewTouchBase class for this line:
protected DisplayType mScaleType = DisplayType.NONE;
then change it to:
protected DisplayType mScaleType = DisplayType.FIT_TO_SCREEN;
By default, DisplayType is set to NONE, its simple, just use FIT_TO_SCREEN and your images will be in the default size when sliding in viewPager.
Cheers
You can set those properties to make image fit to screen always
<it.sephiroth.android.library.imagezoom.ImageViewTouch
android:id="#+id/iv_pager_image"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:adjustViewBounds="true"
android:contentDescription="#string/descr_image"
android:scaleType="matrix" />
Related
I have a class that simply displays an image. And I want to implement zoom in/out effect by pinch, so I refer to the code here.
public class FullScreenImageActivity extends AppCompatActivity {
public static final String IMAGE = "IMAGE";
public static Bitmap image;
private float mScaleFactor = 1.0f;
private ScaleGestureDetector mScaleGestureDetector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_full_screen_image);
ConstraintLayout fullscreen_background = findViewById(R.id.fullscreen_background);
fullscreen_background.setOnClickListener(v -> finish());
ImageView fullscreen_image = findViewById(R.id.fullscreen_image);
mScaleGestureDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.OnScaleGestureListener() {
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
Log.d("fuck", "onScaleEnd");
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
Log.d("fuck", "onScaleBegin");
return true;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
Log.d("fuck", "onScale");
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor * detector.getScaleFactor(), 10.0f));
fullscreen_image.setScaleX(mScaleFactor);
fullscreen_image.setScaleY(mScaleFactor);
return true;
}
});
if (image == null) Helper.LoadImage(Glide.with(this), getIntent().getStringExtra(IMAGE), fullscreen_image);
else fullscreen_image.setImageBitmap(image);
}
#Override
public boolean onTouchEvent(MotionEvent motionEvent) {
Log.d("fuck", "onTouchEvent");
return mScaleGestureDetector.onTouchEvent(motionEvent);
}
#Override
public void finish() {
super.finish();
image = null;
}
}
But my code doesn't seem to work. None of the logs is printed out. Any ideas?
I also have another question, how can I finish this activity only when not tapping on the image?
This is the layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/fullscreen_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black">
<ImageView
android:id="#+id/fullscreen_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerInside"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/food_image_place_holder" />
</android.support.constraint.ConstraintLayout>
My project is here.
Try using this custom image view i have created to handle pinch and drag operaions on image.
ZoomDragImageView
link check this news app, i want to develop like this.
i want swipe up/down side effect..
i tried this code,but not correctly swiping as i want.
in this code when i swipe up/down,only the text is changing not a layout.
public class ArticlesActivity extends Activity implements GestureDetector.OnGestureListener{
ImageView image,imageArticle;
TextView tv1,tv2,tv3;
private GestureDetector gd;
LinearLayout layout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_articles);
//Creating GestureDetector Object
gd = new GestureDetector(this, this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
//Registering TouchEvent with GestureDetector
return gd.onTouchEvent(event);
}
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
//Destroying Activity
finish();
}
#Override
public boolean onDown(MotionEvent arg0) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// TODO Auto-generated method stub
//Defining Sensitivity
float sensitivity = 50;
//Swipe Up Check
if(e1.getY() - e2.getY() > sensitivity){
//Setting Image Resource to Up_Arrow on Swipe Up
tv1.setText("Some Text");
tv2.setText("Some Text");
tv3.setText("Some Text");
image.setImageResource(R.drawable.logo);
imageArticle.setImageResource(R.drawable.ic_launcher);
return true;
}
//Swipe Down Check
else if(e2.getY() - e1.getY() > sensitivity){
//Setting Image Resource to Down_Arrow on Swipe Down
tv1.setText("Some Text");
tv2.setText("Some Text");
tv3.setText("Some Text");
image.setImageResource(R.drawable.ic_launcher);
imageArticle.setImageResource(R.drawable.logo);
return true;
}
else{
//If some error occurrs, setting again to Default_Image (Actually it will never happen in this case)
image.setImageResource(R.drawable.logo);
return true;
}
}
#Override
public void onLongPress(MotionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
return false;
}
#Override
public void onShowPress(MotionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public boolean onSingleTapUp(MotionEvent arg0) {
// TODO Auto-generated method stub
return false;
}
}
This will work like inshorts app
main.xml
<com.cardviewanimation.VerticalViewPager
android:id="#+id/verticleViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:id="#+id/card_view"
android:layout_gravity="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
card_view:cardCornerRadius="2dp"
card_view:contentPadding="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:src="#drawable/background"
android:scaleType="fitXY"/>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
VerticleViewPagerActivity.java
public class VerticleViewPagerActivity extends AppCompatActivity {
VerticalViewPager verticalViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
verticalViewPager = (VerticalViewPager) findViewById(R.id.verticleViewPager);
verticalViewPager.setAdapter(new VerticlePagerAdapter(this));
}
}
VerticlePagerAdapter.java
public class VerticlePagerAdapter extends PagerAdapter {
String mResources[] = {"To start off lets understand what exactly Android CardView is? Its a new widget for Android, which can be used to display a card sort of a layout in android. As you may know Android material design is inspired from paper and ink concept. Mostly it displays views on top of each other, with shadows. In simple terms, Android CardView is such a view which has all material design properties, most importantly showing shadows according the elevation. The best part about this view is that it extends FrameLayout and it can be displayed on all the platforms of android since it’s available through the Support v7 library. Lets have a look at some of its properties:","To start off lets understand what exactly Android CardView is? Its a new widget for Android, which can be used to display a card sort of a layout in android. As you may know Android material design is inspired from paper and ink concept. Mostly it displays views on top of each other, with shadows. In simple terms, Android CardView is such a view which has all material design properties, most importantly showing shadows according the elevation. The best part about this view is that it extends FrameLayout and it can be displayed on all the platforms of android since it’s available through the Support v7 library. Lets have a look at some of its properties:"};
Context mContext;
LayoutInflater mLayoutInflater;
public VerticlePagerAdapter(Context context) {
mContext = context;
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mResources.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((LinearLayout) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.content_main, container, false);
TextView label = (TextView) itemView.findViewById(R.id.textView);
label.setText(mResources[position]);
container.addView(itemView);
return itemView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
VerticalViewPager.java
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));
}
}
I got the solution for your problem as inshorts app using vertical view pager. I am sharing some code that can serve your purpose.
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
this(context, null);
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
setPageTransformer(false, new DefaultTransformer());
}
private MotionEvent swapTouchEvent(MotionEvent event) {
float width = getWidth();
float height = getHeight();
float swappedX = (event.getY() / height) * width;
float swappedY = (event.getX() / width) * height;
event.setLocation(swappedX, swappedY);
return event;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercept = super.onInterceptTouchEvent(swapTouchEvent(event));
//If not intercept, touch event should not be swapped.
swapTouchEvent(event);
return intercept;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapTouchEvent(ev));
}
}
As you can see the class above uses the DefaultTransformer class for transformation which implement ViewPager.PageTransformer for custom animation this class code is given below
public class DefaultTransformer implements ViewPager.PageTransformer {
#Override
public void transformPage(View view, float position) {
float alpha = 0;
if (0 <= position && position <= 1) {
alpha = 1 - position;
} else if (-1 < position && position < 0) {
alpha = position + 1;
}
System.out.println("alpha--" + alpha);
view.setAlpha(alpha);
System.out.println("position--" + position);
System.out.println("view.getWidth()--" + view.getWidth());
view.setTranslationX(view.getWidth() * -position);
float yPosition = position * view.getHeight();
System.out.println("yPosition---"+yPosition);
view.setTranslationY(yPosition);
}
}
and my activity code is like this
public class SwipeUpActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_swipe_layout);
setTitle("");
initViewPager();
}
private void initViewPager() {
VerticalViewPager viewPager = (VerticalViewPager) findViewById(R.id.vertical_viewpager);
//viewPager.setPageTransformer(false, new ZoomOutTransformer());
//viewPager.setPageTransformer(true, new StackTransformer());
String title = "ContentFragment";
viewPager.setAdapter(new ContentFragmentAdapter.Holder(getSupportFragmentManager())
.add(ContentFragment.newInstance(title, 1))
.add(ContentFragment.newInstance(title, 2))
.add(ContentFragment.newInstance(title, 3))
.add(ContentFragment.newInstance(title, 4))
.add(ContentFragment.newInstance(title, 5))
.set());
//If you setting other scroll mode, the scrolled fade is shown from either side of display.
viewPager.setOverScrollMode(View.OVER_SCROLL_NEVER);
}
The layout named activity_swipe_layout is like this
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#A6000000"
android:layout_height="match_parent">
<gmaillogindemo.com.irk.transforms.VerticalViewPager
android:id="#+id/vertical_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
and the result is looking like this:
after swipe up
I've tried to implement swiperefreshlayout in my code but it keeps refreshing the entire pager instead of just refreshing when i'm on the first Fragment i.e the 0th Fragment.
I tried setting refresh to false as shown, but the loader still appears, and doesn't reset when I go back to the 0th Fragment again.
On setting swiperefreshlayout.enable(false), I can't refresh it anywhere.
Can anyone suggest an alternative?
This is my XML file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parentLinearLayout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<lbbdapp.littleblackbook.com.littleblackbook.Support.VerticalViewPager
android:id="#+id/verticalViewPager"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
And this is my activity:
public class FeedActivity extends ActionBarActivity {
PageAdapter mPageAdapter;
ProgressBar mProgressBar;
ProgressDialog prog;
ViewPager pager;
String article_id;
private float x1, x2;
SwipeRefreshLayout mSwipeRefreshLayout;
ArrayList<FragmentObject> mObjectsDataList;
ArrayList<ArticleObject> mArticleDataList=new ArrayList<ArticleObject>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.feed_layout);
mObjectsDataList = new ArrayList<FragmentObject>();
DataFetcherTask myTask = new DataFetcherTask();
myTask.execute();
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
mSwipeRefreshLayout.setColorScheme(android.R.color.holo_blue_bright,
android.R.color.holo_blue_dark,
android.R.color.holo_purple,
android.R.color.darker_gray);
pager=(ViewPager)findViewById(R.id.verticalViewPager);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
if(pager.getCurrentItem()==0) {
Log.wtf("current item is 0", "0");
mSwipeRefreshLayout.setRefreshing(true);
Log.wtf("Swiping", "Refreshing");
(new Handler()).postDelayed(new Runnable() {
#Override
public void run() {
mSwipeRefreshLayout.setRefreshing(false);
DataFetcherTask myTask = new DataFetcherTask();
myTask.execute();
}
}, 3000);
}
else{
mSwipeRefreshLayout.setRefreshing(false);
}
}
});
}
This is my vertical ViewPager:
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));
}
}
Instead of extending SwipeRefesh over ViewGroup use it inside the each Fragment of ViewPager.
Once this is resolved now you need to check scroll down gesture of ViewPager and SwipeRefresh.
public class NEWSwipeRefresh extends SwipeRefreshLayout {
private View mTargetView;
public NEWSwipeRefresh(Context context) {
this(context, null);
}
public NEWSwipeRefresh(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setTarget(View view) {
mTargetView = view;
}
#Override
public boolean canChildScrollUp() {
if (mTargetView instanceof YOURVIEW) {
final StaggeredGridView absListView = (YOURVIEW)mTargetView;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView
.getChildAt(0).getTop() < absListView
.getPaddingTop());
} else {
return super.canChildScrollUp();
}
}
}
Normally swipe refresh returns -1 and it gives swipe gesture to it's parent now I have overridden it here for listview/gridview so that it can check for visible items, if it is on top then it will give swipe to parent or otherwise it will give it to children.
Well, I was trying to figure this out since few days and it's quite simple. Just put the SwipeRefreshLayout as the parent element of ViewPager and just disable the SwipeRefreshLayout on the particular fragment using OnPageChangeListener as follows:
// viewpager change listener
OnPageChangeListener viewPagerPageChangeListener = new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
//for the 0th pos disable SwipeRefreshLayout;
if(position != 0){
swipeRefreshLayout.setEnabled(false);
}else swipeRefreshLayout.setEnabled(true);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}};
I am working on multi finger gestures, I tried using Google gesture builder but it will not support for multi finger gestures, How to recognize two finger V shape gesture in android.?
I'm sure you can use a ScaleGestureDetector for this.
After all a "V" from the top is just a pinch with some translation on the Y axis.
So I think you can analyse the focus point and scale factor to determine a "V" has taken place.
Here is a working example. In the end I didn't need to look at the scale. There are a couple of sensitivity values you can adjust such as the range of acceptable angles and the ratio of Y to X movement.
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.TextView;
public class MainActivity extends Activity {
public static class VListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
private float initialFocusX;
private float initialFocusY;
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
Log.d(TAG, String.format("%.2f,%.2f s:%.2f", detector.getFocusX(),
detector.getFocusY(), detector.getScaleFactor()));
initialFocusX = detector.getFocusX();
initialFocusY = detector.getFocusY();
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
float deltaX = detector.getFocusX() - initialFocusX;
float deltaY = detector.getFocusY() - initialFocusY;
if (deltaY == 0) {
Log.d(TAG, "Not a V, no Y movement");
onNonV();
return;
}
float yMovementRatio = Math.abs(deltaY / deltaX);
if (yMovementRatio < 4) {
Log.d(TAG,
String.format(
"Not a V, the ratio of Y movement to X was not high enough: %.2f",
yMovementRatio));
onNonV();
return;
}
float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
if (angle > 80 && angle < 100) {
Log.d(TAG, "V!");
onV();
return;
} else {
Log.d(TAG,
String.format(
"Not a V, the angle shows the V was drawn in the wrong direction: %.2f",
angle));
onNonV();
}
}
protected void onV() {
}
protected void onNonV() {
}
}
protected static final String TAG = "MainActivity";
private ScaleGestureDetector mScaleGestureDetector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView t = (TextView) findViewById(R.id.vTextIndicator);
mScaleGestureDetector = new ScaleGestureDetector(this, new VListener() {
#Override
protected void onV() {
t.setText("V!");
}
#Override
protected void onNonV() {
t.setText("Non V");
}
});
}
public boolean onTouchEvent(MotionEvent event) {
boolean retVal = mScaleGestureDetector.onTouchEvent(event);
return retVal || super.onTouchEvent(event);
}
}
activity_main.xml Layout is just:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/vTextIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
Sample app - InteractiveChart.zip
This links might help - Draggin & Scaling and Handling Multi-Touch Gestures
I have a fairly simple question which somehow I can't figure out. I'm using a FrameLayout with an own view (onDrawn is overriden) and another transparent view which extends LinearLayout. I want to add scrolling for the transparent view, but if I use ScrollView in the XML a Classcast exception is thrown.
My alternative was to implement scrolling on my own (e.g. with scrollTo in LinearLayout, where I can't find any example using that method), but the OnGestureListener doesn't trigger onScroll, while onShowPress and onLongPress are triggered. I then tried to use onTouchEvent in the LinearLayout, but it only recognises ACTION_DOWN, not ACTION_MOVE. In my own view all that works perfectly.
Here the XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/home_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.unimelb.pt2.ui.WaterfallView
android:id="#+id/waterfall_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:apiKey="0DUEIIn35xtmfWC2DXprK5kqNF-aEaNgRJ4ONxw"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="bottom"
android:paddingLeft="0px"
android:paddingTop="0px"
android:paddingRight="0px">
<com.unimelb.pt2.ui.TransparentPanel
android:id="#+id/workbench"
android:layout_width="fill_parent"
android:layout_height="10px"
android:paddingTop="0px"
android:paddingLeft="0px"
android:paddingBottom="0px"
android:paddingRight="0px">
</com.unimelb.pt2.ui.TransparentPanel>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:gravity="right"
android:paddingLeft="0px"
android:paddingTop="0px"
android:paddingRight="0px">
<com.unimelb.pt2.ui.TransparentPanel
android:id="#+id/tagarea"
android:layout_width="50px"
android:layout_height="fill_parent"
android:paddingTop="0px"
android:paddingLeft="0px"
android:paddingBottom="0px"
android:paddingRight="0px">
</com.unimelb.pt2.ui.TransparentPanel>
</LinearLayout>
</FrameLayout>
Here the basic construct of the WaterfallView:
public class WaterfallView extends View {
private GestureDetector gestureScanner;
private Vector<PictureEntry> allPictures = new Vector<PictureEntry>();
public WaterfallView(Context context) {
super(context);
this.initialize(context);
}
public void initialize(Context context) {
this.setFocusable(true);
this.setClickable(true);
this.context = context;
allPictures.add(new PictureEntry(context, R.drawable.sample_0));
allPictures.add(new PictureEntry(context, R.drawable.sample_1));
allPictures.add(new PictureEntry(context, R.drawable.sample_2));
allPictures.add(new PictureEntry(context, R.drawable.sample_3));
allPictures.add(new PictureEntry(context, R.drawable.sample_4));
allPictures.add(new PictureEntry(context, R.drawable.sample_5));
allPictures.add(new PictureEntry(context, R.drawable.sample_6));
allPictures.add(new PictureEntry(context, R.drawable.sample_7));
}
public void setGestureDetector(GlassPane gp) {
gestureScanner = new GestureDetector(context, gp);
}
#Override
protected void onDraw(Canvas canvas) {
Iterator<PictureEntry> iter = allPictures.iterator();
int i = 0;
while (iter.hasNext()) {
PictureEntry pic = iter.next();
pic.draw(canvas)
}
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureScanner.onTouchEvent(event)) {
return Prototype.glass.pictureTouch(event);
} else return false;
}
}
Here the basic construct of the GlassPane:
public class GlassPane implements OnGestureListener {
public GlassPane(WaterfallView waterfall) {
super();
waterfall.setGestureDetector(this);
}
public boolean pictureTouch(MotionEvent event) {
// Handles drag and drop and zoom pinch
}
public boolean onDown(MotionEvent e) {
Log.i("Test", "DOWN");
return false;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
Log.i("Test", "FLING");
return false;
}
#Override
public void onLongPress(MotionEvent e) {
Log.i("Test", "LONG PRESS");
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
Log.i("Test", "SCROLL");
return true;
}
#Override
public void onShowPress(MotionEvent e) {
Log.i("Test", "SHOW PRESS");
}
}
And here the construct of the TransparentPanel:
public class TransparentPanel extends LinearLayout {
private Paint innerPaint, borderPaint;
private int width, height, scrollOffset;
private Context mContext;
public TransparentPanel(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
public TransparentPanel(Context context) {
super(context);
init();
}
private void init() {
innerPaint = new Paint();
innerPaint.setARGB(225, 75, 75, 75); // gray
innerPaint.setAntiAlias(true);
}
public void setDimension(int w, int h) {
width = w; height = h;
this.setLayoutParams(new LayoutParams(width, height));
this.invalidate();
}
#Override
protected void dispatchDraw(Canvas canvas) {
RectF drawRect = new RectF();
drawRect.set(0, 0, width, height);
canvas.drawRect(drawRect, innerPaint);
super.dispatchDraw(canvas);
}
private void measure() {
if(this.getOrientation()==LinearLayout.VERTICAL) {
int h = 0;
for(int i=0; i<this.getChildCount(); i++) {
View v = this.getChildAt(i);
h += v.getMeasuredHeight();
}
height = (h < height) ? height : h;
Log.d(Prototype.TAG, "mW:"+width+", mH:"+height);
}
this.setMeasuredDimension(width, height);
}
}
Okay, I think I finally figured everything out:
The ClassCastException was thrown because in my TransparentPanel I try to assign LayoutParams to the panel without stating which kind of LayoutParams. I thought it should be LinearLayout.LayoutParams but in fact I need to assign the LayoutParams of the ViewGroup in which I place the View, i.e. RelativeLayout in my case.
My GlassPanel is placed better on the bottom of the FrameLayout rather than on the top. The MotionEvents are than passed down from top to bottom as expected. I just start with whatever is lying on top and if the event is not handled by that layer I return false and pass down the event to the next layer rather than having a real GlassPane on top.
In order to handle the event in the GlassPane on top of the FrameLayout I just need to override the onTouchEvent method in all the Views that use the GlassPane as EventListener. Just like in WaterfallView in the above code. But careful MotionEvent.getX() and MotionEvent.getY() return values relative to that View and not absolute. Solution (2) works perfect relatively to the GlassPane.