How can I create circular list for round watch as in android wear 2.0 ?.
Like this:
Circular list is seen in android wear app launcher.
First of all you need to replace your ListView with a WearableRecyclerView.
It can be used like a normal ListView. But make sure, you import the right one from android.support.wear.widget. DON'T use the one from android.support.wearable.view. This one should be crossed out, so it won't take you long to check if you're using the right one. If there's just one WearableRecyclerViewto choose from, make sure to add compile 'com.android.support:wear:27.0.0' to the dependencies in your build.gradle (wear) file.
Also make sure that you're using <android.support.wear.widget.WearableRecyclerView/> in your activity.xml. If you just want a circular ListView without any custom item-scaling, just call this in your onLayoutInflated() method:
your_recyclerview.setEdgeItemsCenteringEnabled(true);
your_recyclerview.setLayoutManager(new WearableLinearLayoutManager(your_activity_context));
If you want to make items scaling up when they get closer to the center of your screen, things get a little more complicated.
First: paste this in your Activity.java:
private class CustomScrollingLayoutCallback extends WearableLinearLayoutManager.LayoutCallback {
private static final float MAX_ICON_PROGRESS = 2F;
#Override
public void onLayoutFinished(View child, RecyclerView parent) {
float centerOffset = ((float) child.getHeight() / 2.0f) / (float) parent.getHeight();
float yRelativeToCenterOffset = (child.getY() / parent.getHeight()) + centerOffset;
float progresstoCenter = (float) Math.sin(yRelativeToCenterOffset * Math.PI);
float mProgressToCenter = Math.abs(0.5f - yRelativeToCenterOffset);
mProgressToCenter = Math.min(mProgressToCenter, MAX_ICON_PROGRESS);
child.setScaleX(1 - mProgressToCenter);
child.setScaleY(1 - mProgressToCenter);
child.setX(+(1 - progresstoCenter) * 100);
}
}
Then go back to your onLayoutInflated() method, and type the following:
CustomScrollingLayoutCallback customScrollingLayoutCallback = new CustomScrollingLayoutCallback();
your_recycler_view.setLayoutManager(new WearableLinearLayoutManager(your_context, customScrollingLayoutCallback));
your_recycler_view.setCircularScrollingGestureEnabled(true);
done.
This is now possible with Android Wear 2.0's WearableRecyclerView.
According to Android Developer Docs:
Wear 2.0 introduces the WearableRecyclerView class for displaying and
manipulating a vertical list of items optimized for round displays.
WearableRecyclerView extends the existing RecyclerView class to
provide a curved layout and a circular scrolling gesture in wearable
apps.
You may like to read more about Android Wear 2.0 Preview 3.
Related
TLDR: I need a way to disable Android 10 gesture navigation programmatically so that they don't accidentally go back when they swipe from the sides
The backstory: Android 10 introduced gesture navigation as opposed to the buttons at the bottom. So now on Android 10 devices that have it enabled, they can swipe from either side of the screen to go back and swipe from the bottom to navigate home or between apps. However, I am working on an implementation in AR and want to lock the screen to portrait but allow users to go landscape.
If a user turns their phone to landscape but the activity is locked to portrait, the back gesture navigation is now a swipe from the top which is a common way to access the status bar in a full screen app (which this one is) so users will inadvertently go back and leave the experience if they are used to android navigations.
Does anybody know how to either a) disable the gesture navigation (but then how does the user go back/to home?) for Android 10 programmatically or b) know how to just change the orientation for the gestures without needing your activity to support landscape?
It's very easy to block the gestures programmatically, but you can't do that for entire edges on both side.
SO you have to decide on how much portion of the screen you want to disable the gestures?
Here is the code :
Define this code in your Utils class.
static List<Rect> exclusionRects = new ArrayList<>();
public static void updateGestureExclusion(AppCompatActivity activity) {
if (Build.VERSION.SDK_INT < 29) return;
exclusionRects.clear();
Rect rect = new Rect(0, 0, SystemUtil.dpToPx(activity, 16), getScreenHeight(activity));
exclusionRects.add(rect);
activity.findViewById(android.R.id.content).setSystemGestureExclusionRects(exclusionRects);
}
public static int getScreenHeight(AppCompatActivity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
return height;
}
public static int dpToPx(Context context, int i) {
return (int) (((float) i) * context.getResources().getDisplayMetrics().density);
}
Check if your layout is set in that activity where you want to exclude the edge getures and then apply this code.
// 'content' is the root view of your layout xml.
ViewTreeObserver treeObserver = content.getViewTreeObserver();
treeObserver.addOnGlobalLayoutListener(new
ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
SystemUtil.updateGestureExclusion(MainHomeActivity.this);
}
});
We are adding the exclusion rectangle width to 16dp to fetch the back gesture which you can change according to your preferrences.
Here are some things to note :-
You must not block both side gestures. If you do so, it'll be the worst user experience.
"getScreenHeight(activity)" is the height of the rectangle, So if you want to block the gesture in left side & top half of the screen then simply replace it with getScreenHeight(activity)/2
1st argument in new Rect() is 0 because we want the gestures on left sdie, If you want it right side then simply put - "getScreenWidth(activity) - SystemUtil.dpToPx(activity, 16)"
Hope this will solve your problem permanently. :)
Remember:
setSystemGestureExclusionRects() must be called in doOnLayout() for your view
My implementation:
binding.root.apply { // changing gesture rects for root view
doOnLayout {
// updating exclusion rect
val rects = mutableListOf<Rect>()
rects.add(Rect(0,0,width,(150 * resources.displayMetrics.density).toInt()))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
systemGestureExclusionRects = rects
}
}
}
I excluded gestures for 150dp from the top, and for the entire width (just to test)
While #Dev4Life's answer was helpful, I had no success until visiting the documentation:
Android Docs: setSystemGestureExclusionRects
I am developing an application on Unity for Android Mobile platform. In which I am rotating object with single finger touch gesture, with the help of this script,
using UnityEngine;
public class MouseDragRotate : MonoBehaviour {
float rotationSpeed = 0.02f;
void OnMouseDrag()
{
float XaxisRotation = Input.GetAxis("Mouse X")*rotationSpeed;
float YaxisRotation = Input.GetAxis("Mouse Y")*rotationSpeed;
// select the axis by which you want to rotate the GameObject
transform.RotateAround (Vector3.down, XaxisRotation);
transform.RotateAround (Vector3.right, YaxisRotation);
}
}
But the problem is that, this script is working only on all Unity Assets for example cube, sphere, capsule and others. But not working with third party 3d objects,
So simply the question is why this script is not working on third party 3d objects ?
You have to have some sort of a Collider attached to the 3d-model/Object you would like to interact with. What you can do is add a BoxCollider to any Imported Object, or if there is a MeshFilter attached you could also add a MeshCollider.
You should make sure the script you show is added to the right top level object and not in a nested component of that object.
If you are still having problems please show us more about the objects you are trying to apply this to. And what components and option are set to it.
I am working on an application which requires me to manually handle the fling process rather than giving it to the framework. What I want to achieve is basically calculate the amount of pixels a listview moves when it receives a fling action. As the scroll method already provides distance in form of delta, I have handled it easily. But is there a way to get fling distance as only velocity parameter is being passed in the super method.
Note- I have to move another view in accordance with the fling distance, so I need to get it simultaneously just like onScroll provides it.
Thanks.
It is passed 3 years but no answer yet. I found some workaround to achieve it.
Actually it is kind of advanced topic as there are a lot of nuances but basically you can refer to Android source code(OverScroller class in particular) and use this method. You will need to copy it into your class and use it.
private double getSplineFlingDistance(int velocity) {
final double l = getSplineDeceleration(velocity);
final double decelMinusOne = DECELERATION_RATE - 1.0;
return mFlingFriction * PHYSICAL_COEF * Math.exp(DECELERATION_RATE / decelMinusOne * l);
}
Other methods and values can be obtained from the same class.
The link to the source code: https://android.googlesource.com/platform/frameworks/base/+/jb-release/core/java/android/widget/OverScroller.java
Keep in mind that in some devices the value can be different (not too much). Some vendors change the formula depending on their requirements and hardware to make it more smooth.
It looks like the original question ended up with nothing, but it was formulated pretty good, so I landed here and started my research. Here are my results.
My question was: What is the final value at the end of Android standard FlingAnimation?
new FlingAnimation(new FloatValueHolder(0f))
.addEndListener((animation, canceled, value, velocity) -> {
? value
I needed that value before animation start based on the start velocity to make some preparations at the destination point of the FlingAnimation.
Actually I started with Overscroller.java mentioned by #Adil Aliyev. I collected all the portions of code, but the result was way less, that came from the animation.
Then I took a look into FlingAnimation.java in pair with DynamicAnimation.java.
The key function in FlingAnimation.java to start the research was:
MassState updateValueAndVelocity(float value, float velocity, long deltaT) {
After playing with some equations I composed this final code. It gives not totally exact estimation to the last digit, but very close. I will use it for my needs. You are welcome too:
final float DEFAULT_FRICTION = -4.2f;
final float VELOCITY_THRESHOLD_MULTIPLIER = 1000f / 16f;
float mFriction = 1.1f * DEFAULT_FRICTION; // set here friction that you set in .setFriction(1.1f) or 1 by default
final float THRESHOLD_MULTIPLIER = 0.75f;
float mVelocityThreshold = THRESHOLD_MULTIPLIER * VELOCITY_THRESHOLD_MULTIPLIER;
double time = Math.log(mVelocityThreshold / startVelocity) * 1000d / mFriction;
double flingDistance = startVelocity / mFriction * (Math.exp(mFriction * time / 1000d) - 1);
Background
I am working on an implementation of the "KenBurns effect" (demo here) on the action bar , as shown on this library's sample (except for the icon that moves, which I've done so myself).
In fact, I even asked about it a long time ago (here), which at this point I didn't even know its name. I was sure I've found a solution, but it has some problems.
Also, since I sometimes show the images from the device, some of them even need to be rotated, so I use a rotatableDrawable (as shown here).
The problem
The current implementation cannot handle multiple bitmaps that are given dynamically (from the Internet, for example), and doesn't even look at the input images' size.
Instead, it just does the zooming and translation in a random way, so many times it can zoom too much/little, and empty spaces can be shown.
The code
Here's the code that is related to the problems:
private float pickScale() {
return MIN_SCALE_FACTOR + this.random.nextFloat() * (MAX_SCALE_FACTOR - MIN_SCALE_FACTOR);
}
private float pickTranslation(final int value, final float ratio) {
return value * (ratio - 1.0f) * (this.random.nextFloat() - 0.5f);
}
public void animate(final ImageView view) {
final float fromScale = pickScale();
final float toScale = pickScale();
final float fromTranslationX = pickTranslation(view.getWidth(), fromScale);
final float fromTranslationY = pickTranslation(view.getHeight(), fromScale);
final float toTranslationX = pickTranslation(view.getWidth(), toScale);
final float toTranslationY = pickTranslation(view.getHeight(), toScale);
start(view, KenBurnsView.DELAY_BETWEEN_IMAGE_SWAPPING_IN_MS, fromScale, toScale, fromTranslationX,
fromTranslationY, toTranslationX, toTranslationY);
}
And here's the part of the animation itself, which animates the current ImageView:
private void start(View view, long duration, float fromScale, float toScale, float fromTranslationX, float fromTranslationY, float toTranslationX, float toTranslationY) {
view.setScaleX(fromScale);
view.setScaleY(fromScale);
view.setTranslationX(fromTranslationX);
view.setTranslationY(fromTranslationY);
ViewPropertyAnimator propertyAnimator = view.animate().translationX(toTranslationX).translationY(toTranslationY).scaleX(toScale).scaleY(toScale).setDuration(duration);
propertyAnimator.start();
}
As you can see, this doesn't look at the view/bitmap sizes, and just randomly selects how to zoom and pan.
What I've tried
I've made it work with dynamic bitmaps, but I don't understand what to change on it so that it will handle the sizes correctly.
I've also noticed there is another library (here) that does this work, but it also has the same problems, and it's even harder to understand how to fix them there. Plus it randomly crashes . Here's a post I've reported about it.
The question
What should be done in order to implement Ken-Burns effect correctly, so that it could handle dynamically created bitmaps?
I'm thinking that maybe the best solution is to customize the way the ImageView draws its content, so that at any given time, it will show a part of the bitmap that is given to it, and the real animation would be between two rectangles of the bitmap . Sadly, I'm not sure how to do this.
Again, the question isn't about getting bitmaps or decoding. It's about how to make them work well with this effect without crashes or weird zoom in/out which show empty spaces.
I have look at the source code of the KenBurnsView and it isn't actually that hard to implement the features you want, but there are a few things I have to clarify first:
1. Loading images dynamically
The current implementation cannot handle multiple bitmaps that are
given dynamically (from the Internet, for example),...
It isn't difficult to download images dynamically from the internet if you know what you are doing, but there are many ways to do it. Many people don't actually come up with their own solution but use a networking library like Volley to download the image or they go straight for Picasso or something similar. Personally I mostly use my own set of helper classes but you have to decide how exactly you want to download the images. Using a library like Picasso is most likely the best solution for you. My code samples in this answer will use the Picasso library, here is a quick example of how to use Picasso:
Picasso.with(context).load("http://foo.com/bar.png").into(imageView);
2. Image Size
...and doesn't even look at the input images' size.
I really don't understand what you mean by that. Internally the KenBurnsView uses ImageViews to display the images. They take care of properly scaling and displaying the image and they most certainly take the size of the images into account. I think your confusion might be caused by the scaleType which is set for the ImageViews. If you look at the layout file R.layout.view_kenburns which contains the layout of the KenBurnsView you see this:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/image0"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="#+id/image1"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Notice that there are two ImageViews instead of just one to create the crossfade effect. The important part is this tag which is found on both ImageViews:
android:scaleType="centerCrop"
What this does is tell the ImageView to:
Center the image inside the ImageView
Scale the image so its width fits inside the ImageView
If the image is taller than the ImageView it will be cropped to the size of the ImageView
So in its current state the images inside the KenBurnsView may be cropped at all times. If you want the image to scale to fit completely inside the ImageView so nothing has to be cropped or removed you need to change the scaleType to one of those two:
android:scaleType="fitCenter"
android:scaleType="centerInside"
I don't remember the exact difference between those two, but they should both have the desired effect of scaling the image so it fits both on the X and Y axis inside the ImageView while at the same time centering it inside the ImageView.
IMPORTANT: Changing the scaleType potentially messes up the KenBurnsView!
If you really just use the KenBurnsView to display two images then changing the scaleType won't matter aside from how the images are displayed, but if you resize the KenBurnsView - for example in an Animation - and the ImageViews have the scaleType set to something other than centerCrop you will loose the parallax effect! Using centerCrop as scaleType of an ImageView is a quick and easy way to create parallax-like effects. The drawback of this trick is probably what you noticed: The image in the ImageView will most likely be cropped and not completely visible!
If you look at the layout you can see that all Views in there have match_parent as layout_height and layout_width. This could also be a problem for certain images as the match_parent constraint and certain scaleTypes sometimes produce strange results when the images are considerably smaller or larger than the ImageView.
The translate animation also takes the size of the image into account - or at least the size of the ImageView. If you look at the source code of animate(...) and pickTranslation(...) you will see this:
// The value which is passed to pickTranslation() is the size of the View!
private float pickTranslation(final int value, final float ratio) {
return value * (ratio - 1.0f) * (this.random.nextFloat() - 0.5f);
}
public void animate(final ImageView view) {
final float fromScale = pickScale();
final float toScale = pickScale();
// Depending on the axis either the width or the height is passed to pickTranslation()
final float fromTranslationX = pickTranslation(view.getWidth(), fromScale);
final float fromTranslationY = pickTranslation(view.getHeight(), fromScale);
final float toTranslationX = pickTranslation(view.getWidth(), toScale);
final float toTranslationY = pickTranslation(view.getHeight(), toScale);
start(view, KenBurnsView.DELAY_BETWEEN_IMAGE_SWAPPING_IN_MS, fromScale, toScale, fromTranslationX, fromTranslationY, toTranslationX, toTranslationY);
}
So the view already accounts for the images size and how much the image is scaled when calculating the translation. So the concept of how this works is okay, the only problem I see is that both the start and end values are randomised without any dependencies between those two values. What this means is one simple thing: The start and endpoint of the animation might be the exact same position or may be very close to each other. As a result of that the animation may sometimes be very significant and other times barely noticeable at all.
I can think of three main ways to fix that:
Instead of randomising both start and end values you just randomise
the start values and pick the end values based on the start values.
You keep the current strategy of randomising all values, but you impose range restrictions on each value. For example the fromScale should be a random value between 1.2f and 1.4f and toScale should be a random value between 1.6f and 1.8f.
Implement a fixed translation and scale animation (In other words the boring way).
Whether you choose approach #1 or #2 you are going to need this method:
// Returns a random value between min and max
private float randomRange(float min, float max) {
return random.nextFloat() * (max - min) + max;
}
Here I have modified the animate() method to force a certain distance between start and end points of the animation:
public void animate(View view) {
final float fromScale = randomRange(1.2f, 1.4f);
final float fromTranslationX = pickTranslation(view.getWidth(), fromScale);
final float fromTranslationY = pickTranslation(view.getHeight(), fromScale);
final float toScale = randomRange(1.6f, 1.8f);
final float toTranslationX = pickTranslation(view.getWidth(), toScale);
final float toTranslationY = pickTranslation(view.getHeight(), toScale);
start(view, this.mSwapMs, fromScale, toScale, fromTranslationX, fromTranslationY, toTranslationX, toTranslationY);
}
As you can see I only need to modify how fromScale and toScale are calculated because the translations values are calculated from the scale values. This is not a 100% fix, but it is a big improvement.
3. Solution #1: Fixing KenBurnsView
(Use solution #2 if possible)
To fix the KenBurnsView you can implement the suggestions I mentioned above. Additionally we need to implement a way for the images to be added dynamically. The implementation of how the KenBurnsView handles images is a little weird. We are going to need to modify that a bit. Since we are using Picasso this is actually going to be pretty simple:
Essentially you just need to modify the swapImage() method, I tested it like this and it is working:
private void swapImage() {
if (this.urlList.size() > 0) {
if(mActiveImageIndex == -1) {
mActiveImageIndex = 1;
animate(mImageViews[mActiveImageIndex]);
return;
}
final int inactiveIndex = mActiveImageIndex;
mActiveImageIndex = (1 + mActiveImageIndex) % mImageViews.length;
Log.d(TAG, "new active=" + mActiveImageIndex);
String url = this.urlList.get(this.urlIndex++);
this.urlIndex = this.urlIndex % this.urlList.size();
final ImageView activeImageView = mImageViews[mActiveImageIndex];
activeImageView.setAlpha(0.0f);
Picasso.with(this.context).load(url).into(activeImageView, new Callback() {
#Override
public void onSuccess() {
ImageView inactiveImageView = mImageViews[inactiveIndex];
animate(activeImageView);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(mFadeInOutMs);
animatorSet.playTogether(
ObjectAnimator.ofFloat(inactiveImageView, "alpha", 1.0f, 0.0f),
ObjectAnimator.ofFloat(activeImageView, "alpha", 0.0f, 1.0f)
);
animatorSet.start();
}
#Override
public void onError() {
Log.i(LOG_TAG, "Could not download next image");
}
});
}
}
I have omitted a few trivial parts, urlList is just a List<String> which contains all the urls to the images we want to display, urlIndex is used to cycle through the urlList. I moved the animation into the Callback. That way the image will be downloaded in the background and as soon as the image has been downloaded successfully the animations will play and the ImageViews will crossfade. A lot of the old code from the KenBurnsView can now be deleted, for example the methods setResourceIds() or fillImageViews() are now unnecessary.
4. Solution #2: Better KenBurnsView + Picasso
The second library you link to, this one, actually contains a MUCH better KenBurnsView. The KenBurnsView I talk about above is a subclass of FrameLayout and there are a few problems with the approach this View takes. The KenBurnsView from the second library is a subclass of ImageView, this is already a huge improvement. Because of it we can use image loader libraries like Picasso directly on the KenBurnsView and we don't have to take care of anything ourselves. You say that you experience random crashes with the second library? I have been testing it rather extensively the last few hours and didn't encounter a single crash.
With the KenBurnsView from the second library and Picasso this all becomes very easy and very few lines of code, you just have to create a KenBurnsView for example in xml:
<com.flaviofaria.kenburnsview.KenBurnsView
android:id="#+id/kbvExample"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/image" />
And then in your Fragment you first have to find the view in the layout and then in onViewCreated() we load the image with Picasso:
private KenBurnsView kbvExample;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_kenburns_test, container, false);
this.kbvExample = (KenBurnsView) view.findViewById(R.id.kbvExample);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Picasso.with(getActivity()).load(IMAGE_URL).into(this.kbvExample);
}
5. Testing
I tested everything on my Nexus 5 running Android 4.4.2. Since ViewPropertyAnimators are used this should all be compatible somewhere down to API Level 16, maybe even 12.
I have a omitted a few lines of code here and there so if you have any questions feel free to ask!
In a similar approach to this question, I am looking for a way to plot data points on to a view in Android. Preferably, a library which will do this for arbitrary-ranged input, as well as allow panning and zooming (via pinch or zoom bar).
Right now, I have subclass-ed a view which does the following normalization:
final int width = View.MeasureSpec.getSize(this.widthMeasureSpec);
final int height = View.MeasureSpec.getSize(this.heightMeasureSpec);
final float factorA = width / (maxA - minA);
final float factorS = height / (maxS - minS);
final float constFactorA = factorA * minA;
final float constFactorS = factorS * minS;
final int dataLength = data.length;
for (int i = 0; i < dataLength; ++i) {
if (i % 2 == 0)
_data[i] = _data[i] * factorA - constFactorA;
else
_data[i] = _data[i] * factorS - constFactorS;
}
and a call in onDraw() to the drawPoints method of a canvas (also, I update this.widthMeasureSpec and this.heightMeasureSpec in onMeasure()).
This is with minA/maxA as the bounds for my independent variable and minS/maxS as the bounds for my dependent variable.
This works fine for displaying the data, but I am hoping someone else has solved the problem of drawing the axes and panning/zooming.
I have ~150,000 data points, and I would prefer to keep these as floats to save half the memory. I don't know how big decimal numbers are in JavaScript, but I really don't want to resort to passing data in through JavaScript for the Google Charts API or an HTML-based solution for memory's sake.
I'm running this on a MyTouch 3g (the original, before 3.5mm jack and it's RAM upgrade), so performance is an issue. I'd like to release the final project under the GPLv3, so this excludes GraphView.
The graphs are of the same type as this, so any optimization by excluding points that are too close together to show up on screen would definitely make a difference.
sargas , do check android-misc-widgets.It contains a widget named PlotView with an example TestInterPolator.
Hope it helps.
Original post: Chart and Graph Library for Android
With the library GraphView it's possible to create a line and bar graphs.
GraphView is a library for Android to programmatically create flexible and nice-looking line and bar diagramms. It is easy to understand, to integrate and to customize it.
First checkout the library and integrate it into your project.
Source code is hosted on github.
GraphView library on github
It's also possible to let the graph be scalable (zooming) and scrollable. More information about this library on Original post: Chart and Graph Library for Android
This is how it will look like:
Then you can easily create it with a few lines of code (see snippet):
// graph with dynamically genereated horizontal and vertical labels
GraphView graphView = new LineGraphView(
this // context
, new GraphViewData[] {
new GraphViewData(1, 2.0d)
, new GraphViewData(2, 1.5d)
, new GraphViewData(2.5, 3.0d) // another frequency
, new GraphViewData(3, 2.5d)
, new GraphViewData(4, 1.0d)
, new GraphViewData(5, 3.0d)
} // data
, "GraphViewDemo" // heading
, null // dynamic labels
, null // dynamic labels
);
LinearLayout layout = (LinearLayout) findViewById(R.id.graph1);
layout.addView(graphView);
There is http://www.jfree.org/jfreechart/ which can suit your needs, it is a Java library so it should fit nice to Android. And it is LGPL.
If you can go the JavaScript route, then you could check out ProtoVis http://vis.stanford.edu/protovis/