Gambar 1. Sample Chart HoloGraphLibrary |
Unduh library dan sample melalui link diatas
1. Ekstrak hasil unduhan
Gambar 2. Esktrak hasil unduhan |
2. buka file .java library direktorinya seperti pada Gambar 3.
Gambar 3. File .java library |
Gambar 4. Copy file .java ke Android Studio |
Gambar 5, Error pada PieGraph.java |
5. Copy source code sample ke ActivtyMain. Source code sample dapat diperoleh dari direktori seperti Gambar 6. Tutorial ini hanya akan menggunakan sample PieFragment. Java sesuai dengan chart yang akan dibuat PieGraph.
Gambar 6. Souce code .java sample |
6. Kita akan mengubah source code PieFragment.java menjadi MainActivity.java
tanpa pengguna fragment sehingga ubah source code seperti gambar Gambar 7 menjadi Gambar 8 (sesuaikan source code yang tadinya untuk fragment menjadi
untuk activity)
Gambar 7. PieFragment.java di-copy ke MainActivity.java |
Gambar 8. PieFragment.java yang sudah di-copy dan disesuaikan pada MainActivity.java |
Gambar 9. dimens,xml dan color,xml |
source code full (sumber:https://bitbucket.org/danielnadeau/holographlibrary/src dengan sedikit penyesuaian ) :
MainActivity.java
import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.Activity; import android.content.res.Resources; import android.os.Build; import android.support.v7.app.AppCompatActivity; import android.view.animation.*; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.SeekBar; import android.widget.Toast; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final Resources resources = getResources(); final PieGraph pg = (PieGraph) findViewById(R.id.piegraph); final Button animateButton = (Button) findViewById(R.id.animatePieButton); PieSlice slice = new PieSlice(); slice.setColor(resources.getColor(R.color.green_light)); slice.setSelectedColor(resources.getColor(R.color.transparent_orange)); slice.setValue(2); slice.setTitle("first"); pg.addSlice(slice); slice = new PieSlice(); slice.setColor(resources.getColor(R.color.orange)); slice.setValue(3); pg.addSlice(slice); slice = new PieSlice(); slice.setColor(resources.getColor(R.color.purple)); slice.setValue(8); pg.addSlice(slice); pg.setOnSliceClickedListener(new PieGraph.OnSliceClickedListener() { @Override public void onClick(int index) { Toast.makeText(MainActivity.this, "Slice " + index + " clicked", Toast.LENGTH_SHORT) .show(); } }); Bitmap b = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); pg.setBackgroundBitmap(b); SeekBar seekBar = (SeekBar) findViewById(R.id.seekBarRatio); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { pg.setInnerCircleRatio(progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); seekBar = (SeekBar) findViewById(R.id.seekBarPadding); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { pg.setPadding(progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) animateButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for (PieSlice s : pg.getSlices()) s.setGoalValue((float)Math.random() * 10); pg.setDuration(1000);//default if unspecified is 300 ms pg.setInterpolator(new AccelerateDecelerateInterpolator());//default if unspecified is linear; constant speed pg.setAnimationListener(getAnimationListener()); pg.animateToGoalValues();//animation will always overwrite. Pass true to call the onAnimationCancel Listener with onAnimationEnd } }); } @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) public Animator.AnimatorListener getAnimationListener(){ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) return new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { Log.d("piefrag", "anim end"); } @Override public void onAnimationCancel(Animator animation) {//you might want to call slice.setvalue(slice.getGoalValue) Log.d("piefrag", "anim cancel"); } @Override public void onAnimationRepeat(Animator animation) { } }; else return null; } }
HoloGraphAnimate.java
import android.animation.Animator; import android.view.animation.Interpolator; import android.animation.Animator; import android.view.animation.Interpolator; /** * Created by DouglasW on 6/8/2014. */ public interface HoloGraphAnimate { final int ANIMATE_NORMAL = 0; final int ANIMATE_INSERT = 1; final int ANIMATE_DELETE = 2; int getDuration(); void setDuration(int duration); Interpolator getInterpolator(); void setInterpolator(Interpolator interpolator); boolean isAnimating(); boolean cancelAnimating(); void animateToGoalValues(); void setAnimationListener(Animator.AnimatorListener animationListener); }
PieGraph.java
import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.graphics.RectF; import android.graphics.Region; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import java.util.ArrayList; public class PieGraph extends View implements HoloGraphAnimate { private int mPadding; private int mInnerCircleRatio; private ArrayList<PieSlice> mSlices = new ArrayList<PieSlice>(); private Paint mPaint = new Paint(); private int mSelectedIndex = -1; private OnSliceClickedListener mListener; private boolean mDrawCompleted = false; private RectF mRectF = new RectF(); private Bitmap mBackgroundImage = null; private Point mBackgroundImageAnchor = new Point(0,0); private boolean mBackgroundImageCenter = false; private int mDuration = 300;//in ms private Interpolator mInterpolator; private Animator.AnimatorListener mAnimationListener; private ValueAnimator mValueAnimator; public PieGraph(Context context) { this(context, null); } public PieGraph(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PieGraph(Context context, AttributeSet attrs, int defStyle) { super(context, attrs); } public void onDraw(Canvas canvas) { float midX, midY, radius, innerRadius; canvas.drawColor(Color.TRANSPARENT); mPaint.reset(); mPaint.setAntiAlias(true); if(mBackgroundImage != null) { if(mBackgroundImageCenter) mBackgroundImageAnchor.set( getWidth() / 2 - mBackgroundImage.getWidth() / 2, getHeight() / 2 - mBackgroundImage.getHeight() / 2 ); canvas.drawBitmap(mBackgroundImage, mBackgroundImageAnchor.x, mBackgroundImageAnchor.y, mPaint); } float currentAngle = 270; float currentSweep = 0; float totalValue = 0; midX = getWidth() / 2; midY = getHeight() / 2; if (midX < midY) { radius = midX; } else { radius = midY; } radius -= mPadding; innerRadius = radius * mInnerCircleRatio / 255; for (PieSlice slice : mSlices) { totalValue += slice.getValue(); } int count = 0; for (PieSlice slice : mSlices) { Path p = slice.getPath(); p.reset(); if (mSelectedIndex == count && mListener != null) { mPaint.setColor(slice.getSelectedColor()); } else { mPaint.setColor(slice.getColor()); } currentSweep = (slice.getValue() / totalValue) * (360); mRectF.set(midX - radius, midY - radius, midX + radius, midY + radius); createArc(p, mRectF, currentSweep, currentAngle + mPadding, currentSweep - mPadding); mRectF.set(midX - innerRadius, midY - innerRadius, midX + innerRadius, midY + innerRadius); createArc(p, mRectF, currentSweep, (currentAngle + mPadding) + (currentSweep - mPadding), -(currentSweep - mPadding)); p.close(); // Create selection region Region r = slice.getRegion(); r.set((int) (midX - radius), (int) (midY - radius), (int) (midX + radius), (int) (midY + radius)); canvas.drawPath(p, mPaint); currentAngle = currentAngle + currentSweep; count++; } mDrawCompleted = true; } private void createArc(Path p, RectF mRectF, float currentSweep, float startAngle, float sweepAngle) { if (currentSweep == 360) { p.addArc(mRectF, startAngle, sweepAngle); } else { p.arcTo(mRectF, startAngle, sweepAngle); } } @Override public boolean onTouchEvent(MotionEvent event) { if (mDrawCompleted) { Point point = new Point(); point.x = (int) event.getX(); point.y = (int) event.getY(); int count = 0; Region r = new Region(); for (PieSlice slice : mSlices) { r.setPath(slice.getPath(), slice.getRegion()); switch (event.getAction()) { default: break; case MotionEvent.ACTION_DOWN: if (r.contains(point.x, point.y)) { mSelectedIndex = count; postInvalidate(); } break; case MotionEvent.ACTION_UP: if (count == mSelectedIndex && mListener != null && r.contains(point.x, point.y)) { mListener.onClick(mSelectedIndex); } break; } count++; } } // Case we click somewhere else, also get feedback! if(MotionEvent.ACTION_UP == event.getAction() && mSelectedIndex == -1 && mListener != null) { mListener.onClick(mSelectedIndex); } // Reset selection if (MotionEvent.ACTION_UP == event.getAction() || MotionEvent.ACTION_CANCEL == event.getAction()) { mSelectedIndex = -1; postInvalidate(); } return true; } public Bitmap getBackgroundBitmap() { return mBackgroundImage; } public void setBackgroundBitmap(Bitmap backgroundBitmap, int pos_x, int pos_y) { mBackgroundImage = backgroundBitmap; mBackgroundImageAnchor.set(pos_x, pos_y); postInvalidate(); } public void setBackgroundBitmap(Bitmap backgroundBitmap) { mBackgroundImageCenter = true; mBackgroundImage = backgroundBitmap; postInvalidate(); } /** * sets padding * @param padding */ public void setPadding(int padding) { mPadding = padding; postInvalidate(); } public void setInnerCircleRatio(int innerCircleRatio) { mInnerCircleRatio = innerCircleRatio; postInvalidate(); } public ArrayList<PieSlice> getSlices() { return mSlices; } public void setSlices(ArrayList<PieSlice> slices) { mSlices = slices; postInvalidate(); } public PieSlice getSlice(int index) { return mSlices.get(index); } public void addSlice(PieSlice slice) { mSlices.add(slice); postInvalidate(); } public void setOnSliceClickedListener(OnSliceClickedListener listener) { mListener = listener; } public void removeSlices() { mSlices.clear(); postInvalidate(); } @Override public int getDuration() { return mDuration; } @Override public void setDuration(int duration) {mDuration = duration;} @Override public Interpolator getInterpolator() { return mInterpolator; } @Override public void setInterpolator(Interpolator interpolator) {mInterpolator = interpolator;} @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) @Override public boolean isAnimating() { if(mValueAnimator != null) return mValueAnimator.isRunning(); return false; } @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) @Override public boolean cancelAnimating() { if (mValueAnimator != null) mValueAnimator.cancel(); return false; } /** * Stops running animation and starts a new one, animating each slice from their current to goal value. * If removing a slice, consider animating to 0 then removing in onAnimationEnd listener. * Default inerpolator is linear; constant speed. */ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) @Override public void animateToGoalValues() { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1){ Log.e("HoloGraphLibrary compatibility error", "Animation not supported on api level 12 and below. Returning without animating."); return; } if (mValueAnimator != null) mValueAnimator.cancel(); for (PieSlice s : mSlices) s.setOldValue(s.getValue()); ValueAnimator va = ValueAnimator.ofFloat(0,1); mValueAnimator = va; va.setDuration(getDuration()); if (mInterpolator == null) mInterpolator = new LinearInterpolator(); va.setInterpolator(mInterpolator); if (mAnimationListener != null) va.addListener(mAnimationListener); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = Math.max(animation.getAnimatedFraction(), 0.01f);//avoid blank frames; never multiply values by 0 // Log.d("f", String.valueOf(f)); for (PieSlice s : mSlices) { float x = s.getGoalValue() - s.getOldValue(); s.setValue(s.getOldValue() + (x * f)); } postInvalidate(); }}); va.start(); } @Override public void setAnimationListener(Animator.AnimatorListener animationListener) { mAnimationListener = animationListener;} public interface OnSliceClickedListener { public abstract void onClick(int index); } }
PieSlice.java
import android.graphics.Path; import android.graphics.Region; public class PieSlice { private final Path mPath = new Path(); private final Region mRegion = new Region(); private int mColor = 0xFF33B5E5; private int mSelectedColor = -1; private float mValue; private float mOldValue; private float mGoalValue; private String mTitle; public String getTitle() { return mTitle; } public void setTitle(String title) { mTitle = title; } public int getColor() { return mColor; } public void setColor(int color) { mColor = color; } public int getSelectedColor() { if (-1 == mSelectedColor) mSelectedColor = Utils.darkenColor(mColor); return mSelectedColor; } public void setSelectedColor(int selectedColor) { mSelectedColor = selectedColor; } public float getValue() { return mValue; } public void setValue(float value) { mValue = value; } public float getOldValue() { return mOldValue; } public void setOldValue(float oldValue) { mOldValue = oldValue; } public float getGoalValue() { return mGoalValue; } public void setGoalValue(float goalValue) { mGoalValue = goalValue; } public Path getPath() { return mPath; } public Region getRegion() { return mRegion; } }
Utils.java
import android.graphics.Color; /** * Created by sbaiget on 11/04/2014. */ public class Utils { public static int darkenColor(int color) { float[] hsv = new float[3]; Color.colorToHSV(color, hsv); hsv[2] *= 0.8f; return Color.HSVToColor(hsv); } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <id.oddsaydev.graphcoba.PieGraph android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:layout_margin="@dimen/default_margin" android:id="@+id/piegraph" /> <LinearLayout android:orientation="horizontal" android:layout_margin="@dimen/default_margin" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="0dip" android:layout_height="match_parent" android:gravity="center_vertical" android:text="Inner Circle Ratio" android:layout_weight="1"/> <SeekBar android:layout_width="0dip" android:layout_height="wrap_content" android:progress="128" android:max="240" android:id="@+id/seekBarRatio" android:layout_weight="1"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_margin="@dimen/default_margin" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="0dip" android:layout_height="match_parent" android:gravity="center_vertical" android:text="Padding" android:layout_weight="1"/> <SeekBar android:layout_width="0dip" android:layout_height="wrap_content" android:progress="0" android:max="10" android:id="@+id/seekBarPadding" android:layout_weight="1"/> </LinearLayout> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Animate to random values" android:id="@+id/animatePieButton" android:layout_weight="0"/> </LinearLayout>
colors.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="red">#FFFF0000</color> <color name="blue">#FF0000FF</color> <color name="transparent_blue">#800000FF</color> <color name="green">#FF00FF00</color> <color name="green_light">#FF99CC00</color> <color name="orange">#FFFFBB33</color> <color name="transparent_orange">#80FFBB33</color> <color name="purple">#FFAA66CC</color> </resources>
dimens.xml
<resources> <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="default_margin">12dip</dimen> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> </resources>
Sekian Trimakasih..
buat chart tipe lain gan..
ReplyDeletesample tinggal dicopas2 kok hehe..
ReplyDelete