I want to show left and right arrows over my ViewPager, to indicate swiping.
I added two ImageButtons over the ViewPager-element but those areas then block the ViewPager from triggering the "swiping".
I also want presses on those arrows to trigger the fragment to change accordingly.
In short: The ImageButtons should not interfere with swiping but they should register pressing.
How can I achieve this?
Thanks!
The code below worked for me perfectly well.
NB: Use FrameLayout as it allows overlapping views
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="wrap_content"
android:layout_height="200dp" />
<ImageButton
android:id="#+id/left_nav"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical|left"
android:src="#drawable/ic_chevron_left_black_24dp" />
<ImageButton
android:id="#+id/right_nav"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical|right"
android:src="#drawable/ic_chevron_right_black_24dp" />
</FrameLayout>
The following part I used to handle ImageButton's click events
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
leftNav = (ImageButton) view.findViewById(R.id.left_nav);
rightNav = (ImageButton) view.findViewById(R.id.right_nav);
// Images left navigation
leftNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
if (tab > 0) {
tab--;
viewPager.setCurrentItem(tab);
} else if (tab == 0) {
viewPager.setCurrentItem(tab);
}
}
});
// Images right navigatin
rightNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
tab++;
viewPager.setCurrentItem(tab);
}
});
Output
No need to manage the current index for this or to handle any swiping manually. Simply call method viewPager.arrowScroll(int direction) method on the click events of left and right arrows.
In a nutshell follow these 2 simple steps:
Implement 2 ImageViews/ImageButtons/Buttons for left and right arrows.
On clicking them, call:
a) if left arrow is clicked - viewPager.arrowScroll(View.FOCUS_LEFT);
b) if right arrow is clicked - viewPager.arrowScroll(View.FOCUS_RIGHT);
Implement left & right arrow buttons in your fragment. Then register their onClick in your activity and call viewpager's arrowScroll method to scroll the viewPager programmatically.
public void onRightClick(View view) {
viewPager.arrowScroll(ViewPager.FOCUS_RIGHT);
}
public void onLeftClick(View view) {
viewPager.arrowScroll(ViewPager.FOCUS_LEFT);
}
Create a method to toggle left/right arrow visibility in your fragment.
public void toggleArrowVisibility(boolean isAtZeroIndex, boolean isAtLastIndex) {
if(isAtZeroIndex)
leftBtn.setVisibility(View.INVISIBLE);
else
leftBtn.setVisibility(View.VISIBLE);
if(isAtLastIndex)
rightBtn.setVisibility(View.INVISIBLE);
else
rightBtn.setVisibility(View.VISIBLE);
}
Now implement ViewPager.OnPageChangeListener in your activity. Use SmartFragmentStatePagerAdapter to keep track of registered fragments in memory.
#Override
public void onPageSelected(int position) {
MyFragment fragment = (MyFragment) smartAdapter.getRegisteredFragment(position);
fragment.toggleArrowVisibility(position == 0, position == list.size() - 1);
}
Instead of using ImageButtons for displaying the arrows, I now use ImageViews because they pass on any touch events to the layer underneath.
Then, I put transparent Buttons on the fragments themselves instead, that way they won't block the ViewPagers swiping behaviour but they will fire onClick Events!
First use relative layout as your parent layout
second then add view pager inside it with match parent attribute on it
third take two image buttons over the view pager but in under the hierarchy of parent layout
give them center vertical as a gravity and keep their side as right and left as per your requirement
fourth write functional code for buttons
fifth take static counter to get current view pager page
on left and right button set minus and plus the view pager counter resp. and according to that show data in view pager
this is the simple logic for code you can search it on google you will easily get it
Related
I am currently struggling to find a good way how to animate some views in a specific way.
Following screenshots should show what I want to achieve:
First state (HIDDEN):
Second state (COLLAPSED)
Third state (EXPANDED)
The change between these states should be animated.
Those views are not draggable or slideable at all.
I know that there is the SlidingUpPanel by umano but I think that would be kind of an overkill.
At the moment the way I achieve this behaviour is the following:
I wrap the 2 panels (top and bot) in a relative layout and use the property animator to animate a change of the height of the relative layout.
So when the state is COLLAPSED then the height of the relative layout will be animated from 0 to the height of the top panel.
This works fine but I think that this is a really bad way to do this.
I already tried out to create a custom ViewGroup but the animating part didnt work yet.
Any input is appreciated.
I would use FrameLayout here as follows:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/screen"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<FrameLayout
android:id="#+id/top_panel"
android:layout_width="match_parent"
android:layout_height="#dimen/top_panel_height"
android:layout_gravity="bottom"/>
<FrameLayout
android:id="#+id/bottom_panel"
android:layout_width="match_parent"
android:layout_height="#dimen/bottom_panel_height"
android:layout_gravity="bottom"/>
</FrameLayout>
Then, create enum for states
enum State {
HIDDEN {
#Override
public void moveTo(View topPanel, View bottomPanel, long animationDuration) {
topPanel.animate().translationY(topPanel.getHeight()).setDuration(animationDuration);
bottomPanel.animate().translationY(topPanel.getHeight() + bottomPanel.getHeight()).setDuration(animationDuration);
}
},
COLLAPSED {
#Override
public void moveTo(View topPanel, View bottomPanel, long animationDuration) {
topPanel.animate().translationY(0).setDuration(animationDuration);
bottomPanel.animate().translationY(bottomPanel.getHeight()).setDuration(animationDuration);
}
},
EXPANDED {
#Override
public void moveTo(View topPanel, View bottomPanel, long animationDuration) {
topPanel.animate().translationY(-bottomPanel.getHeight()).setDuration(animationDuration);
bottomPanel.animate().translationY(0).setDuration(animationDuration);
}
};
public abstract void moveTo(View topPanel, View bottomPanel, long animationDuration);
}
Usage of this would be as follows:
State newState = State.EXPANDED;
newState.moveTo(topPanel, bottomPanel, 200);
There are a lot of questions with regard to crossfading in Android, but they all include animations. My question is about crossfading using the OnPageChangeListener of a ViewPager.
I have a ViewPager which could have an unlimited number of views, but in practice uses about 6 or 7 views. Not much going on there.
Each View in the ViewPager has a background Bitmap which should be fixed and crossfade with the background of the next (or previous) View instead of scrolling along with the rest of the View.
To achieve this I decoupled the backgrounds and add to an ArrayList and assign them to ImageViews later. But since I don't want to risk the chance that my Activity will end up with numerous ImageViews I thought of the following structure:
<FrameLayout
android:id="#+id/backgroundContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/bottomImage"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:scaleType="center" />
<ImageView
android:id="#+id/middleImage"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:scaleType="center" />
<ImageView
android:id="#+id/topImage"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:scaleType="center" />
</FrameLayout>
Then a OnPageChangeListener is assigned to the ViewPager to assign the backgrounds to the ImageViews.
#Override
public void onPageSelected(int position) {
MyLog.i(TAG, "PAGE SELECTED: " + position);
if(position == 0) {
_bottomBackground.setImageBitmap(null);
_topBackground.setImageBitmap(_backgroundStack.get(position+1));
} else if (position == NUM_ITEMS-1) {
_bottomBackground.setImageBitmap(_backgroundStack.get(position-1));
_topBackground.setImageBitmap(null);
} else {
_bottomBackground.setImageBitmap(_backgroundStack.get(position-1));
_topBackground.setImageBitmap(_backgroundStack.get(position+1));
}
_middleBackground.setImageBitmap(_backgroundStack.get(position));
// Make the top front background transparent
_topBackground.setAlpha(0f);
_currentBackgroundPosition = position;
}
This works fine if I would've liked to just swap the backgrounds. I want the backgrounds to cross fade into each other while the user swipes the ViewPager. I've got the fade for a forward scroll working, but I don't understand why the fade for the backward scroll somehow doesn't give a good result. During a backward scroll the middle background should fade into the bottom background.
I'm afraid I'm missing something. I'm never changing the alpha of the bottom background, but the Log results always show the exact same value for getAlpha() as for the middle background.
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(_currentBackgroundPosition == position) {
// scroll forward
_topBackground.setAlpha(positionOffset)
} else {
//scroll backward
_middleBackground.setAlpha(positionOffset);
}
MyLog.i(TAG, "Bottom BackgroundAlpha: " + _bottomBackground.getAlpha());
MyLog.i(TAG, "Middle BackgroundAlpha: " + _middleBackground.getAlpha());
MyLog.i(TAG, "Top BackgroundAlpha: " + _topBackground.getAlpha());
}
And wait! There's one more thing I really am not able to figure out how to fix. Although the forward scroll fade is working. There's a super short flickering in the background. I assume this is happening because of way I set up the onPageSelected method.
Is there another way how I can create/fix this behavior?
ViewPager.PageTransformer is your friend. I'm going to take a different approach to what you tried, but it results in what I understand to bed your desired result - swiping left/right swipes the content, but fades between two background images that don't move.
Each Fragment in the ViewPager will have a layout like so:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/image_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:scaleType="center" />
<LinearLayout
android:id="#+id/content_area"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- content goes here -->
</LinearLayout>
</FrameLayout>
And you will create a PageTransformer that manipulates the layout depending the position it has been swiped:
public class CustomPageTransformer implements ViewPager.PageTransformer {
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
View imageView = view.findViewById(R.id.image_view);
View contentView = view.findViewById(R.id.content_area);
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left
} else if (position <= 0) { // [-1,0]
// This page is moving out to the left
// Counteract the default swipe
view.setTranslationX(pageWidth * -position);
if (contentView != null) {
// But swipe the contentView
contentView.setTranslationX(pageWidth * position);
}
if (imageView != null) {
// Fade the image in
imageView.setAlpha(1 + position);
}
} else if (position <= 1) { // (0,1]
// This page is moving in from the right
// Counteract the default swipe
view.setTranslationX(pageWidth * -position);
if (contentView != null) {
// But swipe the contentView
contentView.setTranslationX(pageWidth * position);
}
if (imageView != null) {
// Fade the image out
imageView.setAlpha(1 - position);
}
} else { // (1,+Infinity]
// This page is way off-screen to the right
}
}
}
And finally hook this PageTransformer up to your ViewPager:
mViewPager.setPageTransformer(true, new CustomPageTransformer());
I've tested it in an existing app and it works well as long as the fragment layouts have a transparent background.
I'm building an app with 2 fragments (with ViewPager). Each fragment consists of 4 buttons. I use also onTouch listener with these buttons. I use getLocationOnScreen(...) method to get the view's (button) position on screen. But as I slide from first fragment to the second one, the location of the next 4 buttons is relative to the first fragment. I mean, the x coordinate of these buttons is taking into consideration the previous fragment width.
My question is - how do I get the second fragment's buttons position relative to this fragment page and not the previous one?
Thanks!
EDIT : Added a code snippet:
Here is how i keep my buttons position on screen into a hash-map collection (this is done for each of the two fragments i have):
public void getViewsPosition(final ArrayList<View> viewArr) {
for (final View v : viewArr) {
final ViewTreeObserver vto = v.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int[] i = new int[2];
v.getLocationOnScreen(i);
_rect = new Rect(i[0], i[1], i[0] + v.getWidth(), i[1]
+ v.getHeight());
view_to_rect.put(v, _rect);
if (Build.VERSION.SDK_INT < 16) {
vto.removeGlobalOnLayoutListener(this);
} else {
vto.removeOnGlobalLayoutListener(this);
}
}
});
}
}
This keeps the correct coordinates for the first fragment's buttons. But for the second fragment, the x-coordinate is relative to the first fragment. So I get the button's x coordinate within this fragment PLUS the first fragment width. The y-coordinate is, of course, correct within all fragment.
I have a layout in which i have two button:
1)back
2)help
and ScollView in which i place two image Views:
1) firstImageVeiw
1) SecondImageVeiw
first image View will be displayed on front and second is placed beneath the 1st image.
Now i want that if i click on help button the the scrollView automatically slides to 2nd image view.
Any help or suggestion.
You can use scrollview's scrollTo() method
helpbutton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
yourscrollview.scrollTo(0, yourImageView.getTop());
}
});
At runtime you can get the location of every view object with
getleft()
getTop()
getRight()
getBottom()
I have a ToggleButton. I want to have more place for click on this button. If I add layout_width, layout_height etc. the image looks bad. I also tried using android:padding but it did not help me.
It is needed for the convenience of users.
Easy solution : If you have image for the button, then create transparent area around it (i.e. for touch area).
The grey area can be transparent to increase the touch area.
Or use TouchDelegate
You can also increase touch area by setting touch delegates Android Developer Training Blog Post
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get the parent view
View parentView = findViewById(R.id.parent_layout);
parentView.post(new Runnable() {
// Post in the parent's message queue to make sure the parent
// lays out its children before you call getHitRect()
#Override
public void run() {
// The bounds for the delegate view (an ImageButton
// in this example)
Rect delegateArea = new Rect();
ImageButton myButton = (ImageButton) findViewById(R.id.button);
myButton.setEnabled(true);
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(MainActivity.this,
"Touch occurred within ImageButton touch region.",
Toast.LENGTH_SHORT).show();
}
});
// The hit rectangle for the ImageButton
myButton.getHitRect(delegateArea);
// Extend the touch area of the ImageButton beyond its bounds
// on the right and bottom.
delegateArea.right += 100;
delegateArea.bottom += 100;
// Instantiate a TouchDelegate.
// "delegateArea" is the bounds in local coordinates of
// the containing view to be mapped to the delegate view.
// "myButton" is the child view that should receive motion
// events.
TouchDelegate touchDelegate = new TouchDelegate(delegateArea,
myButton);
// Sets the TouchDelegate on the parent view, such that touches
// within the touch delegate bounds are routed to the child.
if (View.class.isInstance(myButton.getParent())) {
((View) myButton.getParent()).setTouchDelegate(touchDelegate);
}
}
});
}
}
Use TouchDelegate for your ToggleButton as ammar26 have commented you.
Or
Try this:
Make one parent layout like LinearLayout or RelativeLayout that cover the ToggleButton. And now put margin to that Parent layout.
Now, on click of that Parent layout do action for the toggle button.
Hope it will help you to increase touch area for your view.
Happy Coding.
Instead of putting the touch event in button put it in the layout containg the only the button..and fix the size of the layout as ur wish
increase the values of android:padding:
<SeekBar android:id="#+id/seek" android:layout_width="0dip"
android:layout_height="wrap_content" android:layout_weight="1"
android:paddingTop="5dp" android:paddingBottom="5dp"
android:progressDrawable="#drawable/green_scrubber_progress_horizontal_holo_light"
android:thumb="#drawable/thumb" />
Take the margin (place of padding) of your Button from its parent layout and then perform opration on your Layout
mLayout.setonTouchListener(View.onTouchListener{
// here your working code
});