I'm using KenBurnsView to add animation to my image.
Is it possible to set the max scale programmatically ? Small images are too zooming.
Yes, you can . For this purpose you have to implement your own TransitionGenerator.
Like given blow .In following code you can call SetMinRectFactor() to set minimum zoom sacale.
public class BackgroundTransitionGenerator implements TransitionGenerator
{
private static final int DEFAULT_TRANSITION_DURATION = 9000;
private static final Interpolator DEFAULT_TRANSITION_INTERPOLATOR =
new LinearInterpolator();
private long transitionDuration;
private Interpolator transitionInterpolator;
private Transition lastTransition;
private RectF lastDrawableBounds;
private boolean forward;
private float MIN_RECT_FACTOR=1.0f;
public void SetMinRectFactor(float f)
{
MIN_RECT_FACTOR=f;
}
public BackgroundTransitionGenerator() {
transitionDuration = DEFAULT_TRANSITION_DURATION;
transitionInterpolator = DEFAULT_TRANSITION_INTERPOLATOR;
}
#Override
public Transition generateNextTransition(RectF drawableBounds, RectF viewport) {
float drawableRatio = getRectRatio(drawableBounds);
float viewportRectRatio = getRectRatio(viewport);
RectF startRect;
RectF endRect;
if (drawableRatio >= viewportRectRatio) {
float w = drawableBounds.height() * viewportRectRatio;
float h = drawableBounds.height();
startRect = new RectF(0, 0, w, h);
endRect =generateRandomRect(drawableBounds,viewport); //new RectF((drawableBounds.width() - w), drawableBounds.height(),drawableBounds.width(), h);
} else {
float w = drawableBounds.width();
float h = drawableBounds.width() / viewportRectRatio;
startRect = new RectF(0, 0, w, h);
endRect =generateRandomRect(drawableBounds,viewport); //new RectF(0, drawableBounds.height() - h, w, drawableBounds.height());
}
if (!drawableBounds.equals(lastDrawableBounds) || !haveSameAspectRatio(lastTransition.getDestinyRect(), viewport)) {
forward = false;
}
forward = !forward;
if (forward) {
lastTransition = new Transition(startRect, endRect, transitionDuration, transitionInterpolator);
} else {
lastTransition = new Transition(endRect, startRect, transitionDuration, transitionInterpolator);
}
lastDrawableBounds = new RectF(drawableBounds);
return lastTransition;
}
private static boolean haveSameAspectRatio(RectF r1, RectF r2) {
return (Math.abs(getRectRatio(r1) - getRectRatio(r2)) <= 0.01f);
}
private RectF generateRandomRect(RectF drawableBounds, RectF viewportRect) {
float drawableRatio = getRectRatio(drawableBounds);
float viewportRectRatio = getRectRatio(viewportRect);
RectF maxCrop;
if (drawableRatio > viewportRectRatio) {
float r = (drawableBounds.height() / viewportRect.height()) * viewportRect.width();
float b = drawableBounds.height();
maxCrop = new RectF(0, 0, r, b);
} else {
float r = drawableBounds.width();
float b = (drawableBounds.width() / viewportRect.width()) * viewportRect.height();
maxCrop = new RectF(0, 0, r, b);
}
float factor = MIN_RECT_FACTOR ;
float width = factor * maxCrop.width();
float height = factor * maxCrop.height();
int widthDiff = (int) (drawableBounds.width() - width);
int heightDiff = (int) (drawableBounds.height() - height);
int left = widthDiff;
int top = heightDiff;
return new RectF(left, top, left + width, top + height);
}
private static float getRectRatio(RectF rect) {
return rect.width() / rect.height();
}
}
Related
My UI designer has come up with a design like this for a progress bar:
The numbers should be the progress and max.
The circle image will change. On some of the designs there is also an image next to the 1500.
Whenever I try to do this on a layer-list I always end up with the image in the middle of the drawable. Ideally I would prefer to use vector graphics but I can use png if needed. I haven't even looked at putting the numbers there yet.
So how can this be accomplished?
Thank you.
Using canvas seems to me easier for this case. Here a short sample i could come up with right now:
public class Loading extends View {
private final static float VIEW_HEIGHT = 80;
private RectF viewRectangle;
private RectF progressRectangle;
private float cornersRadius;
private Paint progressBarPaint;
private Paint progressTextPaint;
private Paint containerBarPaint;
private Paint containerTextPaint;
private final int progressMaxValue = 1500;
private float containerTextY;
private float containerTextX;
private final float whiteCircleRadius = 14;
private final float progressTextPadding = 16;
private float progress;
public Loading(Context context) {
super(context);
progress = 0.5f;
progressTextPaint = new Paint(LINEAR_TEXT_FLAG|SUBPIXEL_TEXT_FLAG|ANTI_ALIAS_FLAG);
progressTextPaint.setColor(Color.WHITE);
progressTextPaint.setStyle(Paint.Style.FILL_AND_STROKE);
progressTextPaint.setTextSize(28);
progressTextPaint.setStrokeWidth(1f);
containerTextPaint = new Paint(progressTextPaint);
containerTextPaint.setColor(Color.BLACK);
progressBarPaint = new Paint(ANTI_ALIAS_FLAG);
progressBarPaint.setColor(Color.BLUE);
progressBarPaint.setStyle(Paint.Style.FILL);
containerBarPaint = new Paint(ANTI_ALIAS_FLAG);
containerBarPaint.setColor(Color.GRAY);
containerBarPaint.setStyle(Paint.Style.FILL);
}
#Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
int horizontalPadding = getPaddingLeft() + getPaddingRight();
int verticalPadding = getPaddingTop() + getPaddingBottom();
float measuredHeight = height - verticalPadding;
float targetHeight = measuredHeight < VIEW_HEIGHT ? measuredHeight : VIEW_HEIGHT;
float containerWidth = width - horizontalPadding;
viewRectangle = new RectF(getPaddingLeft(), getPaddingTop(), containerWidth, targetHeight);
progressRectangle = new RectF(viewRectangle);
cornersRadius = targetHeight / 2;
String maxValueText = String.valueOf(progressMaxValue);
Rect containerTextBounds = new Rect();
containerTextPaint.getTextBounds(maxValueText, 0, maxValueText.length(), containerTextBounds);
containerTextX = viewRectangle.right - containerTextBounds.width() - cornersRadius / 2;
containerTextY = viewRectangle.centerY() - containerTextBounds.exactCenterY();
}
#Override
protected void onDraw(Canvas canvas) {
float progressWidth = viewRectangle.right * progress;
float cornerDiameter = cornersRadius * 2;
progressRectangle.right = progressWidth > cornerDiameter ? progressWidth : cornerDiameter;
canvas.drawRoundRect(viewRectangle, cornersRadius, cornersRadius, containerBarPaint);
canvas.drawRoundRect(progressRectangle, cornersRadius, cornersRadius, progressBarPaint);
canvas.drawCircle(progressRectangle.right - cornersRadius, progressRectangle.centerY(), whiteCircleRadius, progressTextPaint);
canvas.drawText(String.valueOf(progressMaxValue), containerTextX, containerTextY, containerTextPaint);
String progressText = String.valueOf((int) (progressMaxValue * progress));
float progressTextWidth = progressTextPaint.measureText(progressText);
if (progressTextWidth < progressRectangle.right * 2) {
float requiredProgressTextSpace = cornersRadius + whiteCircleRadius + progressTextPadding + progressTextWidth;
canvas.drawText(progressText, progressRectangle.right - requiredProgressTextSpace, containerTextY, progressTextPaint);
}
}
}
It's far from perfect and not very flexible, but a good place to start with. If you need some comments here, feel free to ask. Here the render of the code above:
World!
I'm using Harism Page Curl to implement page curl in my project all thing are ok but the direction of curling.
I'm using arabic language in my app so the curl must be from Left to Right(Arabic books).
what this library offer is curling from right to left (english books).
so i tried the following with no succeed:
Reversing the image loading.
Changing parameters in CurlView Class.
as shown below :
public class CurlView extends GLSurfaceView implements View.OnTouchListener,
CurlRenderer.Observer {
// Shows one page at the center of view.
public static final int SHOW_ONE_PAGE = 1;
// Shows two pages side by side.
public static final int SHOW_TWO_PAGES = 2;
// Curl state. We are flipping none, left or right page.
private static final int CURL_LEFT = 1;
private static final int CURL_NONE = 0;
private static final int CURL_RIGHT = 2;
// Constants for mAnimationTargetEvent.
private static final int SET_CURL_TO_LEFT = 1;
private static final int SET_CURL_TO_RIGHT = 2;
private boolean mAllowLastPageCurl = true;
private boolean mAnimate = false;
private long mAnimationDurationTime = 300;
private PointF mAnimationSource = new PointF();
private long mAnimationStartTime;
private PointF mAnimationTarget = new PointF();
private int mAnimationTargetEvent;
private PointF mCurlDir = new PointF();
private PointF mCurlPos = new PointF();
private int mCurlState = CURL_NONE;
// Current bitmap index. This is always showed as front of right page.
private int mCurrentIndex = 0;
// Start position for dragging.
private PointF mDragStartPos = new PointF();
private boolean mEnableTouchPressure = false;
// Bitmap size. These are updated from renderer once it's initialized.
private int mPageBitmapHeight = -1;
private int mPageBitmapWidth = -1;
// Page meshes. Left and right meshes are 'static' while curl is used to
// show page flipping.
private CurlMesh mPageCurl;
private CurlMesh mPageLeft;
private PageProvider mPageProvider;
private CurlMesh mPageRight;
private PointerPosition mPointerPos = new PointerPosition();
private CurlRenderer mRenderer;
private boolean mRenderLeftPage = true;
private SizeChangedObserver mSizeChangedObserver;
// One page is the default.
private int mViewMode = SHOW_ONE_PAGE;`
and
#Override
public int getPageCount() {
return mBitmapIds.length;
}
public Bitmap loadBitmap(int width, int height, int index) {
tracker.send(new HitBuilders.EventBuilder()
.setCategory("UX")
.setAction("click")
.setLabel("submit")
.build());
Bitmap b = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
b.eraseColor(Color.WHITE);
Canvas c = new Canvas(b);
Drawable d = getResources().getDrawable(mBitmapIds[index]);
int margin = 0;
int border = 0;
Rect r = new Rect(margin, margin, width, height);
int imageWidth = r.width() - (border);
assert d != null;
int imageHeight = imageWidth * d.getIntrinsicHeight()
/ d.getIntrinsicWidth();
if (imageHeight > r.height() - (border)) {
imageHeight = r.height() - (border);
imageWidth = imageHeight * d.getIntrinsicWidth()
/ d.getIntrinsicHeight();
}
r.left += ((r.width() - imageWidth));
r.right = r.left + imageWidth + border;
r.top += r.top;
Paint p = new Paint();
p.setColor(0xFFC0C0C0);
c.drawRect(r, p);
r.left += border;
r.right -= border;
r.top += border;
r.bottom -= border;
d.setBounds(r);
d.draw(c);
return b;
}
#Override
public void updatePage(CurlPage page, int width, int height, int index) {
if (index == i) {
Bitmap back = loadBitmap(width, height, index);
page.setTexture(back, CurlPage.SIDE_BOTH);
index++;
i++;
} else {
Bitmap back = loadBitmap(width, height, index);
page.setTexture(back, CurlPage.SIDE_BOTH);
}
}
I have created a donut chart, which is shown below:
MY resultant Donut chart should be in the following way:
My Question is, How can i achieve the lines with image (They are rounded off in second screen shot)
For reference, Here is the code which I have written:
public class PieChartView extends View {
private int[] values = {30, 60, 90, 100, 150};
private int c[] = {Color.MAGENTA,Color.BLUE,Color.RED,Color.CYAN,Color.YELLOW};
private int valuesLength = values.length;
private RectF rectF;
private Paint slicePaint, textPaint;
private Path path;
public PieChartView(Context context, AttributeSet attrs) {
super(context, attrs);
valuesLength = values.length;
slicePaint = new Paint();
slicePaint.setAntiAlias(true);
slicePaint.setDither(true);
slicePaint.setStyle(Paint.Style.FILL);
path = new Path();
}
#SuppressLint("DrawAllocation")
#Override
protected void onDraw(Canvas canvas) {
if(values != null) {
int startTop = 0;
int startLeft = 0;
int endBottom = getHeight();
int endRight = endBottom;// This makes an equal square.
rectF = new RectF(startLeft, startTop, endRight, endBottom);
float[] scaledValues = scale();
float sliceStartPoint = 0;
path.addCircle(rectF.centerX(), rectF.centerY(), 125, Direction.CW);
canvas.clipPath(path, Op.DIFFERENCE);
for(int i = 0; i < valuesLength; i++) {
slicePaint.setColor(c[i]);
path.reset();
path.addArc(rectF, sliceStartPoint, scaledValues[i]);
path.lineTo(rectF.centerX(), rectF.centerY());
canvas.drawPath(path, slicePaint);
sliceStartPoint += scaledValues[i];//This updates the starting point of next slice.
}
}
}
private float[] scale() {
float[] scaledValues = new float[this.values.length];
float total = getTotal(); //Total all values supplied to the chart
for (int i = 0; i < this.values.length; i++) {
scaledValues[i] = (this.values[i] / total) * 360; //Scale each value
}
return scaledValues;
}
private float getTotal() {
float total = 0;
for (float val : this.values)
total += val;
return total;
}
}
Also, How can I find out a co-ordinate from an angle(Start or sweep angle). If i want to draw a line from centre of a circle to the coordinate?
Here's how i finally did it after two days of search with help of this library https://github.com/Ken-Yang/AndroidPieChart
And equations to center text done with help of my friends and alot of search
on MainActivity onCreate or oncreateView if you are using fragments:
PieChart pie = (PieChart) rootView.findViewById(R.id.pieChart);
ArrayList<Float> alPercentage = new ArrayList<Float>();
alPercentage.add(2.0f);
alPercentage.add(8.0f);
alPercentage.add(20.0f);
alPercentage.add(10.0f);
alPercentage.add(10.0f);
alPercentage.add(10.0f);
alPercentage.add(10.0f);
alPercentage.add(10.0f);
alPercentage.add(10.85f);
alPercentage.add(9.15f);
try {
// setting data
pie.setAdapter(alPercentage);
// setting a listener
pie.setOnSelectedListener(new OnSelectedLisenter() {
#Override
public void onSelected(int iSelectedIndex) {
Toast.makeText(getActivity(),
"Select index:" + iSelectedIndex,
Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
if (e.getMessage().equals(PieChart.ERROR_NOT_EQUAL_TO_100)) {
Log.e("kenyang", "percentage is not equal to 100");
}
}
public class PieChart extends View {
public interface OnSelectedLisenter {
public abstract void onSelected(int iSelectedIndex);
}
private OnSelectedLisenter onSelectedListener = null;
private static final String TAG = PieChart.class.getName();
public static final String ERROR_NOT_EQUAL_TO_100 = "NOT_EQUAL_TO_100";
private static final int DEGREE_360 = 360;
private static String[] PIE_COLORS = null;
private static int iColorListSize = 0;
ArrayList<Float> array;
private Paint paintPieFill;
private Paint paintPieBorder;
private Paint paintCenterCircle;
private ArrayList<Float> alPercentage = new ArrayList<Float>();
private int mCenterX = 320;
private int mCenterY = 320;
private int iDisplayWidth, iDisplayHeight;
private int iSelectedIndex = -1;
private int iCenterWidth = 0;
private int iShift = 0;
private int iMargin = 0; // margin to left and right, used for get Radius
private int iDataSize = 0;
private Canvas canvas1;
private RectF r = null;
private RectF centerCircle = null;
private float fDensity = 0.0f;
private float fStartAngle = 0.0f;
private float fEndAngle = 0.0f;
float fX;
float fY;
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
PIE_COLORS = getResources().getStringArray(R.array.colors);
iColorListSize = PIE_COLORS.length;
array = new ArrayList<Float>();
fnGetDisplayMetrics(context);
iShift = (int) fnGetRealPxFromDp(30);
iMargin = (int) fnGetRealPxFromDp(40);
centerCircle = new RectF(200, 200, 440, 440);
// used for paint circle
paintPieFill = new Paint(Paint.ANTI_ALIAS_FLAG);
paintPieFill.setStyle(Paint.Style.FILL);
// used for paint centerCircle
paintCenterCircle = new Paint(Paint.ANTI_ALIAS_FLAG);
paintCenterCircle.setStyle(Paint.Style.FILL);
paintCenterCircle.setColor(Color.WHITE);
// used for paint border
paintPieBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
paintPieBorder.setStyle(Paint.Style.STROKE);
paintPieBorder.setStrokeWidth(fnGetRealPxFromDp(3));
paintPieBorder.setColor(Color.WHITE);
Log.i(TAG, "PieChart init");
}
// set listener
public void setOnSelectedListener(OnSelectedLisenter listener) {
this.onSelectedListener = listener;
}
float temp = 0;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.i(TAG, "onDraw");
float centerX = (r.left + r.right) / 2;
float centerY = (r.top + r.bottom) / 2;
float radius1 = (r.right - r.left) / 2;
radius1 *= 0.5;
float startX = mCenterX;
float startY = mCenterY;
float radius = mCenterX;
float medianAngle = 0;
Path path = new Path();
for (int i = 0; i < iDataSize; i++) {
// check whether the data size larger than color list size
if (i >= iColorListSize) {
paintPieFill.setColor(Color.parseColor(PIE_COLORS[i
% iColorListSize]));
} else {
paintPieFill.setColor(Color.parseColor(PIE_COLORS[i]));
}
fEndAngle = alPercentage.get(i);
// convert percentage to angle
fEndAngle = fEndAngle / 100 * DEGREE_360;
// if the part of pie was selected then change the coordinate
if (iSelectedIndex == i) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
float fAngle = fStartAngle + fEndAngle / 2;
double dxRadius = Math.toRadians((fAngle + DEGREE_360)
% DEGREE_360);
fY = (float) Math.sin(dxRadius);
fX = (float) Math.cos(dxRadius);
canvas.translate(fX * iShift, fY * iShift);
}
canvas.drawArc(r, fStartAngle, fEndAngle, true, paintPieFill);
float angle = (float) ((fStartAngle + fEndAngle / 2) * Math.PI / 180);
float stopX = (float) (startX + (radius/2) * Math.cos(angle));
float stopY = (float) (startY + (radius/2) * Math.sin(angle));
// if the part of pie was selected then draw a border
if (iSelectedIndex == i) {
canvas.drawArc(r, fStartAngle, fEndAngle, true, paintPieBorder);
canvas.drawLine(startX, startY, stopX, stopY, paintPieFill);
canvas.restore();
}
fStartAngle = fStartAngle + fEndAngle;
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// get screen size
iDisplayWidth = MeasureSpec.getSize(widthMeasureSpec);
iDisplayHeight = MeasureSpec.getSize(heightMeasureSpec);
if (iDisplayWidth > iDisplayHeight) {
iDisplayWidth = iDisplayHeight;
}
/*
* determine the rectangle size
*/
iCenterWidth = iDisplayWidth / 2;
int iR = iCenterWidth - iMargin;
if (r == null) {
r = new RectF(iCenterWidth - iR, // top
iCenterWidth - iR, // left
iCenterWidth + iR, // right
iCenterWidth + iR); // bottom
}
if (centerCircle == null) {
// centerCircle=new RectF(left, top, right, bottom);
}
setMeasuredDimension(iDisplayWidth, iDisplayWidth);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// get degree of the touch point
double dx = Math.atan2(event.getY() - iCenterWidth, event.getX()
- iCenterWidth);
float fDegree = (float) (dx / (2 * Math.PI) * DEGREE_360);
fDegree = (fDegree + DEGREE_360) % DEGREE_360;
// get the percent of the selected degree
float fSelectedPercent = fDegree * 100 / DEGREE_360;
// check which pie was selected
float fTotalPercent = 0;
for (int i = 0; i < iDataSize; i++) {
fTotalPercent += alPercentage.get(i);
if (fTotalPercent > fSelectedPercent) {
iSelectedIndex = i;
break;
}
}
if (onSelectedListener != null) {
onSelectedListener.onSelected(iSelectedIndex);
}
invalidate();
return super.onTouchEvent(event);
}
private void fnGetDisplayMetrics(Context cxt) {
final DisplayMetrics dm = cxt.getResources().getDisplayMetrics();
fDensity = dm.density;
}
private float fnGetRealPxFromDp(float fDp) {
return (fDensity != 1.0f) ? fDensity * fDp : fDp;
}
public void setAdapter(ArrayList<Float> alPercentage) throws Exception {
this.alPercentage = alPercentage;
iDataSize = alPercentage.size();
float fSum = 0;
for (int i = 0; i < iDataSize; i++) {
fSum += alPercentage.get(i);
}
if (fSum != 100) {
Log.e(TAG, ERROR_NOT_EQUAL_TO_100);
iDataSize = 0;
throw new Exception(ERROR_NOT_EQUAL_TO_100);
}
}
<com.example.piecharts.PieChart
android:id="#+id/pieChart"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.example.piecharts.PieChart>
I have a custom view for my app and I'm trying to update a text everytime OnDraw is called.
(some of) my code lookes like this:
#Override
protected void onDraw(Canvas canvas) {
Log.e(TAG,"onDraw");
drawBackground(canvas);
float scale = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(scale, scale);
drawTitle(canvas);
drawHand(canvas);
canvas.restore();
if (handNeedsToMove()) {
moveHand();
}
}
private void drawTitle(Canvas canvas) {
String title = getTitle();
Log.i("drawtitle",title);
titlePaint = new Paint();
titlePaint.setColor(0xffffffff);
titlePaint.setAntiAlias(true);
titlePaint.setTypeface(Typeface.MONOSPACE);
titlePaint.setTextAlign(Paint.Align.CENTER);
titlePaint.setTextSize(0.05f);
titlePaint.setTextScaleX(0.8f);
canvas.drawText(title, 0.45f, 0.95f, titlePaint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Log.d(TAG, "Size changed to " + w + "x" + h);
regenerateBackground();
}
private void regenerateBackground() {
if (background != null) {
background.recycle();
}
//Some stuff here
drawInstruments(backgroundCanvas);
drawTitle(backgroundCanvas);
}
The text get updated when it's called by regenerateBackground but stays the same on OnDraw. Why? How to fix? Maby there is a better way than using canvas.drawText? I am making a speedometer and I just want to print the value of which the "needle" is pointing at...
edit: full code
public final class Dashboard extends View {
private static final String TAG = Dashboard.class.getSimpleName();
// drawing tools
private Paint dashPaint;
private static Bitmap dash;
private Matrix dashMatrix;
private float dashScalex;
private float dashScaley;
private Paint handPaint;
private Path handPath;
private Paint handScrewPaint;
private Paint backgroundPaint;
// end drawing tools
private Bitmap background; // holds the cached static part
private Paint titlePaint;
private Path titlePath;
// scale configuration
private static final int totalNicks = 100;
private static final float degreesPerNick = 360.0f / totalNicks;
private static final int centerDegree = 40; // the one in the top center (12 o'clock)
private static final int minDegrees = -30;
private static final int maxDegrees = 110;
// hand dynamics -- all are angular expressed in F degrees
private boolean handInitialized = true;
private float handPosition = centerDegree;
private float handTarget = centerDegree;
private float handVelocity = 0.0f;
private float handAcceleration = 0.0f;
private long lastHandMoveTime = -1L;
private static float circlePosx = 0.5f;
private static float circlePosy = 0.36f;
private static float circleRadius = 0.1f;
public Dashboard(Context context) {
super(context);
init();
}
public Dashboard(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Dashboard(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
Parcelable superState = bundle.getParcelable("superState");
super.onRestoreInstanceState(superState);
handInitialized = bundle.getBoolean("handInitialized");
handPosition = bundle.getFloat("handPosition");
handTarget = bundle.getFloat("handTarget");
handVelocity = bundle.getFloat("handVelocity");
handAcceleration = bundle.getFloat("handAcceleration");
lastHandMoveTime = bundle.getLong("lastHandMoveTime");
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
Bundle state = new Bundle();
state.putParcelable("superState", superState);
state.putBoolean("handInitialized", handInitialized);
state.putFloat("handPosition", handPosition);
state.putFloat("handTarget", handTarget);
state.putFloat("handVelocity", handVelocity);
state.putFloat("handAcceleration", handAcceleration);
state.putLong("lastHandMoveTime", lastHandMoveTime);
return state;
}
public static float[] getCircle(){
float[] circle = {circlePosx,circlePosy,circleRadius};
return circle;
}
public static int[] getCanvasSize(){
int[] size = {dash.getWidth(),dash.getHeight()};
return size;
}
private void init() {
initDrawingTools();
}
private void initDrawingTools() {
/******/
dashPaint = new Paint();
dashPaint.setFilterBitmap(true);
dash = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.dashboard);
dashMatrix = new Matrix();
dashScaley = 1f/dash.getHeight();// * 0.3f;;
dashScalex = 1f/dash.getWidth();
dashMatrix.setScale(dashScalex, dashScaley);
Log.i("dash width", "= "+dash.getWidth()+" - "+getWidth());
Log.i("dash height", "= "+dash.getHeight());
Log.i("dash scale x ", "= "+1f/dash.getHeight());
Log.i("dash scale y ", "= "+dashScaley);
/******/
handPaint = new Paint();
handPaint.setAntiAlias(true);
handPaint.setColor(0xffff0000);
handPaint.setShadowLayer(0.01f, -0.005f, -0.005f, 0x7f000000);
handPaint.setStyle(Paint.Style.FILL);
handPath = new Path();
handPath.moveTo(circlePosx-0.01f, circlePosy+0.06f);
handPath.lineTo(circlePosx, circlePosy - 0.16f);
handPath.lineTo(circlePosx+0.01f, circlePosy + 0.06f);
handPath.addCircle(circlePosx, circlePosy, 0.025f, Path.Direction.CW);
handScrewPaint = new Paint();
handScrewPaint.setAntiAlias(true);
handScrewPaint.setColor(0xf0000021);
handScrewPaint.setStyle(Paint.Style.FILL);
backgroundPaint = new Paint();
backgroundPaint.setFilterBitmap(true);
titlePaint = new Paint();
titlePaint.setColor(0xffffffff);
titlePaint.setAntiAlias(true);
titlePaint.setTypeface(Typeface.DEFAULT_BOLD);
titlePaint.setTextAlign(Paint.Align.CENTER);
titlePaint.setTextSize(0.05f);
titlePaint.setTextScaleX(0.8f);
titlePath = new Path();
titlePath.moveTo(0.5f, 0.8f);
titlePath.lineTo(0.6f, 0.8f);
//titlePath = new Path();
//titlePath.addArc(new RectF(0.24f, 0.24f, 0.76f, 0.76f), -180.0f, -180.0f);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, "Width spec: " + MeasureSpec.toString(widthMeasureSpec));
Log.d(TAG, "Height spec: " + MeasureSpec.toString(heightMeasureSpec));
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
setMeasuredDimension(chosenWidth, chosenHeight);
//int chosenDimension = Math.min(chosenWidth, chosenHeight);
//setMeasuredDimension(chosenDimension, chosenDimension);
}
private int chooseDimension(int mode, int size) {
if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
return size;
} else { // (mode == MeasureSpec.UNSPECIFIED)
return getPreferredSize();
}
}
// in case there is no size specified
private int getPreferredSize() {
return 600;
}
private int nickToDegree(int nick) {
int rawDegree = ((nick < totalNicks / 2) ? nick : (nick - totalNicks)) * 2;
int shiftedDegree = rawDegree + centerDegree;
return shiftedDegree;
}
private float degreeToAngle(float degree) {
return (degree - centerDegree) / 2.0f * degreesPerNick;
}
private void drawInstruments(Canvas canvas){
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(0.5f - dash.getWidth() * dashScalex / 2.0f,
0.5f - dash.getHeight() * dashScaley / 2.0f);
//canvas.drawBitmap(dash, 0, 0, dashPaint);
canvas.drawBitmap(dash, dashMatrix, dashPaint);
canvas.restore();
}
private void drawHand(Canvas canvas) {
Log.e(TAG, "drawHand");
if (handInitialized) {
float handAngle = degreeToAngle(handPosition);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(handAngle, circlePosx, circlePosy);
canvas.drawPath(handPath, handPaint);
canvas.restore();
canvas.drawCircle(0.5f, 0.36f, 0.01f, handScrewPaint);
}
}
private String getTitle(){
return String.valueOf(handPosition);
}
private void drawTitle(Canvas canvas) {
String title = getTitle();
//canvas.drawTextOnPath(title, titlePath, 0.0f,0.0f, titlePaint);
//canvas.drawPaint(titlePaint);
Log.i("drawtitle",title);
canvas.drawText(title, 0.45f, 0.95f, titlePaint);
//invalidate();
}
private void drawBackground(Canvas canvas) {
if (background == null) {
Log.w(TAG, "Background not created");
} else {
canvas.drawBitmap(background, 0, 0, backgroundPaint);
}
}
#Override
protected void onDraw(Canvas canvas) {
Log.e(TAG,"onDraw");
drawBackground(canvas);
float scale = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(scale, scale);
drawTitle(canvas);
drawHand(canvas);
canvas.restore();
if (handNeedsToMove()) {
moveHand();
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Log.d(TAG, "Size changed to " + w + "x" + h);
regenerateBackground();
}
private void regenerateBackground() {
// free the old bitmap
if (background != null) {
background.recycle();
}
Log.e("Width="+getWidth(), "Height="+getHeight());
background = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas backgroundCanvas = new Canvas(background);
float scalex = (float) getWidth();
float scaley = (float) getHeight();
backgroundCanvas.scale(scalex, scaley);
drawInstruments(backgroundCanvas);
}
private boolean handNeedsToMove() {
return Math.abs(handPosition - handTarget) > 0.01f;
}
private void moveHand() {
Log.e(TAG, "moveHand!!");
if (! handNeedsToMove()) {
return;
}
if (lastHandMoveTime != -1L) {
long currentTime = System.currentTimeMillis();
float delta = (currentTime - lastHandMoveTime) / 1000.0f;
float direction = Math.signum(handVelocity);
if (Math.abs(handVelocity) < 90.0f) {
handAcceleration = 5.0f * (handTarget - handPosition);
} else {
handAcceleration = 0.0f;
}
handPosition += handVelocity * delta;
handVelocity += handAcceleration * delta;
if ((handTarget - handPosition) * direction < 0.01f * direction) {
handPosition = handTarget;
handVelocity = 0.0f;
handAcceleration = 0.0f;
lastHandMoveTime = -1L;
} else {
lastHandMoveTime = System.currentTimeMillis();
}
invalidate();
} else {
lastHandMoveTime = System.currentTimeMillis();
moveHand();
}
}
private float getRelativeTemperaturePosition() {
if (handPosition < centerDegree) {
return - (centerDegree - handPosition) / (float) (centerDegree - minDegrees);
} else {
return (handPosition - centerDegree) / (float) (maxDegrees - centerDegree);
}
}
public void setHandTarget(float temperature) {
Log.e(TAG, "setHandTarget!");
if (temperature < minDegrees) {
temperature = minDegrees;
} else if (temperature > maxDegrees) {
temperature = maxDegrees;
}
handTarget = temperature;
Log.e(TAG, "handTarget="+handTarget);
handInitialized = true;
invalidate();
}
public float getHandTarget(){
return handTarget;
}
}
Remove the call to drawTitle() from regenerateBackground() and call invalidate inside your setTitle() method.
I would like to make vertical 3d list view like here, but for ImageButtons and for free. Is there any library or sample code for that? I am new in Android development, so I don't know how to animate such thing
its actually pretty simple. You need to extend ListView and override onDrawChild(). In there you can apply 3d transformation matrices to get the effect you want.
I have a working example on my github
Or you can have a look at this question which is quite similar.
For your convenience this is my implementation of a 3d ListView:
public class ListView3d extends ListView {
/** Ambient light intensity */
private static final int AMBIENT_LIGHT = 55;
/** Diffuse light intensity */
private static final int DIFFUSE_LIGHT = 200;
/** Specular light intensity */
private static final float SPECULAR_LIGHT = 70;
/** Shininess constant */
private static final float SHININESS = 200;
/** The max intensity of the light */
private static final int MAX_INTENSITY = 0xFF;
private final Camera mCamera = new Camera();
private final Matrix mMatrix = new Matrix();
/** Paint object to draw with */
private Paint mPaint;
public ListView3d(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
// get top left coordinates
final int top = child.getTop();
final int left = child.getLeft();
Bitmap bitmap = child.getDrawingCache();
if (bitmap == null) {
child.setDrawingCacheEnabled(true);
child.buildDrawingCache();
bitmap = child.getDrawingCache();
}
final int centerY = child.getHeight() / 2;
final int centerX = child.getWidth() / 2;
final int radius = getHeight() / 2;
final int absParentCenterY = getTop() + getHeight() / 2;
final int absChildCenterY = child.getTop() + centerX;
final int distanceY = absParentCenterY - absChildCenterY;
final int absDistance = Math.min(radius, Math.abs(distanceY));
final float translateZ = (float) Math.sqrt((radius * radius) - (absDistance * absDistance));
double radians = Math.acos((float) absDistance / radius);
double degree = 90 - (180 / Math.PI) * radians;
mCamera.save();
mCamera.translate(0, 0, radius - translateZ);
mCamera.rotateX((float) degree); // remove this line..
if (distanceY < 0) {
degree = 360 - degree;
}
mCamera.rotateY((float) degree); // and change this to rotateX() to get a
// wheel like effect
mCamera.getMatrix(mMatrix);
mCamera.restore();
// create and initialize the paint object
if (mPaint == null) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setFilterBitmap(true);
}
//highlight elements in the middle
mPaint.setColorFilter(calculateLight((float) degree));
mMatrix.preTranslate(-centerX, -centerY);
mMatrix.postTranslate(centerX, centerY);
mMatrix.postTranslate(left, top);
canvas.drawBitmap(bitmap, mMatrix, mPaint);
return false;
}
private LightingColorFilter calculateLight(final float rotation) {
final double cosRotation = Math.cos(Math.PI * rotation / 180);
int intensity = AMBIENT_LIGHT + (int) (DIFFUSE_LIGHT * cosRotation);
int highlightIntensity = (int) (SPECULAR_LIGHT * Math.pow(cosRotation, SHININESS));
if (intensity > MAX_INTENSITY) {
intensity = MAX_INTENSITY;
}
if (highlightIntensity > MAX_INTENSITY) {
highlightIntensity = MAX_INTENSITY;
}
final int light = Color.rgb(intensity, intensity, intensity);
final int highlight = Color.rgb(highlightIntensity, highlightIntensity, highlightIntensity);
return new LightingColorFilter(light, highlight);
}
}
Cheers