In my GridView I show a Popupwindow when user makes a long tap. But according to documentation, if there's no room, the Popupwindow tries to scroll the view's parent. And this's what I want to avoid.
showAsDropDown(View anchor) If there is not enough room on screen to show
* the popup in its entirety, this method tries to find a parent scroll
* view to scroll.
I looked into Popupwindow documentation, and I found the following method to achieve my goal (To avoid scrolling the parent), but it's unsupported for app usage.
/**
* Allow PopupWindow to scroll the anchor's parent to provide more room
* for the popup. Enabled by default.
*
* #param enabled True to scroll the anchor's parent when more room is desired by the popup.
*/
#UnsupportedAppUsage
void setAllowScrollingAnchorParent(boolean enabled) {
mAllowScrollingAnchorParent = enabled;
}
For future readers, here's how I solved GridView being forced to be scrolled by the Popupwindow.
I couldn't find an approach to disable GridView's scrolling before showing the Popupwindow. So I made sure that Popupwindow won't appear near the bottom edge.
public void showDropDownMenu(View aView, PopupWindow aPopupWindow, int aMnuItemsNum){
int[] loc = new int[2];
aView.getLocationOnScreen(loc);
int popHeight = (toPixels(getMnuItemHeightDip()) * aMnuItemsNum) + aView.getHeight();
if(getResources().getDisplayMetrics().heightPixels - loc[1] > popHeight){
aPopupWindow.showAsDropDown(aView);
} else {
aPopupWindow.showAsDropDown(aView, 0, - popHeight, Gravity.START | Gravity.TOP);
}
}
Related
I have a RecyclerView of items and a layoutManager that is of type StaggeredGridLayoutManager. I was in an interesting situation, I wanted my items staggered to look like this:
but my views are all the same size, so they would not stagger. To correct the problem I needed to add an offset at the start of the 2nd column. Since I was also creating my own custom decorator class I figured the best way to accomplish this was to just add an offset for the first right column item in my list using the getItemsOffsets method.
Here is the relevant code for my decorator class:
public class StampListDecoration extends RecyclerView.ItemDecoration {
...
#Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
// good example here: https://stackoverflow.com/questions/29666598/android-recyclerview-finding-out-first-and-last-view-on-itemdecoration/30404499#30404499
/**
* Special case. Te first right side item in the list should have an extra 50% top
* offset so that these equal sized views are perfectly staggered.
*/
if (parent.getChildAdapterPosition(view) == 1) {
/**
* We would normally do a outRect.top = view.getHeight()/2 to create a 50% top offset on the first right item in the list.
* However, problems would arise if we paused the app when the top right item was scrolled off screen.
* In this situation, when we re-inflated the recyclerview since the view was off screen
* Android would say the height of the view was zero. So instead I added code that
* looked for the height of the top most view that was visible (and would therefore
* have a height.
*
* see https://stackoverflow.com/questions/29463560/findfirstvisibleitempositions-doesnt-work-for-recycleview-android
* because as a staggeredGrid layout you have a special case first visible method
* findFirstVisibleItemPositions that returns an array of (notice the S on the end of
* the method name.
*/
StaggeredGridLayoutManager layoutMngr = ((StaggeredGridLayoutManager) parent.getLayoutManager());
int firstVisibleItemPosition = layoutMngr.findFirstVisibleItemPositions(null)[0];
int topPos = 0;
try {
topPos = parent.getChildAt(firstVisibleItemPosition).getMeasuredHeight()/2;
} catch (Exception e) {
e.printStackTrace();
}
outRect.set(0, topPos, 0, 0);
} else {
outRect.set(0, 0, 0, 0);
}
}
}
my problem is that these offsets are not getting saved to state when my activity pauses/resumes. So when I switch to another app and switch back, the right column in my RecyclerView slides back to the top...and I lose my stagger.
Can someone show me how to save my offset state? Where are offsets supposed to be saved? I was assuming the LayoutManager would save this information, and I'm saving the LayoutManager state, but that does not seem to be working.
WHAT I HAVE DONE
I used the following function to resize the size of scrollview (used as a slidinglayout)
the second child if slidinglayout is a map fragment. there is a button "select from map" where the sliding view is completely hided.
Problem
problem is that sometimes when i select from map. the sliding layout collapses but the map fragment doesnt expand properly and it remains as previous . THUS A BIG WHITE SPACE IS LEFT BEHINF.
KeyBoard soft input doesnt resize properly. I am usinf adjust pan but it scrambles the whole view and scroll view doesnt scroll till the end
public void resizeScrollView(final float slideOffset) {
// The scrollViewHeight calculation would need to change based on what views are
// in the sliding panel. The calculation below works because the layout has
// 2 views. 1) The row with the drag view which is layout.getPanelHeight() high.
// 2) The ScrollView.
// Log.d("resizeScrollView - height %d panelHeight %d slideOffset %f", mySlidinglayout.getHeight(), mySlidinglayout.getPanelHeight(), slideOffset);
int scrollViewHeight = (int) mySlidinglayout.getPanelHeight();/*mySlidinglayout.getHeight() - mySlidinglayout.getPanelHeight()) * (1.0f - slideOffset)*/
if(slideOffset==0.0f)
{
scrollViewHeight=(int)(mySlidinglayout.getHeight());
}
final ViewGroup.LayoutParams currentLayoutParams = scrollView.getLayoutParams();
currentLayoutParams.height = scrollViewHeight-30;
scrollView.setLayoutParams(currentLayoutParams);
}
I have a form within a ScrollView. When I tap into an EditText the soft keyboard appears and the ScrollView scrolls the now focused EditText so that it just comes into view.
However, I have hint information just below the EditText that I also would like to show, so the scrolling should go just a bit further up, like this:
The EditText is embedded in a form element and actually I'd like to scroll to the bottom of that. I've checked the source code of ScrollView and it will just scroll to the bottom of the currently focused view. Maybe there's a way to tell the ScrollView that the form element is the currently focused element?
Of course I could write my own ScrollView sub class and override the scroll behavior, but I wonder if there's a more elegant way of doing this.
Any other suggestions (with adjust scrolling with a fixed offset or so) are also appreciated.
I have not really found any way to configure the scrolling behavior of the ScrollView from the outside. So I ended up to define my own sub class of ScrollView:`
/**
* {#link ScrollView} extension that allows to configure scroll offset.
*/
public class ConfigurableScrollView extends ScrollView {
private int scrollOffset = 0;
public ConfigurableScrollView (final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public void setScrollOffset (final int scrollOffset) {
this.scrollOffset = scrollOffset;
}
#Override
protected int computeScrollDeltaToGetChildRectOnScreen (final Rect rect) {
// adjust by scroll offset
int scrollDelta = super.computeScrollDeltaToGetChildRectOnScreen(rect);
int newScrollDelta = (int) Math.signum(scrollDelta) * (scrollDelta + this.scrollOffset);
return newScrollDelta;
}
}
computeScrollDelta(...) is the only protected method that can be targeted for overriding, apart from onSizeChanged(...).
The signum function in the example above ensures that scrolling is only increased, if the ScrollView really thinks that scrolling is necessary (e.g. when keyboard pops up).
I can now set the extra scroll offset once from the outside, as calculated from the height of the hint.
It's not hard to use the extended ConfigurableScrollView instead of the standard ScrollView, I only had to replace the ScrollView XML tag with the FQN of the new class.
Considering that you are using ScrollView you have the possibility to use the method ScrollTo as follow:
scrollView.post(new Runnable() {
#Override
public void run() {
sv.scrollTo(x-value, y-value);
}
});
where the first argument is the scroll value for X, while the second argument is the scroll value for Y. So you just have to set your scrollView offset when the keyboard is displayed.
Hope it helps;)
I have a ViewPager with a custom PagerAdapter that displays a set of fragments.
These fragments are (purposely) positioned on top of each other so that I can use a PageTransformer that makes it look as if the user is sliding the fragments from a stack (almost like a deck of cards).
The issue is that each fragment has their own Views/Widgets (e.g. a seekbar) which, due to the overlapping, are occupying the same coordinates and sometimes the touch event is caught by the fragment bellow the current one (e.g. the user adjusts a seekbar's position, but instead of updating the currently shown seekbar, it's the seekbar in the next fragment that's gets its progress updated).
I've come across this answer but it's not the same exact problem.
Has anyone ever found a similar issue? What's the smartest way (except for the lazy solution: change the PageTransformer to one that doesn't overlap the fragments) of dealing with this issue?
EDIT:
In my Fragment class I have:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment, container, false);
rootView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
}
as suggested by Zsombor Erdődy-Nagy, but this doesn't help: it's still possible for the widget bellow the current fragment to receive the event instead of the current one's.
I've also looked at this open issue, with no success.
If you are still looking for the solution, than you should look at this:
Issue 58918.
Here you can find the answer to your problem. Quoting from the link:
If I remember right it was after 4.1 that the framework respects a
custom child drawing order as implied Z-ordering for dispatching touch
events. If your views overlap after this page transformation they may
not receive touch events in the expected order on older platform
versions. Check which view is receiving the touch events to be
certain.
If this is what you are seeing you have a few options:
Enforce the desired ordering as you add/remove child views in your PagerAdapter
Remove the X translation applied by the PageTransformer when a page is no longer fully visible - i.e. the "position" parameter reports a full -1 or 1.
Example:
this.viewPager.setPageTransformer(true, new PageTransformer() {
#Override
public void transformPage(View page, float position) {
float translationX;
float scale;
float alpha;
if (position >= 1 || position <= -1) {
// Fix for https://code.google.com/p/android/issues/detail?id=58918
translationX = 0;
scale = 1;
alpha = 1;
} else if (position >= 0) {
translationX = -page.getWidth() * position;
scale = -0.2f * position + 1;
alpha = Math.max(1 - position, 0);
} else {
translationX = 0.5f * page.getWidth() * position;
scale = 1.0f;
alpha = Math.max(0.1f * position + 1, 0);
}
ViewHelper.setTranslationX(page, translationX);
ViewHelper.setScaleX(page, scale);
ViewHelper.setScaleY(page, scale);
ViewHelper.setAlpha(page, alpha);
}
});
In this case all your fragments must have a background View that stops TochEvents propagating to fragments in the back.
I'm guessing that you already have opaque backgrounds for these fragments, or else the fragments in the back would show through the fragment currently on the top of the stack. So try setting a ClickListener for all your fragment's root ViewGroup instance that does nothing, it just catches the touch events.
In case anyone runs into this issue (which can happen not only with ViewPager), it is just a matter of adding this attribute to the layout at the top of your hierarchy:
android:clickable="true"
This way it will handle the clicks, not doing really anything with them, but at least avoiding them to reach the fragment in the background.
I want to implement a sliding Menu like FB or G+ app and I have found some sample code from FB Menu Demo and https://github.com/jfeinstein10/SlidingMenu
These are good to begin with, But I need something extra from them. Like here it works only on the click of the menu button but I want to move it by gestures as well.
I want to have the behavior that there is a center view and on moving that center towards the right, one view will appear and on moving that towards left, the menu will appear. Say there are three views A,B,C and when I swipe C towards left then A appear and when I swipe C towards right then B appear. C is in the middle of A and B.
1.Middle view moves towards right
Move towards right
2.Move the middle view towards left side
Move towards left
Now my question is: What are the best practices to develop the views like that. I have heard from someone that I should use fragments and View pager as well. So how can I develop this? Is there any sample implementation done by anyone ? Any help and suggestions are appreciated.
For reference see this app which uses this type of sliding b/w views Skout app
The simplest solution may be to use android-undergarment, which has bezel swiping built in, based on the project README:
The user will also be able to control the drawer by bezel swiping from the left side of the screen to open the drawer and doing the same from the right to close it. If you want to prevent this touch functionality, you can call setDrawerEnabled(false).
You can simply use TranslateAnimationon the view which you wish to move along with a pop up for fade and another pop up window for you menu. I have implemented it in my application, and it works like a charm.
Code:
public class SlidingOptionsMenuActivity extends Activity {
/**
* Signifies that the menu is already visible
*/
boolean alreadyShowing = false;
/**
* Width of the current window
*/
private int windowWidth;
/**
* Height of the current window
*/
private int windowHeight;
/**
* Reference of the {#link PopupWindow} which dims the screen
*/
private PopupWindow fadePopup;
/**
* The translate animation
*/
private Animation ta;
/**
* The view which needs to be translated
*/
private RelativeLayout baseView;
/**
* Reference of the {#link LayoutInflater}
*/
LayoutInflater inflater;
/*
* (non-Javadoc)
*
* #see android.app.Activity#onCreate(android.os.Bundle)
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Display display = getWindowManager().getDefaultDisplay();
windowWidth = display.getWidth();
windowHeight = display.getHeight();
inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
findViewById(R.id.btnOptions).setOnClickListener(new OnClickListener() {
/*
* (non-Javadoc)
*
* #see android.view.View.OnClickListener#onClick(android.view.View)
*/
#Override
public void onClick(View v) {
if(!alreadyShowing){
alreadyShowing = true;
openSlidingMenu();
}
}
});
}
/**
* Fades the entire screen, gives a dim background
*/
private void showFadePopup() {
final View layout = inflater.inflate(R.layout.fadepopup, (ViewGroup) findViewById(R.id.fadePopup));
fadePopup = new PopupWindow(layout, windowWidth, windowHeight, false);
fadePopup.showAtLocation(layout, Gravity.NO_GRAVITY, 0, 0);
}
/**
* Opens the sliding Menu
*/
private void openSlidingMenu() {
showFadePopup();
// The amount of view which needs to be moved out. equivalent to the
// width of the menu
int width = (int) (windowWidth * 0.6f);
translateView((float) (width));
int height = LayoutParams.FILL_PARENT;
// creating a popup
final View layout = inflater.inflate(R.layout.option_popup_layout,(ViewGroup) findViewById(R.id.popup_element));
final PopupWindow optionsPopup = new PopupWindow(layout, width, height, true);
optionsPopup.setBackgroundDrawable(new PaintDrawable());
optionsPopup.showAtLocation(layout, Gravity.NO_GRAVITY, 0, 0);
optionsPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
public void onDismiss() {
//Removing the fade effect
fadePopup.dismiss();
//to clear the previous animation transition in
cleanUp();
//move the view out
translateView(0);
//to clear the latest animation transition out
cleanUp();
//resetting the variable
alreadyShowing = false;
}
});
}
/**
* This method is responsible for view translation. It applies a translation
* animation on the root view of the activity
*
* #param right The position to translate to
*/
private void translateView(float right) {
ta = new TranslateAnimation(0f, right, 0f, 0f);
ta.setDuration(300);
ta.setFillEnabled(true);
ta.setFillAfter(true);
baseView = (RelativeLayout) findViewById(R.id.baseView);
baseView.startAnimation(ta);
baseView.setVisibility(View.VISIBLE);
}
/**
* Basic cleanup to avoid memory issues. Not everything is release after
* animation, so to immediately release it doing it manually
*/
private void cleanUp(){
if (null != baseView) {
baseView.clearAnimation();
baseView = null;
}
if (null != ta) {
ta.cancel();
ta = null;
}
fadePopup = null;
}
} //END of Class
//END of file
Hope this would help.
Another open source library I've found that's been very good is SlidingMenu. It should suit your needs as you can open and close the drawer by a "Menu" click and by bezel swiping. I found integrating this along with an Actionbar library like ActionBarSherlock or johannilsson's android-actionbar library is simply a matter of changing a line or two of code in the library project. The Readme for the SlidingMenu library explains how to integrate with the ABSherlock library.
One thing worth noting is that the SlidingMenu example project demonstrates a number of different drawer open-close animations. These are some of the best animations I've seen for this style of menu/navigation.
There is an Official Way ... useful and light (by use of v4 Support Library):
Creating a Navigation Drawer:
https://developer.android.com/training/implementing-navigation/nav-drawer.html