I would like to implement a list effect in android as the one displayed in the Ultravisual Iphone app :
The similar effect can be view on the Expo Milano 2015 app in android.
I would like the top item get bigger when sliding down the ListView.
I have no idea how this can be done... Is it an animation on the first item in the current view?
If someone has an example or a clue to achieve this, it will be great!
Thanks
Well I tried to achieve that effect and it looked like this:
First you need to start defining your max and min font size. I did this:
private final int MAX_FONTSIZE=50;
private final int MIN_FONTSIZE=12;
Next you need to save your screen total height. On your onCreate save it like this:
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
mScreenHeight = size.y;
Then override your listview onScroll event and do something like this:
#Override
public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
if(listview.getChildCount()>0)
for(int i=0;i<listview.getChildCount();i++)
{
float font = Math.min(Math.max(MAX_FONTSIZE - (float)(((MAX_FONTSIZE-MIN_FONTSIZE)/(mScreenHeight*0.3)))*Math.max(listview.getChildAt(i).getTop(),0),MIN_FONTSIZE),MAX_FONTSIZE);
((TextView)listview.getChildAt(i)).setTextSize(font);
}
}
The 0.3 means that at about 30% of your screen it will always be the minimum font size. You can tweak that value for whatever you want. Remember that listview.getChildAt(i) will return the view that you inflate on your adapter. In my case it's just a simple textview, that's why it's safe to cast it to TextView. You might need to call findViewById to get your textview.
You also might need to make the TextView centered so it looks prettier.
EDIT:
Since op want to change the view's size (which should not be used with this code) here is some hack you can do. Start by changing your min/max constants to what you want (100 & 250). Then proceed to create a flag that controls if the listview is scrolling or not. On your onScrollStateChanged add this line isScrolling= i == SCROLL_STATE_TOUCH_SCROLL || i == SCROLL_STATE_FLING; where i is the second parameter of the onScrollStateChanged. Next change the line to if(listview.getChildCount()>0 && isScrolling). The next step is to change the view height. To do this you have to change it's layoutParams.
listview.getChildAt(i).setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Math.round(font * getResources().getDisplayMetrics().density)));
Remember this is, like I said, a simple hack. This is not, by far, the best solution to this. But since it's a complex thing to do let's stick with the basics.
Related
This question is very specific, What I am trying to do (with a list view) is described in great detail in the following article: http://www.pushing-pixels.org/2011/07/18/android-tips-and-tricks-synchronized-scrolling.html
Thanks #kaushal trivedi for the link
Details:
I have an android application I am working on that uses a list view with a custom adapter. The Listview Contains a Custom header of a non-fixed height. Also please note that the list items are also of variable height. My goal is to mimic the effect produced in the latest gmail app (as an example) where when you are viewing an email, and scroll past the header, it sticks to the top of the screen just under the action bar and the content continues to scroll under it. What I would like to do, is stick the bottom half of my header to the top of the screen.
My initial reasoning was to create an invisible view fixed in the desired location, and when the user scrolled to or past that location, make the view visible. The issue in this logic, is I need the exact pixel scroll height, which after many attempts I have determined very difficult to do. The exact issue I ran into is, it is not possible from what I can gather to retrieve the pixel level Y-scroll in an onScroll event, I have only been able to retrieve the value in the onScrollStateChanged event. Which as described above will not achieve the desired functionality.
Working with the onScroll event "int firstVisibleItem, int visibleItemCount, int totalItemCount" parameters is also not an option because of the fact that the content I want to "stick" is not the size of a list item, but a fraction of the size of the variable height header.
Is there a correct way to accomplish this effect? My current minSDK level is 10.
Update 10/10/13
I made some progress. The following code syncs the Y position floating view I have on the screen with the list view. b is the view I am setting just as an example.
NOTE: This is used in the onScroll event of the list view.
View c = view.getChildAt(0);
if (c != null) {
int currY = c.getTop();
int diffY = currY - lastY;
lastY = currY;
b.setTop(b.getTop() + diffY);
}
Now the issue is, the header of my List is a non fixed height as I said earlier. So I need to get the height of the header and apply an offset to "b" to place it at the bottom of the list header floating above the list.
This is the code I've tried so far.
header.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
The issue here us header.getMeasuredHeight(); always resolves to the same value no matter how tall the actual height is.
I understand I cannot get the height until after it is displayed. Is there a way I can get that value and set the offset after it is rendered?
Update 10/11/13
I Answered my last question as soon as I woke up this morning.
While the View.measure() code was returning a height. It appears to be the default height of the view, assuming there was no text (that would ultimately stretch the view). So I used the below event to listen for when the view is displayed, and then record its actual height (which works exactly as I had hoped :) )
ViewTreeObserver vto = header.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
b.setY(header.getMeasuredHeight() - 80); //80 is a temp offset
}
});
I have to go to work soon and being that I have still not fully achieved the desired effect, I will not mark this as answered yet. Hopefully I will be able to sit down and finish this in the next day or two. I am still open to suggestions on better ways of doing this.
Okay, so after a lot of time and research, I have found an answer to my question.
First off, Thank you #kaushal for this link: http://www.pushing-pixels.org/2011/07/18/android-tips-and-tricks-synchronized-scrolling.html
My solution ended up being somewhat complex. So instead of trying to describe it here, I made an example app and posted it here: https://github.com/gh123man/Partial-Header-ListView-Scroll-Sync
The specific file containing the code for the solution is here: https://github.com/gh123man/Partial-Header-ListView-Scroll-Sync/blob/master/src/com/example/partialheaderlistviewscrollsync/MainActivity.java
Is there a way the this problem can be fixed? I tried invalidate() but, it still displays the same problem. What happens is that, after opening the page/ the Activity, the images behaves like the one in Figure A. It only renders to my desired layout (Figure B) after scrolling it back and forth.
What I'm trying to do is set the width and heigth of the image during runtime. So this is also in relation to my previous questions : Images in my HorizontalListView changes it size randomly and ImageView dynamic width and height which received a very little help.
Any tips regarding this matter, please?
EDIT: btw, my classes are:
MyCustomAdapter (extends baseadapter, this calls the displayimage() from ImageLoader ),
MyActivity and
ImageLoader (this is where my image url are loaded, decoded, displayed asynchronously)
Im also confused as to where i will set the height and width of the imageView. For now, i set it at ImageLoader. It was okay. but i dont know if i did the right thing.
If you want to set the width and height manually at runtime, grab a reference to the ImageView's LayoutParams AFTER the View has been measured by the layout system. If you do this too early in the rendering phase, your view's width and height as well as its parent view and so on will be 0.
I have some code in an open source library that might help you. The process is two parts:
Set up an OnPreDrawListener attached to the ViewTreeObserver for your control. My example does this inside of a custom control, but you can do this in your activity as well.
Inside the onPreDraw method, your image and it's parent will now have their width and height values assigned to them. You can make your calculations and then set your width and/or height manually to the LayoutParams object of your view (don't forget to set it back).
Check out this example where I'm applying an aspect ratio to a custom ImageView just before it's rendered to the screen. I don't know if this exactly fits your use case, but this will demonstrate how to add an OnPreDrawListener to a ViewTreeObserver, removing it when you're done, and applying dynamic sizing to a View at runtime
https://github.com/aguynamedrich/beacon-utils/blob/master/Library/src/us/beacondigital/utils/RemoteImageView.java#L78
Here's a modified version that removes my particular resizing logic. It also grabs the ViewTreeObserver from the imageView, which is a more likely scenario if you're not implementing a custom control and you only want to do this in the Activity
private void initResizeLogic() {
final ViewTreeObserver obs = imageView.getViewTreeObserver();
obs.addOnPreDrawListener(new OnPreDrawListener() {
public boolean onPreDraw() {
dynamicResize();
obs.removeOnPreDrawListener(this);
return true;
}
});
}
protected void dynamicResize() {
ViewGroup.LayoutParams lp = imageView.getLayoutParams();
// resize logic goes here...
// imageView.getWidth() and imageView.getHeight() now return
// their initial layout values
lp.height = someCalculatedHeight;
lp.width = someCalculatedWidth;
imageView.setLayoutParams(lp);
}
}
I would like to make a curved on-screen keyboard for Android. I have examined the softkeyboard, and various other on screen keyboards on google code. None that I have found involve a keyboard shape that is anything other than a rectangle. Ideally, I would like to create a keyboard that consists of keys distributed across two semi-circles on opposite sides of the screen (i.e., imagine holding a tablet by the sides and being able to hit the keys with your thumbs).
Of the code I have examined, onscreen keyboards are created as views (usually extending KeyboardView) and appear as a continuous bar across the bottom of the screen. As an approximation to my goal, I have tried to alter code I found on google code (dotdash-keyboard-android) to only draw its keys in the lower left-hand corner and leave the lower-right hand corner transparent. I have been able to override onMeasure to affect the dimensions of the view (see below), but this only seems to alter the positions of the keys and not the positions of the container. In other words, there is still a black bar filling the bottom of the screen.
//Located within KeyboardView
#Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
this.setMeasuredDimension(200, 200);
}
Is what I want to do even possible? Is there a more correct term for this? Are there projects I can use as examples?
I have also tried to set the view's dimensions using this.setLayoutParams -- but, these calls seem to have no effect. I have also tried to use this.getParent to access the parent view (if one even exists) and change it's dimensions but this approach does not work (or, I'm just doing it wrong). Any help would be much appreciated.
Thank you
UPDATE: 12/21/2012 - I think I need to override the parent class's onDraw method. Looking here , it looks like KeyboardView's onDraw method draws to a canvas that is equal to the size of the screen using the following code:
final int width = Math.max(1, getWidth());
final int height = Math.max(1, getHeight());
mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBuffer);
I think I can override onDraw and draw whatever I want on the canvas.
UPDATE: 12/21/2012 - I've overridden onDraw and it's now clear that the keyboardView is the dimensions I'm setting it to (200x200). Using Hierarchyview, I can see that the keyboardview is inside a framelayout with the id InputArea. So, the horizontal bar that fills the entire width is this framelayout. But, I'm not creating it -- where does it come from and how can I alter its dimensions?
UPDATE: 12/22/2012 - After more testing, it seems like the behavior (dimensions) of a keyboardview are in-part determined by the activity that calls it. In the browser, I get the behavior I've been describing: the height of the browser window shrinks to accommodate a bar across the bottom of the screen that holds the keyboard, even if the width of the keyboard is less than the width of the screen. In the calendar app, the keyboard size appears as I have set it (as a square in the lower-left hand corner) with the calendar appearing unchanged beneath it. So, it seems impossible to reach my goal with most apps using this approach. An alternative approach might be to have the IME service create a popupwindow or dialog. One problem is that popupwindows need a parent view or anchor to attach to and I don't think it's possible to find the top-most view from the IME service. Perhaps I can create a transparent view over the current activity and place the popup on top of that?
UPDATE: 12/23/2012 - Progress. I've figured out how to display a popup from the keyboard IME. The next step is to figure out how to make the popups a little round/organic. Here's a screenshot of what I accomplished followed by source.
Source. The following method is in the service IME class and called by the child (of the service) view's onMeasure method so that the popups open at the same time the keyboard is drawn. I've set the dimensions of the keyboard to 1x1 so it isn't visible. The log statements are there to help me figure out how to position the popups.
public void initiatePopupWindow()
{
try {
WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
//display.getSize(p);
Log.i("dotdashkeyboard","initiatePopupWindow (from IME service)");
LayoutInflater inflater = (LayoutInflater) this.getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layoutLeft = inflater.inflate(R.layout.popup_layout_left,null);
View layoutRight = inflater.inflate(R.layout.popup_layout_right, null);
// create a 300px width and 470px height PopupWindow
int popupHeight = 300;
int popupWidth = 200;
if (popUpLeft == null) popUpLeft = new PopupWindow(layoutLeft, popupWidth, popupHeight, false);
if (popUpRight == null) popUpRight = new PopupWindow(layoutRight, popupWidth, popupHeight, false);
int ypos = 0;
int xposRight = 0;
if (display.getRotation() == Surface.ROTATION_0) {
ypos = -(dm.heightPixels / 2 + popupHeight/2);
xposRight = (dm.widthPixels - popupWidth);
Log.i("dotdashkeyboard","test rotation=normal");
} else if (display.getRotation() == Surface.ROTATION_90) {
ypos = -(dm.heightPixels / 2 + popupHeight/2)/2;
xposRight = (dm.widthPixels - popupWidth)*2-popupWidth;
Log.i("dotdashkeyboard","test rotation=90-degrees");
} else {
Log.i("dotdashkeyboard","test rotation=unknown=" + display.getRotation());
}
popUpLeft.showAtLocation(inputView, Gravity.NO_GRAVITY, 0, ypos);
popUpRight.showAtLocation(inputView, Gravity.NO_GRAVITY, xposRight, ypos);
Log.i("dotdashkeyboard","test created popup at ypos="+ypos + " xposRight=" + xposRight);
Log.i("dotdashkeyboard","test screenWidth=" + dm.widthPixels + " screenHeight=" + dm.heightPixels);
Button cancelButton = (Button) layoutLeft.findViewById(R.id.popup_cancel_button);
//cancelButton.setOnClickListener(inputView.cancel_button_click_listener);
cancelButton.setOnClickListener(cancel_button_click_listener);
} catch (Exception e) {
e.printStackTrace();
}
}
You seem to be on the right track here. As you've noted, most activities will shrink their views to provide space for the keyboard window, so if you want the calling activity to fill the screen you need to use a secondary window, such as a PopupWindow. You can set the dimensions of your main window to 0x0.
Have you tried calling popUpLeft.setBackgroundDrawable(null) / popUpRight.setBackgroundDrawable(null)? This should remove the semi-transparent background and show only whatever you draw on top.
OK, so I am starting to get a hang of building Android apps, well at least as much a programmer can after a few days - I am proud of what I have learned so far.
Anyways, I want to force login on the main activity - this I am doing by fetching a SharedPrefernece and than checking if that piece of information is null and than getting a PopupWindow which holds the "login" fields and options.
This PopupWindow has a Flipper inside, which is fine and I got working fine when the certain options are choosen.
I am having problems displaying this PopupWindow to just be the size of the content (wrap_content) as when I set the PopupWindow.setAtLocation()
Now, here is what I have been trying to do to get the size of the popup - as mentioned a few times on here:
popup.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
final PopupWindow pw = new PopupWindow(popup,popup.getMeasuredHeight(),popup.getMeasuredWidth(), true);
Note that popup is the inflator of the actual layout of the Popup, pw is the actual PopupWindow object.
Now, I want to get the actual size of just the popup window (so that way it isn't streched out over the page, but rather just in the center looking like a normal popup should.
Also, with the ViewFlipper. I want it to update the size of the popup when it switch pages (so the page should be sizing up and down per page) is there a way to make this work as well? I tried pw.update() but that didn't work out very well.
You want to measure the actual layout you will be adding to your PopupWindow. I just solved a similar problem of putting a ListView inside a PopupWindow. The trick is to override onMeasure() in your View. Remeasure that ViewFlipper everytime it changes.
#Override
public void onMeasure(int x, int y)
{
super.onMeasure(x, y);
if(x == View.MeasureSpec.UNSPECIFIED && y == View.MeasureSpec.UNSPECIFIED)
{
//measure your child views here
//tell the view it has been measured
this.setMeasuredDimension(width, height);
}
}
Does anyone know of a way to center a ListView based on its current selection or selection set with setSelection?
I did see this other StackOverflow question without any answers: Android ListView center selection
Thanks,
Kevin
First, get the height of the ListView using getHeight, which returns the height of the ListView in pixels.
Then, get the height of the row's View using the same method.
Then, use setSelectionFromTop and pass in half of the ListView's height minus half of the row's height.
Something like:
int h1 = mListView.getHeight();
int h2 = v.getHeight();
mListView.setSelectionFromTop(position, h1/2 - h2/2);
Or, instead of doing the math, you might just pick a constant for the offset from the top, but I would think it might be more fragile on different devices since the second argument for setSelectionFromTop appears to be in pixels rather than device independent pixels.
I haven't tested this code, but it should work as long as your rows are all roughly the same height.
You will need to have the scroll view and the view of the item selected. Then you can simply do:
scrollView.smoothScrollTo(0, selectedView.getTop() - (scrollView.getHeight() / 2) + (selectedView.getHeight() / 2), 0);
This will center the scroll view exactly on selectedView
I haven't tried any of this but based on the current selection could you use public void smoothScrollByOffset (int offset) to get the view to scroll to where you want so that your selection is in the middle of the view?